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