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