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