Clear hungup in start_link() instead of connect_tty()
[ppp.git] / pppd / tty.c
index 9fa0326cb0ca2d438eb181289d8cd3b0849b3d1b..d571b111d7e34755d9304104c075dcbcc16f4e44 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>".
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: tty.c,v 1.11 2003/03/03 05:11:46 paulus Exp $"
+#define RCSID  "$Id: tty.c,v 1.27 2008/07/01 12:27:56 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <unistd.h>
 #include <signal.h>
 #include <errno.h>
@@ -108,7 +104,7 @@ int  connect_tty __P((void));
 void disconnect_tty __P((void));
 void tty_close_fds __P((void));
 void cleanup_tty __P((void));
-int  tty_do_send_config __P((int, u_int32_t, int, int));
+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));
@@ -157,6 +153,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 */
@@ -300,7 +298,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;
@@ -448,7 +446,12 @@ tty_check_options()
        struct stat statbuf;
        int fdflags;
 
-       if (demand && connect_script == 0) {
+       if (demand && notty) {
+               option_error("demand-dialling is incompatible with notty");
+               exit(EXIT_OPTION_ERROR);
+       }
+       if (demand && connect_script == 0 && ptycommand == NULL
+           && pty_socket == NULL) {
                option_error("connect script is required for demand-dialling\n");
                exit(EXIT_OPTION_ERROR);
        }
@@ -459,7 +462,7 @@ tty_check_options()
        if (using_pty) {
                if (!default_device) {
                        option_error("%s option precludes specifying device name",
-                                    notty? "notty": "pty");
+                                    pty_socket? "socket": notty? "notty": "pty");
                        exit(EXIT_OPTION_ERROR);
                }
                if (ptycommand != NULL && notty) {
@@ -512,7 +515,9 @@ int connect_tty()
 {
        char *connector;
        int fdflags;
+#ifndef __linux__
        struct stat statbuf;
+#endif
        char numbuf[16];
 
        /*
@@ -538,7 +543,7 @@ int connect_tty()
        status = EXIT_LOCK_FAILED;
        if (lockflag && !privopen) {
                if (lock(devnam) < 0)
-                       return -1;
+                       goto errret;
                locked = 1;
        }
 
@@ -549,8 +554,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,13 +563,17 @@ 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);
+                               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) {
@@ -573,13 +581,18 @@ int connect_tty()
                                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 +601,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.
@@ -626,26 +640,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 */
@@ -663,11 +688,11 @@ int connect_tty()
                        if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
                                error("Initializer script failed");
                                status = EXIT_INIT_FAILED;
-                               return -1;
+                               goto errretf;
                        }
-                       if (kill_link) {
+                       if (got_sigterm) {
                                disconnect_tty();
-                               return -1;
+                               goto errretf;
                        }
                        info("Serial port initialized.");
                }
@@ -676,11 +701,11 @@ int connect_tty()
                        if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
                                error("Connect script failed");
                                status = EXIT_CONNECT_FAILED;
-                               return -1;
+                               goto errretf;
                        }
-                       if (kill_link) {
+                       if (got_sigterm) {
                                disconnect_tty();
-                               return -1;
+                               goto errretf;
                        }
                        info("Serial connection established.");
                }
@@ -704,8 +729,8 @@ int connect_tty()
                                error("Failed to reopen %s: %m", devnam);
                                status = EXIT_OPEN_FAILED;
                        }
-                       if (!persist || errno != EINTR || hungup || kill_link)
-                               return -1;
+                       if (!persist || errno != EINTR || hungup || got_sigterm)
+                               goto errret;
                }
                close(i);
        }
@@ -724,10 +749,23 @@ 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;
 }
 
 
@@ -742,12 +780,11 @@ void disconnect_tty()
        } else {
                info("Serial link disconnected.");
        }
+       stop_charshunt(NULL, 0);
 }
 
 void tty_close_fds()
 {
-       if (pty_master >= 0)
-               close(pty_master);
        if (pty_slave >= 0)
                close(pty_slave);
        if (real_ttyfd >= 0) {
@@ -772,14 +809,14 @@ void cleanup_tty()
  * tty_do_send_config - set transmit-side PPP configuration.
  * We set the extended transmit ACCM here as well.
  */
-int
+void
 tty_do_send_config(mtu, accm, pcomp, accomp)
     int mtu;
     u_int32_t accm;
     int pcomp, accomp;
 {
        tty_set_xaccm(xmit_accm);
-       return tty_send_config(mtu, accm, pcomp, accomp);
+       tty_send_config(mtu, accm, pcomp, accomp);
 }
 
 /*
@@ -800,12 +837,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;
@@ -887,29 +924,27 @@ start_charshunt(ifd, ofd)
 {
     int cpid;
 
-    cpid = safe_fork();
+    cpid = 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);
+       reopen_log();
+       if (!nodetach)
+           log_to_fd = -1;
+       else if (log_to_fd >= 0)
+           log_to_fd = 2;
+       setgid(getgid());
        setuid(uid);
        if (getuid() != uid)
            fatal("setuid failed");
-       setgid(getgid());
-       if (!nodetach)
-           log_to_fd = -1;
-       charshunt(ifd, ofd, record_file);
+       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;
 }
 
@@ -1105,9 +1140,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;
@@ -1144,7 +1176,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)