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