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  * Module: run_erl.c
22  *
23  * This module implements a reader/writer process that opens two specified
24  * FIFOs, one for reading and one for writing; reads from the read FIFO
25  * and writes to stdout and the write FIFO.
26  *
27   ________                            _________
28  |        |--<-- pipe.r (fifo1) --<--|         |
29  | to_erl |                          | run_erl | (parent)
30  |________|-->-- pipe.w (fifo2) -->--|_________|
31                                           ^ master pty
32                                           |
33                                           | slave pty
34                                       ____V____
35                                      |         |
36                                      |  "erl"  | (child)
37                                      |_________|
38 */
39 
40 
41 #ifdef HAVE_CONFIG_H
42 #  include "config.h"
43 #endif
44 #ifdef HAVE_WORKING_POSIX_OPENPT
45 #  ifndef _XOPEN_SOURCE
46      /* On OS X, BSD and Solaris, we must leave _XOPEN_SOURCE undefined in order
47       * for the prototype of vsyslog() to be included.
48       */
49 #    if !(defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__sun))
50 #      define _XOPEN_SOURCE 600
51 #    endif
52 #  endif
53 #endif
54 #include <sys/types.h>
55 #include <sys/wait.h>
56 #include <sys/stat.h>
57 #include <sys/time.h>
58 #include <sys/types.h>
59 #include <sys/select.h>
60 #include <fcntl.h>
61 #include <unistd.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <stdarg.h>
65 #include <string.h>
66 #include <errno.h>
67 #include <signal.h>
68 #include <dirent.h>
69 #include <termios.h>
70 #include <time.h>
71 
72 #ifdef HAVE_SYSLOG_H
73 #  include <syslog.h>
74 #endif
75 #ifdef HAVE_PTY_H
76 #  include <pty.h>
77 #endif
78 #ifdef HAVE_UTMP_H
79 #  include <utmp.h>
80 #endif
81 #ifdef HAVE_LIBUTIL_H
82 #  include <libutil.h>
83 #endif
84 #ifdef HAVE_UTIL_H
85 #  include <util.h>
86 #endif
87 #ifdef HAVE_SYS_IOCTL_H
88 #  include <sys/ioctl.h>
89 #endif
90 #if defined(__sun) && defined(__SVR4)
91 #  include <stropts.h>
92 #endif
93 
94 #ifdef __clang_analyzer__
95    /* CodeChecker does not seem to understand inline asm in FD_ZERO */
96 #  undef FD_ZERO
97 #  define FD_ZERO(FD_SET_PTR) memset(FD_SET_PTR, 0, sizeof(fd_set))
98 #endif
99 
100 #include "run_erl.h"
101 #include "safe_string.h"    /* sn_printf, strn_cpy, strn_cat, etc */
102 
103 #ifdef O_NONBLOCK
104 #  define DONT_BLOCK_PLEASE O_NONBLOCK
105 #else
106 #  define DONT_BLOCK_PLEASE O_NDELAY
107 #  ifndef EAGAIN
108 #    define EAGAIN -3898734
109 #  endif
110 #endif
111 
112 #define noDEBUG
113 
114 #define DEFAULT_LOG_GENERATIONS 5
115 #define LOG_MAX_GENERATIONS     1000      /* No more than 1000 log files */
116 #define LOG_MIN_GENERATIONS     2         /* At least two to switch between */
117 #define DEFAULT_LOG_MAXSIZE     100000
118 #define LOG_MIN_MAXSIZE         1000      /* Smallast value for changing log file */
119 #define LOG_STUBNAME            "erlang.log."
120 #define LOG_PERM                0664
121 #define DEFAULT_LOG_ACTIVITY_MINUTES    5
122 #define DEFAULT_LOG_ALIVE_MINUTES       15
123 #define DEFAULT_LOG_ALIVE_FORMAT        "%a %b %e %T %Z %Y"
124 #define ALIVE_BUFFSIZ                   256
125 
126 #define PERM            0600
127 #define STATUSFILENAME  "/run_erl.log"
128 #define PIPE_STUBNAME   "erlang.pipe"
129 #define PIPE_STUBLEN    strlen(PIPE_STUBNAME)
130 
131 #ifndef FILENAME_MAX
132 #define FILENAME_MAX 250
133 #endif
134 
135 #ifndef O_SYNC
136 #define O_SYNC 0
137 #define USE_FSYNC 1
138 #endif
139 
140 #define MAX(x,y)  ((x) > (y) ? (x) : (y))
141 
142 #define FILENAME_BUFSIZ FILENAME_MAX
143 
144 /* prototypes */
145 static void usage(char *);
146 static int create_fifo(char *name, int perm);
147 static int open_pty_master(char **name, int *sfd);
148 static int open_pty_slave(char *name);
149 static void pass_on(pid_t);
150 static void exec_shell(char **);
151 static void status(const char *format,...);
152 static void error_logf(int priority, int line, const char *format,...);
153 static void catch_sigchild(int);
154 static int next_log(int log_num);
155 static int prev_log(int log_num);
156 static int find_next_log_num(void);
157 static int open_log(int log_num, int flags);
158 static void write_to_log(int* lfd, int* log_num, char* buf, int len);
159 static void daemon_init(void);
160 static char *simple_basename(char *path);
161 static void init_outbuf(void);
162 static int outbuf_size(void);
163 static void clear_outbuf(void);
164 static char* outbuf_first(void);
165 static void outbuf_delete(int bytes);
166 static void outbuf_append(const char* bytes, int n);
167 static int write_all(int fd, const char* buf, int len);
168 static int extract_ctrl_seq(char* buf, int len);
169 static void set_window_size(unsigned col, unsigned row);
170 
171 static ssize_t sf_write(int fd, const void *buffer, size_t len);
172 static ssize_t sf_read(int fd, void *buffer, size_t len);
173 static int sf_open(const char *path, int flags, mode_t mode);
174 static int sf_close(int fd);
175 
176 #ifdef DEBUG
177 static void show_terminal_settings(struct termios *t);
178 #endif
179 
180 /* static data */
181 static char fifo1[FILENAME_BUFSIZ], fifo2[FILENAME_BUFSIZ];
182 static char statusfile[FILENAME_BUFSIZ];
183 static char log_dir[FILENAME_BUFSIZ];
184 static char pipename[FILENAME_BUFSIZ];
185 static FILE *stdstatus = NULL;
186 static int log_generations = DEFAULT_LOG_GENERATIONS;
187 static int log_maxsize     = DEFAULT_LOG_MAXSIZE;
188 static int log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES;
189 static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES;
190 static int log_alive_in_gmt = 0;
191 static char log_alive_format[ALIVE_BUFFSIZ+1];
192 static int run_daemon = 0;
193 static char *program_name;
194 static int mfd; /* master pty fd */
195 static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */
196 
197 /*
198  * Output buffer.
199  *
200  * outbuf_base <= outbuf_out <= outbuf_in <= outbuf_base+outbuf_total
201  */
202 static char* outbuf_base;
203 static int outbuf_total;
204 static char* outbuf_out;
205 static char* outbuf_in;
206 
207 #if defined(NO_SYSCONF) || !defined(_SC_OPEN_MAX)
208 #    if defined(OPEN_MAX)
209 #        define HIGHEST_FILENO() OPEN_MAX
210 #    else
211 #        define HIGHEST_FILENO() 64 /* arbitrary value */
212 #    endif
213 #else
214 #    define HIGHEST_FILENO() sysconf(_SC_OPEN_MAX)
215 #endif
216 
217 
218 #ifndef HAVE_SYSLOG_H
219 #    define OPEN_SYSLOG() ((void) 0)
220 #    define LOG_ERR NULL
221 #else
222 #    define OPEN_SYSLOG() openlog(simple_basename(program_name),   \
223                                   LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER)
224 #endif
225 
226 #define ERROR0(Prio,Format) error_logf(Prio,__LINE__,Format"\n")
227 #define ERROR1(Prio,Format,A1) error_logf(Prio,__LINE__,Format"\n",A1)
228 #define ERROR2(Prio,Format,A1,A2) error_logf(Prio,__LINE__,Format"\n",A1,A2)
229 
230 #ifdef HAVE_STRERROR
231 #    define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno)
232 #else
233 #    define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno
234 #endif
235 #define ERRNO_ERR0(Prio,Format) error_logf(Prio,__LINE__,ADD_ERRNO(Format))
236 #define ERRNO_ERR1(Prio,Format,A1) error_logf(Prio,__LINE__,ADD_ERRNO(Format),A1)
237 
238 
main(int argc,char ** argv)239 int main(int argc, char **argv)
240 {
241   int childpid;
242   int sfd = -1;
243   int fd;
244   char *p, *ptyslave=NULL;
245   int i = 1;
246   int off_argv;
247   int calculated_pipename = 0;
248   int highest_pipe_num = 0;
249   int sleepy_child = 0;
250 
251   program_name = argv[0];
252 
253   if(argc<4) {
254     usage(argv[0]);
255     exit(1);
256   }
257 
258   init_outbuf();
259 
260   if (!strcmp(argv[1],"-sleepy-child")) {  /* For test purpose only */
261       sleepy_child = 1;
262       ++i;
263   }
264 
265   if (!strcmp(argv[1],"-daemon")) {
266       daemon_init();
267       ++i;
268   }
269 
270   off_argv = i;
271   strn_cpy(pipename, sizeof(pipename), argv[i++]);
272   strn_cpy(log_dir, sizeof(log_dir), argv[i]);
273   strn_cpy(statusfile, sizeof(statusfile), log_dir);
274   strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME);
275 
276 #ifdef DEBUG
277   status("%s: pid is : %d\n", argv[0], getpid());
278 #endif
279 
280   /* Get values for LOG file handling from the environment */
281   if ((p = getenv("RUN_ERL_LOG_ALIVE_MINUTES"))) {
282       log_alive_minutes = atoi(p);
283       if (!log_alive_minutes) {
284 	  ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 "
285 		 "(current value is %s)",p);
286       }
287       log_activity_minutes = log_alive_minutes / 3;
288       if (!log_activity_minutes) {
289 	  ++log_activity_minutes;
290       }
291   }
292   if ((p = getenv("RUN_ERL_LOG_ACTIVITY_MINUTES"))) {
293      log_activity_minutes = atoi(p);
294       if (!log_activity_minutes) {
295 	  ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 "
296 		 "(current value is %s)",p);
297       }
298   }
299   if ((p = getenv("RUN_ERL_LOG_ALIVE_FORMAT"))) {
300       if (strlen(p) > ALIVE_BUFFSIZ) {
301 	  ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of "
302 		 "%d characters", ALIVE_BUFFSIZ);
303       }
304       strn_cpy(log_alive_format, sizeof(log_alive_format), p);
305   } else {
306       strn_cpy(log_alive_format, sizeof(log_alive_format), DEFAULT_LOG_ALIVE_FORMAT);
307   }
308   if ((p = getenv("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) {
309       ++log_alive_in_gmt;
310   }
311   if ((p = getenv("RUN_ERL_LOG_GENERATIONS"))) {
312     log_generations = atoi(p);
313     if (log_generations < LOG_MIN_GENERATIONS)
314       ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS);
315     if (log_generations > LOG_MAX_GENERATIONS)
316       ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS);
317   }
318 
319   if ((p = getenv("RUN_ERL_LOG_MAXSIZE"))) {
320     log_maxsize = atoi(p);
321     if (log_maxsize < LOG_MIN_MAXSIZE)
322       ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE);
323   }
324 
325   /*
326    * Create FIFOs and open them
327    */
328 
329   if(*pipename && pipename[strlen(pipename)-1] == '/') {
330     /* The user wishes us to find a unique pipe name in the specified */
331     /* directory */
332     DIR *dirp;
333     struct dirent *direntp;
334 
335     calculated_pipename = 1;
336     dirp = opendir(pipename);
337     if(!dirp) {
338       ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename);
339       exit(1);
340     }
341 
342     /* Check the directory for existing pipes */
343 
344     while((direntp=readdir(dirp)) != NULL) {
345       if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
346 	int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
347 	if(num > highest_pipe_num)
348 	  highest_pipe_num = num;
349       }
350     }
351     closedir(dirp);
352     strn_catf(pipename, sizeof(pipename), "%s.%d",
353 	      PIPE_STUBNAME, highest_pipe_num+1);
354   } /* if */
355 
356   for(;;) {
357       /* write FIFO - is read FIFO for `to_erl' program */
358       strn_cpy(fifo1, sizeof(fifo1), pipename);
359       strn_cat(fifo1, sizeof(fifo1), ".r");
360       if (create_fifo(fifo1, PERM) < 0) {
361 	  ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", fifo1);
362 	  exit(1);
363       }
364 
365       /* read FIFO - is write FIFO for `to_erl' program */
366       strn_cpy(fifo2, sizeof(fifo2), pipename);
367       strn_cat(fifo2, sizeof(fifo2), ".w");
368 
369       /* Check that nobody is running run_erl already */
370       if ((fd = sf_open(fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
371 	  /* Open as client succeeded -- run_erl is already running! */
372 	  sf_close(fd);
373 	  if (calculated_pipename) {
374 	      ++highest_pipe_num;
375 	      strn_catf(pipename, sizeof(pipename), "%s.%d",
376 			PIPE_STUBNAME, highest_pipe_num+1);
377 	      continue;
378 	  }
379 	  fprintf(stderr, "Erlang already running on pipe %s.\n", pipename);
380 	  exit(1);
381       }
382       if (create_fifo(fifo2, PERM) < 0) {
383 	  ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", fifo2);
384 	  exit(1);
385       }
386       break;
387   }
388 
389   /*
390    * Open master pseudo-terminal
391    */
392 
393   if ((mfd = open_pty_master(&ptyslave, &sfd)) < 0) {
394     ERRNO_ERR0(LOG_ERR,"Could not open pty master");
395     exit(1);
396   }
397 
398   /*
399    * Now create a child process
400    */
401 
402   if ((childpid = fork()) < 0) {
403     ERRNO_ERR0(LOG_ERR,"Cannot fork");
404     exit(1);
405   }
406   if (childpid == 0) {
407       if (sleepy_child)
408 	  sleep(1);
409 
410     /* Child */
411     sf_close(mfd);
412     /* disassociate from control terminal */
413 #ifdef USE_SETPGRP_NOARGS       /* SysV */
414     setpgrp();
415 #elif defined(USE_SETPGRP)       /* BSD */
416     setpgrp(0,getpid());
417 #else                           /* POSIX */
418     setsid();
419 #endif
420     /* Open the slave pty */
421     if (sfd < 0) {
422 	/* not allocated by open_pty_master */
423 	if ((sfd = open_pty_slave(ptyslave)) < 0) {
424 	    ERRNO_ERR1(LOG_ERR,"Could not open pty slave '%s'", ptyslave);
425 	    exit(1);
426 	}
427 	/* But sfd may be one of the stdio fd's now, and we should be unmodern and not use dup2... */
428 	/* easiest to dup it up... */
429 	while (sfd < 3) {
430 	    sfd = dup(sfd);
431 	}
432     }
433 #if defined(HAVE_OPENPTY) && defined(TIOCSCTTY)
434     else {
435 	/* sfd is from open_pty_master
436 	 * openpty -> fork -> login_tty (forkpty)
437 	 *
438 	 * It would be preferable to implement a portable
439 	 * forkpty instead of open_pty_master / open_pty_slave
440 	 */
441 	/* login_tty(sfd);  <- FAIL */
442 	ioctl(sfd, TIOCSCTTY, (char *)NULL);
443     }
444 #endif
445 
446 #ifdef HAVE_SYSLOG_H
447     /* Before fiddling with file descriptors we make sure syslog is turned off
448        or "closed". In the single case where we might want it again,
449        we will open it again instead. Would not want syslog to
450        go to some other fd... */
451     if (run_daemon) {
452 	closelog();
453     }
454 #endif
455 
456     /* Close stdio */
457     sf_close(0);
458     sf_close(1);
459     sf_close(2);
460 
461     if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) {
462       status("Cannot dup\n");
463     }
464     sf_close(sfd);
465     exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */
466                         /* the command name, so we have to */
467                         /* adjust. */
468   } else {
469     /* Parent */
470     /* Ignore the SIGPIPE signal, write() will return errno=EPIPE */
471     struct sigaction sig_act;
472     sigemptyset(&sig_act.sa_mask);
473     sig_act.sa_flags = 0;
474     sig_act.sa_handler = SIG_IGN;
475     sigaction(SIGPIPE, &sig_act, (struct sigaction *)NULL);
476 
477     sigemptyset(&sig_act.sa_mask);
478     sig_act.sa_flags = SA_NOCLDSTOP;
479     sig_act.sa_handler = catch_sigchild;
480     sigaction(SIGCHLD, &sig_act, (struct sigaction *)NULL);
481 
482     /*
483      * read and write: enter the workloop
484      */
485 
486     pass_on(childpid);
487   }
488   return 0;
489 } /* main() */
490 
491 /* pass_on()
492  * Is the work loop of the logger. Selects on the pipe to the to_erl
493  * program erlang. If input arrives from to_erl it is passed on to
494  * erlang.
495  */
pass_on(pid_t childpid)496 static void pass_on(pid_t childpid)
497 {
498     int len;
499     fd_set readfds;
500     fd_set writefds;
501     fd_set* writefds_ptr;
502     struct timeval timeout;
503     time_t last_activity;
504     char buf[BUFSIZ];
505     char log_alive_buffer[ALIVE_BUFFSIZ+1];
506     int lognum;
507     int rfd, wfd=0, lfd=0;
508     int maxfd;
509     int ready;
510     int got_some = 0; /* from to_erl */
511 
512     /* Open the to_erl pipe for reading.
513      * We can't open the writing side because nobody is reading and
514      * we'd either hang or get an error.
515      */
516     if ((rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
517 	ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2);
518 	exit(1);
519     }
520 
521 #ifdef DEBUG
522     status("run_erl: %s opened for reading\n", fifo2);
523 #endif
524 
525     /* Open the log file */
526 
527     lognum = find_next_log_num();
528     lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC);
529 
530     /* Enter the work loop */
531 
532     while (1) {
533 	int exit_status;
534 	maxfd = MAX(rfd, mfd);
535 	maxfd = MAX(wfd, maxfd);
536 	FD_ZERO(&readfds);
537 	FD_SET(rfd, &readfds);
538 	FD_SET(mfd, &readfds);
539 	FD_ZERO(&writefds);
540 	if (outbuf_size() == 0) {
541 	    writefds_ptr = NULL;
542 	} else {
543 	    FD_SET(wfd, &writefds);
544 	    writefds_ptr = &writefds;
545 	}
546 	time(&last_activity);
547 	timeout.tv_sec  = log_alive_minutes*60; /* don't assume old BSD bug */
548 	timeout.tv_usec = 0;
549 	ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout);
550 	if (ready < 0) {
551 	    if (errno == EINTR) {
552 		if (waitpid(childpid, &exit_status, WNOHANG) == childpid) {
553 		    /*
554 		     * The Erlang emulator has terminated. Give us some more
555 		     * time to write out any pending data before we terminate too.
556 		     */
557 		    alarm(5);
558 		}
559 		FD_ZERO(&readfds);
560 		FD_ZERO(&writefds);
561 	    } else {
562 		/* Some error occurred */
563 		ERRNO_ERR0(LOG_ERR,"Error in select.");
564 		exit(1);
565 	    }
566 	} else {
567 	    time_t now;
568 
569 	    if (waitpid(childpid, &exit_status, WNOHANG) == childpid) {
570 		alarm(5);
571 		FD_ZERO(&readfds);
572 		FD_ZERO(&writefds);
573 	    }
574 
575 	    /* Check how long time we've been inactive */
576 	    time(&now);
577 	    if(!ready || now - last_activity > log_activity_minutes*60) {
578 		/* Either a time out: 15 minutes without action, */
579 		/* or something is coming in right now, but it's a long time */
580 		/* since last time, so let's write a time stamp this message */
581 		struct tm *tmptr;
582 		if (log_alive_in_gmt) {
583 		    tmptr = gmtime(&now);
584 		} else {
585 		    tmptr = localtime(&now);
586 		}
587 		if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format,
588 			      tmptr)) {
589 		    strn_cpy(log_alive_buffer, sizeof(log_alive_buffer),
590 			     "(could not format time in 256 positions "
591 			     "with current format string.)");
592 		}
593 		log_alive_buffer[ALIVE_BUFFSIZ] = '\0';
594 
595 		sn_printf(buf, sizeof(buf), "\n===== %s%s\n",
596 			  ready?"":"ALIVE ", log_alive_buffer);
597 		write_to_log(&lfd, &lognum, buf, strlen(buf));
598 	    }
599 	}
600 
601 	/*
602 	 * Write any pending output first.
603 	 */
604 	if (FD_ISSET(wfd, &writefds)) {
605 	    int written;
606 	    char* buf = outbuf_first();
607 
608 	    len = outbuf_size();
609 	    written = sf_write(wfd, buf, len);
610 	    if (written < 0 && errno == EAGAIN) {
611 		/*
612 		 * Nothing was written - this is really strange because
613 		 * select() told us we could write. Ignore.
614 		 */
615 	    } else if (written < 0) {
616 		/*
617 		 * A write error. Assume that to_erl has terminated.
618 		 */
619 		clear_outbuf();
620 		sf_close(wfd);
621 		wfd = 0;
622 	    } else {
623 		/* Delete the written part (or all) from the buffer. */
624 		outbuf_delete(written);
625 	    }
626 	}
627 
628 	/*
629 	 * Read master pty and write to FIFO.
630 	 */
631 	if (FD_ISSET(mfd, &readfds)) {
632 #ifdef DEBUG
633 	    status("Pty master read; ");
634 #endif
635 	    if ((len = sf_read(mfd, buf, BUFSIZ)) <= 0) {
636 		int saved_errno = errno;
637 		sf_close(rfd);
638 		if(wfd) sf_close(wfd);
639 		sf_close(mfd);
640 		unlink(fifo1);
641 		unlink(fifo2);
642 		if (len < 0) {
643 		    errno = saved_errno;
644 		    if(errno == EIO)
645 			ERROR0(LOG_ERR,"Erlang closed the connection.");
646 		    else
647 			ERRNO_ERR0(LOG_ERR,"Error in reading from terminal");
648 		    exit(1);
649 		}
650 		exit(0);
651 	    }
652 
653 	    write_to_log(&lfd, &lognum, buf, len);
654 
655 	    /*
656 	     * Save in the output queue.
657 	     */
658 
659 	    if (wfd) {
660 		outbuf_append(buf, len);
661 	    }
662 	}
663 
664 	/*
665 	 * Read from FIFO, write to master pty
666 	 */
667 	if (FD_ISSET(rfd, &readfds)) {
668 #ifdef DEBUG
669 	    status("FIFO read; ");
670 #endif
671 	    if ((len = sf_read(rfd, buf, BUFSIZ)) < 0) {
672 		sf_close(rfd);
673 		if(wfd) sf_close(wfd);
674 		sf_close(mfd);
675 		unlink(fifo1);
676 		unlink(fifo2);
677 		ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO.");
678 		exit(1);
679 	    }
680 
681 	    if(!len) {
682 		/* to_erl closed its end of the pipe */
683 		sf_close(rfd);
684 		rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0);
685 		if (rfd < 0) {
686 		    ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2);
687 		    exit(1);
688 		}
689 		got_some = 0; /* reset for next session */
690 	    }
691 	    else {
692 		if(!wfd) {
693 		    /* Try to open the write pipe to to_erl. Now that we got some data
694 		     * from to_erl, to_erl should already be reading this pipe - open
695 		     * should succeed. But in case of error, we just ignore it.
696 		     */
697 		    if ((wfd = sf_open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
698 			status("Client expected on FIFO %s, but can't open (len=%d)\n",
699 			       fifo1, len);
700 			sf_close(rfd);
701 			rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0);
702 			if (rfd < 0) {
703 			    ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2);
704 			    exit(1);
705 			}
706 			wfd = 0;
707 		    }
708 		    else {
709 #ifdef DEBUG
710 			status("run_erl: %s opened for writing\n", fifo1);
711 #endif
712 		    }
713 		}
714 
715 		if (!got_some && wfd && buf[0] == '\014') {
716 		    char wbuf[30];
717 		    int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n",
718 					 RUN_ERL_HI_VER, RUN_ERL_LO_VER);
719 		    outbuf_append(wbuf,wlen);
720 		}
721 		got_some = 1;
722 
723 
724 		/* Write the message */
725 #ifdef DEBUG
726 		status("Pty master write; ");
727 #endif
728 		len = extract_ctrl_seq(buf, len);
729 
730 		if(len==1 && buf[0] == '\003') {
731 		    kill(childpid,SIGINT);
732 		}
733 		else if (len>0 && write_all(mfd, buf, len) != len) {
734 		    ERRNO_ERR0(LOG_ERR,"Error in writing to terminal.");
735 		    sf_close(rfd);
736 		    if(wfd) sf_close(wfd);
737 		    sf_close(mfd);
738 		    exit(1);
739 		}
740 	    }
741 #ifdef DEBUG
742 	    status("OK\n");
743 #endif
744 	}
745     }
746 } /* pass_on() */
747 
catch_sigchild(int sig)748 static void catch_sigchild(int sig)
749 {
750 }
751 
752 /*
753  * next_log:
754  * Returns the index number that follows the given index number.
755  * (Wrapping after log_generations)
756  */
next_log(int log_num)757 static int next_log(int log_num) {
758   return log_num>=log_generations?1:log_num+1;
759 }
760 
761 /*
762  * prev_log:
763  * Returns the index number that precedes the given index number.
764  * (Wrapping after log_generations)
765  */
prev_log(int log_num)766 static int prev_log(int log_num) {
767   return log_num<=1?log_generations:log_num-1;
768 }
769 
770 /*
771  * find_next_log_num()
772  * Searches through the log directory to check which logs that already
773  * exist. It finds the "hole" in the sequence, and returns the index
774  * number for the last log in the log sequence. If there is no hole, index
775  * 1 is returned.
776  */
find_next_log_num(void)777 static int find_next_log_num(void) {
778   int i, next_gen, log_gen;
779   DIR *dirp;
780   struct dirent *direntp;
781   int log_exists[LOG_MAX_GENERATIONS+1];
782   int stub_len = strlen(LOG_STUBNAME);
783 
784   /* Initialize exiting log table */
785 
786   for(i=log_generations; i>=0; i--)
787     log_exists[i] = 0;
788   dirp = opendir(log_dir);
789   if(!dirp) {
790     ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir);
791     exit(1);
792   }
793 
794   /* Check the directory for existing logs */
795 
796   while((direntp=readdir(dirp)) != NULL) {
797     if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) {
798       int num = atoi(direntp->d_name+stub_len);
799       if(num < 1 || num > log_generations)
800 	continue;
801       log_exists[num] = 1;
802     }
803   }
804   closedir(dirp);
805 
806   /* Find out the next available log file number */
807 
808   next_gen = 0;
809   for(i=log_generations; i>=0; i--) {
810     if(log_exists[i])
811       if(next_gen)
812 	break;
813       else
814 	;
815     else
816       next_gen = i;
817   }
818 
819   /* Find out the current log file number */
820 
821   if(next_gen)
822     log_gen = prev_log(next_gen);
823   else
824     log_gen = 1;
825 
826   return log_gen;
827 } /* find_next_log_num() */
828 
829 /* open_log()
830  * Opens a log file (with given index) for writing. Writing may be
831  * at the end or a trucnating write, according to flags.
832  * A LOGGING STARTED and time stamp message is inserted into the log file
833  */
open_log(int log_num,int flags)834 static int open_log(int log_num, int flags)
835 {
836   char buf[FILENAME_MAX];
837   time_t now;
838   struct tm *tmptr;
839   char log_buffer[ALIVE_BUFFSIZ+1];
840   int lfd;
841 
842   /* Remove the next log (to keep a "hole" in the log sequence) */
843   sn_printf(buf, sizeof(buf), "%s/%s%d",
844 	    log_dir, LOG_STUBNAME, next_log(log_num));
845   unlink(buf);
846 
847   /* Create or continue on the current log file */
848   sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num);
849   if((lfd = sf_open(buf, flags, LOG_PERM))<0){
850       ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf);
851     exit(1);
852   }
853 
854   /* Write a LOGGING STARTED and time stamp into the log file */
855   time(&now);
856   if (log_alive_in_gmt) {
857       tmptr = gmtime(&now);
858   } else {
859       tmptr = localtime(&now);
860   }
861   if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format,
862 		tmptr)) {
863       strn_cpy(log_buffer, sizeof(log_buffer),
864 	      "(could not format time in 256 positions "
865 	      "with current format string.)");
866   }
867   log_buffer[ALIVE_BUFFSIZ] = '\0';
868 
869   sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n",
870 	    log_buffer);
871   if (write_all(lfd, buf, strlen(buf)) < 0)
872       status("Error in writing to log.\n");
873 
874 #ifdef USE_FSYNC
875   fsync(lfd);
876 #endif
877 
878   return lfd;
879 }
880 
881 /* write_to_log()
882  * Writes a message to a log file. If the current log file is full,
883  * a new log file is opened.
884  */
write_to_log(int * lfd,int * log_num,char * buf,int len)885 static void write_to_log(int* lfd, int* log_num, char* buf, int len)
886 {
887   int size;
888 
889   /* Decide if new logfile needed, and open if so */
890 
891   size = lseek(*lfd,0,SEEK_END);
892   if(size+len > log_maxsize) {
893     sf_close(*lfd);
894     *log_num = next_log(*log_num);
895     *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC);
896   }
897 
898   /* Write to log file */
899 
900   if (write_all(*lfd, buf, len) < 0) {
901     status("Error in writing to log.\n");
902   }
903 
904 #ifdef USE_FSYNC
905   fsync(*lfd);
906 #endif
907 }
908 
909 /* create_fifo()
910  * Creates a new fifo with the given name and permission.
911  */
create_fifo(char * name,int perm)912 static int create_fifo(char *name, int perm)
913 {
914   if ((mkfifo(name, perm) < 0) && (errno != EEXIST))
915     return -1;
916   return 0;
917 }
918 
919 
920 /* open_pty_master()
921  * Find a master device, open and return fd and slave device name.
922  */
923 
924 #ifdef HAVE_WORKING_POSIX_OPENPT
925    /*
926     * Use openpty() on OpenBSD even if we have posix_openpt()
927     * as there is a race when read from master pty returns 0
928     * if child has not yet opened slave pty.
929     * (maybe other BSD's have the same problem?)
930     */
931 #  if !(defined(__OpenBSD__) && defined(HAVE_OPENPTY))
932 #    define TRY_POSIX_OPENPT
933 #  endif
934 #endif
935 
open_pty_master(char ** ptyslave,int * sfdp)936 static int open_pty_master(char **ptyslave, int *sfdp)
937 {
938   int mfd;
939 
940 /* Use the posix_openpt if working, as this guarantees creation of the
941    slave device properly. */
942 #if defined(TRY_POSIX_OPENPT) || (defined(__sun) && defined(__SVR4))
943 #  ifdef TRY_POSIX_OPENPT
944   mfd = posix_openpt(O_RDWR);
945 #  elif defined(__sun) && defined(__SVR4)
946   mfd = sf_open("/dev/ptmx", O_RDWR, 0);
947 #  endif
948 
949   if (mfd >= 0) {
950       if ((*ptyslave = ptsname(mfd)) != NULL &&
951 	  grantpt(mfd) == 0 &&
952 	  unlockpt(mfd) == 0) {
953 
954 	  return mfd;
955       }
956       sf_close(mfd);
957   }
958   /* fallback to openpty if it exist */
959 #endif
960 
961 #if defined(HAVE_OPENPTY)
962 #  ifdef PATH_MAX
963 #    define SLAVE_SIZE PATH_MAX
964 #  else
965 #    define SLAVE_SIZE 1024
966 #  endif
967   {
968       static char slave[SLAVE_SIZE];
969 #  undef SLAVE_SIZE
970       if (openpty(&mfd, sfdp, slave, NULL, NULL) == 0) {
971 	  *ptyslave = slave;
972 	  return mfd;
973       }
974   }
975 
976 #elif !defined(HAVE_WORKING_POSIX_OPENPT)
977   /*
978    * The traditional way to find ptys. We only try it if neither
979    * posix_openpt or openpty() are available.
980    */
981   char *major, *minor;
982 
983   static char majorchars[] = "pqrstuvwxyzabcdePQRSTUVWXYZABCDE";
984   static char minorchars[] = "0123456789abcdefghijklmnopqrstuv"
985 			     "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_+";
986 
987   /* In the old time the names where /dex/ptyXY where */
988   /* X is in "pqrs" and Y in "0123456789abcdef" but FreeBSD */
989   /* and some Linux version has extended this. */
990 
991   /* This code could probebly be improved alot. For example look at */
992   /* http://www.xcf.berkeley.edu/~ali/K0D/UNIX/PTY/code/pty.c.html */
993   /* http://www.xcf.berkeley.edu/~ali/K0D/UNIX/PTY/code/upty.h.html */
994 
995   {
996     /* New style devpts or devfs /dev/pty/{m,s}{0,1....} */
997 
998     static char ptyname[] = "/dev/pty/mX";
999 
1000     for (minor = minorchars; *minor; minor++) {
1001       ptyname[10] = *minor;
1002 
1003       if ((mfd = sf_open(ptyname, O_RDWR, 0)) >= 0) {
1004 	ptyname[9] = 's';
1005 	*ptyslave = ptyname;
1006 	return mfd;
1007       }
1008     }
1009   }
1010 
1011   {
1012     /* Unix98 style /dev/ptym/ptyXY and /dev/pty/ttyXY */
1013 
1014     static char ptyname[] = "/dev/ptym/ptyXY";
1015     static char ttyname[] = "/dev/pty/ttyXY";
1016 
1017     for (major = majorchars; *major; major++) {
1018       ptyname[13] = *major;
1019       for (minor = minorchars; *minor; minor++) {
1020 	ptyname[14] = *minor;
1021 	if ((mfd = sf_open(ptyname, O_RDWR, 0)) >= 0) {
1022 	  ttyname[12] = *major;
1023 	  ttyname[13] = *minor;
1024 	  *ptyslave = ttyname;
1025 	  return mfd;
1026 	}
1027       }
1028     }
1029   }
1030 
1031   {
1032     /* Old style /dev/ptyXY */
1033 
1034     static char ptyname[] = "/dev/ptyXY";
1035 
1036     for (major = majorchars; *major; major++) {
1037       ptyname[8] = *major;
1038       for (minor = minorchars; *minor; minor++) {
1039 	ptyname[9] = *minor;
1040 	if ((mfd = sf_open(ptyname, O_RDWR, 0)) >= 0) {
1041 	  ptyname[5] = 't';
1042 	  *ptyslave = ptyname;
1043 	  return mfd;
1044 	}
1045       }
1046     }
1047   }
1048 #endif /* !HAVE_OPENPTY */
1049   return -1;
1050 }
1051 
open_pty_slave(char * name)1052 static int open_pty_slave(char *name)
1053 {
1054   int sfd;
1055   struct termios tty_rmode;
1056 
1057   if ((sfd = sf_open(name, O_RDWR, 0)) < 0) {
1058     return -1;
1059   }
1060 
1061 #if defined(__sun) && defined(__SVR4)
1062   /* Load the necessary STREAMS modules for Solaris */
1063   if ((ioctl(sfd, I_FIND, "ldterm")) < 0) {
1064     ERROR0(LOG_ERR, "Failed to find ldterm STREAMS module");
1065     return -1;
1066   }
1067   if (ioctl(sfd, I_PUSH, "ptem") < 0) {
1068     ERROR0(LOG_ERR, "Failed to push ptem STREAMS module");
1069     return -1;
1070   }
1071   if (ioctl(sfd, I_PUSH, "ldterm") < 0) {
1072     ERROR0(LOG_ERR, "Failed to push ldterm STREAMS module");
1073     return -1;
1074   }
1075   if (ioctl(sfd, I_PUSH, "ttcompat") < 0) {
1076     ERROR0(LOG_ERR, "Failed to push ttcompat STREAMS module");
1077     return -1;
1078   }
1079 #endif
1080 
1081   if (getenv("RUN_ERL_DISABLE_FLOWCNTRL")) {
1082     if (tcgetattr(sfd, &tty_rmode) < 0) {
1083       fprintf(stderr, "Cannot get terminal's current mode\n");
1084       exit(-1);
1085     }
1086 
1087     tty_rmode.c_iflag &= ~IXOFF;
1088     if (tcsetattr(sfd, TCSANOW, &tty_rmode) < 0) {
1089       fprintf(stderr, "Cannot disable terminal's flow control on input\n");
1090       exit(-1);
1091     }
1092 
1093     tty_rmode.c_iflag &= ~IXON;
1094     if (tcsetattr(sfd, TCSANOW, &tty_rmode) < 0) {
1095       fprintf(stderr, "Cannot disable terminal's flow control on output\n");
1096       exit(-1);
1097     }
1098   }
1099 
1100 #ifdef DEBUG
1101   if (tcgetattr(sfd, &tty_rmode) < 0) {
1102     fprintf(stderr, "Cannot get terminals current mode\n");
1103     exit(-1);
1104   }
1105   show_terminal_settings(&tty_rmode);
1106 #endif
1107 
1108   return sfd;
1109 }
1110 
1111 /* exec_shell()
1112  * Executes the named command (in argv format) in a /bin/sh. IO redirection
1113  * should already have been taken care of, and this process should be the
1114  * child of a fork.
1115  */
exec_shell(char ** argv)1116 static void exec_shell(char **argv)
1117 {
1118   char *sh, **vp;
1119   int i;
1120 
1121   sh = "/bin/sh";
1122   if ((argv[0] = strrchr(sh, '/')) != NULL)
1123     argv[0]++;
1124   else
1125     argv[0] = sh;
1126   argv[1] = "-c";
1127   status("Args before exec of shell:\n");
1128   for (vp = argv, i = 0; *vp; vp++, i++)
1129     status("argv[%d] = %s\n", i, *vp);
1130   if (stdstatus) {
1131       fclose(stdstatus);
1132   }
1133   execv(sh, argv);
1134   if (run_daemon) {
1135       OPEN_SYSLOG();
1136   }
1137   ERRNO_ERR0(LOG_ERR,"Could not execv");
1138 }
1139 
1140 /* status()
1141  * Prints the arguments to a status file
1142  * Works like printf (see vfrpintf)
1143  */
status(const char * format,...)1144 static void status(const char *format,...)
1145 {
1146   va_list args;
1147   time_t now;
1148 
1149   if (stdstatus == NULL)
1150     stdstatus = fopen(statusfile, "w");
1151   if (stdstatus == NULL)
1152     return;
1153   now = time(NULL);
1154   fprintf(stdstatus, "run_erl [%d] %s", (int)getpid(), ctime(&now));
1155   va_start(args, format);
1156   vfprintf(stdstatus, format, args);
1157   va_end(args);
1158   fflush(stdstatus);
1159 }
1160 
daemon_init(void)1161 static void daemon_init(void)
1162      /* As R Stevens wants it, to a certain extent anyway... */
1163 {
1164     pid_t pid;
1165     int i, maxfd = HIGHEST_FILENO();
1166 
1167     if ((pid = fork()) != 0)
1168 	exit(0);
1169 #if defined(USE_SETPGRP_NOARGS)
1170     setpgrp();
1171 #elif defined(USE_SETPGRP)
1172     setpgrp(0,getpid());
1173 #else
1174     setsid(); /* Seems to be the case on all current platforms */
1175 #endif
1176     signal(SIGHUP, SIG_IGN);
1177     if ((pid = fork()) != 0)
1178 	exit(0);
1179 
1180     /* Should change working directory to "/" and change umask now, but that
1181        would be backward incompatible */
1182 
1183     for (i = 0; i < maxfd; ++i ) {
1184 	sf_close(i);
1185     }
1186 
1187     /* Necessary on some platforms */
1188 
1189     open("/dev/null", O_RDONLY); /* Order is important! */
1190     open("/dev/null", O_WRONLY);
1191     open("/dev/null", O_WRONLY);
1192 
1193     errno = 0;  /* if set by open */
1194 
1195     OPEN_SYSLOG();
1196     run_daemon = 1;
1197 }
1198 
1199 /* error_logf()
1200  * Prints the arguments to stderr or syslog
1201  * Works like printf (see vfprintf)
1202  */
error_logf(int priority,int line,const char * format,...)1203 static void error_logf(int priority, int line, const char *format, ...)
1204 {
1205     va_list args;
1206     va_start(args, format);
1207 
1208 #ifdef HAVE_SYSLOG_H
1209     if (run_daemon) {
1210 #ifdef HAVE_VSYSLOG
1211 	vsyslog(priority,format,args);
1212 #else
1213 	/* Some OSes like AIX lack vsyslog. */
1214 	va_list ap;
1215 	char message[900]; /* AIX man page says truncation past this */
1216 
1217 	va_start (ap, format);
1218 	vsnprintf(message, 900, format, ap);
1219 	va_end(ap);
1220 
1221 	syslog(priority, message);
1222 #endif
1223     }
1224     else
1225 #endif
1226     {
1227 	time_t now = time(NULL);
1228 	fprintf(stderr, "run_erl:%d [%d] %s", line, (int)getpid(), ctime(&now));
1229 	vfprintf(stderr, format, args);
1230     }
1231     va_end(args);
1232 }
1233 
usage(char * pname)1234 static void usage(char *pname)
1235 {
1236   fprintf(stderr, "Usage: %s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"\n", pname);
1237   fprintf(stderr, "\nYou may also set the environment variables RUN_ERL_LOG_GENERATIONS\n");
1238   fprintf(stderr, "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n");
1239   fprintf(stderr, "size of the log file when to switch to the next log file\n");
1240 }
1241 
1242 /* Instead of making sure basename exists, we do our own */
simple_basename(char * path)1243 static char *simple_basename(char *path)
1244 {
1245     char *ptr;
1246     for (ptr = path; *ptr != '\0'; ++ptr) {
1247 	if (*ptr == '/') {
1248 	    path = ptr + 1;
1249 	}
1250     }
1251     return path;
1252 }
1253 
init_outbuf(void)1254 static void init_outbuf(void)
1255 {
1256     outbuf_total = 1;
1257     outbuf_base = malloc(BUFSIZ);
1258     clear_outbuf();
1259 }
1260 
clear_outbuf(void)1261 static void clear_outbuf(void)
1262 {
1263     outbuf_in = outbuf_out = outbuf_base;
1264 }
1265 
outbuf_size(void)1266 static int outbuf_size(void)
1267 {
1268     return outbuf_in - outbuf_out;
1269 }
1270 
outbuf_first(void)1271 static char* outbuf_first(void)
1272 {
1273     return outbuf_out;
1274 }
1275 
outbuf_delete(int bytes)1276 static void outbuf_delete(int bytes)
1277 {
1278     outbuf_out += bytes;
1279     if (outbuf_out >= outbuf_in) {
1280 	outbuf_in = outbuf_out = outbuf_base;
1281     }
1282 }
1283 
outbuf_append(const char * buf,int n)1284 static void outbuf_append(const char* buf, int n)
1285 {
1286     if (outbuf_base+outbuf_total < outbuf_in+n) {
1287 	/*
1288 	 * The new data does not fit at the end of the buffer.
1289 	 * Slide down the data to the beginning of the buffer.
1290 	 */
1291 	if (outbuf_out > outbuf_base) {
1292 	    int size = outbuf_in - outbuf_out;
1293 	    char* p;
1294 
1295 	    outbuf_in -= outbuf_out - outbuf_base;
1296 	    p = outbuf_base;
1297 	    while (size-- > 0) {
1298 		*p++ = *outbuf_out++;
1299 	    }
1300 	    outbuf_out = outbuf_base;
1301 	}
1302 
1303 	/*
1304 	 * Allocate a larger buffer if we still cannot fit the data.
1305 	 */
1306 	if (outbuf_base+outbuf_total < outbuf_in+n) {
1307 	    int size = outbuf_in - outbuf_out;
1308 	    outbuf_total = size+n;
1309 	    outbuf_base = realloc(outbuf_base, outbuf_total);
1310 	    outbuf_out = outbuf_base;
1311 	    outbuf_in = outbuf_base + size;
1312 	}
1313     }
1314 
1315     /*
1316      * Copy data to the end of the buffer.
1317      */
1318     memcpy(outbuf_in, buf, n);
1319     outbuf_in += n;
1320 }
1321 
1322 /* Call write() until entire buffer has been written or error.
1323  * Return len or -1.
1324  */
write_all(int fd,const char * buf,int len)1325 static int write_all(int fd, const char* buf, int len)
1326 {
1327     int left = len;
1328     int written;
1329     for (;;) {
1330 	written = sf_write(fd,buf,left);
1331 	if (written == left) {
1332 	    return len;
1333 	}
1334 	if (written < 0) {
1335 	    return -1;
1336 	}
1337 	left -= written;
1338 	buf += written;
1339     }
1340 }
1341 
sf_read(int fd,void * buffer,size_t len)1342 static ssize_t sf_read(int fd, void *buffer, size_t len) {
1343     ssize_t n = 0;
1344 
1345     do { n = read(fd, buffer, len); } while (n < 0 && errno == EINTR);
1346 
1347     return n;
1348 }
1349 
sf_write(int fd,const void * buffer,size_t len)1350 static ssize_t sf_write(int fd, const void *buffer, size_t len) {
1351     ssize_t n = 0;
1352 
1353     do { n = write(fd, buffer, len); } while (n < 0 && errno == EINTR);
1354 
1355     return n;
1356 }
1357 
sf_open(const char * path,int type,mode_t mode)1358 static int sf_open(const char *path, int type, mode_t mode) {
1359     int fd = 0;
1360 
1361     do { fd = open(path, type, mode); } while(fd < 0 && errno == EINTR);
1362 
1363     return fd;
1364 }
1365 
sf_close(int fd)1366 static int sf_close(int fd) {
1367     /* "close() should not be retried after an EINTR" */
1368     return close(fd);
1369 }
1370 
1371 /* Extract any control sequences that are ment only for run_erl
1372  * and should not be forwarded to the pty.
1373  */
extract_ctrl_seq(char * buf,int len)1374 static int extract_ctrl_seq(char* buf, int len)
1375 {
1376     static const char prefix[] = "\033_";
1377     static const char suffix[] = "\033\\";
1378     char* bufend = buf + len;
1379     char* start = buf;
1380     char* command;
1381     char* end;
1382 
1383     for (;;) {
1384 	start = find_str(start, bufend-start, prefix);
1385 	if (!start) break;
1386 
1387 	command = start + strlen(prefix);
1388 	end = find_str(command, bufend-command, suffix);
1389 	if (end) {
1390 	    unsigned col, row;
1391 	    if (sscanf(command,"version=%u", &protocol_ver)==1) {
1392 		/*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/
1393 	    }
1394 	    else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) {
1395 		set_window_size(col,row);
1396 	    }
1397 	    else {
1398 		ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n",
1399 		       (int)(end-command), command);
1400 	    }
1401 
1402 	    /* Remove ctrl sequence from buf */
1403 	    end += strlen(suffix);
1404 	    memmove(start, end, bufend-end);
1405 	    bufend -= end - start;
1406 	}
1407 	else {
1408 	    ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n",
1409 		   (int)(bufend-start), start);
1410 	    break;
1411 	}
1412     }
1413     return bufend - buf;
1414 }
1415 
set_window_size(unsigned col,unsigned row)1416 static void set_window_size(unsigned col, unsigned row)
1417 {
1418 #ifdef TIOCSWINSZ
1419     struct winsize ws;
1420     ws.ws_col = col;
1421     ws.ws_row = row;
1422     if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) {
1423 	ERRNO_ERR0(LOG_ERR,"Failed to set window size");
1424     }
1425 #endif
1426 }
1427 
1428 
1429 #ifdef DEBUG
1430 
1431 #define S(x)  ((x) > 0 ? 1 : 0)
1432 
show_terminal_settings(struct termios * t)1433 static void show_terminal_settings(struct termios *t)
1434 {
1435   printf("c_iflag:\n");
1436   printf("Signal interrupt on break:   BRKINT  %d\n", S(t->c_iflag & BRKINT));
1437   printf("Map CR to NL on input:       ICRNL   %d\n", S(t->c_iflag & ICRNL));
1438   printf("Ignore break condition:      IGNBRK  %d\n", S(t->c_iflag & IGNBRK));
1439   printf("Ignore CR:                   IGNCR   %d\n", S(t->c_iflag & IGNCR));
1440   printf("Ignore char with par. err's: IGNPAR  %d\n", S(t->c_iflag & IGNPAR));
1441   printf("Map NL to CR on input:       INLCR   %d\n", S(t->c_iflag & INLCR));
1442   printf("Enable input parity check:   INPCK   %d\n", S(t->c_iflag & INPCK));
1443   printf("Strip character              ISTRIP  %d\n", S(t->c_iflag & ISTRIP));
1444   printf("Enable start/stop input ctrl IXOFF   %d\n", S(t->c_iflag & IXOFF));
1445   printf("ditto output ctrl            IXON    %d\n", S(t->c_iflag & IXON));
1446   printf("Mark parity errors           PARMRK  %d\n", S(t->c_iflag & PARMRK));
1447   printf("\n");
1448   printf("c_oflag:\n");
1449   printf("Perform output processing    OPOST   %d\n", S(t->c_oflag & OPOST));
1450   printf("\n");
1451   printf("c_cflag:\n");
1452   printf("Ignore modem status lines    CLOCAL  %d\n", S(t->c_cflag & CLOCAL));
1453   printf("\n");
1454   printf("c_local:\n");
1455   printf("Enable echo                  ECHO    %d\n", S(t->c_lflag & ECHO));
1456   printf("\n");
1457   printf("c_cc:\n");
1458   printf("c_cc[VEOF]                           %d\n", t->c_cc[VEOF]);
1459 }
1460 
1461 #endif /* DEBUG */
1462 
1463 
1464