]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/tty.c
pppd.8: Document netmask option
[ppp.git] / pppd / tty.c
index a7ebe6ff0151055cfab29c706260d51bd3c126f9..d22ded751e2f9b60bbc0d6d596774f1b01a2c36e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * tty.c - code for handling serial ports in pppd.
  *
- * Copyright (C) 2000-2002 Paul Mackerras. All rights reserved.
+ * Copyright (C) 2000-2004 Paul Mackerras. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The name(s) of the authors of this software must not be used to
+ * 2. The name(s) of the authors of this software must not be used to
  *    endorse or promote products derived from this software without
  *    prior written permission.
  *
- * 4. Redistributions of any form whatsoever must retain the following
+ * 3. Redistributions of any form whatsoever must retain the following
  *    acknowledgment:
  *    "This product includes software developed by Paul Mackerras
- *     <paulus@samba.org>".
+ *     <paulus@ozlabs.org>".
  *
  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: tty.c,v 1.10 2003/02/24 11:29:53 fcusack Exp $"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
 
 #include <stdio.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <unistd.h>
 #include <signal.h>
 #include <errno.h>
@@ -87,7 +85,6 @@
 #include <netdb.h>
 #include <utmp.h>
 #include <pwd.h>
-#include <setjmp.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include "pppd.h"
+#include "pppd-private.h"
+#include "options.h"
 #include "fsm.h"
 #include "lcp.h"
 
-void tty_process_extra_options __P((void));
-void tty_check_options __P((void));
-int  connect_tty __P((void));
-void disconnect_tty __P((void));
-void tty_close_fds __P((void));
-void cleanup_tty __P((void));
-void tty_do_send_config __P((int, u_int32_t, int, int));
-
-static int setdevname __P((char *, char **, int));
-static int setspeed __P((char *, char **, int));
-static int setxonxoff __P((char **));
-static int setescape __P((char **));
-static void printescape __P((option_t *, void (*)(void *, char *,...),void *));
-static void finish_tty __P((void));
-static int start_charshunt __P((int, int));
-static void stop_charshunt __P((void *, int));
-static void charshunt_done __P((void *));
-static void charshunt __P((int, int, char *));
-static int record_write __P((FILE *, int code, u_char *buf, int nb,
-                            struct timeval *));
-static int open_socket __P((char *));
-static void maybe_relock __P((void *, int));
+void tty_process_extra_options(void);
+void tty_check_options(void);
+int  connect_tty(void);
+void disconnect_tty(void);
+void tty_close_fds(void);
+void cleanup_tty(void);
+void tty_do_send_config(int, u_int32_t, int, int);
+
+static int setdevname(char *, char **, int);
+static int setspeed(char *, char **, int);
+static int setxonxoff(char **);
+static int setescape(char **);
+static void printescape(struct option *, void (*)(void *, char *,...),void *);
+static void finish_tty(void);
+static int start_charshunt(int, int);
+static void stop_charshunt(void *, int);
+static void charshunt_done(void *);
+static void charshunt(int, int, char *);
+static int record_write(FILE *, int code, u_char *buf, int nb,
+                       struct timeval *);
+static int open_socket(char *);
+static void maybe_relock(void *, int);
 
 static int pty_master;         /* fd for master side of pty */
 static int pty_slave;          /* fd for slave side of pty */
@@ -139,7 +137,10 @@ int locked;                        /* lock() has succeeded */
 struct stat devstat;           /* result of stat() on devnam */
 
 /* option variables */
+char   devnam[MAXPATHLEN];     /* Device name */
+char   ppp_devname[MAXPATHLEN];/* name of PPP tty (maybe ttypx) */
 int    crtscts = 0;            /* Use hardware flow control */
+int    stop_bits = 1;          /* Number of serial port stop bits */
 bool   modem = 1;              /* Use modem control lines */
 int    inspeed = 0;            /* Input/Output speed requested */
 bool   lockflag = 0;           /* Create lock file to lock the serial dev */
@@ -157,6 +158,8 @@ int using_pty = 0;          /* we're allocating a pty as the device */
 
 extern uid_t uid;
 extern int kill_link;
+extern int asked_to_quit;
+extern int got_sigterm;
 
 /* XXX */
 extern int privopen;           /* don't lock, open device as root */
@@ -164,7 +167,7 @@ extern int privopen;                /* don't lock, open device as root */
 u_int32_t xmit_accm[8];                /* extended transmit ACCM */
 
 /* option descriptors */
-option_t tty_options[] = {
+static struct option tty_options[] = {
     /* device name must be first, or change connect_tty() below! */
     { "device name", o_wild, (void *) &setdevname,
       "Serial port device name",
@@ -223,6 +226,9 @@ option_t tty_options[] = {
       OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
     { "xonxoff", o_special_noarg, (void *)setxonxoff,
       "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
+    { "stop-bits", o_int, &stop_bits,
+      "Number of stop bits in serial port",
+      OPT_PRIO | OPT_PRIVFIX | OPT_LIMITS, NULL, 2, 1 },
 
     { "modem", o_bool, &modem,
       "Use modem control lines", OPT_PRIO | 1 },
@@ -258,16 +264,68 @@ struct channel tty_channel = {
        &tty_close_fds
 };
 
+bool
+ppp_sync_serial()
+{
+    return sync_serial;
+}
+
+bool
+ppp_get_modem()
+{
+    return modem;
+}
+
+void
+ppp_set_modem(bool on)
+{
+    modem = on;
+}
+
+bool
+ppp_using_pty()
+{
+    return using_pty;
+}
+
+int
+ppp_set_pppdevnam(const char *name)
+{
+    if (name) {
+        return strlcpy(ppp_devname, name, sizeof(ppp_devname));
+    }
+    return -1;
+}
+
+const char *
+ppp_pppdevnam()
+{
+    return ppp_devname;
+}
+
+const char *
+ppp_devnam()
+{
+    return devnam;
+}
+
+int
+ppp_set_devnam(const char *name)
+{
+    if (name) {
+        return strlcpy(devnam, name, sizeof(devnam));
+    }
+    return -1;
+}
+
+
 /*
  * setspeed - Set the serial port baud rate.
  * If doit is 0, the call is to check whether this option is
  * potentially a speed value.
  */
 static int
-setspeed(arg, argv, doit)
-    char *arg;
-    char **argv;
-    int doit;
+setspeed(char *arg, char **argv, int doit)
 {
        char *ptr;
        int spd;
@@ -289,10 +347,7 @@ setspeed(arg, argv, doit)
  * potentially a device name.
  */
 static int
-setdevname(cp, argv, doit)
-    char *cp;
-    char **argv;
-    int doit;
+setdevname(char *cp, char **argv, int doit)
 {
        struct stat statbuf;
        char dev[MAXPATHLEN];
@@ -300,7 +355,7 @@ setdevname(cp, argv, doit)
        if (*cp == 0)
                return 0;
 
-       if (strncmp("/dev/", cp, 5) != 0) {
+       if (*cp != '/') {
                strlcpy(dev, "/dev/", sizeof(dev));
                strlcat(dev, cp, sizeof(dev));
                cp = dev;
@@ -312,17 +367,17 @@ setdevname(cp, argv, doit)
        if (stat(cp, &statbuf) < 0) {
                if (!doit)
                        return errno != ENOENT;
-               option_error("Couldn't stat %s: %m", cp);
+               ppp_option_error("Couldn't stat %s: %m", cp);
                return 0;
        }
        if (!S_ISCHR(statbuf.st_mode)) {
                if (doit)
-                       option_error("%s is not a character device", cp);
+                       ppp_option_error("%s is not a character device", cp);
                return 0;
        }
 
        if (doit) {
-               strlcpy(devnam, cp, sizeof(devnam));
+               strlcpy(devnam, cp, MAXPATHLEN);
                devstat = statbuf;
                default_device = 0;
        }
@@ -331,8 +386,7 @@ setdevname(cp, argv, doit)
 }
 
 static int
-setxonxoff(argv)
-    char **argv;
+setxonxoff(char **argv)
 {
        lcp_wantoptions[0].asyncmap |= 0x000A0000;      /* escape ^S and ^Q */
        lcp_wantoptions[0].neg_asyncmap = 1;
@@ -345,8 +399,7 @@ setxonxoff(argv)
  * setescape - add chars to the set we escape on transmission.
  */
 static int
-setescape(argv)
-    char **argv;
+setescape(char **argv)
 {
     int n, ret;
     char *p, *endp;
@@ -356,16 +409,16 @@ setescape(argv)
     while (*p) {
        n = strtol(p, &endp, 16);
        if (p == endp) {
-           option_error("escape parameter contains invalid hex number '%s'",
+           ppp_option_error("escape parameter contains invalid hex number '%s'",
                         p);
            return 0;
        }
        p = endp;
        if (n < 0 || n == 0x5E || n > 0xFF) {
-           option_error("can't escape character 0x%x", n);
+           ppp_option_error("can't escape character 0x%x", n);
            ret = 0;
        } else
-           xmit_accm[n >> 5] |= 1 << (n & 0x1F);
+           xmit_accm[n >> 5] |= 1U << (n & 0x1F);
        while (*p == ',' || *p == ' ')
            ++p;
     }
@@ -374,10 +427,7 @@ setescape(argv)
 }
 
 static void
-printescape(opt, printer, arg)
-    option_t *opt;
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
+printescape(struct option *opt, void (*printer)(void *, char *, ...), void *arg)
 {
        int n;
        int first = 1;
@@ -385,7 +435,7 @@ printescape(opt, printer, arg)
        for (n = 0; n < 256; ++n) {
                if (n == 0x7d)
                        n += 2;         /* skip 7d, 7e */
-               if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) {
+               if (xmit_accm[n >> 5] & (1U << (n & 0x1f))) {
                        if (!first)
                                printer(arg, ",");
                        else
@@ -400,18 +450,18 @@ printescape(opt, printer, arg)
 /*
  * tty_init - do various tty-related initializations.
  */
-void tty_init()
+void tty_init(void)
 {
-    add_notifier(&pidchange, maybe_relock, 0);
+    ppp_add_notify(NF_PID_CHANGE, maybe_relock, 0);
     the_channel = &tty_channel;
-    xmit_accm[3] = 0x60000000;
+    xmit_accm[3] = 0x60000000U;
 }
 
 /*
  * tty_process_extra_options - work out which tty device we are using
  * and read its options file.
  */
-void tty_process_extra_options()
+void tty_process_extra_options(void)
 {
        using_pty = notty || ptycommand != NULL || pty_socket != NULL;
        if (using_pty)
@@ -419,10 +469,10 @@ void tty_process_extra_options()
        if (default_device) {
                char *p;
                if (!isatty(0) || (p = ttyname(0)) == NULL) {
-                       option_error("no device specified and stdin is not a tty");
+                       ppp_option_error("no device specified and stdin is not a tty");
                        exit(EXIT_OPTION_ERROR);
                }
-               strlcpy(devnam, p, sizeof(devnam));
+               strlcpy(devnam, p, MAXPATHLEN);
                if (stat(devnam, &devstat) < 0)
                        fatal("Couldn't stat default device %s: %m", devnam);
        }
@@ -443,13 +493,18 @@ void tty_process_extra_options()
  * tty_check_options - do consistency checks on the options we were given.
  */
 void
-tty_check_options()
+tty_check_options(void)
 {
        struct stat statbuf;
        int fdflags;
 
-       if (demand && connect_script == 0) {
-               option_error("connect script is required for demand-dialling\n");
+       if (demand && notty) {
+               ppp_option_error("demand-dialling is incompatible with notty");
+               exit(EXIT_OPTION_ERROR);
+       }
+       if (demand && connect_script == 0 && ptycommand == NULL
+           && pty_socket == NULL) {
+               ppp_option_error("connect script is required for demand-dialling\n");
                exit(EXIT_OPTION_ERROR);
        }
        /* default holdoff to 0 if no connect script has been given */
@@ -458,16 +513,16 @@ tty_check_options()
 
        if (using_pty) {
                if (!default_device) {
-                       option_error("%s option precludes specifying device name",
-                                    notty? "notty": "pty");
+                       ppp_option_error("%s option precludes specifying device name",
+                                    pty_socket? "socket": notty? "notty": "pty");
                        exit(EXIT_OPTION_ERROR);
                }
                if (ptycommand != NULL && notty) {
-                       option_error("pty option is incompatible with notty option");
+                       ppp_option_error("pty option is incompatible with notty option");
                        exit(EXIT_OPTION_ERROR);
                }
                if (pty_socket != NULL && (ptycommand != NULL || notty)) {
-                       option_error("socket option is incompatible with pty and notty");
+                       ppp_option_error("socket option is incompatible with pty and notty");
                        exit(EXIT_OPTION_ERROR);
                }
                default_device = notty;
@@ -508,25 +563,27 @@ tty_check_options()
  * That is, open the serial port, set its speed and mode, and run
  * the connector and/or welcomer.
  */
-int connect_tty()
+int connect_tty(void)
 {
        char *connector;
        int fdflags;
+#ifndef __linux__
        struct stat statbuf;
+#endif
        char numbuf[16];
 
        /*
         * Get a pty master/slave pair if the pty, notty, socket,
         * or record options were specified.
         */
-       strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+       strlcpy(ppp_devname, devnam, MAXPATHLEN);
        pty_master = -1;
        pty_slave = -1;
        real_ttyfd = -1;
        if (using_pty || record_file != NULL) {
-               if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
+               if (!get_pty(&pty_master, &pty_slave, ppp_devname, uid)) {
                        error("Couldn't allocate pseudo-tty");
-                       status = EXIT_FATAL_ERROR;
+                       ppp_set_status(EXIT_FATAL_ERROR);
                        return -1;
                }
                set_up_tty(pty_slave, 1);
@@ -535,10 +592,10 @@ int connect_tty()
        /*
         * Lock the device if we've been asked to.
         */
-       status = EXIT_LOCK_FAILED;
+       ppp_set_status(EXIT_LOCK_FAILED);
        if (lockflag && !privopen) {
                if (lock(devnam) < 0)
-                       return -1;
+                       goto errret;
                locked = 1;
        }
 
@@ -549,8 +606,7 @@ int connect_tty()
         * out and we want to use the modem lines, we reopen it later
         * in order to wait for the carrier detect signal from the modem.
         */
-       hungup = 0;
-       kill_link = 0;
+       got_sigterm = 0;
        connector = doing_callback? callback_script: connect_script;
        if (devnam[0] != 0) {
                for (;;) {
@@ -559,27 +615,36 @@ int connect_tty()
                        int err, prio;
 
                        prio = privopen? OPRIO_ROOT: tty_options[0].priority;
-                       if (prio < OPRIO_ROOT)
-                               seteuid(uid);
-                       ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+                       if (prio < OPRIO_ROOT && seteuid(uid) == -1) {
+                               error("Unable to drop privileges before opening %s: %m\n",
+                                     devnam);
+                               ppp_set_status(EXIT_OPEN_FAILED);
+                               goto errret;
+                       }
+                       real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
                        err = errno;
-                       if (prio < OPRIO_ROOT)
-                               seteuid(0);
-                       if (ttyfd >= 0)
+                       if (prio < OPRIO_ROOT && seteuid(0) == -1)
+                               fatal("Unable to regain privileges");
+                       if (real_ttyfd >= 0)
                                break;
                        errno = err;
                        if (err != EINTR) {
                                error("Failed to open %s: %m", devnam);
-                               status = EXIT_OPEN_FAILED;
+                               ppp_set_status(EXIT_OPEN_FAILED);
                        }
                        if (!persist || err != EINTR)
-                               return -1;
+                               goto errret;
                }
-               real_ttyfd = ttyfd;
+               ttyfd = real_ttyfd;
                if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
                    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
                        warn("Couldn't reset non-blocking mode on device: %m");
 
+#ifndef __linux__
+               /*
+                * Linux 2.4 and above blocks normal writes to the tty
+                * when it is in PPP line discipline, so this isn't needed.
+                */
                /*
                 * Do the equivalent of `mesg n' to stop broadcast messages.
                 */
@@ -588,6 +653,7 @@ int connect_tty()
                        warn("Couldn't restrict write permissions to %s: %m", devnam);
                } else
                        tty_mode = statbuf.st_mode;
+#endif /* __linux__ */
 
                /*
                 * Set line speed, flow control, etc.
@@ -607,7 +673,7 @@ int connect_tty()
         * If the pty, socket, notty and/or record option was specified,
         * start up the character shunt now.
         */
-       status = EXIT_PTYCMD_FAILED;
+       ppp_set_status(EXIT_PTYCMD_FAILED);
        if (ptycommand != NULL) {
                if (record_file != NULL) {
                        int ipipe[2], opipe[2], ok;
@@ -626,26 +692,37 @@ int connect_tty()
                        close(opipe[0]);
                        close(opipe[1]);
                        if (!ok)
-                               return -1;
+                               goto errret;
                } else {
                        if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
-                               return -1;
-                       ttyfd = pty_slave;
-                       close(pty_master);
-                       pty_master = -1;
+                               goto errret;
                }
        } else if (pty_socket != NULL) {
                int fd = open_socket(pty_socket);
                if (fd < 0)
-                       return -1;
+                       goto errret;
                if (!start_charshunt(fd, fd))
-                       return -1;
+                       goto errret;
+               close(fd);
        } else if (notty) {
                if (!start_charshunt(0, 1))
-                       return -1;
+                       goto errret;
+               dup2(fd_devnull, 0);
+               dup2(fd_devnull, 1);
+               if (log_to_fd == 1)
+                       log_to_fd = -1;
+               if (log_to_fd != 2)
+                       dup2(fd_devnull, 2);
        } else if (record_file != NULL) {
-               if (!start_charshunt(ttyfd, ttyfd))
-                       return -1;
+               int fd = dup(ttyfd);
+               if (!start_charshunt(fd, fd))
+                       goto errret;
+       }
+
+       if (using_pty || record_file != NULL) {
+               ttyfd = pty_slave;
+               close(pty_master);
+               pty_master = -1;
        }
 
        /* run connection script */
@@ -662,12 +739,12 @@ int connect_tty()
                if (initializer && initializer[0]) {
                        if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
                                error("Initializer script failed");
-                               status = EXIT_INIT_FAILED;
-                               return -1;
+                               ppp_set_status(EXIT_INIT_FAILED);
+                               goto errretf;
                        }
-                       if (kill_link) {
+                       if (got_sigterm) {
                                disconnect_tty();
-                               return -1;
+                               goto errretf;
                        }
                        info("Serial port initialized.");
                }
@@ -675,12 +752,12 @@ int connect_tty()
                if (connector && connector[0]) {
                        if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
                                error("Connect script failed");
-                               status = EXIT_CONNECT_FAILED;
-                               return -1;
+                               ppp_set_status(EXIT_CONNECT_FAILED);
+                               goto errretf;
                        }
-                       if (kill_link) {
+                       if (got_sigterm) {
                                disconnect_tty();
-                               return -1;
+                               goto errretf;
                        }
                        info("Serial connection established.");
                }
@@ -702,16 +779,16 @@ int connect_tty()
                                break;
                        if (errno != EINTR) {
                                error("Failed to reopen %s: %m", devnam);
-                               status = EXIT_OPEN_FAILED;
+                               ppp_set_status(EXIT_OPEN_FAILED);
                        }
-                       if (!persist || errno != EINTR || hungup || kill_link)
-                               return -1;
+                       if (!persist || errno != EINTR || hungup || got_sigterm)
+                               goto errret;
                }
                close(i);
        }
 
        slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
-       script_setenv("SPEED", numbuf, 0);
+       ppp_script_setenv("SPEED", numbuf, 0);
 
        /* run welcome script, if any */
        if (welcomer && welcomer[0]) {
@@ -724,14 +801,27 @@ int connect_tty()
         * time for something from the peer.  This can avoid bouncing
         * our packets off his tty before he has it set up.
         */
-       if (connector != NULL || ptycommand != NULL)
+       if (connector != NULL || ptycommand != NULL || pty_socket != NULL)
                listen_time = connect_delay;
 
        return ttyfd;
+
+ errretf:
+       if (real_ttyfd >= 0)
+               tcflush(real_ttyfd, TCIOFLUSH);
+ errret:
+       if (pty_master >= 0) {
+               close(pty_master);
+               pty_master = -1;
+       }
+       ttyfd = -1;
+       if (got_sigterm)
+               asked_to_quit = 1;
+       return -1;
 }
 
 
-void disconnect_tty()
+void disconnect_tty(void)
 {
        if (disconnect_script == NULL || hungup)
                return;
@@ -742,12 +832,11 @@ void disconnect_tty()
        } else {
                info("Serial link disconnected.");
        }
+       stop_charshunt(NULL, 0);
 }
 
-void tty_close_fds()
+void tty_close_fds(void)
 {
-       if (pty_master >= 0)
-               close(pty_master);
        if (pty_slave >= 0)
                close(pty_slave);
        if (real_ttyfd >= 0) {
@@ -757,7 +846,7 @@ void tty_close_fds()
        /* N.B. ttyfd will == either pty_slave or real_ttyfd */
 }
 
-void cleanup_tty()
+void cleanup_tty(void)
 {
        if (real_ttyfd >= 0)
                finish_tty();
@@ -773,10 +862,7 @@ void cleanup_tty()
  * We set the extended transmit ACCM here as well.
  */
 void
-tty_do_send_config(mtu, accm, pcomp, accomp)
-    int mtu;
-    u_int32_t accm;
-    int pcomp, accomp;
+tty_do_send_config(int mtu, u_int32_t accm, int pcomp, int accomp)
 {
        tty_set_xaccm(xmit_accm);
        tty_send_config(mtu, accm, pcomp, accomp);
@@ -786,7 +872,7 @@ tty_do_send_config(mtu, accm, pcomp, accomp)
  * finish_tty - restore the terminal device to its original settings
  */
 static void
-finish_tty()
+finish_tty(void)
 {
        /* drop dtr to hang up */
        if (!default_device && modem) {
@@ -800,12 +886,12 @@ finish_tty()
 
        restore_tty(real_ttyfd);
 
+#ifndef __linux__
        if (tty_mode != (mode_t) -1) {
-               if (fchmod(real_ttyfd, tty_mode) != 0) {
-                       /* XXX if devnam is a symlink, this will change the link */
-                       chmod(devnam, tty_mode);
-               }
+               if (fchmod(real_ttyfd, tty_mode) != 0)
+                       error("Couldn't restore tty permissions");
        }
+#endif /* __linux__ */
 
        close(real_ttyfd);
        real_ttyfd = -1;
@@ -815,9 +901,7 @@ finish_tty()
  * maybe_relock - our PID has changed, maybe update the lock file.
  */
 static void
-maybe_relock(arg, pid)
-    void *arg;
-    int pid;
+maybe_relock(void *arg, int pid)
 {
     if (locked)
        relock(pid);
@@ -828,8 +912,7 @@ maybe_relock(arg, pid)
  * host and port.
  */
 static int
-open_socket(dest)
-    char *dest;
+open_socket(char *dest)
 {
     char *sep, *endp = NULL;
     int sock, port = -1;
@@ -882,49 +965,46 @@ open_socket(dest)
  * start_charshunt - create a child process to run the character shunt.
  */
 static int
-start_charshunt(ifd, ofd)
-    int ifd, ofd;
+start_charshunt(int ifd, int ofd)
 {
-    int cpid;
+    int cpid, ret;
 
-    cpid = fork();
+    cpid = ppp_safe_fork(ifd, ofd, (log_to_fd >= 0? log_to_fd: 2));
     if (cpid == -1) {
        error("Can't fork process for character shunt: %m");
        return 0;
     }
     if (cpid == 0) {
        /* child */
-       close(pty_slave);
-       setuid(uid);
-       if (getuid() != uid)
-           fatal("setuid failed");
-       setgid(getgid());
-       sys_close();
+       reopen_log();
        if (!nodetach)
            log_to_fd = -1;
-       charshunt(ifd, ofd, record_file);
+       else if (log_to_fd >= 0)
+           log_to_fd = 2;
+       ret = setgid(getgid());
+       if (ret != 0) {
+               fatal("setgid failed, %m");
+       }
+       ret = setuid(uid);
+       if (ret != 0 || getuid() != uid) {
+               fatal("setuid failed, %m");
+       }
+       charshunt(0, 1, record_file);
        exit(0);
     }
     charshunt_pid = cpid;
-    add_notifier(&sigreceived, stop_charshunt, 0);
-    close(pty_master);
-    pty_master = -1;
-    ttyfd = pty_slave;
-    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
+    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL, 1);
     return 1;
 }
 
 static void
-charshunt_done(arg)
-    void *arg;
+charshunt_done(void *arg)
 {
        charshunt_pid = 0;
 }
 
 static void
-stop_charshunt(arg, sig)
-    void *arg;
-    int sig;
+stop_charshunt(void *arg, int sig)
 {
        if (charshunt_pid)
                kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
@@ -937,9 +1017,7 @@ stop_charshunt(arg, sig)
  * (We assume ofd >= ifd which is true the way this gets called. :-).
  */
 static void
-charshunt(ifd, ofd, record_file)
-    int ifd, ofd;
-    char *record_file;
+charshunt(int ifd, int ofd, char *record_file)
 {
     int n, nfds;
     fd_set ready, writey;
@@ -1034,7 +1112,7 @@ charshunt(ifd, ofd, record_file)
     pty_readable = stdin_readable = 1;
 
     ilevel = olevel = 0;
-    gettimeofday(&levelt, NULL);
+    ppp_get_time(&levelt);
     if (max_data_rate) {
        max_level = max_data_rate / 10;
        if (max_level < 100)
@@ -1083,7 +1161,7 @@ charshunt(ifd, ofd, record_file)
            int nbt;
            struct timeval now;
 
-           gettimeofday(&now, NULL);
+           ppp_get_time(&now);
            dt = (now.tv_sec - levelt.tv_sec
                  + (now.tv_usec - levelt.tv_usec) / 1e6);
            nbt = (int)(dt * max_data_rate);
@@ -1106,9 +1184,6 @@ charshunt(ifd, ofd, record_file)
            } else if (nibuf == 0) {
                /* end of file from stdin */
                stdin_readable = 0;
-               /* do a 0-length write, hopefully this will generate
-                  an EOF (hangup) on the slave side. */
-               write(pty_master, inpacket_buf, 0);
                if (recordf)
                    if (!record_write(recordf, 4, NULL, 0, &lasttime))
                        recordf = NULL;
@@ -1145,7 +1220,8 @@ charshunt(ifd, ofd, record_file)
                    if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
                        recordf = NULL;
            }
-       }
+       } else if (!stdin_readable)
+           pty_readable = 0;
        if (FD_ISSET(ofd, &writey)) {
            n = nobuf;
            if (olevel + n > max_level)
@@ -1189,12 +1265,7 @@ charshunt(ifd, ofd, record_file)
 }
 
 static int
-record_write(f, code, buf, nb, tp)
-    FILE *f;
-    int code;
-    u_char *buf;
-    int nb;
-    struct timeval *tp;
+record_write(FILE *f, int code, u_char *buf, int nb, struct timeval *tp)
 {
     struct timeval now;
     int diff;