1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1997-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  *
20  */
21 
22 /* An exception from using eidef.h, use config.h directly */
23 #include "config.h"
24 
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 
29 #ifdef __WIN32__
30 #include <winsock2.h>
31 #include <windows.h>
32 #include <winbase.h>
33 
34 #elif VXWORKS
35 #include <stdio.h>
36 #include <string.h>
37 #include <vxWorks.h>
38 #include <hostLib.h>
39 #include <selectLib.h>
40 #include <ifLib.h>
41 #include <sockLib.h>
42 #include <taskLib.h>
43 #include <inetLib.h>
44 #include <unistd.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <netinet/tcp.h>
48 #include <symLib.h>
49 #include <sysSymTbl.h>
50 #include <sysLib.h>
51 #include <tickLib.h>
52 
53 #if TIME_WITH_SYS_TIME
54 # include <sys/time.h>
55 # include <time.h>
56 #else
57 # if HAVE_SYS_TIME_H
58 #  include <sys/time.h>
59 # else
60 #  include <time.h>
61 # endif
62 #endif
63 
64 #include <a_out.h>
65 
66 /* #include "netdb.h" */
67 #else /* other unix */
68 #include <errno.h>
69 #include <netdb.h>
70 #include <netinet/in.h>
71 #include <arpa/inet.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <string.h>
75 #include <sys/param.h>
76 #include <sys/socket.h>
77 #include <sys/stat.h>
78 #include <sys/wait.h>
79 #include <sys/time.h>
80 #include <time.h>
81 #include <unistd.h>
82 #include <sys/types.h>
83 #include <signal.h>
84 #endif
85 
86 #include "ei.h"
87 #include "ei_resolve.h"
88 #include "erl_start.h"
89 
90 /* FIXME is this a case a vfork can be used? */
91 #if !HAVE_WORKING_VFORK
92 # define vfork fork
93 #endif
94 
95 #ifndef MAXPATHLEN
96 #define MAXPATHLEN 1024
97 #endif
98 
99 #ifndef RSH
100 #define RSH "/usr/bin/ssh"
101 #endif
102 
103 #ifndef HAVE_SOCKLEN_T
104 typedef int SocklenType;
105 #else
106 typedef socklen_t SocklenType;
107 #endif
108 
109 /* FIXME check errors from malloc */
110 
111 static struct in_addr *get_addr(const char *hostname, struct in_addr *oaddr);
112 
113 static int wait_for_erlang(int sockd, int magic, struct timeval *timeout);
114 #if defined(VXWORKS) || defined(__WIN32__)
115 static int unique_id(void);
116 static unsigned long spawn_erlang_epmd(ei_cnode *ec,
117 				       char *alive,
118 				       Erl_IpAddr adr,
119 				       int flags,
120 				       char *erl_or_epmd,
121 				       char *args[],
122 				       int port,
123 				       int is_erlang);
124 #else
125 static int exec_erlang(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
126 		       char *erl, char *args[],int port);
127 #endif
128 /* Start an Erlang node. return value 0 indicates that node was
129  * started successfully, negative values indicate error.
130  *
131  * node -  the name of the remote node to start (alivename@hostname).
132  * flags - turn on or off certain options. See erl_start.h for a list.
133  * erl -  is the name of the erl script to call. If NULL, the default
134  * name "erl" will be used.
135  * args - a NULL-terminated list of strings containing
136  * additional arguments to be sent to the remote Erlang node. These
137  * strings are simply appended to the end of the command line, so any
138  * quoting of special characters, etc must be done by the caller.
139  * There may be some conflicts between some of these arguments and the
140  * default arguments hard-coded into this function, so be careful.
141  */
erl_start_sys(ei_cnode * ec,char * alive,Erl_IpAddr adr,int flags,char * erl,char * args[])142 int erl_start_sys(ei_cnode *ec, char *alive, Erl_IpAddr adr, int flags,
143 		  char *erl, char *args[])
144 {
145   struct timeval timeout;
146   struct sockaddr_in addr;
147   SocklenType namelen;
148   int port;
149   int sockd = 0;
150   int one = 1;
151 #if defined(VXWORKS) || defined(__WIN32__)
152   unsigned long pid = 0;
153 #else
154   int pid = 0;
155 #endif
156   int r = 0;
157 
158   if (((sockd = socket(AF_INET, SOCK_STREAM, 0)) < 0) ||
159       (setsockopt(sockd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0)) {
160     r = ERL_SYS_ERROR;
161     goto done;
162   }
163 
164   memset(&addr,0,sizeof(addr));
165   addr.sin_family = AF_INET;
166   addr.sin_addr.s_addr = htonl(INADDR_ANY);
167   addr.sin_port = 0;
168 
169   if (bind(sockd,(struct sockaddr *)&addr,sizeof(addr))<0) {
170       return ERL_SYS_ERROR;
171   }
172   namelen = sizeof(addr);
173   if (getsockname(sockd,(struct sockaddr *)&addr,&namelen)<0) {
174       return ERL_SYS_ERROR;
175   }
176   port = ntohs(addr.sin_port);
177 
178   listen(sockd,5);
179 
180 #if defined(VXWORKS) || defined(__WIN32__)
181   if((pid = spawn_erlang_epmd(ec,alive,adr,flags,erl,args,port,1))
182       == 0)
183      return ERL_SYS_ERROR;
184   timeout.tv_usec = 0;
185   timeout.tv_sec = 10; /* ignoring ERL_START_TIME */
186   if((r = wait_for_erlang(sockd,unique_id(),&timeout))
187      == ERL_TIMEOUT) {
188 #if defined(VXWORKS)
189       taskDelete((int) pid);
190       if(taskIdVerify((int) pid) != ERROR)
191 	  taskDeleteForce((int) pid);
192 #else /* Windows */
193       /* Well, this is not a nice way to do it, and it does not
194 	 always kill the emulator, but the alternatives are few.*/
195       TerminateProcess((HANDLE) pid,1);
196 #endif /* defined(VXWORKS) */
197   }
198 #else /* Unix */
199   switch ((pid = fork())) {
200   case -1:
201     r = ERL_SYS_ERROR;
202     break;
203 
204   case 0:
205     /* child - start the erlang node */
206     exec_erlang(ec, alive, adr, flags, erl, args, port);
207 
208     /* error if reached - parent reports back to caller after timeout
209        so we just exit here */
210     exit(1);
211     break;
212 
213   default:
214 
215     /* parent - waits for response from Erlang node */
216     /* child pid used here as magic number */
217     timeout.tv_usec = 0;
218     timeout.tv_sec = 10; /* ignoring ERL_START_TIME */
219     if ((r = wait_for_erlang(sockd,pid,&timeout)) == ERL_TIMEOUT) {
220       /* kill child if no response */
221       kill(pid,SIGINT);
222       sleep(1);
223       if (waitpid(pid,NULL,WNOHANG) != pid) {
224 	/* no luck - try harder */
225 	kill(pid,SIGKILL);
226 	sleep(1);
227 	waitpid(pid,NULL,WNOHANG);
228       }
229     }
230 
231   }
232 #endif /* defined(VXWORKS) || defined(__WIN32__) */
233 
234 done:
235 #if defined(__WIN32__)
236   if (sockd) closesocket(sockd);
237 #else
238   if (sockd) close(sockd);
239 #endif
240   return r;
241 } /* erl_start_sys() */
242 
243 #if defined(VXWORKS) || defined(__WIN32__)
244 #if defined(VXWORKS)
245 #define DEF_ERL_COMMAND ""
246 #define DEF_EPMD_COMMAND ""
247 #define ERLANG_SYM "start_erl"
248 #define EPMD_SYM "start_epmd"
249 #define ERL_REPLY_FMT   "-s erl_reply reply %s %d %d"
250 #else
251 #define DEF_ERL_COMMAND "erl"
252 #define DEF_EPMD_COMMAND "epmd"
253 #define ERL_REPLY_FMT   "-s erl_reply reply \"%s\" \"%d\" \"%d\""
254 #endif
255 #define ERL_NAME_FMT    "-noinput -name %s"
256 #define ERL_SNAME_FMT   "-noinput -sname %s"
257 
258 #define IP_ADDR_CHARS 15
259 #define FORMATTED_INT_LEN 10
260 
unique_id(void)261 static int unique_id(void){
262 #if defined(VXWORKS)
263     return taskIdSelf();
264 #else
265     return (int) GetCurrentThreadId();
266 #endif
267 }
268 
enquote_args(char ** oargs,char *** qargs)269 static int enquote_args(char **oargs, char ***qargs){
270     char **args;
271     int len;
272     int i;
273     int qwhole;
274     int extra;
275     char *ptr;
276     char *ptr2;
277 
278     if(oargs == NULL){
279 	*qargs = malloc(sizeof(char *));
280 	**qargs = NULL;
281 	return 0;
282     };
283 
284     for(len=0;oargs[len] != NULL; ++len)
285 	;
286     args = malloc(sizeof(char *) * (len + 1));
287 
288     for(i = 0; i < len; ++i){
289 	qwhole = strchr(oargs[i],' ') != NULL;
290 	extra = qwhole * 2;
291 	for(ptr = oargs[i]; *ptr != '\0'; ++ptr)
292 	    extra += (*ptr == '"');
293 	args[i] = malloc(strlen(oargs[i]) +
294 			     extra +
295 			     1);
296 	ptr2 = args[i];
297 	if(qwhole)
298 	    *(ptr2++) = '"';
299 	for(ptr = oargs[i]; *ptr != '\0'; ++ptr){
300 	    if(*ptr == '"')
301 		*(ptr2++) = '\\';
302 	    *(ptr2++) = *ptr;
303 	}
304 	if(qwhole)
305 	    *(ptr2++) = '"';
306 	*ptr2 = '\0';
307     }
308     args[len] = NULL;
309     *qargs = args;
310     return len;
311 }
312 
free_args(char ** args)313 static void free_args(char **args){
314     char **ptr = args;
315     while(*ptr != NULL)
316 	free(*(ptr++));
317     free(args);
318 }
319 
320 #if defined(VXWORKS)
lookup_function(char * symname)321 static  FUNCPTR lookup_function(char *symname){
322     char *value;
323     SYM_TYPE type;
324     if(symFindByName(sysSymTbl,
325 		     symname,
326 		     &value,
327 		     &type) == ERROR /*|| type != N_TEXT*/)
328 	return NULL;
329     return (FUNCPTR) value;
330 }
331 #endif /* defined(VXWORKS) */
332 
333 /* In NT and VxWorks, we cannot fork(), Erlang and Epmd gets
334    spawned by this function instead. */
335 
spawn_erlang_epmd(ei_cnode * ec,char * alive,Erl_IpAddr adr,int flags,char * erl_or_epmd,char * args[],int port,int is_erlang)336 static unsigned long spawn_erlang_epmd(ei_cnode *ec,
337 				       char *alive,
338 				       Erl_IpAddr adr,
339 				       int flags,
340 				       char *erl_or_epmd,
341 				       char *args[],
342 				       int port,
343 				       int is_erlang)
344 {
345 #if defined(VXWORKS)
346     FUNCPTR erlfunc;
347 #else /* Windows */
348     STARTUPINFO sinfo;
349     SECURITY_ATTRIBUTES sa;
350     PROCESS_INFORMATION pinfo;
351 #endif
352     char *cmdbuf;
353     int cmdlen;
354     char *ptr;
355     int i;
356     int num_args;
357     char *name_format;
358     struct in_addr myaddr;
359     struct in_addr *hisaddr = (struct in_addr *)adr;
360     char iaddrbuf[IP_ADDR_CHARS + 1];
361     int ret;
362 
363     if(is_erlang){
364 	get_addr(ei_thishostname(ec), &myaddr);
365 #if defined(VXWORKS)
366         inet_ntoa_b(myaddr, iaddrbuf);
367 #else /* Windows */
368 	if((ptr = inet_ntoa(myaddr)) == NULL)
369 	    return 0;
370 	else
371 	    strcpy(iaddrbuf,ptr);
372 #endif
373     }
374     if ((flags & ERL_START_REMOTE) ||
375 	(is_erlang && (hisaddr->s_addr != myaddr.s_addr))) {
376 	return 0;
377     } else {
378 	num_args = enquote_args(args, &args);
379 	for(cmdlen = i = 0; args[i] != NULL; ++i)
380 	    cmdlen += strlen(args[i]) + 1;
381 #if !defined(VXWORKS)
382 	/* On VxWorks, we dont actually run a command,
383 	   we call start_erl() */
384 	if(!erl_or_epmd)
385 #endif
386 	    erl_or_epmd = (is_erlang) ? DEF_ERL_COMMAND :
387 	    DEF_EPMD_COMMAND;
388 	if(is_erlang){
389 	    name_format = (flags & ERL_START_LONG) ? ERL_NAME_FMT :
390 		ERL_SNAME_FMT;
391 	    cmdlen +=
392 		strlen(erl_or_epmd) + (*erl_or_epmd != '\0') +
393 		strlen(name_format) + 1 + strlen(alive) +
394 		strlen(ERL_REPLY_FMT) + 1 + strlen(iaddrbuf) + 2 * FORMATTED_INT_LEN + 1;
395 	    ptr = cmdbuf = malloc(cmdlen);
396 	    if(*erl_or_epmd != '\0')
397 		ptr += sprintf(ptr,"%s ",erl_or_epmd);
398 	    ptr += sprintf(ptr, name_format,
399 			   alive);
400 	    ptr += sprintf(ptr, " " ERL_REPLY_FMT,
401 		       iaddrbuf, port, unique_id());
402 	} else { /* epmd */
403 	    cmdlen += strlen(erl_or_epmd) + (*erl_or_epmd != '\0') + 1;
404 	    ptr = cmdbuf = malloc(cmdlen);
405 	    if(*erl_or_epmd != '\0')
406 		ptr += sprintf(ptr,"%s ",erl_or_epmd);
407 	    else
408 		*(ptr++) = '\0';
409 	}
410 	for(i= 0; args[i] != NULL; ++i){
411 	    *(ptr++) = ' ';
412 	    strcpy(ptr,args[i]);
413 	    ptr += strlen(args[i]);
414 	}
415 	free_args(args);
416 	if (flags & ERL_START_VERBOSE) {
417 	    fprintf(stderr,"erl_call: commands are %s\n",cmdbuf);
418 	}
419 	/* OK, one single command line... */
420 #if defined(VXWORKS)
421 	erlfunc = lookup_function((is_erlang) ? ERLANG_SYM :
422 				  EPMD_SYM);
423 	if(erlfunc == NULL){
424 	    if (flags & ERL_START_VERBOSE) {
425 		fprintf(stderr,"erl_call: failed to find symbol %s\n",
426 			(is_erlang) ? ERLANG_SYM : EPMD_SYM);
427 	    }
428 	    ret = 0;
429 	} else {
430 	/* Just call it, it spawns itself... */
431 	    ret = (unsigned long)
432 		(*erlfunc)((int) cmdbuf,0,0,0,0,0,0,0,0,0);
433 	    if(ret == (unsigned long) ERROR)
434 		ret = 0;
435 	}
436 #else /* Windows */
437 	/* Hmmm, hidden or unhidden window??? */
438 	memset(&sinfo,0,sizeof(sinfo));
439 	sinfo.cb = sizeof(STARTUPINFO);
440 	sinfo.dwFlags = STARTF_USESHOWWINDOW /*|
441 	    STARTF_USESTDHANDLES*/;
442 	sinfo.wShowWindow = SW_HIDE; /* Hidden! */
443 	sinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
444 	sinfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
445 	sinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
446 	sa.nLength = sizeof(sa);
447 	sa.lpSecurityDescriptor = NULL;
448 	sa.bInheritHandle = /*TRUE*/ FALSE;
449 	if(!CreateProcess(
450 			  NULL,
451 			  cmdbuf,
452 			  &sa,
453 			  NULL,
454 			  /*TRUE*/ FALSE,
455 			  0 | CREATE_NEW_CONSOLE,
456 			  NULL,
457 			  NULL,
458 			  &sinfo,
459 			  &pinfo))
460 	    ret = 0;
461 	else
462 	    ret = (unsigned long) pinfo.hProcess;
463 #endif
464 	free(cmdbuf);
465 	return ret;
466     }
467     /* NOTREACHED */
468 }
469 #else /* Unix */
470 
471 /* call this from the child process to start an erlang system. This
472  * function just builds the erlang command line and then calls it.
473  *
474  * node - the nodename for the new node
475  * flags - various options that can be set (see erl_start.h)
476  * erl - name of the erlang executable, or NULL for default ("erl")
477  * args - additional arguments to pass to erlang executable
478  * port - the port number where we wait for acknowledgment from the enode
479  *
480  * we have a potential problem if args conflicts with any of the
481  * arguments we use here.
482  */
exec_erlang(ei_cnode * ec,char * alive,Erl_IpAddr adr,int flags,char * erl,char * args[],int port)483 static int exec_erlang(ei_cnode *ec,
484 		       char *alive,
485 		       Erl_IpAddr adr,
486 		       int flags,
487 		       char *erl,
488 		       char *args[],
489 		       int port)
490 {
491 #if !defined(__WIN32__) && !defined(VXWORKS)
492   int fd,len,l,i;
493   char **s;
494   char *argv[4];
495   char argbuf[BUFSIZ];
496   struct in_addr myaddr;
497   struct in_addr *hisaddr = (struct in_addr *)adr;
498 
499   if (!get_addr(ei_thishostname(ec), &myaddr)) {
500       fprintf(stderr,"erl_call: failed to find hostname\r\n");
501       return ERL_SYS_ERROR;
502   }
503 
504   /* on this host? */
505   /* compare ip addresses, unless forced by flag setting to use rsh */
506   if ((flags & ERL_START_REMOTE) || (hisaddr->s_addr != myaddr.s_addr)) {
507     argv[0] = RSH;
508     len = strlen(inet_ntoa(*hisaddr));
509     argv[1] = malloc(len+1);
510     strcpy(argv[1],inet_ntoa(*hisaddr));
511   }
512   else {
513   /* Yes - use sh to start local Erlang */
514     argv[0] = "sh";
515     argv[1] = "-c";
516   }
517   argv[2] = argbuf;
518   argv[3] = NULL;
519 
520   len = 0;
521   *argbuf=(char)0;
522 
523   sprintf(argbuf,"exec %s ", (erl? erl: "erl"));
524   len = strlen(argbuf);
525 
526   /* *must* be noinput or node (seems to) hang... */
527   /* long or short names? */
528   sprintf(&argbuf[len], "-noinput %s %s ",
529 	  ((flags & ERL_START_LONG) ? "-name" : "-sname"),
530 	  alive);
531   len = strlen(argbuf);
532 
533   /* now make the new node report back when it's ready */
534   /* add: myip, myport and replymsg */
535   sprintf(&argbuf[len],
536 	  "-s erl_reply reply %s %d %d ",
537 	  inet_ntoa(myaddr),port,(int)getpid());
538 #ifdef DEBUG
539   fprintf(stderr,"erl_call: debug %s\n",&argbuf[len]);
540 #endif
541   len = strlen(argbuf);
542 
543   /* additional arguments to be passed to the other system */
544   /* make sure that they will fit first */
545   for (l=0, s = args; s && *s; s++) l+= strlen(*s) + 1;
546 
547   if (len + l + 1 > BUFSIZ) return ERL_BADARG;
548   else {
549     for (s = args; s && *s; s++) {
550       strcat(argbuf," ");
551       strcat(argbuf,*s);
552     }
553     len += l + 1;
554   }
555 
556   if (flags & ERL_START_VERBOSE) {
557     fprintf(stderr,"erl_call: %s %s %s\n",argv[0],argv[1],argv[2]);
558   }
559 
560   /* close all descriptors in child */
561   for (i=0; i<64; i++) close(i);
562 
563   /* debug output to file? */
564   if (flags & ERL_START_DEBUG) {
565     char debugfile[MAXPATHLEN+1];
566     char *home=getenv("HOME");
567     sprintf(debugfile,"%s/%s.%s",home,ERL_START_LOGFILE,alive);
568     if ((fd=open(debugfile, O_WRONLY | O_CREAT | O_APPEND, 0644)) >= 0) {
569       time_t t = time(NULL);
570       dup2(fd,1);
571       dup2(fd,2);
572       fprintf(stderr,"\n\n===== Log started ======\n%s \n",ctime(&t));
573       fprintf(stderr,"erl_call: %s %s %s\n",argv[0],argv[1],argv[2]);
574     }
575   }
576 
577   /* start the system */
578   execvp(argv[0], argv);
579 
580   if (flags & ERL_START_DEBUG) {
581     fprintf(stderr,"erl_call: exec failed: (%d) %s %s %s\n",
582 	    errno,argv[0],argv[1],argv[2]);
583   }
584 
585 #endif
586   /* (hopefully) NOT REACHED */
587   return ERL_SYS_ERROR;
588 } /* exec_erlang() */
589 
590 #endif /* defined(VXWORKS) || defined(WINDOWS) */
591 
592 #if defined(__WIN32__)
gettimeofday(struct timeval * now,void * dummy)593 static void gettimeofday(struct timeval *now,void *dummy){
594     SYSTEMTIME systime;
595 	FILETIME ft;
596     DWORD x;
597     GetSystemTime(&systime);
598     SystemTimeToFileTime(&systime,&ft);
599     x = ft.dwLowDateTime / 10;
600     now->tv_sec = x / 1000000;
601     now->tv_usec = x % 1000000;
602 }
603 
604 #elif defined(VXWORKS)
gettimeofday(struct timeval * now,void * dummy)605 static void gettimeofday(struct timeval *now, void *dummy){
606     int rate = sysClkRateGet(); /* Ticks per second */
607     unsigned long ctick = tickGet();
608     now->tv_sec = ctick / rate; /* secs since reboot */
609     now->tv_usec = ((ctick - (now->tv_sec * rate))*1000000)/rate;
610 }
611 #endif
612 
613 
614 /* wait for the remote system to reply */
615 /*
616  * sockd - an open socket where we expect a connection from the e-node
617  * magic - sign on message the e-node must provide for verification
618  * timeout - how long to wait before returning failure
619  *
620  * OBS: the socket is blocking, and there is a potential deadlock if we
621  * get an accept but the peer sends no data (and does not close).
622  * in normal cases the timeout will work ok however, i.e. either we
623  * never get any connection, or we get connection then close().
624  */
wait_for_erlang(int sockd,int magic,struct timeval * timeout)625 static int wait_for_erlang(int sockd, int magic, struct timeval *timeout)
626 {
627   struct timeval to;
628   struct timeval stop_time;
629   struct timeval now;
630   fd_set rdset;
631   int fd;
632   int n,i;
633   char buf[16];
634   struct sockaddr_in peer;
635   SocklenType len = (SocklenType) sizeof(peer);
636 
637   /* determine when we should exit this function */
638   gettimeofday(&now,NULL);
639   stop_time.tv_sec = now.tv_sec + timeout->tv_sec;
640   stop_time.tv_usec = now.tv_usec + timeout->tv_usec;
641   while (stop_time.tv_usec > 1000000) {
642     stop_time.tv_sec++;
643     stop_time.tv_usec -= 1000000;
644   }
645 
646 #ifdef DEBUG
647   fprintf(stderr,"erl_call: debug time is %ld.%06ld, "
648 	  "will timeout at %ld.%06ld\n",
649 	  now.tv_sec,now.tv_usec,stop_time.tv_sec,stop_time.tv_usec);
650 #endif
651 
652   while (1) {
653     FD_ZERO(&rdset);
654     FD_SET(sockd,&rdset);
655 
656     /* adjust the timeout to (stoptime - now) */
657     gettimeofday(&now,NULL);
658     to.tv_sec = stop_time.tv_sec - now.tv_sec;
659     to.tv_usec = stop_time.tv_usec - now.tv_usec;
660     while ((to.tv_usec < 0) && (to.tv_sec > 0)) {
661       to.tv_usec += 1000000;
662       to.tv_sec--;
663     }
664     if (to.tv_sec < 0) return ERL_TIMEOUT;
665 
666 #ifdef DEBUG
667     fprintf(stderr,"erl_call: debug remaining to timeout: %ld.%06ld\n",
668 	    to.tv_sec,to.tv_usec);
669 #endif
670     switch ((i = select(sockd+1,&rdset,NULL,NULL,&to))) {
671     case -1:
672       return ERL_SYS_ERROR;
673       break;
674 
675     case 0: /* timeout */
676 #ifdef DEBUG
677       gettimeofday(&now,NULL);
678       fprintf(stderr,"erl_call: debug timed out at %ld.%06ld\n",
679 	      now.tv_sec,now.tv_usec);
680 #endif
681       return ERL_TIMEOUT;
682       break;
683 
684     default: /* ready descriptors */
685 #ifdef DEBUG
686       gettimeofday(&now,NULL);
687       fprintf(stderr,"erl_call: debug got select at %ld.%06ld\n",
688 	      now.tv_sec,now.tv_usec);
689 #endif
690       if (FD_ISSET(sockd,&rdset)) {
691 	if ((fd = accept(sockd,(struct sockaddr *)&peer,&len)) < 0)
692 	  return ERL_SYS_ERROR;
693 
694 	/* now get sign-on message and terminate it */
695 #if defined(__WIN32__)
696 	if ((n=recv(fd,buf,16,0)) >= 0) buf[n]=0x0;
697 	closesocket(fd);
698 #else
699 	if ((n=read(fd,buf,16)) >= 0) buf[n]=0x0;
700 	close(fd);
701 #endif
702 #ifdef DEBUG
703 	fprintf(stderr,"erl_call: debug got %d, expected %d\n",
704 		atoi(buf),magic);
705 #endif
706 	if (atoi(buf) == magic) return 0; /* success */
707       } /* if FD_SET */
708     } /* switch */
709   } /* while */
710 
711   /* unreached? */
712   return ERL_SYS_ERROR;
713 } /* wait_for_erlang() */
714 
715 
get_addr(const char * hostname,struct in_addr * oaddr)716 static struct in_addr *get_addr(const char *hostname, struct in_addr *oaddr)
717 {
718   struct hostent *hp;
719 
720 #if !defined (__WIN32__)
721   char buf[1024];
722   struct hostent host;
723   int herror;
724 
725   hp = ei_gethostbyname_r(hostname,&host,buf,1024,&herror);
726 #else
727   hp = ei_gethostbyname(hostname);
728 #endif
729 
730   if (hp) {
731     memmove(oaddr,hp->h_addr_list[0],sizeof(*oaddr));
732     return oaddr;
733   }
734   return NULL;
735 }
736