Home | History | Annotate | Line # | Download | only in pppd
      1 /*	$NetBSD: tty.c,v 1.6 2025/01/08 19:59:39 christos Exp $	*/
      2 
      3 /*
      4  * tty.c - code for handling serial ports in pppd.
      5  *
      6  * Copyright (C) 2000-2024 Paul Mackerras. All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in
     17  *    the documentation and/or other materials provided with the
     18  *    distribution.
     19  *
     20  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
     21  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     22  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
     23  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     24  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     25  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     26  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     27  *
     28  * Portions derived from main.c, which is:
     29  *
     30  * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
     31  *
     32  * Redistribution and use in source and binary forms, with or without
     33  * modification, are permitted provided that the following conditions
     34  * are met:
     35  *
     36  * 1. Redistributions of source code must retain the above copyright
     37  *    notice, this list of conditions and the following disclaimer.
     38  *
     39  * 2. Redistributions in binary form must reproduce the above copyright
     40  *    notice, this list of conditions and the following disclaimer in
     41  *    the documentation and/or other materials provided with the
     42  *    distribution.
     43  *
     44  * 3. The name "Carnegie Mellon University" must not be used to
     45  *    endorse or promote products derived from this software without
     46  *    prior written permission. For permission or any legal
     47  *    details, please contact
     48  *      Office of Technology Transfer
     49  *      Carnegie Mellon University
     50  *      5000 Forbes Avenue
     51  *      Pittsburgh, PA  15213-3890
     52  *      (412) 268-4387, fax: (412) 268-7395
     53  *      tech-transfer (at) andrew.cmu.edu
     54  *
     55  * 4. Redistributions of any form whatsoever must retain the following
     56  *    acknowledgment:
     57  *    "This product includes software developed by Computing Services
     58  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
     59  *
     60  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
     61  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     62  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
     63  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     64  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     65  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     66  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     67  */
     68 
     69 #include <sys/cdefs.h>
     70 __RCSID("$NetBSD: tty.c,v 1.6 2025/01/08 19:59:39 christos Exp $");
     71 
     72 #ifdef HAVE_CONFIG_H
     73 #include "config.h"
     74 #endif
     75 
     76 #include <stdio.h>
     77 #include <ctype.h>
     78 #include <stdlib.h>
     79 #include <string.h>
     80 #include <termios.h>
     81 #include <unistd.h>
     82 #include <signal.h>
     83 #include <errno.h>
     84 #include <fcntl.h>
     85 #include <syslog.h>
     86 #include <netdb.h>
     87 #include <utmp.h>
     88 #include <pwd.h>
     89 #include <sys/param.h>
     90 #include <sys/types.h>
     91 #include <sys/wait.h>
     92 #include <sys/time.h>
     93 #include <sys/resource.h>
     94 #include <sys/stat.h>
     95 #include <sys/socket.h>
     96 #include <netinet/in.h>
     97 #include <arpa/inet.h>
     98 
     99 #include "pppd-private.h"
    100 #include "options.h"
    101 #include "fsm.h"
    102 #include "lcp.h"
    103 
    104 void tty_process_extra_options(void);
    105 void tty_check_options(void);
    106 int  connect_tty(void);
    107 void disconnect_tty(void);
    108 void tty_close_fds(void);
    109 void cleanup_tty(void);
    110 void tty_do_send_config(int, u_int32_t, int, int);
    111 
    112 static int setdevname(char *, char **, int);
    113 static int setspeed(char *, char **, int);
    114 static int setxonxoff(char **);
    115 static int setescape(char **);
    116 static void printescape(struct option *, void (*)(void *, char *,...),void *);
    117 static void finish_tty(void);
    118 static int start_charshunt(int, int);
    119 static void stop_charshunt(void *, int);
    120 static void charshunt_done(void *);
    121 static void charshunt(int, int, char *);
    122 static int record_write(FILE *, int code, u_char *buf, int nb,
    123 			struct timeval *);
    124 static int open_socket(char *);
    125 static void maybe_relock(void *, int);
    126 
    127 static int pty_master;		/* fd for master side of pty */
    128 static int pty_slave;		/* fd for slave side of pty */
    129 static int real_ttyfd;		/* fd for actual serial port (not pty) */
    130 static int ttyfd;		/* Serial port file descriptor */
    131 static char speed_str[16];	/* Serial port speed as string */
    132 
    133 mode_t tty_mode = (mode_t)-1;	/* Original access permissions to tty */
    134 int baud_rate;			/* Actual bits/second for serial device */
    135 char *callback_script;		/* script for doing callback */
    136 int charshunt_pid;		/* Process ID for charshunt */
    137 int locked;			/* lock() has succeeded */
    138 struct stat devstat;		/* result of stat() on devnam */
    139 
    140 /* option variables */
    141 char	devnam[MAXPATHLEN];	/* Device name */
    142 char	ppp_devname[MAXPATHLEN];/* name of PPP tty (maybe ttypx) */
    143 int	crtscts = 0;		/* Use hardware flow control */
    144 int	stop_bits = 1;		/* Number of serial port stop bits */
    145 bool	modem = 1;		/* Use modem control lines */
    146 int	inspeed = 0;		/* Input/Output speed requested */
    147 bool	lockflag = 0;		/* Create lock file to lock the serial dev */
    148 char	*initializer = NULL;	/* Script to initialize physical link */
    149 char	*connect_script = NULL;	/* Script to establish physical link */
    150 char	*disconnect_script = NULL; /* Script to disestablish physical link */
    151 char	*welcomer = NULL;	/* Script to run after phys link estab. */
    152 char	*ptycommand = NULL;	/* Command to run on other side of pty */
    153 bool	notty = 0;		/* Stdin/out is not a tty */
    154 char	*record_file = NULL;	/* File to record chars sent/received */
    155 int	max_data_rate;		/* max bytes/sec through charshunt */
    156 bool	sync_serial = 0;	/* Device is synchronous serial device */
    157 char	*pty_socket = NULL;	/* Socket to connect to pty */
    158 int	using_pty = 0;		/* we're allocating a pty as the device */
    159 
    160 extern uid_t uid;
    161 extern int kill_link;
    162 extern int asked_to_quit;
    163 extern int got_sigterm;
    164 
    165 /* XXX */
    166 extern int privopen;		/* don't lock, open device as root */
    167 
    168 u_int32_t xmit_accm[8];		/* extended transmit ACCM */
    169 
    170 /* option descriptors */
    171 static struct option tty_options[] = {
    172     /* device name must be first, or change connect_tty() below! */
    173     { "device name", o_wild, (void *) &setdevname,
    174       "Serial port device name",
    175       OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
    176       devnam},
    177 
    178     { "tty speed", o_wild, (void *) &setspeed,
    179       "Baud rate for serial port",
    180       OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str },
    181 
    182     { "lock", o_bool, &lockflag,
    183       "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 },
    184     { "nolock", o_bool, &lockflag,
    185       "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV },
    186 
    187     { "init", o_string, &initializer,
    188       "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX },
    189 
    190     { "connect", o_string, &connect_script,
    191       "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX },
    192 
    193     { "disconnect", o_string, &disconnect_script,
    194       "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX },
    195 
    196     { "welcome", o_string, &welcomer,
    197       "Script to welcome client", OPT_PRIO | OPT_PRIVFIX },
    198 
    199     { "pty", o_string, &ptycommand,
    200       "Script to run on pseudo-tty master side",
    201       OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM },
    202 
    203     { "notty", o_bool, &notty,
    204       "Input/output is not a tty", OPT_DEVNAM | 1 },
    205 
    206     { "socket", o_string, &pty_socket,
    207       "Send and receive over socket, arg is host:port",
    208       OPT_PRIO | OPT_DEVNAM },
    209 
    210     { "record", o_string, &record_file,
    211       "Record characters sent/received to file", OPT_PRIO },
    212 
    213     { "crtscts", o_int, &crtscts,
    214       "Set hardware (RTS/CTS) flow control",
    215       OPT_PRIO | OPT_NOARG | OPT_VAL(1) },
    216     { "cdtrcts", o_int, &crtscts,
    217       "Set alternate hardware (DTR/CTS) flow control",
    218       OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) },
    219     { "nocrtscts", o_int, &crtscts,
    220       "Disable hardware flow control",
    221       OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
    222     { "-crtscts", o_int, &crtscts,
    223       "Disable hardware flow control",
    224       OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
    225     { "nocdtrcts", o_int, &crtscts,
    226       "Disable hardware flow control",
    227       OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
    228     { "xonxoff", o_special_noarg, (void *)setxonxoff,
    229       "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
    230     { "stop-bits", o_int, &stop_bits,
    231       "Number of stop bits in serial port",
    232       OPT_PRIO | OPT_PRIVFIX | OPT_LIMITS, NULL, 2, 1 },
    233 
    234     { "modem", o_bool, &modem,
    235       "Use modem control lines", OPT_PRIO | 1 },
    236     { "local", o_bool, &modem,
    237       "Don't use modem control lines", OPT_PRIOSUB | 0 },
    238 
    239     { "sync", o_bool, &sync_serial,
    240       "Use synchronous HDLC serial encoding", 1 },
    241 
    242     { "datarate", o_int, &max_data_rate,
    243       "Maximum data rate in bytes/sec (with pty, notty or record option)",
    244       OPT_PRIO },
    245 
    246     { "escape", o_special, (void *)setescape,
    247       "List of character codes to escape on transmission",
    248       OPT_A2PRINTER, (void *)printescape },
    249 
    250     { NULL }
    251 };
    252 
    253 
    254 struct channel tty_channel = {
    255 	tty_options,
    256 	&tty_process_extra_options,
    257 	&tty_check_options,
    258 	&connect_tty,
    259 	&disconnect_tty,
    260 	&tty_establish_ppp,
    261 	&tty_disestablish_ppp,
    262 	&tty_do_send_config,
    263 	&tty_recv_config,
    264 	&cleanup_tty,
    265 	&tty_close_fds
    266 };
    267 
    268 bool
    269 ppp_sync_serial()
    270 {
    271     return sync_serial;
    272 }
    273 
    274 bool
    275 ppp_get_modem()
    276 {
    277     return modem;
    278 }
    279 
    280 void
    281 ppp_set_modem(bool on)
    282 {
    283     modem = on;
    284 }
    285 
    286 bool
    287 ppp_using_pty()
    288 {
    289     return using_pty;
    290 }
    291 
    292 int
    293 ppp_set_pppdevnam(const char *name)
    294 {
    295     if (name) {
    296         return strlcpy(ppp_devname, name, sizeof(ppp_devname));
    297     }
    298     return -1;
    299 }
    300 
    301 const char *
    302 ppp_pppdevnam()
    303 {
    304     return ppp_devname;
    305 }
    306 
    307 const char *
    308 ppp_devnam()
    309 {
    310     return devnam;
    311 }
    312 
    313 int
    314 ppp_set_devnam(const char *name)
    315 {
    316     if (name) {
    317         return strlcpy(devnam, name, sizeof(devnam));
    318     }
    319     return -1;
    320 }
    321 
    322 
    323 /*
    324  * setspeed - Set the serial port baud rate.
    325  * If doit is 0, the call is to check whether this option is
    326  * potentially a speed value.
    327  */
    328 static int
    329 setspeed(char *arg, char **argv, int doit)
    330 {
    331 	char *ptr;
    332 	int spd;
    333 
    334 	spd = strtol(arg, &ptr, 0);
    335 	if (ptr == arg || *ptr != 0 || spd == 0)
    336 		return 0;
    337 	if (doit) {
    338 		inspeed = spd;
    339 		slprintf(speed_str, sizeof(speed_str), "%d", spd);
    340 	}
    341 	return 1;
    342 }
    343 
    344 
    345 /*
    346  * setdevname - Set the device name.
    347  * If doit is 0, the call is to check whether this option is
    348  * potentially a device name.
    349  */
    350 static int
    351 setdevname(char *cp, char **argv, int doit)
    352 {
    353 	struct stat statbuf;
    354 	char dev[MAXPATHLEN];
    355 
    356 	if (*cp == 0)
    357 		return 0;
    358 
    359 	if (*cp != '/') {
    360 		strlcpy(dev, "/dev/", sizeof(dev));
    361 		strlcat(dev, cp, sizeof(dev));
    362 		cp = dev;
    363 	}
    364 
    365 	/*
    366 	 * Check if there is a character device by this name.
    367 	 */
    368 	if (stat(cp, &statbuf) < 0) {
    369 		if (!doit)
    370 			return errno != ENOENT;
    371 		ppp_option_error("Couldn't stat %s: %m", cp);
    372 		return 0;
    373 	}
    374 	if (!S_ISCHR(statbuf.st_mode)) {
    375 		if (doit)
    376 			ppp_option_error("%s is not a character device", cp);
    377 		return 0;
    378 	}
    379 
    380 	if (doit) {
    381 		strlcpy(devnam, cp, MAXPATHLEN);
    382 		devstat = statbuf;
    383 		default_device = 0;
    384 	}
    385 
    386 	return 1;
    387 }
    388 
    389 static int
    390 setxonxoff(char **argv)
    391 {
    392 	lcp_wantoptions[0].asyncmap |= 0x000A0000;	/* escape ^S and ^Q */
    393 	lcp_wantoptions[0].neg_asyncmap = 1;
    394 
    395 	crtscts = -2;
    396 	return 1;
    397 }
    398 
    399 /*
    400  * setescape - add chars to the set we escape on transmission.
    401  */
    402 static int
    403 setescape(char **argv)
    404 {
    405     int n, ret;
    406     char *p, *endp;
    407 
    408     p = *argv;
    409     ret = 1;
    410     while (*p) {
    411 	n = strtol(p, &endp, 16);
    412 	if (p == endp) {
    413 	    ppp_option_error("escape parameter contains invalid hex number '%s'",
    414 			 p);
    415 	    return 0;
    416 	}
    417 	p = endp;
    418 	if (n < 0 || n == 0x5E || n > 0xFF) {
    419 	    ppp_option_error("can't escape character 0x%x", n);
    420 	    ret = 0;
    421 	} else
    422 	    xmit_accm[n >> 5] |= 1U << (n & 0x1F);
    423 	while (*p == ',' || *p == ' ')
    424 	    ++p;
    425     }
    426     lcp_allowoptions[0].asyncmap = xmit_accm[0];
    427     return ret;
    428 }
    429 
    430 static void
    431 printescape(struct option *opt, void (*printer)(void *, char *, ...), void *arg)
    432 {
    433 	int n;
    434 	int first = 1;
    435 
    436 	for (n = 0; n < 256; ++n) {
    437 		if (n == 0x7d)
    438 			n += 2;		/* skip 7d, 7e */
    439 		if (xmit_accm[n >> 5] & (1U << (n & 0x1f))) {
    440 			if (!first)
    441 				printer(arg, ",");
    442 			else
    443 				first = 0;
    444 			printer(arg, "%x", n);
    445 		}
    446 	}
    447 	if (first)
    448 		printer(arg, "oops # nothing escaped");
    449 }
    450 
    451 /*
    452  * tty_init - do various tty-related initializations.
    453  */
    454 void tty_init(void)
    455 {
    456     ppp_add_notify(NF_PID_CHANGE, maybe_relock, 0);
    457     the_channel = &tty_channel;
    458     xmit_accm[3] = 0x60000000U;
    459 }
    460 
    461 /*
    462  * tty_process_extra_options - work out which tty device we are using
    463  * and read its options file.
    464  */
    465 void tty_process_extra_options(void)
    466 {
    467 	using_pty = notty || ptycommand != NULL || pty_socket != NULL;
    468 	if (using_pty)
    469 		return;
    470 	if (default_device) {
    471 		char *p;
    472 		if (!isatty(0) || (p = ttyname(0)) == NULL) {
    473 			ppp_option_error("no device specified and stdin is not a tty");
    474 			exit(EXIT_OPTION_ERROR);
    475 		}
    476 		strlcpy(devnam, p, MAXPATHLEN);
    477 		if (stat(devnam, &devstat) < 0)
    478 			fatal("Couldn't stat default device %s: %m", devnam);
    479 	}
    480 
    481 
    482 	/*
    483 	 * Parse the tty options file.
    484 	 * The per-tty options file should not change
    485 	 * ptycommand, pty_socket, notty or devnam.
    486 	 * options_for_tty doesn't override options set on the command line,
    487 	 * except for some privileged options.
    488 	 */
    489 	if (!options_for_tty())
    490 		exit(EXIT_OPTION_ERROR);
    491 }
    492 
    493 /*
    494  * tty_check_options - do consistency checks on the options we were given.
    495  */
    496 void
    497 tty_check_options(void)
    498 {
    499 	struct stat statbuf;
    500 	int fdflags;
    501 
    502 	if (demand && notty) {
    503 		ppp_option_error("demand-dialling is incompatible with notty");
    504 		exit(EXIT_OPTION_ERROR);
    505 	}
    506 	if (demand && connect_script == 0 && ptycommand == NULL
    507 	    && pty_socket == NULL) {
    508 		ppp_option_error("connect script is required for demand-dialling\n");
    509 		exit(EXIT_OPTION_ERROR);
    510 	}
    511 	/* default holdoff to 0 if no connect script has been given */
    512 	if (connect_script == 0 && !holdoff_specified)
    513 		holdoff = 0;
    514 
    515 	if (using_pty) {
    516 		if (!default_device) {
    517 			ppp_option_error("%s option precludes specifying device name",
    518 				     pty_socket? "socket": notty? "notty": "pty");
    519 			exit(EXIT_OPTION_ERROR);
    520 		}
    521 		if (ptycommand != NULL && notty) {
    522 			ppp_option_error("pty option is incompatible with notty option");
    523 			exit(EXIT_OPTION_ERROR);
    524 		}
    525 		if (pty_socket != NULL && (ptycommand != NULL || notty)) {
    526 			ppp_option_error("socket option is incompatible with pty and notty");
    527 			exit(EXIT_OPTION_ERROR);
    528 		}
    529 		default_device = notty;
    530 		lockflag = 0;
    531 		modem = 0;
    532 		if (notty && log_to_fd <= 1)
    533 			log_to_fd = -1;
    534 	} else {
    535 		/*
    536 		 * If the user has specified a device which is the same as
    537 		 * the one on stdin, pretend they didn't specify any.
    538 		 * If the device is already open read/write on stdin,
    539 		 * we assume we don't need to lock it, and we can open it
    540 		 * as root.
    541 		 */
    542 		if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)
    543 		    && statbuf.st_rdev == devstat.st_rdev) {
    544 			default_device = 1;
    545 			fdflags = fcntl(0, F_GETFL);
    546 			if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)
    547 				privopen = 1;
    548 		}
    549 	}
    550 	if (default_device)
    551 		nodetach = 1;
    552 
    553 	/*
    554 	 * Don't send log messages to the serial port, it tends to
    555 	 * confuse the peer. :-)
    556 	 */
    557 	if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0
    558 	    && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
    559 		log_to_fd = -1;
    560 }
    561 
    562 /*
    563  * connect_tty - get the serial port ready to start doing PPP.
    564  * That is, open the serial port, set its speed and mode, and run
    565  * the connector and/or welcomer.
    566  */
    567 int connect_tty(void)
    568 {
    569 	char *connector;
    570 	int fdflags;
    571 #ifndef __linux__
    572 	struct stat statbuf;
    573 #endif
    574 	char numbuf[16];
    575 
    576 	/*
    577 	 * Get a pty master/slave pair if the pty, notty, socket,
    578 	 * or record options were specified.
    579 	 */
    580 	strlcpy(ppp_devname, devnam, MAXPATHLEN);
    581 	pty_master = -1;
    582 	pty_slave = -1;
    583 	real_ttyfd = -1;
    584 	if (using_pty || record_file != NULL) {
    585 		if (!get_pty(&pty_master, &pty_slave, ppp_devname, uid)) {
    586 			error("Couldn't allocate pseudo-tty");
    587 			ppp_set_status(EXIT_FATAL_ERROR);
    588 			return -1;
    589 		}
    590 		set_up_tty(pty_slave, 1);
    591 	}
    592 
    593 	/*
    594 	 * Lock the device if we've been asked to.
    595 	 */
    596 	ppp_set_status(EXIT_LOCK_FAILED);
    597 	if (lockflag && !privopen) {
    598 		if (lock(devnam) < 0)
    599 			goto errret;
    600 		locked = 1;
    601 	}
    602 
    603 	/*
    604 	 * Open the serial device and set it up to be the ppp interface.
    605 	 * First we open it in non-blocking mode so we can set the
    606 	 * various termios flags appropriately.  If we aren't dialling
    607 	 * out and we want to use the modem lines, we reopen it later
    608 	 * in order to wait for the carrier detect signal from the modem.
    609 	 */
    610 	got_sigterm = 0;
    611 	connector = doing_callback? callback_script: connect_script;
    612 	if (devnam[0] != 0) {
    613 		for (;;) {
    614 			/* If the user specified the device name, become the
    615 			   user before opening it. */
    616 			int err, prio;
    617 
    618 			prio = privopen? OPRIO_ROOT: tty_options[0].priority;
    619 			if (prio < OPRIO_ROOT && seteuid(uid) == -1) {
    620 				error("Unable to drop privileges before opening %s: %m\n",
    621 				      devnam);
    622 				ppp_set_status(EXIT_OPEN_FAILED);
    623 				goto errret;
    624 			}
    625 			real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
    626 			err = errno;
    627 			if (prio < OPRIO_ROOT && seteuid(0) == -1)
    628 				fatal("Unable to regain privileges");
    629 			if (real_ttyfd >= 0)
    630 				break;
    631 			errno = err;
    632 			if (err != EINTR) {
    633 				error("Failed to open %s: %m", devnam);
    634 				ppp_set_status(EXIT_OPEN_FAILED);
    635 			}
    636 			if (!persist || err != EINTR)
    637 				goto errret;
    638 		}
    639 		ttyfd = real_ttyfd;
    640 		if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
    641 		    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
    642 			warn("Couldn't reset non-blocking mode on device: %m");
    643 
    644 #ifndef __linux__
    645 		/*
    646 		 * Linux 2.4 and above blocks normal writes to the tty
    647 		 * when it is in PPP line discipline, so this isn't needed.
    648 		 */
    649 		/*
    650 		 * Do the equivalent of `mesg n' to stop broadcast messages.
    651 		 */
    652 		if (fstat(ttyfd, &statbuf) < 0
    653 		    || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
    654 			warn("Couldn't restrict write permissions to %s: %m", devnam);
    655 		} else
    656 			tty_mode = statbuf.st_mode;
    657 #endif /* __linux__ */
    658 
    659 		/*
    660 		 * Set line speed, flow control, etc.
    661 		 * If we have a non-null connection or initializer script,
    662 		 * on most systems we set CLOCAL for now so that we can talk
    663 		 * to the modem before carrier comes up.  But this has the
    664 		 * side effect that we might miss it if CD drops before we
    665 		 * get to clear CLOCAL below.  On systems where we can talk
    666 		 * successfully to the modem with CLOCAL clear and CD down,
    667 		 * we could clear CLOCAL at this point.
    668 		 */
    669 		set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
    670 				   || initializer != NULL));
    671 	}
    672 
    673 	/*
    674 	 * If the pty, socket, notty and/or record option was specified,
    675 	 * start up the character shunt now.
    676 	 */
    677 	ppp_set_status(EXIT_PTYCMD_FAILED);
    678 	if (ptycommand != NULL) {
    679 		if (record_file != NULL) {
    680 			int ipipe[2], opipe[2], ok;
    681 
    682 			if (pipe(ipipe) < 0 || pipe(opipe) < 0)
    683 				fatal("Couldn't create pipes for record option: %m");
    684 
    685 			/* don't leak these to the ptycommand */
    686 			(void) fcntl(ipipe[0], F_SETFD, FD_CLOEXEC);
    687 			(void) fcntl(opipe[1], F_SETFD, FD_CLOEXEC);
    688 
    689 			ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
    690 				&& start_charshunt(ipipe[0], opipe[1]);
    691 			close(ipipe[0]);
    692 			close(ipipe[1]);
    693 			close(opipe[0]);
    694 			close(opipe[1]);
    695 			if (!ok)
    696 				goto errret;
    697 		} else {
    698 			if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
    699 				goto errret;
    700 		}
    701 	} else if (pty_socket != NULL) {
    702 		int fd = open_socket(pty_socket);
    703 		if (fd < 0)
    704 			goto errret;
    705 		if (!start_charshunt(fd, fd))
    706 			goto errret;
    707 		close(fd);
    708 	} else if (notty) {
    709 		if (!start_charshunt(0, 1))
    710 			goto errret;
    711 		dup2(fd_devnull, 0);
    712 		dup2(fd_devnull, 1);
    713 		if (log_to_fd == 1)
    714 			log_to_fd = -1;
    715 		if (log_to_fd != 2)
    716 			dup2(fd_devnull, 2);
    717 	} else if (record_file != NULL) {
    718 		int fd = dup(ttyfd);
    719 		if (!start_charshunt(fd, fd))
    720 			goto errret;
    721 	}
    722 
    723 	if (using_pty || record_file != NULL) {
    724 		ttyfd = pty_slave;
    725 		close(pty_master);
    726 		pty_master = -1;
    727 	}
    728 
    729 	/* run connection script */
    730 	if ((connector && connector[0]) || initializer) {
    731 		if (real_ttyfd != -1) {
    732 			/* XXX do this if doing_callback == CALLBACK_DIALIN? */
    733 			if (!default_device && modem) {
    734 				setdtr(real_ttyfd, 0);	/* in case modem is off hook */
    735 				sleep(1);
    736 				setdtr(real_ttyfd, 1);
    737 			}
    738 		}
    739 
    740 		if (initializer && initializer[0]) {
    741 			if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
    742 				error("Initializer script failed");
    743 				ppp_set_status(EXIT_INIT_FAILED);
    744 				goto errretf;
    745 			}
    746 			if (got_sigterm) {
    747 				disconnect_tty();
    748 				goto errretf;
    749 			}
    750 			info("Serial port initialized.");
    751 		}
    752 
    753 		if (connector && connector[0]) {
    754 			if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
    755 				error("Connect script failed");
    756 				ppp_set_status(EXIT_CONNECT_FAILED);
    757 				goto errretf;
    758 			}
    759 			if (got_sigterm) {
    760 				disconnect_tty();
    761 				goto errretf;
    762 			}
    763 			info("Serial connection established.");
    764 		}
    765 
    766 		/* set line speed, flow control, etc.;
    767 		   clear CLOCAL if modem option */
    768 		if (real_ttyfd != -1)
    769 			set_up_tty(real_ttyfd, 0);
    770 
    771 		if (doing_callback == CALLBACK_DIALIN)
    772 			connector = NULL;
    773 	}
    774 
    775 	/* reopen tty if necessary to wait for carrier */
    776 	if (connector == NULL && modem && devnam[0] != 0) {
    777 		int i;
    778 		for (;;) {
    779 			if ((i = open(devnam, O_RDWR)) >= 0)
    780 				break;
    781 			if (errno != EINTR) {
    782 				error("Failed to reopen %s: %m", devnam);
    783 				ppp_set_status(EXIT_OPEN_FAILED);
    784 			}
    785 			if (!persist || errno != EINTR || hungup || got_sigterm)
    786 				goto errret;
    787 		}
    788 		close(i);
    789 	}
    790 
    791 	slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
    792 	ppp_script_setenv("SPEED", numbuf, 0);
    793 
    794 	/* run welcome script, if any */
    795 	if (welcomer && welcomer[0]) {
    796 		if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
    797 			warn("Welcome script failed");
    798 	}
    799 
    800 	/*
    801 	 * If we are initiating this connection, wait for a short
    802 	 * time for something from the peer.  This can avoid bouncing
    803 	 * our packets off his tty before he has it set up.
    804 	 */
    805 	if (connector != NULL || ptycommand != NULL || pty_socket != NULL)
    806 		listen_time = connect_delay;
    807 
    808 	return ttyfd;
    809 
    810  errretf:
    811 	if (real_ttyfd >= 0)
    812 		tcflush(real_ttyfd, TCIOFLUSH);
    813  errret:
    814 	if (pty_master >= 0) {
    815 		close(pty_master);
    816 		pty_master = -1;
    817 	}
    818 	ttyfd = -1;
    819 	if (got_sigterm)
    820 		asked_to_quit = 1;
    821 	return -1;
    822 }
    823 
    824 
    825 void disconnect_tty(void)
    826 {
    827 	if (disconnect_script == NULL || hungup)
    828 		return;
    829 	if (real_ttyfd >= 0)
    830 		set_up_tty(real_ttyfd, 1);
    831 	if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
    832 		warn("disconnect script failed");
    833 	} else {
    834 		info("Serial link disconnected.");
    835 	}
    836 	stop_charshunt(NULL, 0);
    837 }
    838 
    839 void tty_close_fds(void)
    840 {
    841 	if (pty_slave >= 0)
    842 		close(pty_slave);
    843 	if (real_ttyfd >= 0) {
    844 		close(real_ttyfd);
    845 		real_ttyfd = -1;
    846 	}
    847 	/* N.B. ttyfd will == either pty_slave or real_ttyfd */
    848 }
    849 
    850 void cleanup_tty(void)
    851 {
    852 	if (real_ttyfd >= 0)
    853 		finish_tty();
    854 	tty_close_fds();
    855 	if (locked) {
    856 		unlock();
    857 		locked = 0;
    858 	}
    859 }
    860 
    861 /*
    862  * tty_do_send_config - set transmit-side PPP configuration.
    863  * We set the extended transmit ACCM here as well.
    864  */
    865 void
    866 tty_do_send_config(int mtu, u_int32_t accm, int pcomp, int accomp)
    867 {
    868 	tty_set_xaccm(xmit_accm);
    869 	tty_send_config(mtu, accm, pcomp, accomp);
    870 }
    871 
    872 /*
    873  * finish_tty - restore the terminal device to its original settings
    874  */
    875 static void
    876 finish_tty(void)
    877 {
    878 	/* drop dtr to hang up */
    879 	if (!default_device && modem) {
    880 		setdtr(real_ttyfd, 0);
    881 		/*
    882 		 * This sleep is in case the serial port has CLOCAL set by default,
    883 		 * and consequently will reassert DTR when we close the device.
    884 		 */
    885 		sleep(1);
    886 	}
    887 
    888 	restore_tty(real_ttyfd);
    889 
    890 #ifndef __linux__
    891 	if (tty_mode != (mode_t) -1) {
    892 		if (fchmod(real_ttyfd, tty_mode) != 0)
    893 			error("Couldn't restore tty permissions");
    894 	}
    895 #endif /* __linux__ */
    896 
    897 	close(real_ttyfd);
    898 	real_ttyfd = -1;
    899 }
    900 
    901 /*
    902  * maybe_relock - our PID has changed, maybe update the lock file.
    903  */
    904 static void
    905 maybe_relock(void *arg, int pid)
    906 {
    907     if (locked)
    908 	relock(pid);
    909 }
    910 
    911 /*
    912  * open_socket - establish a stream socket connection to the nominated
    913  * host and port.
    914  */
    915 static int
    916 open_socket(char *dest)
    917 {
    918     char *sep, *endp = NULL;
    919     int sock, port = -1;
    920     u_int32_t host;
    921     struct hostent *hent;
    922     struct sockaddr_in sad;
    923 
    924     /* parse host:port and resolve host to an IP address */
    925     sep = strchr(dest, ':');
    926     if (sep != NULL)
    927 	port = strtol(sep+1, &endp, 10);
    928     if (port < 0 || endp == sep+1 || sep == dest) {
    929 	error("Can't parse host:port for socket destination");
    930 	return -1;
    931     }
    932     *sep = 0;
    933     host = inet_addr(dest);
    934     if (host == (u_int32_t) -1) {
    935 	hent = gethostbyname(dest);
    936 	if (hent == NULL) {
    937 	    error("%s: unknown host in socket option", dest);
    938 	    *sep = ':';
    939 	    return -1;
    940 	}
    941 	host = *(u_int32_t *)(hent->h_addr_list[0]);
    942     }
    943     *sep = ':';
    944 
    945     /* get a socket and connect it to the other end */
    946     sock = socket(PF_INET, SOCK_STREAM, 0);
    947     if (sock < 0) {
    948 	error("Can't create socket: %m");
    949 	return -1;
    950     }
    951     memset(&sad, 0, sizeof(sad));
    952     sad.sin_family = AF_INET;
    953     sad.sin_port = htons(port);
    954     sad.sin_addr.s_addr = host;
    955     if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
    956 	error("Can't connect to %s: %m", dest);
    957 	close(sock);
    958 	return -1;
    959     }
    960 
    961     return sock;
    962 }
    963 
    964 
    965 /*
    966  * start_charshunt - create a child process to run the character shunt.
    967  */
    968 static int
    969 start_charshunt(int ifd, int ofd)
    970 {
    971     int cpid, ret;
    972 
    973     cpid = ppp_safe_fork(ifd, ofd, (log_to_fd >= 0? log_to_fd: 2));
    974     if (cpid == -1) {
    975 	error("Can't fork process for character shunt: %m");
    976 	return 0;
    977     }
    978     if (cpid == 0) {
    979 	/* child */
    980 	reopen_log();
    981 	if (!nodetach)
    982 	    log_to_fd = -1;
    983 	else if (log_to_fd >= 0)
    984 	    log_to_fd = 2;
    985 	ret = setgid(getgid());
    986 	if (ret != 0) {
    987 		fatal("setgid failed, %m");
    988 	}
    989 	ret = setuid(uid);
    990 	if (ret != 0 || getuid() != uid) {
    991 		fatal("setuid failed, %m");
    992 	}
    993 	charshunt(0, 1, record_file);
    994 	exit(0);
    995     }
    996     charshunt_pid = cpid;
    997     record_child(cpid, "pppd (charshunt)", charshunt_done, NULL, 1);
    998     return 1;
    999 }
   1000 
   1001 static void
   1002 charshunt_done(void *arg)
   1003 {
   1004 	charshunt_pid = 0;
   1005 }
   1006 
   1007 static void
   1008 stop_charshunt(void *arg, int sig)
   1009 {
   1010 	if (charshunt_pid)
   1011 		kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
   1012 }
   1013 
   1014 /*
   1015  * charshunt - the character shunt, which passes characters between
   1016  * the pty master side and the serial port (or stdin/stdout).
   1017  * This runs as the user (not as root).
   1018  * (We assume ofd >= ifd which is true the way this gets called. :-).
   1019  */
   1020 static void
   1021 charshunt(int ifd, int ofd, char *record_file)
   1022 {
   1023     int n, nfds;
   1024     fd_set ready, writey;
   1025     u_char *ibufp, *obufp;
   1026     int nibuf, nobuf;
   1027     int flags;
   1028     int pty_readable, stdin_readable;
   1029     struct timeval lasttime;
   1030     FILE *recordf = NULL;
   1031     int ilevel, olevel, max_level;
   1032     struct timeval levelt, tout, *top;
   1033     extern u_char inpacket_buf[];
   1034 
   1035     /*
   1036      * Reset signal handlers.
   1037      */
   1038     signal(SIGHUP, SIG_IGN);		/* Hangup */
   1039     signal(SIGINT, SIG_DFL);		/* Interrupt */
   1040     signal(SIGTERM, SIG_DFL);		/* Terminate */
   1041     signal(SIGCHLD, SIG_DFL);
   1042     signal(SIGUSR1, SIG_DFL);
   1043     signal(SIGUSR2, SIG_DFL);
   1044     signal(SIGABRT, SIG_DFL);
   1045     signal(SIGALRM, SIG_DFL);
   1046     signal(SIGFPE, SIG_DFL);
   1047     signal(SIGILL, SIG_DFL);
   1048     signal(SIGPIPE, SIG_DFL);
   1049     signal(SIGQUIT, SIG_DFL);
   1050     signal(SIGSEGV, SIG_DFL);
   1051 #ifdef SIGBUS
   1052     signal(SIGBUS, SIG_DFL);
   1053 #endif
   1054 #ifdef SIGEMT
   1055     signal(SIGEMT, SIG_DFL);
   1056 #endif
   1057 #ifdef SIGPOLL
   1058     signal(SIGPOLL, SIG_DFL);
   1059 #endif
   1060 #ifdef SIGPROF
   1061     signal(SIGPROF, SIG_DFL);
   1062 #endif
   1063 #ifdef SIGSYS
   1064     signal(SIGSYS, SIG_DFL);
   1065 #endif
   1066 #ifdef SIGTRAP
   1067     signal(SIGTRAP, SIG_DFL);
   1068 #endif
   1069 #ifdef SIGVTALRM
   1070     signal(SIGVTALRM, SIG_DFL);
   1071 #endif
   1072 #ifdef SIGXCPU
   1073     signal(SIGXCPU, SIG_DFL);
   1074 #endif
   1075 #ifdef SIGXFSZ
   1076     signal(SIGXFSZ, SIG_DFL);
   1077 #endif
   1078 
   1079     /*
   1080      * Check that the fds won't overrun the fd_sets
   1081      */
   1082     if (ifd >= FD_SETSIZE || ofd >= FD_SETSIZE || pty_master >= FD_SETSIZE)
   1083 	fatal("internal error: file descriptor too large (%d, %d, %d)",
   1084 	      ifd, ofd, pty_master);
   1085 
   1086     /*
   1087      * Open the record file if required.
   1088      */
   1089     if (record_file != NULL) {
   1090 	recordf = fopen(record_file, "a");
   1091 	if (recordf == NULL)
   1092 	    error("Couldn't create record file %s: %m", record_file);
   1093     }
   1094 
   1095     /* set all the fds to non-blocking mode */
   1096     flags = fcntl(pty_master, F_GETFL);
   1097     if (flags == -1
   1098 	|| fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
   1099 	warn("couldn't set pty master to nonblock: %m");
   1100     flags = fcntl(ifd, F_GETFL);
   1101     if (flags == -1
   1102 	|| fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
   1103 	warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
   1104     if (ofd != ifd) {
   1105 	flags = fcntl(ofd, F_GETFL);
   1106 	if (flags == -1
   1107 	    || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
   1108 	    warn("couldn't set stdout to nonblock: %m");
   1109     }
   1110 
   1111     nibuf = nobuf = 0;
   1112     ibufp = obufp = NULL;
   1113     pty_readable = stdin_readable = 1;
   1114 
   1115     ilevel = olevel = 0;
   1116     ppp_get_time(&levelt);
   1117     if (max_data_rate) {
   1118 	max_level = max_data_rate / 10;
   1119 	if (max_level < 100)
   1120 	    max_level = 100;
   1121     } else
   1122 	max_level = PPP_MRU + PPP_HDRLEN + 1;
   1123 
   1124     nfds = (ofd > pty_master? ofd: pty_master) + 1;
   1125     if (recordf != NULL) {
   1126 	gettimeofday(&lasttime, NULL);
   1127 	putc(7, recordf);	/* put start marker */
   1128 	putc(lasttime.tv_sec >> 24, recordf);
   1129 	putc(lasttime.tv_sec >> 16, recordf);
   1130 	putc(lasttime.tv_sec >> 8, recordf);
   1131 	putc(lasttime.tv_sec, recordf);
   1132 	lasttime.tv_usec = 0;
   1133     }
   1134 
   1135     while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
   1136 	top = 0;
   1137 	tout.tv_sec = 0;
   1138 	tout.tv_usec = 10000;
   1139 	FD_ZERO(&ready);
   1140 	FD_ZERO(&writey);
   1141 	if (nibuf != 0) {
   1142 	    if (ilevel >= max_level)
   1143 		top = &tout;
   1144 	    else
   1145 		FD_SET(pty_master, &writey);
   1146 	} else if (stdin_readable)
   1147 	    FD_SET(ifd, &ready);
   1148 	if (nobuf != 0) {
   1149 	    if (olevel >= max_level)
   1150 		top = &tout;
   1151 	    else
   1152 		FD_SET(ofd, &writey);
   1153 	} else if (pty_readable)
   1154 	    FD_SET(pty_master, &ready);
   1155 	if (select(nfds, &ready, &writey, NULL, top) < 0) {
   1156 	    if (errno != EINTR)
   1157 		fatal("select");
   1158 	    continue;
   1159 	}
   1160 	if (max_data_rate) {
   1161 	    double dt;
   1162 	    int nbt;
   1163 	    struct timeval now;
   1164 
   1165 	    ppp_get_time(&now);
   1166 	    dt = (now.tv_sec - levelt.tv_sec
   1167 		  + (now.tv_usec - levelt.tv_usec) / 1e6);
   1168 	    nbt = (int)(dt * max_data_rate);
   1169 	    ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
   1170 	    olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
   1171 	    levelt = now;
   1172 	} else
   1173 	    ilevel = olevel = 0;
   1174 	if (FD_ISSET(ifd, &ready)) {
   1175 	    ibufp = inpacket_buf;
   1176 	    nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN);
   1177 	    if (nibuf < 0 && errno == EIO)
   1178 		nibuf = 0;
   1179 	    if (nibuf < 0) {
   1180 		if (!(errno == EINTR || errno == EAGAIN)) {
   1181 		    error("Error reading standard input: %m");
   1182 		    break;
   1183 		}
   1184 		nibuf = 0;
   1185 	    } else if (nibuf == 0) {
   1186 		/* end of file from stdin */
   1187 		stdin_readable = 0;
   1188 		if (recordf)
   1189 		    if (!record_write(recordf, 4, NULL, 0, &lasttime))
   1190 			recordf = NULL;
   1191 	    } else {
   1192 		FD_SET(pty_master, &writey);
   1193 		if (recordf)
   1194 		    if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
   1195 			recordf = NULL;
   1196 	    }
   1197 	}
   1198 	if (FD_ISSET(pty_master, &ready)) {
   1199 	    obufp = outpacket_buf;
   1200 	    nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN);
   1201 	    if (nobuf < 0 && errno == EIO)
   1202 		nobuf = 0;
   1203 	    if (nobuf < 0) {
   1204 		if (!(errno == EINTR || errno == EAGAIN)) {
   1205 		    error("Error reading pseudo-tty master: %m");
   1206 		    break;
   1207 		}
   1208 		nobuf = 0;
   1209 	    } else if (nobuf == 0) {
   1210 		/* end of file from the pty - slave side has closed */
   1211 		pty_readable = 0;
   1212 		stdin_readable = 0;	/* pty is not writable now */
   1213 		nibuf = 0;
   1214 		close(ofd);
   1215 		if (recordf)
   1216 		    if (!record_write(recordf, 3, NULL, 0, &lasttime))
   1217 			recordf = NULL;
   1218 	    } else {
   1219 		FD_SET(ofd, &writey);
   1220 		if (recordf)
   1221 		    if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
   1222 			recordf = NULL;
   1223 	    }
   1224 	} else if (!stdin_readable)
   1225 	    pty_readable = 0;
   1226 	if (FD_ISSET(ofd, &writey)) {
   1227 	    n = nobuf;
   1228 	    if (olevel + n > max_level)
   1229 		n = max_level - olevel;
   1230 	    n = write(ofd, obufp, n);
   1231 	    if (n < 0) {
   1232 		if (errno == EIO) {
   1233 		    pty_readable = 0;
   1234 		    nobuf = 0;
   1235 		} else if (errno != EAGAIN && errno != EINTR) {
   1236 		    error("Error writing standard output: %m");
   1237 		    break;
   1238 		}
   1239 	    } else {
   1240 		obufp += n;
   1241 		nobuf -= n;
   1242 		olevel += n;
   1243 	    }
   1244 	}
   1245 	if (FD_ISSET(pty_master, &writey)) {
   1246 	    n = nibuf;
   1247 	    if (ilevel + n > max_level)
   1248 		n = max_level - ilevel;
   1249 	    n = write(pty_master, ibufp, n);
   1250 	    if (n < 0) {
   1251 		if (errno == EIO) {
   1252 		    stdin_readable = 0;
   1253 		    nibuf = 0;
   1254 		} else if (errno != EAGAIN && errno != EINTR) {
   1255 		    error("Error writing pseudo-tty master: %m");
   1256 		    break;
   1257 		}
   1258 	    } else {
   1259 		ibufp += n;
   1260 		nibuf -= n;
   1261 		ilevel += n;
   1262 	    }
   1263 	}
   1264     }
   1265     exit(0);
   1266 }
   1267 
   1268 static int
   1269 record_write(FILE *f, int code, u_char *buf, int nb, struct timeval *tp)
   1270 {
   1271     struct timeval now;
   1272     int diff;
   1273 
   1274     gettimeofday(&now, NULL);
   1275     now.tv_usec /= 100000;	/* actually 1/10 s, not usec now */
   1276     diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
   1277     if (diff > 0) {
   1278 	if (diff > 255) {
   1279 	    putc(5, f);
   1280 	    putc(diff >> 24, f);
   1281 	    putc(diff >> 16, f);
   1282 	    putc(diff >> 8, f);
   1283 	    putc(diff, f);
   1284 	} else {
   1285 	    putc(6, f);
   1286 	    putc(diff, f);
   1287 	}
   1288 	*tp = now;
   1289     }
   1290     putc(code, f);
   1291     if (buf != NULL) {
   1292 	putc(nb >> 8, f);
   1293 	putc(nb, f);
   1294 	fwrite(buf, nb, 1, f);
   1295     }
   1296     fflush(f);
   1297     if (ferror(f)) {
   1298 	error("Error writing record file: %m");
   1299 	return 0;
   1300     }
   1301     return 1;
   1302 }
   1303