1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1996-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 /*
23  * Function: Makes it possible to send and receive Erlang
24  *    messages from the (Unix) command line.
25  *    Note: We don't free any memory at all since we only
26  *    live for a short while.
27  *
28  */
29 
30 #ifdef __WIN32__
31 #include <winsock2.h>
32 #include <direct.h>
33 #include <windows.h>
34 #include <winbase.h>
35 
36 #else /* unix */
37 
38 #include <sys/types.h>
39 #include <sys/uio.h>
40 #include <sys/time.h>
41 #include <unistd.h>
42 #include <sys/param.h>
43 #include <netdb.h>
44 #include <sys/times.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 
49 #if TIME_WITH_SYS_TIME
50 # include <sys/time.h>
51 # include <time.h>
52 #else
53 # if HAVE_SYS_TIME_H
54 #  include <sys/time.h>
55 # else
56 #  include <time.h>
57 # endif
58 #endif
59 
60 #endif
61 
62 #include <stdio.h>
63 #include <stdlib.h>
64 
65 #include <string.h>
66 #include <ctype.h>
67 #include <fcntl.h>
68 #include <signal.h>
69 
70 #include "ei.h"
71 #include "ei_resolve.h"
72 #include "erl_start.h"		/* FIXME remove dependency */
73 
74 
75 struct call_flags {
76     int startp;
77     int cookiep;
78     int modp;
79     int evalp;
80     int randomp;
81     int dynamic_name;
82     int use_long_name;	/* indicates if -name was used, else -sname or -n */
83     int use_localhost_fallback;
84     int debugp;
85     int verbosep;
86     int haltp;
87     long port;
88     char *hostname;
89     char *cookie;
90     char *node;
91     char *hidden;
92     char *apply;
93     char *script;
94 };
95 
96 static void usage_arg(const char *progname, const char *switchname);
97 static void usage_error(const char *progname, const char *switchname);
98 static void usage(const char *progname);
99 static int get_module(char **mbuf, char **mname);
100 static int do_connect(ei_cnode *ec, char *nodename, struct call_flags *flags);
101 static int read_stdin(char **buf);
102 static void split_apply_string(char *str, char **mod,
103 			       char **fun, char **args);
104 static void* ei_chk_malloc(size_t size);
105 static void* ei_chk_calloc(size_t nmemb, size_t size);
106 static void* ei_chk_realloc(void *old, size_t size);
107 static char* ei_chk_strdup(char *s);
108 
109 /* Converts the given hostname to a shortname, if required. */
format_node_hostname(const struct call_flags * flags,const char * hostname,char dst[EI_MAXHOSTNAMELEN+1])110 static void format_node_hostname(const struct call_flags *flags,
111                                  const char *hostname,
112                                  char dst[EI_MAXHOSTNAMELEN + 1])
113 {
114     char *ct;
115 
116     strncpy(dst, hostname, EI_MAXHOSTNAMELEN);
117     dst[EI_MAXHOSTNAMELEN] = '\0';
118 
119     /* If shortnames, cut off the name at first '.' */
120     if (flags->use_long_name == 0 && (ct = strchr(dst, '.'))) {
121         *ct = '\0';
122     }
123 }
124 
125 static void start_timeout(int timeout);
126 
127 /***************************************************************************
128  *
129  *  XXXXX
130  *
131  ***************************************************************************/
132 
main(int argc,char * argv[])133 int main(int argc, char *argv[])
134 {
135     int i = 1,fd,creation;
136     struct hostent *hp;
137     char host_name[EI_MAXHOSTNAMELEN+1];
138     char nodename[MAXNODELEN+1];
139     char *p = NULL;
140     int modsize = 0;
141     char *host = NULL;
142     char *module = NULL;
143     char *modname = NULL;
144     struct call_flags flags = {0}; /* Default 0 and NULL in all fields */
145     char* progname = argv[0];
146     ei_cnode ec;
147     flags.port = -1;
148     flags.hostname = NULL;
149 
150     ei_init();
151 
152     /* Get the command line options */
153     while (i < argc) {
154 	if (argv[i][0] != '-') {
155 	    usage_error(progname, argv[i]);
156 	}
157 
158 	if (strcmp(argv[i], "-sname") == 0) { /* -sname NAME */
159 	    if (i+1 >= argc) {
160 		usage_arg(progname, "-sname ");
161 	    }
162 
163 	    flags.node = ei_chk_strdup(argv[i+1]);
164 	    i++;
165 	    flags.use_long_name = 0;
166 	} else if (strcmp(argv[i], "-name") == 0) {  /* -name NAME */
167 	    if (i+1 >= argc) {
168 		usage_arg(progname, "-name ");
169 	    }
170 
171 	    flags.node = ei_chk_strdup(argv[i+1]);
172 	    i++;
173 	    flags.use_long_name = 1;
174 	} else if (strcmp(argv[i], "-address") == 0) {  /* -address [HOST:]PORT */
175 	    if (i+1 >= argc) {
176 		usage_arg(progname, "-address ");
177 	    }
178             {
179                 char* hostname_port_arg = ei_chk_strdup(argv[i+1]);
180                 char* address_string_end = strchr(hostname_port_arg, ':');
181                 if (address_string_end == NULL) {
182                     flags.port = strtol(hostname_port_arg, NULL, 10);
183                 } else {
184                     flags.port = strtol(address_string_end + 1, NULL, 10);
185                     /* Remove port part from hostname_port_arg*/
186                     *address_string_end = '\0';
187                     if (strlen(hostname_port_arg) > 0) {
188                         flags.hostname = hostname_port_arg;
189                     }
190                 }
191 
192                 if (flags.port < 1 || flags.port > 65535) {
193                     usage_error(progname, "-address");
194                 }
195                 i++;
196             }
197         } else if (strcmp(argv[i], "-timeout") == 0) {
198             long timeout;
199 
200             if (i+1 >= argc) {
201                 usage_arg(progname, "-timeout ");
202             }
203 
204             timeout = strtol(argv[i+1], NULL, 10);
205             if (timeout <= 0 || timeout >= (1 << 20)) {
206                 usage_error(progname, "-timeout");
207             }
208 
209             start_timeout(timeout);
210             i++;
211         } else if (strcmp(argv[i], "-__uh_test__") == 0) {
212             /* Fakes a failure in the call to ei_gethostbyname(h_hostname) so
213              * we can test the localhost fallback. */
214             flags.use_localhost_fallback = 1;
215 	} else {
216 	    if (strlen(argv[i]) != 2) {
217 		usage_error(progname, argv[i]);
218 	    }
219 
220 	    switch (argv[i][1]) {
221 	    case 's':
222 		flags.startp = 1;
223 		break;
224 	    case 'q':
225 		flags.haltp = 1;
226 		break;
227 	    case 'v':
228 		flags.verbosep = 1;
229 		break;
230 	    case 'd':
231 		flags.debugp = 1;
232 		break;
233 	    case 'r':
234 		flags.randomp = 1;
235 		break;
236             case 'R':
237                 flags.dynamic_name = 1;
238                 break;
239 	    case 'e':
240 		flags.evalp = 1;
241 		break;
242 	    case 'm':
243 		flags.modp = 1;
244 		break;
245 	    case 'c':
246 		if (i+1 >= argc) {
247 		    usage_arg(progname, "-c ");
248 		}
249 		flags.cookiep = 1;
250 		flags.cookie = ei_chk_strdup(argv[i+1]);
251 		i++;
252 		break;
253 	    case 'n':
254 		if (i+1 >= argc) {
255 		    usage_arg(progname, "-n ");
256 		}
257 		flags.node = ei_chk_strdup(argv[i+1]);
258 		flags.use_long_name = 1;
259 		i++;
260 		break;
261 	    case 'h':
262 		if (i+1 >= argc) {
263 		    usage_arg(progname, "-h ");
264 		}
265 		flags.hidden = ei_chk_strdup(argv[i+1]);
266 		i++;
267 		break;
268 	    case 'x':
269 		if (i+1 >= argc) {
270 		    usage_arg(progname, "-x ");
271 		}
272 		flags.script = ei_chk_strdup(argv[i+1]);
273 		i++;
274 		break;
275 	    case 'a':
276 		if (i+1 >= argc) {
277 		    usage_arg(progname, "-a ");
278 		}
279 		flags.apply = ei_chk_strdup(argv[i+1]);
280 		i++;
281 		break;
282 	    case '?':
283 		usage(progname);
284 	    default:
285 		usage_error(progname, argv[i]);
286 	    }
287 	}
288 	i++;
289 
290     } /* while */
291 
292     /*
293      * Can't have them both !
294      */
295     if ((flags.modp && flags.evalp) ||
296         (flags.port != -1 && flags.startp) ||
297         (flags.port != -1 && flags.node)) {
298       usage(progname);
299     }
300 
301     /*
302      * Read an Erlang module from stdin.
303      */
304     if (flags.modp) {
305       modsize = get_module(&module, &modname);
306     }
307 
308     if (flags.verbosep || flags.debugp) {
309 	fprintf(stderr,"erl_call: "
310 		"node = %s\nCookie = %s\n"
311 		"flags = %s %s %s\n"
312 		"module: name = %s , size = %d\n"
313 		"apply = %s\n",
314 		(flags.node ? flags.node : ""),
315 		(flags.cookie ? flags.cookie : ""),
316 		(flags.startp ? "startp" : ""),
317 		(flags.verbosep ? "verbosep" : ""),
318 		(flags.debugp ? "debugp" : ""),
319 		(modname ? modname : ""), modsize,
320 		(flags.apply ? flags.apply : "" ));
321     }
322 
323     /*
324      * What we, at least, requires !
325      */
326     if (flags.node == NULL && flags.port == -1) {
327 	usage(progname);
328     }
329 
330     if (!flags.cookiep) {
331 	flags.cookie = NULL;
332     }
333 
334     creation = time(NULL) + 1; /* "random" */
335 
336     if (flags.hidden == NULL && !flags.dynamic_name) {
337       /* As default we are c17@gethostname */
338       i = flags.randomp ? (time(NULL) % 997) : 17;
339       flags.hidden = (char *) ei_chk_malloc(10 + 2 ); /* c17 or cXYZ */
340       sprintf(flags.hidden, "c%d",
341 	  i < 0 ?  (int) getpid() : i);
342     }
343     {
344       /* A name for our hidden node was specified */
345       char h_hostname[EI_MAXHOSTNAMELEN+1];
346       char h_nodename_buf[MAXNODELEN+1];
347       char *h_nodename = h_nodename_buf;
348       char *h_alivename = flags.hidden;
349       struct in_addr h_ipadr;
350 
351       /* gethostname requires len to be max(hostname) + 1 */
352       if (gethostname(h_hostname, EI_MAXHOSTNAMELEN+1) < 0) {
353           fprintf(stderr,"erl_call: failed to get host name: %d\n", errno);
354           exit(1);
355       }
356 
357       if (flags.use_localhost_fallback || (hp = ei_gethostbyname(h_hostname)) == 0) {
358           /* Failed to resolve our own hostname; try binding to loopback and
359            * hope for the best. */
360           hp = ei_gethostbyname("localhost");
361           flags.use_localhost_fallback = 1;
362 
363           format_node_hostname(&flags, h_hostname, h_hostname);
364       } else {
365           format_node_hostname(&flags, hp->h_name, h_hostname);
366       }
367 
368       memcpy(&h_ipadr.s_addr, *hp->h_addr_list, sizeof(struct in_addr));
369       if (h_alivename) {
370           if (strlen(h_alivename) + strlen(h_hostname) + 2 > sizeof(h_nodename_buf)) {
371               fprintf(stderr,"erl_call: hostname too long: %s\n", h_hostname);
372               exit(1);
373           }
374           sprintf(h_nodename, "%s@%s", h_alivename, h_hostname);
375       }
376       else {
377           /* dynamic node name */
378           h_nodename = NULL;
379       }
380 
381       if (ei_connect_xinit(&ec, h_hostname, h_alivename, h_nodename,
382 			   (Erl_IpAddr)&h_ipadr, flags.cookie,
383 			   (short) creation) < 0) {
384 	  fprintf(stderr,"erl_call: can't create C node %s; %d\n",
385 		  h_nodename, erl_errno);
386       	  exit(1);
387       }
388 
389     }
390     if (flags.port != -1 && flags.hostname != NULL) {
391         host = flags.hostname;
392         strcpy(host_name, flags.hostname);
393     } else if ((flags.port != -1 && flags.hostname == NULL) ||
394         (strchr((const char *)flags.node, (int) '@') == 0)) {
395 	strcpy(host_name, ei_thishostname(&ec));
396 	host = host_name;
397     } else {
398         p = strchr((const char *)flags.node, (int) '@');
399 	*p = 0;
400 	host = p+1;
401     }
402 
403     if (flags.use_localhost_fallback && strcmp(host, ei_thishostname(&ec)) == 0) {
404         /* We're on the same host *and* have used the localhost fallback, so we
405          * skip canonical name resolution since it's bound to fail.
406          *
407          * `ei_connect` will do the right thing later on. */
408         strcpy(host_name, ei_thishostname(&ec));
409     } else {
410         if ((hp = ei_gethostbyname(host)) == 0) {
411             fprintf(stderr,"erl_call: can't ei_gethostbyname(%s)\n", host);
412             exit(1);
413         }
414 
415         format_node_hostname(&flags, hp->h_name, host_name);
416     }
417 
418     if (flags.port == -1) {
419         if (strlen(flags.node) + strlen(host_name) + 2 > sizeof(nodename)) {
420             fprintf(stderr,"erl_call: nodename too long: %s\n", flags.node);
421             exit(1);
422         }
423         sprintf(nodename, "%s@%s", flags.node, host_name);
424     }
425     /*
426      * Try to connect. Start an Erlang system if the
427      * start option is on and no system is running.
428      */
429     if (flags.startp && !flags.haltp) {
430 	fd = do_connect(&ec, nodename, &flags);
431     } else if (flags.port == -1) {
432         if ((fd = ei_connect(&ec, nodename)) < 0) {
433             /* We failed to connect ourself */
434             /* FIXME do we really know we failed because of node not up? */
435             if (flags.haltp) {
436                 exit(0);
437             } else {
438                 fprintf(stderr,"erl_call: failed to connect to node %s\n",
439                         nodename);
440                 exit(1);
441             }
442         }
443     } else {
444         /* Connect using address:port */
445         if ((fd = ei_connect_host_port(&ec, host, (int)flags.port)) < 0) {
446             /* We failed to connect ourself */
447             /* FIXME do we really know we failed because of node not up? */
448             if (flags.haltp) {
449                 exit(0);
450             } else {
451                 fprintf(stderr,"erl_call: failed to connect to node with address \"%s:%ld\"\n",
452                         flags.hostname == NULL ? "" : flags.hostname,
453                         flags.port);
454                 exit(1);
455             }
456         }
457     }
458 
459     /* If we are connected and the halt switch is set */
460     if (fd && flags.haltp) {
461 	int i = 0;
462 	char *p;
463 	ei_x_buff reply;
464 
465 	ei_encode_empty_list(NULL, &i);
466 
467 	p = (char *)ei_chk_malloc(i);
468 	i = 0;		/* Reset */
469 
470 	ei_encode_empty_list(p, &i);
471 
472 	ei_x_new_with_version(&reply);
473 
474 	/* FIXME if fails we want to exit != 0 ? */
475 	ei_rpc(&ec, fd, "erlang", "halt", p, i, &reply);
476 	free(p);
477 	ei_x_free(&reply);
478 	exit(0);
479     }
480 
481     if (flags.verbosep) {
482         if (flags.port == -1) {
483             fprintf(stderr,"erl_call: we are now connected to node \"%s\"\n",
484                     nodename);
485         } else {
486             fprintf(stderr,"erl_call: we are now connected to node with address \"%s:%ld\"\n",
487                     flags.hostname == NULL ? "": flags.hostname,
488                     flags.port);
489         }
490     }
491 
492     /*
493      * Compile the module read from stdin.
494      */
495     if (flags.modp && (modname != NULL)) {
496       char fname[256];
497 
498       if (strlen(modname) + 4 + 1 > sizeof(fname)) {
499       fprintf(stderr,"erl_call: module name too long: %s\n", modname);
500       exit(1);
501       }
502       strcpy(fname, modname);
503       strcat(fname, ".erl");
504 
505       /*
506        * ei_format("[~s,~w]", fname, erl_mk_binary(module, modsize));
507        */
508 
509       {
510 	  int i = 0;
511 	  char *p;
512 	  ei_x_buff reply;
513 
514 	  ei_encode_list_header(NULL, &i, 2);
515 	  ei_encode_string(NULL, &i, fname);
516 	  ei_encode_binary(NULL, &i, module, modsize);
517 	  ei_encode_empty_list(NULL, &i);
518 
519 	  p = (char *)ei_chk_malloc(i);
520 	  i = 0;		/* Reset */
521 
522 	  ei_encode_list_header(p, &i, 2);
523 	  ei_encode_string(p, &i, fname);
524 	  ei_encode_binary(p, &i, module, modsize);
525 	  ei_encode_empty_list(p, &i);
526 
527 	  ei_x_new_with_version(&reply);
528 
529 	  if (ei_rpc(&ec, fd, "file", "write_file", p, i, &reply) < 0) {
530 	      free(p);
531 	      ei_x_free(&reply);
532 	      fprintf(stderr,"erl_call: can't write to source file %s\n",
533 		      fname);
534 	      exit(1);
535 	  }
536 	  free(p);
537 	  ei_x_free(&reply);
538       }
539 
540       /* Compile AND load file on other node */
541 
542       {
543 	  int i = 0;
544 	  char *p;
545 	  ei_x_buff reply;
546 
547 	  ei_encode_list_header(NULL, &i, 2);
548 	  ei_encode_atom(NULL, &i, fname);
549 	  ei_encode_empty_list(NULL, &i);
550 	  ei_encode_empty_list(NULL, &i);
551 
552 	  p = (char *)ei_chk_malloc(i);
553 	  i = 0;		/* Reset */
554 
555 	  ei_encode_list_header(p, &i, 2);
556 	  ei_encode_atom(p, &i, fname);
557 	  ei_encode_empty_list(p, &i);
558 	  ei_encode_empty_list(p, &i);
559 
560 	  ei_x_new_with_version(&reply);
561 
562 	  /* erl_format("[~a,[]]", modname) */
563 
564 	  if (ei_rpc(&ec, fd, "c", "c", p, i, &reply) < 0) {
565 	      free(p);
566 	      ei_x_free(&reply);
567 	      fprintf(stderr,"erl_call: can't compile file %s\n", fname);
568 	  }
569 	  free(p);
570 	  /* FIXME complete this code
571 	     FIXME print out error message as term
572 	  if (!erl_match(erl_format("{ok,_}"), reply)) {
573 	      fprintf(stderr,"erl_call: compiler errors\n");
574 	  }
575 	  */
576 	  ei_x_free(&reply);
577       }
578 
579     }
580 
581     /*
582      * If we loaded any module source code, we can free the buffer
583      * now. This buffer was allocated in read_stdin().
584      */
585     if (module != NULL) {
586         free(module);
587     }
588 
589     /*
590      * Eval the Erlang functions read from stdin/
591      */
592     if (flags.evalp) {
593       char *evalbuf;
594       int len;
595 
596       len = read_stdin(&evalbuf);
597       {
598 	  int i = 0;
599 	  char *p;
600 	  ei_x_buff reply;
601 
602 	  ei_encode_list_header(NULL, &i, 1);
603 	  ei_encode_binary(NULL, &i, evalbuf, len);
604 	  ei_encode_empty_list(NULL, &i);
605 
606 	  p = (char *)ei_chk_malloc(i);
607 	  i = 0;		/* Reset */
608 
609 	  ei_encode_list_header(p, &i, 1);
610 	  ei_encode_binary(p, &i, evalbuf, len);
611 	  ei_encode_empty_list(p, &i);
612 
613 	  ei_x_new_with_version(&reply);
614 
615 	  /* erl_format("[~w]", erl_mk_binary(evalbuf,len))) */
616 
617 	  if (ei_rpc(&ec, fd, "erl_eval", "eval_str", p, i, &reply) < 0) {
618 	      fprintf(stderr,"erl_call: evaluating input failed: %s\n",
619 		      evalbuf);
620 	      free(p);
621 	      free(evalbuf);	/* Allocated in read_stdin() */
622 	      ei_x_free(&reply);
623 	      exit(1);
624 	  }
625 	  i = 0;
626 	  ei_print_term(stdout,reply.buff,&i);
627 	  free(p);
628 	  free(evalbuf);	/* Allocated in read_stdin() */
629 	  ei_x_free(&reply);
630       }
631     }
632     /*
633      * Any Erlang call to be made ?
634      */
635     if (flags.apply != NULL) {
636       char *mod,*fun,*args;
637       ei_x_buff e, reply;
638 
639       split_apply_string(flags.apply, &mod, &fun, &args);
640       if (flags.verbosep) {
641 	  fprintf(stderr,"erl_call: module = %s, function = %s, args = %s\n",
642 		  mod, fun, args);
643       }
644 
645       ei_x_new(&e);		/* No version to ei_rpc() */
646 
647       if (ei_x_format_wo_ver(&e, args) < 0) {
648 	  /* FIXME no error message and why -1 ? */
649 	  exit(-1);
650       }
651 
652       ei_x_new_with_version(&reply);
653 
654       if (ei_rpc(&ec, fd, mod, fun, e.buff, e.index, &reply) < 0) {
655 	  /* FIXME no error message and why -1 ? */
656 	  ei_x_free(&e);
657 	  ei_x_free(&reply);
658 	  exit(-1);
659       } else {
660 	  int i = 0;
661 	  ei_print_term(stdout,reply.buff,&i);
662 	  ei_x_free(&e);
663 	  ei_x_free(&reply);
664       }
665     }
666 
667     return(0);
668 }
669 
670 
671 /***************************************************************************
672  *
673  *  XXXXX
674  *
675  ***************************************************************************/
676 
677 
678 /*
679  * This function does only return on success.
680  */
do_connect(ei_cnode * ec,char * nodename,struct call_flags * flags)681 static int do_connect(ei_cnode *ec, char *nodename, struct call_flags *flags)
682 {
683     int fd;
684     int start_flags;
685     int r;
686 
687     start_flags = ERL_START_ENODE |
688 	(flags->use_long_name? ERL_START_LONG : 0) |
689 	(flags->verbosep? ERL_START_VERBOSE : 0) |
690 	(flags->debugp? ERL_START_DEBUG : 0);
691 
692     if ((fd = ei_connect(ec, nodename)) >= 0) {
693 	/* success */
694 	if (flags->verbosep) {
695 	    fprintf(stderr,"erl_call: now connected to node %s\n", nodename);
696 	}
697     } else {
698 	char alive[EI_MAXALIVELEN+1];
699 	char *hostname;
700 	struct hostent *h;
701 	char *cookieargs[3];
702 	char **args;
703 
704 	cookieargs[0] = "-setcookie";
705 	cookieargs[1] = flags->cookie;
706 	cookieargs[2] = NULL;
707 
708 	args = (flags->cookie) ? cookieargs : NULL;
709 
710 	if (!(hostname = strrchr(nodename,'@'))) {
711 	    return ERL_BADARG;
712 	}
713 	strncpy(alive,nodename,hostname-nodename);
714 	alive[hostname-nodename] = 0x0;
715 	hostname++;
716 
717 	h = ei_gethostbyname(hostname);
718 
719 
720 	if ((r=erl_start_sys(ec,alive,(Erl_IpAddr)(h->h_addr_list[0]),
721 			     start_flags,flags->script,args)) < 0) {
722 	    fprintf(stderr,"erl_call: unable to start node, error = %d\n", r);
723 	    exit(1);
724 	}
725 
726 	if ((fd=ei_connect(ec, nodename)) >= 0) {
727 	    /* success */
728 	    if (flags->verbosep) {
729 		fprintf(stderr,"erl_call: now connected to node \"%s\"\n",
730 			nodename);
731 	    }
732 	} else {
733 	    /* (failure) */
734 	    switch (fd) {
735 	    case ERL_NO_DAEMON:
736 		fprintf(stderr,"erl_call: no epmd running\n");
737 		exit(1);
738 	    case ERL_CONNECT_FAIL:
739 		fprintf(stderr,"erl_call: connect failed\n");
740 		exit(1);
741 	    case ERL_NO_PORT:
742 		fprintf(stderr,"erl_call: node is not running\n");
743 		exit(1);
744 	    case ERL_TIMEOUT:
745 		fprintf(stderr,"erl_call: connect timed out\n");
746 		exit(1);
747 	    default:
748 		fprintf(stderr,"erl_call: error during connect\n");
749 		exit(1);
750 	    }
751 	}
752     }
753 
754     return fd;
755 } /* do_connect */
756 
757 #define SKIP_SPACE(s) while(isspace((int)*(s))) (s)++
758 #define EAT(s) while (!isspace((int)*(s)) && (*(s) != '\0')) (s)++
759 
split_apply_string(char * str,char ** mod,char ** fun,char ** args)760 static void split_apply_string(char *str,
761 			       char **mod,
762 			       char **fun,
763 			       char **args)
764 {
765   char *begin=str;
766   char *start="start";
767   char *empty_list="[]";
768   int len;
769 
770   SKIP_SPACE(str);
771   if (*str == '\0') {
772       fprintf(stderr,"erl_call: wrong format of apply string (1)\n");
773       exit(1);
774   }
775 
776   EAT(str);
777   len = str-begin;
778   *mod = (char *) ei_chk_calloc(len + 1, sizeof(char));
779   memcpy(*mod, begin, len);
780 
781   SKIP_SPACE(str);
782   if (*str == '\0') {
783     *fun = ei_chk_strdup(start);
784     *args = ei_chk_strdup(empty_list);
785     return;
786   }
787   begin = str;
788   EAT(str);
789   len = str-begin;
790   *fun = (char *) ei_chk_calloc(len + 1, sizeof(char));
791   memcpy(*fun, begin, len);
792 
793   SKIP_SPACE(str);
794   if (*str == '\0') {
795     *args = ei_chk_strdup(empty_list);
796     return;
797   }
798 
799   *args = ei_chk_strdup(str);
800 
801   return;
802 
803 } /* split_apply_string */
804 
805 
806 /*
807  * Read from stdin until EOF is reached.
808  * Allocate the buffer needed.
809  */
read_stdin(char ** buf)810 static int read_stdin(char **buf)
811 {
812     int bsize = BUFSIZ;
813     int len = 0;
814     int i;
815     char *tmp = (char *) ei_chk_malloc(bsize);
816 
817     while (1) {
818 	if ((i = read(0, &tmp[len], bsize-len)) < 0) {
819 	    fprintf(stderr,"erl_call: can't read stdin, errno = %d", errno);
820 	    exit(1);
821 	} else if (i == 0) {
822 	    break;
823 	} else {
824 	    len += i;
825 	    if ((len+50) > bsize) {
826 		bsize = len * 2;
827 		tmp = (char *) ei_chk_realloc(tmp, bsize);
828 	    } else {
829 		continue;
830 	    }
831 	}
832     } /* while */
833     *buf = tmp;
834     return len;
835 
836 } /* read_stdin */
837 
838 /*
839  * Get the module from stdin.
840  */
get_module(char ** mbuf,char ** mname)841 static int get_module(char **mbuf, char **mname)
842 {
843   char *tmp;
844   int len,i;
845 
846   len = read_stdin(mbuf);
847   /*
848    * Now, get the module name.
849    */
850   if ((tmp = strstr(*mbuf, "-module(")) != NULL) {
851     char *start;
852     tmp += strlen("-module(");
853     while ((*tmp) == ' ') tmp++; /* eat space */
854     start = tmp;
855     while (1) {
856       if (isalnum((int)*tmp) || (*tmp == '_')) {
857 	tmp++;
858 	continue;
859       } else {
860 	  break;
861       }
862     } /* while */
863     i = tmp - start;
864     *mname = (char *) ei_chk_calloc(i+1, sizeof(char));
865     memcpy(*mname, start, i);
866   }
867 
868   return len;
869 
870 } /* get_module */
871 
872 #ifdef __WIN32__
timer_thread(void * data)873 static DWORD WINAPI timer_thread(void *data) {
874     DWORD_PTR timeout = (DWORD_PTR)data * 1000;
875 
876     Sleep(timeout);
877     exit(1);
878 }
879 
start_timeout(int timeout)880 static void start_timeout(int timeout) {
881     if (CreateThread(NULL, 0, timer_thread, (void*)timeout, 0, NULL) == NULL) {
882         fprintf(stderr,"erl_call: Failed to start timer thread\n");
883         exit(1);
884     }
885 }
886 #else
start_timeout(int timeout)887 static void start_timeout(int timeout) {
888     alarm(timeout);
889 }
890 #endif
891 
892 /***************************************************************************
893  *
894  *  Different error reporting functions that output usage
895  *
896  ***************************************************************************/
897 
usage_noexit(const char * progname)898 static void usage_noexit(const char *progname) {
899   fprintf(stderr,"\nUsage: %s [-[demqrsv]] [-c Cookie] [-h HiddenName] \n", progname);
900   fprintf(stderr,"            [-x ErlScript] [-a [Mod [Fun [Args]]]] [-timeout Secs]\n");
901   fprintf(stderr,"            (-n Node | -sname Node | -name Node | -address [HOSTNAME:]PORT)\n\n");
902 #ifdef __WIN32__
903   fprintf(stderr,"  where: -a  apply(Mod,Fun,Args) (e.g -a \"erlang length [[a,b,c]]\"\n");
904 #else
905   fprintf(stderr,"  where: -a  apply(Mod,Fun,Args) (e.g -a 'erlang length [[a,b,c]]'\n");
906 #endif
907   fprintf(stderr,"         -c  cookie string; by default read from ~/.erlang.cookie\n");
908   fprintf(stderr,"         -d  direct Erlang output to ~/.erl_call.out.<Nodename>\n");
909   fprintf(stderr,"         -e  evaluate contents of standard input (e.g., echo \"X=1,Y=2,{X,Y}.\"|%s -e ...)\n",
910           progname);
911   fprintf(stderr,"         -h  specify a name for the erl_call client node\n");
912   fprintf(stderr,"         -m  read and compile Erlang module from stdin\n");
913   fprintf(stderr,"         -n  name of Erlang node, same as -name\n");
914   fprintf(stderr,"         -name  name of Erlang node, expanded to a fully qualified\n");
915   fprintf(stderr,"         -sname name of Erlang node, short form will be used\n");
916   fprintf(stderr,"         -address [HOSTNAME:]PORT of Erlang node\n"
917           "                  (the default hostname is the hostname of the local manchine)\n"
918           "                  (e.g., %s -address my_host:36303 ...)\n"
919           "                  (cannot be combinated with -s, -n, -name and -sname)\n",
920           progname);
921   fprintf(stderr,"         -timeout  command timeout, in seconds\n");
922   fprintf(stderr,"         -q  halt the Erlang node (overrides the -s switch)\n");
923   fprintf(stderr,"         -r  use a random name for the erl_call client node\n");
924   fprintf(stderr,"         -s  start a new Erlang node if necessary\n");
925   fprintf(stderr,"         -v  verbose mode, i.e print some information on stderr\n");
926   fprintf(stderr,"         -x  use specified erl start script, default is erl\n");
927 }
928 
usage_arg(const char * progname,const char * switchname)929 static void usage_arg(const char *progname, const char *switchname) {
930   fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname);
931   usage_noexit(progname);
932   exit(1);
933 }
934 
usage_error(const char * progname,const char * switchname)935 static void usage_error(const char *progname, const char *switchname) {
936   fprintf(stderr, "Illegal argument \'%s\'.\n", switchname);
937   usage_noexit(progname);
938   exit(1);
939 }
940 
usage(const char * progname)941 static void usage(const char *progname) {
942   usage_noexit(progname);
943   exit(0);
944 }
945 
946 /***************************************************************************
947  *
948  *  Utility functions
949  *
950  ***************************************************************************/
951 
ei_chk_malloc(size_t size)952 static void* ei_chk_malloc(size_t size)
953 {
954     void *p = malloc(size);
955     if (p == NULL) {
956         fprintf(stderr,"erl_call: insufficient memory\n");
957         exit(1);
958     }
959     return p;
960 }
961 
ei_chk_calloc(size_t nmemb,size_t size)962 static void* ei_chk_calloc(size_t nmemb, size_t size)
963 {
964     void *p = calloc(nmemb, size);
965     if (p == NULL) {
966         fprintf(stderr,"erl_call: insufficient memory\n");
967         exit(1);
968     }
969     return p;
970 }
971 
ei_chk_realloc(void * old,size_t size)972 static void* ei_chk_realloc(void *old, size_t size)
973 {
974     void *p = realloc(old, size);
975     if (!p) {
976         fprintf(stderr, "erl_call: cannot reallocate %u bytes of memory from %p\n",
977                 (unsigned) size, old);
978         exit (1);
979     }
980     return p;
981 }
982 
ei_chk_strdup(char * s)983 static char* ei_chk_strdup(char *s)
984 {
985     char *p = strdup(s);
986     if (p == NULL) {
987         fprintf(stderr,"erl_call: insufficient memory\n");
988         exit(1);
989     }
990     return p;
991 }
992