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