2 * tty.c - code for handling serial ports in pppd.
4 * Copyright (C) 2000 Paul Mackerras.
7 * Portions Copyright (c) 1989 Carnegie Mellon University.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by Carnegie Mellon University. The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 #define RCSID "$Id: tty.c,v 1.1.1.4 2003/10/14 08:09:53 sparq Exp $"
38 #include <sys/param.h>
39 #include <sys/types.h>
42 #include <sys/resource.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
52 void tty_process_extra_options
__P((void));
53 void tty_check_options
__P((void));
54 int connect_tty
__P((void));
55 void disconnect_tty
__P((void));
56 void tty_close_fds
__P((void));
57 void cleanup_tty
__P((void));
58 void tty_do_send_config
__P((int, u_int32_t
, int, int));
60 static int setdevname
__P((char *, char **, int));
61 static int setspeed
__P((char *, char **, int));
62 static int setxonxoff
__P((char **));
63 static int setescape
__P((char **));
64 static void printescape
__P((option_t
*, void (*)(void *, char *,...),void *));
65 static void finish_tty
__P((void));
66 static int start_charshunt
__P((int, int));
67 static void stop_charshunt
__P((void *, int));
68 static void charshunt_done
__P((void *));
69 static void charshunt
__P((int, int, char *));
70 static int record_write
__P((FILE *, int code
, u_char
*buf
, int nb
,
72 static int open_socket
__P((char *));
73 static void maybe_relock
__P((void *, int));
75 static int pty_master
; /* fd for master side of pty */
76 static int pty_slave
; /* fd for slave side of pty */
77 static int real_ttyfd
; /* fd for actual serial port (not pty) */
78 static int ttyfd
; /* Serial port file descriptor */
79 static char speed_str
[16]; /* Serial port speed as string */
81 mode_t tty_mode
= (mode_t
)-1; /* Original access permissions to tty */
82 int baud_rate
; /* Actual bits/second for serial device */
83 char *callback_script
; /* script for doing callback */
84 int charshunt_pid
; /* Process ID for charshunt */
85 int locked
; /* lock() has succeeded */
86 struct stat devstat
; /* result of stat() on devnam */
88 /* option variables */
89 int crtscts
= 0; /* Use hardware flow control */
90 bool modem
= 1; /* Use modem control lines */
91 int inspeed
= 0; /* Input/Output speed requested */
92 bool lockflag
= 0; /* Create lock file to lock the serial dev */
93 char *initializer
= NULL
; /* Script to initialize physical link */
94 char *connect_script
= NULL
; /* Script to establish physical link */
95 char *disconnect_script
= NULL
; /* Script to disestablish physical link */
96 char *welcomer
= NULL
; /* Script to run after phys link estab. */
97 char *ptycommand
= NULL
; /* Command to run on other side of pty */
98 bool notty
= 0; /* Stdin/out is not a tty */
99 char *record_file
= NULL
; /* File to record chars sent/received */
100 int max_data_rate
; /* max bytes/sec through charshunt */
101 bool sync_serial
= 0; /* Device is synchronous serial device */
102 char *pty_socket
= NULL
; /* Socket to connect to pty */
103 int using_pty
= 0; /* we're allocating a pty as the device */
106 extern int kill_link
;
108 extern int privopen
; /* don't lock, open device as root */
110 u_int32_t xmit_accm
[8]; /* extended transmit ACCM */
112 /* option descriptors */
113 option_t tty_options
[] = {
114 /* device name must be first, or change connect_tty() below! */
115 { "device name", o_wild
, (void *) &setdevname
,
116 "Serial port device name",
117 OPT_DEVNAM
| OPT_PRIVFIX
| OPT_NOARG
| OPT_A2STRVAL
| OPT_STATIC
,
120 { "tty speed", o_wild
, (void *) &setspeed
,
121 "Baud rate for serial port",
122 OPT_PRIO
| OPT_NOARG
| OPT_A2STRVAL
| OPT_STATIC
, speed_str
},
124 { "lock", o_bool
, &lockflag
,
125 "Lock serial device with UUCP-style lock file", OPT_PRIO
| 1 },
126 { "nolock", o_bool
, &lockflag
,
127 "Don't lock serial device", OPT_PRIOSUB
| OPT_PRIV
},
129 { "init", o_string
, &initializer
,
130 "A program to initialize the device", OPT_PRIO
| OPT_PRIVFIX
},
132 { "connect", o_string
, &connect_script
,
133 "A program to set up a connection", OPT_PRIO
| OPT_PRIVFIX
},
135 { "disconnect", o_string
, &disconnect_script
,
136 "Program to disconnect serial device", OPT_PRIO
| OPT_PRIVFIX
},
138 { "welcome", o_string
, &welcomer
,
139 "Script to welcome client", OPT_PRIO
| OPT_PRIVFIX
},
141 { "pty", o_string
, &ptycommand
,
142 "Script to run on pseudo-tty master side",
143 OPT_PRIO
| OPT_PRIVFIX
| OPT_DEVNAM
},
145 { "notty", o_bool
, ¬ty
,
146 "Input/output is not a tty", OPT_DEVNAM
| 1 },
148 { "socket", o_string
, &pty_socket
,
149 "Send and receive over socket, arg is host:port",
150 OPT_PRIO
| OPT_DEVNAM
},
152 { "record", o_string
, &record_file
,
153 "Record characters sent/received to file", OPT_PRIO
},
155 { "crtscts", o_int
, &crtscts
,
156 "Set hardware (RTS/CTS) flow control",
157 OPT_PRIO
| OPT_NOARG
| OPT_VAL(1) },
158 { "cdtrcts", o_int
, &crtscts
,
159 "Set alternate hardware (DTR/CTS) flow control",
160 OPT_PRIOSUB
| OPT_NOARG
| OPT_VAL(2) },
161 { "nocrtscts", o_int
, &crtscts
,
162 "Disable hardware flow control",
163 OPT_PRIOSUB
| OPT_NOARG
| OPT_VAL(-1) },
164 { "-crtscts", o_int
, &crtscts
,
165 "Disable hardware flow control",
166 OPT_PRIOSUB
| OPT_ALIAS
| OPT_NOARG
| OPT_VAL(-1) },
167 { "nocdtrcts", o_int
, &crtscts
,
168 "Disable hardware flow control",
169 OPT_PRIOSUB
| OPT_ALIAS
| OPT_NOARG
| OPT_VAL(-1) },
170 { "xonxoff", o_special_noarg
, (void *)setxonxoff
,
171 "Set software (XON/XOFF) flow control", OPT_PRIOSUB
},
173 { "modem", o_bool
, &modem
,
174 "Use modem control lines", OPT_PRIO
| 1 },
175 { "local", o_bool
, &modem
,
176 "Don't use modem control lines", OPT_PRIOSUB
| 0 },
178 { "sync", o_bool
, &sync_serial
,
179 "Use synchronous HDLC serial encoding", 1 },
181 { "datarate", o_int
, &max_data_rate
,
182 "Maximum data rate in bytes/sec (with pty, notty or record option)",
185 { "escape", o_special
, (void *)setescape
,
186 "List of character codes to escape on transmission",
187 OPT_A2PRINTER
, (void *)printescape
},
193 struct channel tty_channel
= {
195 &tty_process_extra_options
,
200 &tty_disestablish_ppp
,
208 * setspeed - Set the serial port baud rate.
209 * If doit is 0, the call is to check whether this option is
210 * potentially a speed value.
213 setspeed(arg
, argv
, doit
)
221 spd
= strtol(arg
, &ptr
, 0);
222 if (ptr
== arg
|| *ptr
!= 0 || spd
== 0)
226 slprintf(speed_str
, sizeof(speed_str
), "%d", spd
);
233 * setdevname - Set the device name.
234 * If doit is 0, the call is to check whether this option is
235 * potentially a device name.
238 setdevname(cp
, argv
, doit
)
244 char dev
[MAXPATHLEN
];
249 if (strncmp("/dev/", cp
, 5) != 0) {
250 strlcpy(dev
, "/dev/", sizeof(dev
));
251 strlcat(dev
, cp
, sizeof(dev
));
256 * Check if there is a character device by this name.
258 if (stat(cp
, &statbuf
) < 0) {
260 return errno
!= ENOENT
;
261 option_error("Couldn't stat %s: %m", cp
);
264 if (!S_ISCHR(statbuf
.st_mode
)) {
266 option_error("%s is not a character device", cp
);
271 strlcpy(devnam
, cp
, sizeof(devnam
));
283 lcp_wantoptions
[0].asyncmap
|= 0x000A0000; /* escape ^S and ^Q */
284 lcp_wantoptions
[0].neg_asyncmap
= 1;
291 * setescape - add chars to the set we escape on transmission.
303 n
= strtol(p
, &endp
, 16);
305 option_error("escape parameter contains invalid hex number '%s'",
310 if (n
< 0 || n
== 0x5E || n
> 0xFF) {
311 option_error("can't escape character 0x%x", n
);
314 xmit_accm
[n
>> 5] |= 1 << (n
& 0x1F);
315 while (*p
== ',' || *p
== ' ')
318 lcp_allowoptions
[0].asyncmap
= xmit_accm
[0];
323 printescape(opt
, printer
, arg
)
325 void (*printer
) __P((void *, char *, ...));
331 for (n
= 0; n
< 256; ++n
) {
333 n
+= 2; /* skip 7d, 7e */
334 if (xmit_accm
[n
>> 5] & (1 << (n
& 0x1f))) {
339 printer(arg
, "%x", n
);
343 printer(arg
, "oops # nothing escaped");
347 * tty_init - do various tty-related initializations.
351 add_notifier(&pidchange
, maybe_relock
, 0);
352 the_channel
= &tty_channel
;
353 xmit_accm
[3] = 0x60000000;
357 * tty_process_extra_options - work out which tty device we are using
358 * and read its options file.
360 void tty_process_extra_options()
362 using_pty
= notty
|| ptycommand
!= NULL
|| pty_socket
!= NULL
;
365 if (default_device
) {
367 if (!isatty(0) || (p
= ttyname(0)) == NULL
) {
368 option_error("no device specified and stdin is not a tty");
369 exit(EXIT_OPTION_ERROR
);
371 strlcpy(devnam
, p
, sizeof(devnam
));
372 if (stat(devnam
, &devstat
) < 0)
373 fatal("Couldn't stat default device %s: %m", devnam
);
378 * Parse the tty options file.
379 * The per-tty options file should not change
380 * ptycommand, pty_socket, notty or devnam.
381 * options_for_tty doesn't override options set on the command line,
382 * except for some privileged options.
384 if (!options_for_tty())
385 exit(EXIT_OPTION_ERROR
);
389 * tty_check_options - do consistency checks on the options we were given.
397 if (demand
&& connect_script
== 0) {
398 option_error("connect script is required for demand-dialling\n");
399 exit(EXIT_OPTION_ERROR
);
401 /* default holdoff to 0 if no connect script has been given */
402 if (connect_script
== 0 && !holdoff_specified
)
406 if (!default_device
) {
407 option_error("%s option precludes specifying device name",
408 notty
? "notty": "pty");
409 exit(EXIT_OPTION_ERROR
);
411 if (ptycommand
!= NULL
&& notty
) {
412 option_error("pty option is incompatible with notty option");
413 exit(EXIT_OPTION_ERROR
);
415 if (pty_socket
!= NULL
&& (ptycommand
!= NULL
|| notty
)) {
416 option_error("socket option is incompatible with pty and notty");
417 exit(EXIT_OPTION_ERROR
);
419 default_device
= notty
;
422 if (notty
&& log_to_fd
<= 1)
426 * If the user has specified a device which is the same as
427 * the one on stdin, pretend they didn't specify any.
428 * If the device is already open read/write on stdin,
429 * we assume we don't need to lock it, and we can open it
432 if (fstat(0, &statbuf
) >= 0 && S_ISCHR(statbuf
.st_mode
)
433 && statbuf
.st_rdev
== devstat
.st_rdev
) {
435 fdflags
= fcntl(0, F_GETFL
);
436 if (fdflags
!= -1 && (fdflags
& O_ACCMODE
) == O_RDWR
)
444 * Don't send log messages to the serial port, it tends to
445 * confuse the peer. :-)
447 if (log_to_fd
>= 0 && fstat(log_to_fd
, &statbuf
) >= 0
448 && S_ISCHR(statbuf
.st_mode
) && statbuf
.st_rdev
== devstat
.st_rdev
)
453 * connect_tty - get the serial port ready to start doing PPP.
454 * That is, open the serial port, set its speed and mode, and run
455 * the connector and/or welcomer.
465 * Get a pty master/slave pair if the pty, notty, socket,
466 * or record options were specified.
468 strlcpy(ppp_devnam
, devnam
, sizeof(ppp_devnam
));
472 if (using_pty
|| record_file
!= NULL
) {
473 if (!get_pty(&pty_master
, &pty_slave
, ppp_devnam
, uid
)) {
474 error("Couldn't allocate pseudo-tty");
475 status
= EXIT_FATAL_ERROR
;
478 set_up_tty(pty_slave
, 1);
482 * Lock the device if we've been asked to.
484 status
= EXIT_LOCK_FAILED
;
485 if (lockflag
&& !privopen
) {
486 if (lock(devnam
) < 0)
492 * Open the serial device and set it up to be the ppp interface.
493 * First we open it in non-blocking mode so we can set the
494 * various termios flags appropriately. If we aren't dialling
495 * out and we want to use the modem lines, we reopen it later
496 * in order to wait for the carrier detect signal from the modem.
500 connector
= doing_callback
? callback_script
: connect_script
;
501 if (devnam
[0] != 0) {
503 /* If the user specified the device name, become the
504 user before opening it. */
507 prio
= privopen
? OPRIO_ROOT
: tty_options
[0].priority
;
508 if (prio
< OPRIO_ROOT
)
510 ttyfd
= open(devnam
, O_NONBLOCK
| O_RDWR
, 0);
512 if (prio
< OPRIO_ROOT
)
518 error("Failed to open %s: %m", devnam
);
519 status
= EXIT_OPEN_FAILED
;
521 if (!persist
|| err
!= EINTR
)
525 if ((fdflags
= fcntl(ttyfd
, F_GETFL
)) == -1
526 || fcntl(ttyfd
, F_SETFL
, fdflags
& ~O_NONBLOCK
) < 0)
527 warn("Couldn't reset non-blocking mode on device: %m");
530 * Do the equivalent of `mesg n' to stop broadcast messages.
532 if (fstat(ttyfd
, &statbuf
) < 0
533 || fchmod(ttyfd
, statbuf
.st_mode
& ~(S_IWGRP
| S_IWOTH
)) < 0) {
534 warn("Couldn't restrict write permissions to %s: %m", devnam
);
536 tty_mode
= statbuf
.st_mode
;
539 * Set line speed, flow control, etc.
540 * If we have a non-null connection or initializer script,
541 * on most systems we set CLOCAL for now so that we can talk
542 * to the modem before carrier comes up. But this has the
543 * side effect that we might miss it if CD drops before we
544 * get to clear CLOCAL below. On systems where we can talk
545 * successfully to the modem with CLOCAL clear and CD down,
546 * we could clear CLOCAL at this point.
548 set_up_tty(ttyfd
, ((connector
!= NULL
&& connector
[0] != 0)
549 || initializer
!= NULL
));
553 * If the pty, socket, notty and/or record option was specified,
554 * start up the character shunt now.
556 status
= EXIT_PTYCMD_FAILED
;
557 if (ptycommand
!= NULL
) {
558 if (record_file
!= NULL
) {
559 int ipipe
[2], opipe
[2], ok
;
561 if (pipe(ipipe
) < 0 || pipe(opipe
) < 0)
562 fatal("Couldn't create pipes for record option: %m");
563 ok
= device_script(ptycommand
, opipe
[0], ipipe
[1], 1) == 0
564 && start_charshunt(ipipe
[0], opipe
[1]);
572 if (device_script(ptycommand
, pty_master
, pty_master
, 1) < 0)
578 } else if (pty_socket
!= NULL
) {
579 int fd
= open_socket(pty_socket
);
582 if (!start_charshunt(fd
, fd
))
585 if (!start_charshunt(0, 1))
587 } else if (record_file
!= NULL
) {
588 if (!start_charshunt(ttyfd
, ttyfd
))
592 /* run connection script */
593 if ((connector
&& connector
[0]) || initializer
) {
594 if (real_ttyfd
!= -1) {
595 if (!default_device
&& modem
) {
596 setdtr(real_ttyfd
, 0); /* in case modem is off hook */
598 setdtr(real_ttyfd
, 1);
602 if (initializer
&& initializer
[0]) {
603 if (device_script(initializer
, ttyfd
, ttyfd
, 0) < 0) {
604 error("Initializer script failed");
605 status
= EXIT_INIT_FAILED
;
612 info("Serial port initialized.");
615 if (connector
&& connector
[0]) {
616 if (device_script(connector
, ttyfd
, ttyfd
, 0) < 0) {
617 error("Connect script failed");
618 status
= EXIT_CONNECT_FAILED
;
625 info("Serial connection established.");
628 /* set line speed, flow control, etc.;
629 clear CLOCAL if modem option */
630 if (real_ttyfd
!= -1)
631 set_up_tty(real_ttyfd
, 0);
633 if (doing_callback
== CALLBACK_DIALIN
)
637 /* reopen tty if necessary to wait for carrier */
638 if (connector
== NULL
&& modem
&& devnam
[0] != 0) {
641 if ((i
= open(devnam
, O_RDWR
)) >= 0)
643 if (errno
!= EINTR
) {
644 error("Failed to reopen %s: %m", devnam
);
645 status
= EXIT_OPEN_FAILED
;
647 if (!persist
|| errno
!= EINTR
|| hungup
|| kill_link
)
653 slprintf(numbuf
, sizeof(numbuf
), "%d", baud_rate
);
654 script_setenv("SPEED", numbuf
, 0);
656 /* run welcome script, if any */
657 if (welcomer
&& welcomer
[0]) {
658 if (device_script(welcomer
, ttyfd
, ttyfd
, 0) < 0)
659 warn("Welcome script failed");
663 * If we are initiating this connection, wait for a short
664 * time for something from the peer. This can avoid bouncing
665 * our packets off his tty before he has it set up.
667 if (connector
!= NULL
|| ptycommand
!= NULL
)
668 listen_time
= connect_delay
;
674 void disconnect_tty()
676 if (disconnect_script
== NULL
|| hungup
)
679 set_up_tty(real_ttyfd
, 1);
680 if (device_script(disconnect_script
, ttyfd
, ttyfd
, 0) < 0) {
681 warn("disconnect script failed");
683 info("Serial link disconnected.");
693 if (real_ttyfd
>= 0) {
697 /* N.B. ttyfd will == either pty_slave or real_ttyfd */
712 * tty_do_send_config - set transmit-side PPP configuration.
713 * We set the extended transmit ACCM here as well.
716 tty_do_send_config(mtu
, accm
, pcomp
, accomp
)
721 tty_set_xaccm(xmit_accm
);
722 tty_send_config(mtu
, accm
, pcomp
, accomp
);
726 * finish_tty - restore the terminal device to its original settings
731 /* drop dtr to hang up */
732 if (!default_device
&& modem
) {
733 setdtr(real_ttyfd
, 0);
735 * This sleep is in case the serial port has CLOCAL set by default,
736 * and consequently will reassert DTR when we close the device.
741 restore_tty(real_ttyfd
);
743 if (tty_mode
!= (mode_t
) -1) {
744 if (fchmod(real_ttyfd
, tty_mode
) != 0) {
745 chmod(devnam
, tty_mode
);
754 * maybe_relock - our PID has changed, maybe update the lock file.
757 maybe_relock(arg
, pid
)
766 * open_socket - establish a stream socket connection to the nominated
773 char *sep
, *endp
= NULL
;
776 struct hostent
*hent
;
777 struct sockaddr_in sad
;
779 /* parse host:port and resolve host to an IP address */
780 sep
= strchr(dest
, ':');
782 port
= strtol(sep
+1, &endp
, 10);
783 if (port
< 0 || endp
== sep
+1 || sep
== dest
) {
784 error("Can't parse host:port for socket destination");
788 host
= inet_addr(dest
);
789 if (host
== (u_int32_t
) -1) {
790 hent
= gethostbyname(dest
);
792 error("%s: unknown host in socket option", dest
);
796 host
= *(u_int32_t
*)(hent
->h_addr_list
[0]);
800 /* get a socket and connect it to the other end */
801 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
803 error("Can't create socket: %m");
806 memset(&sad
, 0, sizeof(sad
));
807 sad
.sin_family
= AF_INET
;
808 sad
.sin_port
= htons(port
);
809 sad
.sin_addr
.s_addr
= host
;
810 if (connect(sock
, (struct sockaddr
*)&sad
, sizeof(sad
)) < 0) {
811 error("Can't connect to %s: %m", dest
);
821 * start_charshunt - create a child process to run the character shunt.
824 start_charshunt(ifd
, ofd
)
831 error("Can't fork process for character shunt: %m");
839 fatal("setuid failed");
843 charshunt(ifd
, ofd
, record_file
);
846 charshunt_pid
= cpid
;
847 add_notifier(&sigreceived
, stop_charshunt
, 0);
851 record_child(cpid
, "pppd (charshunt)", charshunt_done
, NULL
);
863 stop_charshunt(arg
, sig
)
868 kill(charshunt_pid
, (sig
== SIGINT
? sig
: SIGTERM
));
872 * charshunt - the character shunt, which passes characters between
873 * the pty master side and the serial port (or stdin/stdout).
874 * This runs as the user (not as root).
875 * (We assume ofd >= ifd which is true the way this gets called. :-).
878 charshunt(ifd
, ofd
, record_file
)
883 fd_set ready
, writey
;
884 u_char
*ibufp
, *obufp
;
887 int pty_readable
, stdin_readable
;
888 struct timeval lasttime
;
889 FILE *recordf
= NULL
;
890 int ilevel
, olevel
, max_level
;
891 struct timeval levelt
, tout
, *top
;
892 extern u_char inpacket_buf
[];
895 * Reset signal handlers.
897 signal(SIGHUP
, SIG_IGN
); /* Hangup */
898 signal(SIGINT
, SIG_DFL
); /* Interrupt */
899 signal(SIGTERM
, SIG_DFL
); /* Terminate */
900 signal(SIGCHLD
, SIG_DFL
);
901 signal(SIGUSR1
, SIG_DFL
);
902 signal(SIGUSR2
, SIG_DFL
);
903 signal(SIGABRT
, SIG_DFL
);
904 signal(SIGALRM
, SIG_DFL
);
905 signal(SIGFPE
, SIG_DFL
);
906 signal(SIGILL
, SIG_DFL
);
907 signal(SIGPIPE
, SIG_DFL
);
908 signal(SIGQUIT
, SIG_DFL
);
909 signal(SIGSEGV
, SIG_DFL
);
911 signal(SIGBUS
, SIG_DFL
);
914 signal(SIGEMT
, SIG_DFL
);
917 signal(SIGPOLL
, SIG_DFL
);
920 signal(SIGPROF
, SIG_DFL
);
923 signal(SIGSYS
, SIG_DFL
);
926 signal(SIGTRAP
, SIG_DFL
);
929 signal(SIGVTALRM
, SIG_DFL
);
932 signal(SIGXCPU
, SIG_DFL
);
935 signal(SIGXFSZ
, SIG_DFL
);
939 * Open the record file if required.
941 if (record_file
!= NULL
) {
942 recordf
= fopen(record_file
, "a");
944 error("Couldn't create record file %s: %m", record_file
);
947 /* set all the fds to non-blocking mode */
948 flags
= fcntl(pty_master
, F_GETFL
);
950 || fcntl(pty_master
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
951 warn("couldn't set pty master to nonblock: %m");
952 flags
= fcntl(ifd
, F_GETFL
);
954 || fcntl(ifd
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
955 warn("couldn't set %s to nonblock: %m", (ifd
==0? "stdin": "tty"));
957 flags
= fcntl(ofd
, F_GETFL
);
959 || fcntl(ofd
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
960 warn("couldn't set stdout to nonblock: %m");
964 ibufp
= obufp
= NULL
;
965 pty_readable
= stdin_readable
= 1;
968 gettimeofday(&levelt
, NULL
);
970 max_level
= max_data_rate
/ 10;
974 max_level
= PPP_MRU
+ PPP_HDRLEN
+ 1;
976 nfds
= (ofd
> pty_master
? ofd
: pty_master
) + 1;
977 if (recordf
!= NULL
) {
978 gettimeofday(&lasttime
, NULL
);
979 putc(7, recordf
); /* put start marker */
980 putc(lasttime
.tv_sec
>> 24, recordf
);
981 putc(lasttime
.tv_sec
>> 16, recordf
);
982 putc(lasttime
.tv_sec
>> 8, recordf
);
983 putc(lasttime
.tv_sec
, recordf
);
984 lasttime
.tv_usec
= 0;
987 while (nibuf
!= 0 || nobuf
!= 0 || pty_readable
|| stdin_readable
) {
990 tout
.tv_usec
= 10000;
994 if (ilevel
>= max_level
)
997 FD_SET(pty_master
, &writey
);
998 } else if (stdin_readable
)
1001 if (olevel
>= max_level
)
1004 FD_SET(ofd
, &writey
);
1005 } else if (pty_readable
)
1006 FD_SET(pty_master
, &ready
);
1007 if (select(nfds
, &ready
, &writey
, NULL
, top
) < 0) {
1012 if (max_data_rate
) {
1017 gettimeofday(&now
, NULL
);
1018 dt
= (now
.tv_sec
- levelt
.tv_sec
1019 + (now
.tv_usec
- levelt
.tv_usec
) / 1e6
);
1020 nbt
= (int)(dt
* max_data_rate
);
1021 ilevel
= (nbt
< 0 || nbt
> ilevel
)? 0: ilevel
- nbt
;
1022 olevel
= (nbt
< 0 || nbt
> olevel
)? 0: olevel
- nbt
;
1025 ilevel
= olevel
= 0;
1026 if (FD_ISSET(ifd
, &ready
)) {
1027 ibufp
= inpacket_buf
;
1028 nibuf
= read(ifd
, ibufp
, PPP_MRU
+ PPP_HDRLEN
);
1029 if (nibuf
< 0 && errno
== EIO
)
1032 if (!(errno
== EINTR
|| errno
== EAGAIN
)) {
1033 error("Error reading standard input: %m");
1037 } else if (nibuf
== 0) {
1038 /* end of file from stdin */
1040 /* do a 0-length write, hopefully this will generate
1041 an EOF (hangup) on the slave side. */
1042 write(pty_master
, inpacket_buf
, 0);
1044 if (!record_write(recordf
, 4, NULL
, 0, &lasttime
))
1047 FD_SET(pty_master
, &writey
);
1049 if (!record_write(recordf
, 2, ibufp
, nibuf
, &lasttime
))
1053 if (FD_ISSET(pty_master
, &ready
)) {
1054 obufp
= outpacket_buf
;
1055 nobuf
= read(pty_master
, obufp
, PPP_MRU
+ PPP_HDRLEN
);
1056 if (nobuf
< 0 && errno
== EIO
)
1059 if (!(errno
== EINTR
|| errno
== EAGAIN
)) {
1060 error("Error reading pseudo-tty master: %m");
1064 } else if (nobuf
== 0) {
1065 /* end of file from the pty - slave side has closed */
1067 stdin_readable
= 0; /* pty is not writable now */
1071 if (!record_write(recordf
, 3, NULL
, 0, &lasttime
))
1074 FD_SET(ofd
, &writey
);
1076 if (!record_write(recordf
, 1, obufp
, nobuf
, &lasttime
))
1080 if (FD_ISSET(ofd
, &writey
)) {
1082 if (olevel
+ n
> max_level
)
1083 n
= max_level
- olevel
;
1084 n
= write(ofd
, obufp
, n
);
1089 } else if (errno
!= EAGAIN
&& errno
!= EINTR
) {
1090 error("Error writing standard output: %m");
1099 if (FD_ISSET(pty_master
, &writey
)) {
1101 if (ilevel
+ n
> max_level
)
1102 n
= max_level
- ilevel
;
1103 n
= write(pty_master
, ibufp
, n
);
1108 } else if (errno
!= EAGAIN
&& errno
!= EINTR
) {
1109 error("Error writing pseudo-tty master: %m");
1123 record_write(f
, code
, buf
, nb
, tp
)
1133 gettimeofday(&now
, NULL
);
1134 now
.tv_usec
/= 100000; /* actually 1/10 s, not usec now */
1135 diff
= (now
.tv_sec
- tp
->tv_sec
) * 10 + (now
.tv_usec
- tp
->tv_usec
);
1139 putc(diff
>> 24, f
);
1140 putc(diff
>> 16, f
);
1153 fwrite(buf
, nb
, 1, f
);
1157 error("Error writing record file: %m");