X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fmain.c;h=7072d0a871fabcf9b615e3df1b179ff41ae2aa1d;hp=7de1d1374832dfb72915d3d0ea023fd70d7da5d6;hb=55f9a71c296dcdf5bdad0970b55b2f91094ae2e7;hpb=8972198dbc3630137fc364c36f61d65abc36b21a;ds=sidebyside diff --git a/pppd/main.c b/pppd/main.c index 7de1d13..7072d0a 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -18,291 +18,272 @@ */ #ifndef lint -static char rcsid[] = "$Id: main.c,v 1.4 1993/12/15 00:17:43 paulus Exp $"; +static char rcsid[] = "$Id: main.c,v 1.50 1998/09/13 23:38:49 paulus Exp $"; #endif -#define SETSID - #include +#include +#include +#include +#include #include #include #include #include #include #include - -/* - * If REQ_SYSOPTIONS is defined to 1, pppd will not run unless - * /etc/ppp/options exists. - */ -#ifndef REQ_SYSOPTIONS -#define REQ_SYSOPTIONS 0 -#endif - -#ifdef STREAMS -#undef SGTTY -#endif - -#ifdef SGTTY -#include -#else -#ifndef sun -#include -#endif -#include -#endif - +#include #include #include +#include +#include +#include #include #include -#include - -#include "callout.h" -#include -#include - -#include - -#ifndef BSD -#define BSD 43 -#endif /*BSD*/ - -#include "ppp.h" +#include "pppd.h" #include "magic.h" #include "fsm.h" #include "lcp.h" #include "ipcp.h" #include "upap.h" #include "chap.h" - -#include "pppd.h" +#include "ccp.h" #include "pathnames.h" #include "patchlevel.h" +#ifdef CBCP_SUPPORT +#include "cbcp.h" +#endif -#ifndef TRUE -#define TRUE (1) -#endif /*TRUE*/ - -#ifndef FALSE -#define FALSE (0) -#endif /*FALSE*/ +#if defined(SUNOS4) +extern char *strerror(); +#endif -#ifdef PIDPATH -static char *pidpath = PIDPATH; /* filename in which pid will be stored */ -#else -static char *pidpath = _PATH_PIDFILE; -#endif /* PIDFILE */ +#ifdef IPX_CHANGE +#include "ipxcp.h" +#endif /* IPX_CHANGE */ +#ifdef AT_CHANGE +#include "atcp.h" +#endif /* interface vars */ -char ifname[IFNAMSIZ]; /* Interface name */ +char ifname[32]; /* Interface name */ int ifunit; /* Interface unit number */ char *progname; /* Name of this program */ char hostname[MAXNAMELEN]; /* Our hostname */ -char our_name[MAXNAMELEN]; -char remote_name[MAXNAMELEN]; - -static pid_t pid; /* Our pid */ -static pid_t pgrpid; /* Process Group ID */ -static char pidfilename[MAXPATHLEN]; - -char devname[MAXPATHLEN] = "/dev/tty"; /* Device name */ -int default_device = TRUE; /* use default device (stdin/out) */ - -int fd; /* Device file descriptor */ -int s; /* Socket file descriptor */ - -#ifdef SGTTY -static struct sgttyb initsgttyb; /* Initial TTY sgttyb */ -#else -static struct termios inittermios; /* Initial TTY termios */ -#endif - -static int initfdflags = -1; /* Initial file descriptor flags */ - -static int restore_term; /* 1 => we've munged the terminal */ - -u_char outpacket_buf[MTU+DLLHEADERLEN]; /* buffer for outgoing packet */ -static u_char inpacket_buf[MTU+DLLHEADERLEN]; /* buffer for incoming packet */ - +static char pidfilename[MAXPATHLEN]; /* name of pid file */ +static char default_devnam[MAXPATHLEN]; /* name of default device */ +static pid_t pid; /* Our pid */ +static uid_t uid; /* Our real user-id */ +static int conn_running; /* we have a [dis]connector running */ + +int ttyfd = -1; /* Serial port file descriptor */ +mode_t tty_mode = -1; /* Original access permissions to tty */ +int baud_rate; /* Actual bits/second for serial device */ int hungup; /* terminal has been hung up */ +int privileged; /* we're running as real uid root */ +int need_holdoff; /* need holdoff period before restarting */ +int detached; /* have detached from terminal */ + +int phase; /* where the link is at */ +int kill_link; +int open_ccp_flag; + +char **script_env; /* Env. variable values for scripts */ +int s_env_nalloc; /* # words avail at script_env */ + +u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */ +u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */ + +static int n_children; /* # child processes still running */ + +static int locked; /* lock() has succeeded */ + +char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n"; + +/* Prototypes for procedures local to this file. */ + +static void create_pidfile __P((void)); +static void cleanup __P((void)); +static void close_tty __P((void)); +static void get_input __P((void)); +static void calltimeout __P((void)); +static struct timeval *timeleft __P((struct timeval *)); +static void kill_my_pg __P((int)); +static void hup __P((int)); +static void term __P((int)); +static void chld __P((int)); +static void toggle_debug __P((int)); +static void open_ccp __P((int)); +static void bad_signal __P((int)); +static void holdoff_end __P((void *)); +static int device_script __P((char *, int, int)); +static void reap_kids __P((void)); +static void pr_log __P((void *, char *, ...)); + +extern char *ttyname __P((int)); +extern char *getlogin __P((void)); +int main __P((int, char *[])); + +#ifdef ultrix +#undef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif -/* configured variables */ - -int debug = 0; /* Debug flag */ -char user[MAXNAMELEN]; /* username for PAP */ -char passwd[MAXSECRETLEN]; /* password for PAP */ -char *connector = NULL; /* "connect" command */ -int inspeed = 0; /* Input/Output speed */ -u_long netmask = 0; /* netmask to use on ppp interface */ -int crtscts = 0; /* use h/w flow control */ -int nodetach = 0; /* don't fork */ -int modem = 0; /* use modem control lines */ -int auth_required = 0; /* require peer to authenticate */ -int defaultroute = 0; /* assign default route through interface */ -int proxyarp = 0; /* set entry in arp table */ -int persist = 0; /* re-initiate on termination */ -int answer = 0; /* wait for incoming call */ -int uselogin = 0; /* check PAP info against /etc/passwd */ - - -/* prototypes */ -static void hup __ARGS((int, int, struct sigcontext *, char *)); -static void intr __ARGS((int, int, struct sigcontext *, char *)); -static void term __ARGS((int, int, struct sigcontext *, char *)); -static void alrm __ARGS((int, int, struct sigcontext *, char *)); -static void io __ARGS((int, int, struct sigcontext *, char *)); -static void incdebug __ARGS((int)); -static void nodebug __ARGS((int)); -void establish_ppp __ARGS((void)); - -void cleanup __ARGS((int, caddr_t)); -void die __ARGS((int)); -void dumpbuffer __ARGS((unsigned char *, int, int)); - -#ifdef STREAMS -extern char *ttyname __ARGS((int)); +#ifdef ULTRIX +#define setlogmask(x) #endif -extern char *getlogin __ARGS((void)); /* * PPP Data Link Layer "protocol" table. * One entry per supported protocol. + * The last entry must be NULL. */ -static struct protent { - u_short protocol; - void (*init)(); - void (*input)(); - void (*protrej)(); -} prottbl[] = { - { LCP, lcp_init, lcp_input, lcp_protrej }, - { IPCP, ipcp_init, ipcp_input, ipcp_protrej }, - { UPAP, upap_init, upap_input, upap_protrej }, - { CHAP, ChapInit, ChapInput, ChapProtocolReject }, +struct protent *protocols[] = { + &lcp_protent, + &pap_protent, + &chap_protent, +#ifdef CBCP_SUPPORT + &cbcp_protent, +#endif + &ipcp_protent, + &ccp_protent, +#ifdef IPX_CHANGE + &ipxcp_protent, +#endif +#ifdef AT_CHANGE + &atcp_protent, +#endif + NULL }; - +int main(argc, argv) int argc; char *argv[]; { - int mask, i; - struct sigvec sv; - struct cmd *cmdp; - FILE *pidfile; + int i, fdflags; + struct sigaction sa; char *p; + struct passwd *pw; + struct timeval timo; + sigset_t mask; + struct protent *protp; + struct stat statbuf; + char numbuf[16]; - /* - * Initialize syslog system and magic number package. - */ -#if BSD >= 43 || defined(sun) + phase = PHASE_INITIALIZE; + p = ttyname(0); + if (p) + strcpy(devnam, p); + strcpy(default_devnam, devnam); + + script_env = NULL; + + /* Initialize syslog facilities */ +#ifdef ULTRIX + openlog("pppd", LOG_PID); +#else openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); setlogmask(LOG_UPTO(LOG_INFO)); -#else - openlog("pppd", LOG_PID); -#define LOG_UPTO(x) (x) -#define setlogmask(x) (x) -#endif - -#ifdef STREAMS - p = ttyname(fileno(stdin)); - if (p) - strcpy(devname, p); #endif - - magic_init(); if (gethostname(hostname, MAXNAMELEN) < 0 ) { - syslog(LOG_ERR, "couldn't get hostname: %m"); + option_error("Couldn't get hostname: %m"); die(1); } hostname[MAXNAMELEN-1] = 0; - pid = getpid(); - - if (!ppp_available()) { - fprintf(stderr, "Sorry - PPP is not available on this system\n"); - exit(1); - } + uid = getuid(); + privileged = uid == 0; + sprintf(numbuf, "%d", uid); + script_setenv("ORIG_UID", numbuf); /* * Initialize to the standard option set, then parse, in order, - * the system options file, the user's options file, and the command - * line arguments. + * the system options file, the user's options file, + * the tty's options file, and the command line arguments. */ - for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++) - (*prottbl[i].init)(0); - + for (i = 0; (protp = protocols[i]) != NULL; ++i) + (*protp->init)(0); + progname = *argv; - if (!options_from_file(_PATH_SYSOPTIONS, REQ_SYSOPTIONS) || - !options_from_user() || - !parse_args(argc-1, argv+1)) + if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1) + || !options_from_user()) + exit(1); + scan_args(argc-1, argv+1); /* look for tty name on command line */ + if (!options_for_tty() + || !parse_args(argc-1, argv+1)) + exit(1); + + /* + * Check that we are running as root. + */ + if (geteuid() != 0) { + option_error("must be root to run %s, since it is not setuid-root", + argv[0]); die(1); - check_auth_options(); - setipdefault(); + } - p = getlogin(); - if (p == NULL) - p = "(unknown)"; - syslog(LOG_NOTICE, "pppd %s.%d started by %s, uid %d", - VERSION, PATCHLEVEL, p, getuid()); + if (!ppp_available()) { + option_error(no_ppp_msg); + exit(1); + } -#ifdef SETSID /* - * Make sure we can set the serial device to be our controlling terminal. + * Check that the options given are valid and consistent. */ - if (default_device) { - /* - * No device name was specified: - * we are in the device's session already. - */ - if ((pgrpid = getpgrp(0)) < 0) { - syslog(LOG_ERR, "getpgrp(0): %m"); - die(1); - } + sys_check_options(); + auth_check_options(); + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->check_options != NULL) + (*protp->check_options)(); + if (demand && connector == 0) { + option_error("connect script required for demand-dialling\n"); + exit(1); + } - } else { - /* - * Not default device: make sure we're not a process group leader, - * then become session leader of a new session (so we can make - * our device its controlling terminal and thus get SIGHUPs). - */ - if (!nodetach) { - /* fork so we're not a process group leader */ - if (pid = fork()) { - exit(0); /* parent is finished */ - } - if (pid < 0) { - syslog(LOG_ERR, "fork: %m"); - die(1); - } - pid = getpid(); /* otherwise pid is 0 in child */ - } else { - /* - * try to put ourself into our parent's process group, - * so we're not a process group leader - */ - if (setpgrp(pid, getppid()) < 0) - syslog(LOG_WARNING, "setpgrp: %m"); - } + script_setenv("DEVICE", devnam); + sprintf(numbuf, "%d", baud_rate); + script_setenv("SPEED", numbuf); - /* create new session */ - if ((pgrpid = setsid()) < 0) { - syslog(LOG_ERR, "setsid(): %m"); - die(1); - } - } -#endif + /* + * If the user has specified the default device name explicitly, + * pretend they hadn't. + */ + if (!default_device && strcmp(devnam, default_devnam) == 0) + default_device = 1; + if (default_device) + nodetach = 1; - /* Get an internet socket for doing socket ioctl's on. */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "socket : %m"); - die(1); + /* + * Initialize system-dependent stuff and magic number package. + */ + sys_init(); + magic_init(); + if (debug) + setlogmask(LOG_UPTO(LOG_DEBUG)); + + /* + * Detach ourselves from the terminal, if required, + * and identify who is running us. + */ + if (nodetach == 0) + detach(); + pid = getpid(); + p = getlogin(); + if (p == NULL) { + pw = getpwuid(uid); + if (pw != NULL && pw->pw_name != NULL) + p = pw->pw_name; + else + p = "(unknown)"; } + syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d", + VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid); /* * Compute mask of all interesting signals and install signal handlers @@ -312,318 +293,452 @@ main(argc, argv) sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGALRM); - sigaddset(&mask, SIGIO); -#ifdef STREAMS - sigaddset(&mask, SIGPOLL); -#endif + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGCHLD); #define SIGNAL(s, handler) { \ - sv.sv_handler = handler; \ - if (sigvec(s, &sv, NULL) < 0) { \ - syslog(LOG_ERR, "sigvec(%d): %m", s); \ + sa.sa_handler = handler; \ + if (sigaction(s, &sa, NULL) < 0) { \ + syslog(LOG_ERR, "Couldn't establish signal handler (%d): %m", s); \ die(1); \ } \ } - sv.sv_mask = mask; - sv.sv_flags = 0; + sa.sa_mask = mask; + sa.sa_flags = 0; SIGNAL(SIGHUP, hup); /* Hangup */ - SIGNAL(SIGINT, intr); /* Interrupt */ + SIGNAL(SIGINT, term); /* Interrupt */ SIGNAL(SIGTERM, term); /* Terminate */ - SIGNAL(SIGALRM, alrm); /* Timeout */ - SIGNAL(SIGIO, io); /* Input available */ -#ifdef STREAMS - SIGNAL(SIGPOLL, io); /* Input available */ + SIGNAL(SIGCHLD, chld); + + SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */ + SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */ + + /* + * Install a handler for other signals which would otherwise + * cause pppd to exit without cleaning up. + */ + SIGNAL(SIGABRT, bad_signal); + SIGNAL(SIGALRM, bad_signal); + SIGNAL(SIGFPE, bad_signal); + SIGNAL(SIGILL, bad_signal); + SIGNAL(SIGPIPE, bad_signal); + SIGNAL(SIGQUIT, bad_signal); + SIGNAL(SIGSEGV, bad_signal); +#ifdef SIGBUS + SIGNAL(SIGBUS, bad_signal); +#endif +#ifdef SIGEMT + SIGNAL(SIGEMT, bad_signal); +#endif +#ifdef SIGPOLL + SIGNAL(SIGPOLL, bad_signal); +#endif +#ifdef SIGPROF + SIGNAL(SIGPROF, bad_signal); +#endif +#ifdef SIGSYS + SIGNAL(SIGSYS, bad_signal); +#endif +#ifdef SIGTRAP + SIGNAL(SIGTRAP, bad_signal); +#endif +#ifdef SIGVTALRM + SIGNAL(SIGVTALRM, bad_signal); +#endif +#ifdef SIGXCPU + SIGNAL(SIGXCPU, bad_signal); +#endif +#ifdef SIGXFSZ + SIGNAL(SIGXFSZ, bad_signal); #endif - signal(SIGUSR1, incdebug); /* Increment debug flag */ - signal(SIGUSR2, nodebug); /* Reset debug flag */ - + /* + * Apparently we can get a SIGPIPE when we call syslog, if + * syslogd has died and been restarted. Ignoring it seems + * be sufficient. + */ + signal(SIGPIPE, SIG_IGN); /* - * Open the serial device and set it up to be the ppp interface. + * If we're doing dial-on-demand, set up the interface now. */ - if ((fd = open(devname, O_RDWR /*| O_NDELAY*/)) < 0) { - syslog(LOG_ERR, "open(%s): %m", devname); - die(1); - } - hungup = 0; + if (demand) { + /* + * Open the loopback channel and set it up to be the ppp interface. + */ + open_ppp_loopback(); - /* set device to be controlling tty */ - if (!default_device && ioctl(fd, TIOCSCTTY) < 0) { - syslog(LOG_ERR, "ioctl(TIOCSCTTY): %m"); - die(1); + syslog(LOG_INFO, "Using interface ppp%d", ifunit); + (void) sprintf(ifname, "ppp%d", ifunit); + script_setenv("IFNAME", ifname); + + create_pidfile(); /* write pid to file */ + + /* + * Configure the interface and mark it up, etc. + */ + demand_conf(); } - /* set line speed, flow control, etc. */ - set_up_tty(fd); + for (;;) { - /* run connection script */ - if (connector) { - syslog(LOG_INFO, "Connecting with <%s>", connector); + need_holdoff = 1; - /* drop dtr to hang up in case modem is off hook */ - if (!default_device && modem) { - setdtr(fd, FALSE); - sleep(1); - setdtr(fd, TRUE); + if (demand) { + /* + * Don't do anything until we see some activity. + */ + phase = PHASE_DORMANT; + kill_link = 0; + demand_unblock(); + for (;;) { + wait_loop_output(timeleft(&timo)); + calltimeout(); + if (kill_link) { + if (!persist) + die(0); + kill_link = 0; + } + if (get_loop_output()) + break; + reap_kids(); + } + + /* + * Now we want to bring up the link. + */ + demand_block(); + syslog(LOG_INFO, "Starting link"); } - if (set_up_connection(connector, fd, fd) < 0) { - syslog(LOG_ERR, "could not set up connection"); - setdtr(fd, FALSE); - die(1); + /* + * Lock the device if we've been asked to. + */ + if (lockflag && !default_device) { + if (lock(devnam) < 0) + goto fail; + locked = 1; } - syslog(LOG_INFO, "Connected..."); - sleep(1); /* give it time to set up its terminal */ - } - - /* set up the serial device as a ppp interface */ - establish_ppp(); + /* + * Open the serial device and set it up to be the ppp interface. + * First we open it in non-blocking mode so we can set the + * various termios flags appropriately. If we aren't dialling + * 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. + */ + while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0)) < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "Failed to open %s: %m", devnam); + if (!persist || errno != EINTR) + goto fail; + } + if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 + || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + syslog(LOG_WARNING, + "Couldn't reset non-blocking mode on device: %m"); - syslog(LOG_INFO, "Using interface ppp%d", ifunit); - (void) sprintf(ifname, "ppp%d", ifunit); + hungup = 0; + kill_link = 0; - /* write pid to file */ - (void) sprintf(pidfilename, "%s/%s.pid", pidpath, ifname); - if ((pidfile = fopen(pidfilename, "w")) != NULL) { - fprintf(pidfile, "%d\n", pid); - (void) fclose(pidfile); - } else { - syslog(LOG_ERR, "unable to create pid file: %m"); - pidfilename[0] = 0; - } + /* + * Do the equivalent of `mesg n' to stop broadcast messages. + */ + if (fstat(ttyfd, &statbuf) < 0 + || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { + syslog(LOG_WARNING, + "Couldn't restrict write permissions to %s: %m", devnam); + } else + tty_mode = statbuf.st_mode; - /* - * Set process group of device to our process group so we can get - * SIGIOs and SIGHUPs. - */ -#ifdef SETSID - if (default_device) { - int id = tcgetpgrp(fd); - if (id != pgrpid) { - syslog(LOG_WARNING, "warning: not in tty's process group"); + /* run connection script */ + if (connector && connector[0]) { + MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector)); + + /* + * Set line speed, flow control, etc. + * On most systems we set CLOCAL for now so that we can talk + * to the modem before carrier comes up. But this has the + * side effect that we might miss it if CD drops before we + * get to clear CLOCAL below. On systems where we can talk + * successfully to the modem with CLOCAL clear and CD down, + * we can clear CLOCAL at this point. + */ + set_up_tty(ttyfd, 1); + + /* drop dtr to hang up in case modem is off hook */ + if (!default_device && modem) { + setdtr(ttyfd, FALSE); + sleep(1); + setdtr(ttyfd, TRUE); + } + + if (device_script(connector, ttyfd, ttyfd) < 0) { + syslog(LOG_ERR, "Connect script failed"); + setdtr(ttyfd, FALSE); + goto fail; + } + + + syslog(LOG_INFO, "Serial connection established."); + sleep(1); /* give it time to set up its terminal */ } - } else { - if (tcsetpgrp(fd, pgrpid) < 0) { - syslog(LOG_ERR, "tcsetpgrp(): %m"); + + /* set line speed, flow control, etc.; clear CLOCAL if modem option */ + set_up_tty(ttyfd, 0); + + /* reopen tty if necessary to wait for carrier */ + if (connector == NULL && modem) { + while ((i = open(devnam, O_RDWR)) < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "Failed to reopen %s: %m", devnam); + if (!persist || errno != EINTR || hungup || kill_link) + goto fail; + } + close(i); + } + + /* run welcome script, if any */ + if (welcomer && welcomer[0]) { + if (device_script(welcomer, ttyfd, ttyfd) < 0) + syslog(LOG_WARNING, "Welcome script failed"); + } + + /* set up the serial device as a ppp interface */ + establish_ppp(ttyfd); + + if (!demand) { + + syslog(LOG_INFO, "Using interface ppp%d", ifunit); + (void) sprintf(ifname, "ppp%d", ifunit); + script_setenv("IFNAME", ifname); + + create_pidfile(); /* write pid to file */ + } + + /* + * Start opening the connection and wait for + * incoming events (reply, timeout, etc.). + */ + syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam); + lcp_lowerup(0); + lcp_open(0); /* Start protocol */ + for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) { + wait_input(timeleft(&timo)); + calltimeout(); + get_input(); + if (kill_link) { + lcp_close(0, "User request"); + kill_link = 0; + } + if (open_ccp_flag) { + if (phase == PHASE_NETWORK) { + ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ + (*ccp_protent.open)(0); + } + open_ccp_flag = 0; + } + reap_kids(); /* Don't leave dead kids lying around */ + } + + /* + * If we may want to bring the link up again, transfer + * the ppp unit back to the loopback. Set the + * real serial device back to its normal mode of operation. + */ + clean_check(); + if (demand) + restore_loop(); + disestablish_ppp(ttyfd); + + /* + * Run disconnector script, if requested. + * XXX we may not be able to do this if the line has hung up! + */ + if (disconnector && !hungup) { + set_up_tty(ttyfd, 1); + if (device_script(disconnector, ttyfd, ttyfd) < 0) { + syslog(LOG_WARNING, "disconnect script failed"); + } else { + syslog(LOG_INFO, "Serial link disconnected."); + } + } + + fail: + if (ttyfd >= 0) + close_tty(); + if (locked) { + unlock(); + locked = 0; + } + + if (!demand) { + if (pidfilename[0] != 0 + && unlink(pidfilename) < 0 && errno != ENOENT) + syslog(LOG_WARNING, "unable to delete pid file: %m"); + pidfilename[0] = 0; + } + + if (!persist) die(1); + + if (demand) + demand_discard(); + if (holdoff > 0 && need_holdoff) { + phase = PHASE_HOLDOFF; + TIMEOUT(holdoff_end, NULL, holdoff); + do { + wait_time(timeleft(&timo)); + calltimeout(); + if (kill_link) { + if (!persist) + die(0); + kill_link = 0; + phase = PHASE_DORMANT; /* allow signal to end holdoff */ + } + reap_kids(); + } while (phase == PHASE_HOLDOFF); } } -#else - /* set process group on tty so we get SIGIO's */ - if (ioctl(fd, TIOCSPGRP, &pgrpid) < 0) { - syslog(LOG_ERR, "ioctl(TIOCSPGRP): %m"); - die(1); - } -#endif - /* - * Record initial device flags, then set device to cause SIGIO - * signals to be generated. - */ - if ((initfdflags = fcntl(fd, F_GETFL)) == -1) { - syslog(LOG_ERR, "fcntl(F_GETFL): %m"); - die(1); - } - if (fcntl(fd, F_SETFL, FNDELAY | FASYNC) == -1) { - syslog(LOG_ERR, "fcntl(F_SETFL, FNDELAY | FASYNC): %m"); - die(1); - } - - /* - * Block all signals, start opening the connection, and wait for - * incoming signals (reply, timeout, etc.). - */ - syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devname); - sigprocmask(SIG_BLOCK, &mask, NULL); /* Block signals now */ - lcp_lowerup(0); /* XXX Well, sort of... */ - lcp_open(0); /* Start protocol */ - for (;;) { - sigpause(0); /* Wait for next signal */ - } + die(0); + return 0; } -#if B9600 == 9600 /* - * XXX assume speed_t values numerically equal bits per second - * (so we can ask for any speed). + * detach - detach us from the controlling terminal. */ -#define translate_speed(bps) (bps) +void +detach() +{ + if (detached) + return; + if (daemon(0, 0) < 0) { + perror("Couldn't detach from controlling terminal"); + die(1); + } + detached = 1; + pid = getpid(); + /* update pid file if it has been written already */ + if (pidfilename[0]) + create_pidfile(); +} -#else /* - * List of valid speeds. + * Create a file containing our process ID. */ -struct speed { - int speed_int, speed_val; -} speeds[] = { -#ifdef B50 - { 50, B50 }, -#endif -#ifdef B75 - { 75, B75 }, -#endif -#ifdef B110 - { 110, B110 }, -#endif -#ifdef B134 - { 134, B134 }, -#endif -#ifdef B150 - { 150, B150 }, -#endif -#ifdef B200 - { 200, B200 }, -#endif -#ifdef B300 - { 300, B300 }, -#endif -#ifdef B600 - { 600, B600 }, -#endif -#ifdef B1200 - { 1200, B1200 }, -#endif -#ifdef B1800 - { 1800, B1800 }, -#endif -#ifdef B2000 - { 2000, B2000 }, -#endif -#ifdef B2400 - { 2400, B2400 }, -#endif -#ifdef B3600 - { 3600, B3600 }, -#endif -#ifdef B4800 - { 4800, B4800 }, -#endif -#ifdef B7200 - { 7200, B7200 }, -#endif -#ifdef B9600 - { 9600, B9600 }, -#endif -#ifdef B19200 - { 19200, B19200 }, -#endif -#ifdef B38400 - { 38400, B38400 }, -#endif -#ifdef EXTA - { 19200, EXTA }, -#endif -#ifdef EXTB - { 38400, EXTB }, -#endif -#ifdef B57600 - { 57600, B57600 }, -#endif -#ifdef B115200 - { 115200, B115200 }, -#endif - { 0, 0 } -}; +static void +create_pidfile() +{ + FILE *pidfile; + + (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname); + if ((pidfile = fopen(pidfilename, "w")) != NULL) { + fprintf(pidfile, "%d\n", pid); + (void) fclose(pidfile); + } else { + syslog(LOG_ERR, "Failed to create pid file %s: %m", pidfilename); + pidfilename[0] = 0; + } +} /* - * Translate from bits/second to a speed_t. + * holdoff_end - called via a timeout when the holdoff period ends. */ -int -translate_speed(bps) - int bps; +static void +holdoff_end(arg) + void *arg; { - struct speed *speedp; - - if (bps == 0) - return 0; - for (speedp = speeds; speedp->speed_int; speedp++) - if (bps == speedp->speed_int) - return speedp->speed_val; - syslog(LOG_WARNING, "speed %d not supported", bps); - return 0; + phase = PHASE_DORMANT; } -#endif /* - * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity, - * at the requested speed, etc. + * get_input - called when incoming data is available. */ -set_up_tty(fd) - int fd; +static void +get_input() { -#ifndef SGTTY - int speed; - struct termios tios; + int len, i; + u_char *p; + u_short protocol; + struct protent *protp; - if (tcgetattr(fd, &tios) < 0) { - syslog(LOG_ERR, "tcgetattr: %m"); - die(1); - } + p = inpacket_buf; /* point to beginning of packet buffer */ + + len = read_packet(inpacket_buf); + if (len < 0) + return; - if (!restore_term) - inittermios = tios; - - tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL | CRTSCTS); - tios.c_cflag |= CS8 | CREAD | HUPCL; - if (crtscts) - tios.c_cflag |= CRTSCTS; - if (!modem) - tios.c_cflag |= CLOCAL; - tios.c_iflag = IGNBRK | IGNPAR; - tios.c_oflag = 0; - tios.c_lflag = 0; - tios.c_cc[VMIN] = 1; - tios.c_cc[VTIME] = 0; - speed = translate_speed(inspeed); - if (speed) { - cfsetospeed(&tios, speed); - cfsetispeed(&tios, speed); + if (len == 0) { + syslog(LOG_NOTICE, "Modem hangup"); + hungup = 1; + lcp_lowerdown(0); /* serial link is no longer available */ + link_terminated(0); + return; } - if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { - syslog(LOG_ERR, "tcsetattr: %m"); - die(1); + if (debug /*&& (debugflags & DBG_INPACKET)*/) + log_packet(p, len, "rcvd ", LOG_DEBUG); + + if (len < PPP_HDRLEN) { + MAINDEBUG((LOG_INFO, "io(): Received short packet.")); + return; } -#else /* SGTTY */ - int speed; - struct sgttyb sgttyb; + + p += 2; /* Skip address and control */ + GETSHORT(protocol, p); + len -= PPP_HDRLEN; /* - * Put the tty in raw mode. + * Toss all non-LCP packets unless LCP is OPEN. */ - if (ioctl(fd, TIOCGETP, &sgttyb) < 0) { - syslog(LOG_ERR, "ioctl(TIOCGETP): %m"); - die(1); + if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) { + MAINDEBUG((LOG_INFO, + "get_input: Received non-LCP packet when LCP not open.")); + return; } - if (!restore_term) - initsgttyb = sgttyb; - - sgttyb.sg_flags = RAW | ANYP; - speed = translate_speed(inspeed); - if (speed) - sgttyb.sg_ispeed = speed; + /* + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if (phase <= PHASE_AUTHENTICATE + && !(protocol == PPP_LCP || protocol == PPP_LQR + || protocol == PPP_PAP || protocol == PPP_CHAP)) { + MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d", + protocol, phase)); + return; + } - if (ioctl(fd, TIOCSETP, &sgttyb) < 0) { - syslog(LOG_ERR, "ioctl(TIOCSETP): %m"); - die(1); + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + (*protp->input)(0, p, len); + return; + } + if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag + && protp->datainput != NULL) { + (*protp->datainput)(0, p, len); + return; + } } -#endif - restore_term = TRUE; + + if (debug) + syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol); + lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN); } /* - * quit - Clean up state and exit. + * quit - Clean up state and exit (with an error indication). */ void quit() { - die(0); + die(1); } /* @@ -633,7 +748,7 @@ void die(status) int status; { - cleanup(0, NULL); + cleanup(); syslog(LOG_INFO, "Exit."); exit(status); } @@ -642,43 +757,59 @@ die(status) * cleanup - restore anything which needs to be restored before we exit */ /* ARGSUSED */ -void -cleanup(status, arg) - int status; - caddr_t arg; +static void +cleanup() { - if (fd != 0) { - /* drop dtr to hang up */ - if (modem) - setdtr(fd, FALSE); + sys_cleanup(); - if (fcntl(fd, F_SETFL, initfdflags) < 0) - syslog(LOG_ERR, "fcntl(F_SETFL, fdflags): %m"); + if (ttyfd >= 0) + close_tty(); - disestablish_ppp(); + if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) + syslog(LOG_WARNING, "unable to delete pid file: %m"); + pidfilename[0] = 0; - if (restore_term) { -#ifndef SGTTY - if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) - syslog(LOG_ERR, "tcsetattr: %m"); -#else - if (ioctl(fd, TIOCSETP, &initsgttyb) < 0) - syslog(LOG_ERR, "ioctl(TIOCSETP): %m"); -#endif - } + if (locked) + unlock(); +} + +/* + * close_tty - restore the terminal device and close it. + */ +static void +close_tty() +{ + disestablish_ppp(ttyfd); - close(fd); - fd = 0; + /* drop dtr to hang up */ + if (modem) { + setdtr(ttyfd, FALSE); + /* + * This sleep is in case the serial port has CLOCAL set by default, + * and consequently will reassert DTR when we close the device. + */ + sleep(1); } - if (pidfilename[0] != 0 && unlink(pidfilename) < 0) - syslog(LOG_WARNING, "unable to unlink pid file: %m"); - pidfilename[0] = 0; + restore_tty(ttyfd); + + if (tty_mode != (mode_t) -1) + chmod(devnam, tty_mode); + + close(ttyfd); + ttyfd = -1; } -static struct callout *callout = NULL; /* Callout list */ -static struct timeval schedtime; /* Time last timeout was set */ +struct callout { + struct timeval c_time; /* time at which to call routine */ + void *c_arg; /* argument to routine */ + void (*c_func) __P((void *)); /* routine */ + struct callout *c_next; +}; + +static struct callout *callout = NULL; /* Callout list */ +static struct timeval timenow; /* Current time */ /* * timeout - Schedule a timeout. @@ -688,15 +819,14 @@ static struct timeval schedtime; /* Time last timeout was set */ */ void timeout(func, arg, time) - void (*func)(); - caddr_t arg; + void (*func) __P((void *)); + void *arg; int time; { - struct itimerval itv; - struct callout *newp, **oldpp; + struct callout *newp, *p, **pp; - MAINDEBUG((LOG_DEBUG, "Timeout %x:%x in %d seconds.", - (int) func, (int) arg, time)); + MAINDEBUG((LOG_DEBUG, "Timeout %lx:%lx in %d seconds.", + (long) func, (long) arg, time)); /* * Allocate timeout. @@ -707,40 +837,20 @@ timeout(func, arg, time) } newp->c_arg = arg; newp->c_func = func; + gettimeofday(&timenow, NULL); + newp->c_time.tv_sec = timenow.tv_sec + time; + newp->c_time.tv_usec = timenow.tv_usec; /* - * Find correct place to link it in and decrement its time by the - * amount of time used by preceding timeouts. - */ - for (oldpp = &callout; - *oldpp && (*oldpp)->c_time <= time; - oldpp = &(*oldpp)->c_next) - time -= (*oldpp)->c_time; - newp->c_time = time; - newp->c_next = *oldpp; - if (*oldpp) - (*oldpp)->c_time -= time; - *oldpp = newp; - - /* - * If this is now the first callout then we have to set a new - * itimer. + * Find correct place and link it in. */ - if (callout == newp) { - itv.it_interval.tv_sec = itv.it_interval.tv_usec = - itv.it_value.tv_usec = 0; - itv.it_value.tv_sec = callout->c_time; - MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.", - itv.it_value.tv_sec)); - if (setitimer(ITIMER_REAL, &itv, NULL)) { - syslog(LOG_ERR, "setitimer(ITIMER_REAL): %m"); - die(1); - } - if (gettimeofday(&schedtime, NULL)) { - syslog(LOG_ERR, "gettimeofday: %m"); - die(1); - } - } + for (pp = &callout; (p = *pp); pp = &p->c_next) + if (newp->c_time.tv_sec < p->c_time.tv_sec + || (newp->c_time.tv_sec == p->c_time.tv_sec + && newp->c_time.tv_usec < p->c_time.tv_sec)) + break; + newp->c_next = p; + *pp = newp; } @@ -749,486 +859,492 @@ timeout(func, arg, time) */ void untimeout(func, arg) - void (*func)(); - caddr_t arg; + void (*func) __P((void *)); + void *arg; { - struct itimerval itv; struct callout **copp, *freep; - int reschedule = 0; - MAINDEBUG((LOG_DEBUG, "Untimeout %x:%x.", (int) func, (int) arg)); + MAINDEBUG((LOG_DEBUG, "Untimeout %lx:%lx.", (long) func, (long) arg)); /* - * If the first callout is unscheduled then we have to set a new - * itimer. + * Find first matching timeout and remove it from the list. */ - if (callout && - callout->c_func == func && - callout->c_arg == arg) - reschedule = 1; - - /* - * Find first matching timeout. Add its time to the next timeouts - * time. - */ - for (copp = &callout; *copp; copp = &(*copp)->c_next) - if ((*copp)->c_func == func && - (*copp)->c_arg == arg) { - freep = *copp; + for (copp = &callout; (freep = *copp); copp = &freep->c_next) + if (freep->c_func == func && freep->c_arg == arg) { *copp = freep->c_next; - if (*copp) - (*copp)->c_time += freep->c_time; (void) free((char *) freep); break; } - - if (reschedule) { - itv.it_interval.tv_sec = itv.it_interval.tv_usec = - itv.it_value.tv_usec = 0; - itv.it_value.tv_sec = callout ? callout->c_time : 0; - MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.", - itv.it_value.tv_sec)); - if (setitimer(ITIMER_REAL, &itv, NULL)) { - syslog(LOG_ERR, "setitimer(ITIMER_REAL): %m"); - die(1); - } - if (gettimeofday(&schedtime, NULL)) { - syslog(LOG_ERR, "gettimeofday: %m"); +} + + +/* + * calltimeout - Call any timeout routines which are now due. + */ +static void +calltimeout() +{ + struct callout *p; + + while (callout != NULL) { + p = callout; + + if (gettimeofday(&timenow, NULL) < 0) { + syslog(LOG_ERR, "Failed to get time of day: %m"); die(1); } + if (!(p->c_time.tv_sec < timenow.tv_sec + || (p->c_time.tv_sec == timenow.tv_sec + && p->c_time.tv_usec <= timenow.tv_usec))) + break; /* no, it's not time yet */ + + callout = p->c_next; + (*p->c_func)(p->c_arg); + + free((char *) p); } } /* - * adjtimeout - Decrement the first timeout by the amount of time since - * it was scheduled. + * timeleft - return the length of time until the next timeout is due. */ -void -adjtimeout() +static struct timeval * +timeleft(tvp) + struct timeval *tvp; { - struct timeval tv; - int timediff; - if (callout == NULL) - return; - /* - * Make sure that the clock hasn't been warped dramatically. - * Account for recently expired, but blocked timer by adding - * small fudge factor. - */ - if (gettimeofday(&tv, NULL)) { - syslog(LOG_ERR, "gettimeofday: %m"); - die(1); + return NULL; + + gettimeofday(&timenow, NULL); + tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec; + tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec; + if (tvp->tv_usec < 0) { + tvp->tv_usec += 1000000; + tvp->tv_sec -= 1; } - timediff = tv.tv_sec - schedtime.tv_sec; - if (timediff < 0 || - timediff > callout->c_time + 1) - return; - - callout->c_time -= timediff; /* OK, Adjust time */ + if (tvp->tv_sec < 0) + tvp->tv_sec = tvp->tv_usec = 0; + + return tvp; } /* - * hup - Catch SIGHUP signal. - * - * Indicates that the physical layer has been disconnected. + * kill_my_pg - send a signal to our process group, and ignore it ourselves. */ -/*ARGSUSED*/ static void -hup(sig, code, scp, addr) - int sig, code; - struct sigcontext *scp; - char *addr; +kill_my_pg(sig) + int sig; { - syslog(LOG_INFO, "Hangup (SIGHUP)"); + struct sigaction act, oldact; - hungup = 1; /* they hung up on us! */ - persist = 0; /* don't try to restart */ - adjtimeout(); /* Adjust timeouts */ - lcp_lowerdown(0); /* Reset connection */ - quit(); /* and die */ + act.sa_handler = SIG_IGN; + act.sa_flags = 0; + kill(0, sig); + sigaction(sig, &act, &oldact); + sigaction(sig, &oldact, NULL); } /* - * term - Catch SIGTERM signal. + * hup - Catch SIGHUP signal. * - * Indicates that we should initiate a graceful disconnect and exit. + * Indicates that the physical layer has been disconnected. + * We don't rely on this indication; if the user has sent this + * signal, we just take the link down. */ -/*ARGSUSED*/ static void -term(sig, code, scp, addr) - int sig, code; - struct sigcontext *scp; - char *addr; +hup(sig) + int sig; { - syslog(LOG_INFO, "Terminating link."); - persist = 0; /* don't try to restart */ - adjtimeout(); /* Adjust timeouts */ - lcp_close(0); /* Close connection */ + syslog(LOG_INFO, "Hangup (SIGHUP)"); + kill_link = 1; + if (conn_running) + /* Send the signal to the [dis]connector process(es) also */ + kill_my_pg(sig); } /* - * intr - Catch SIGINT signal (DEL/^C). + * term - Catch SIGTERM signal and SIGINT signal (^C/del). * * Indicates that we should initiate a graceful disconnect and exit. */ /*ARGSUSED*/ static void -intr(sig, code, scp, addr) - int sig, code; - struct sigcontext *scp; - char *addr; +term(sig) + int sig; { - syslog(LOG_INFO, "Interrupt received: terminating link"); + syslog(LOG_INFO, "Terminating on signal %d.", sig); persist = 0; /* don't try to restart */ - adjtimeout(); /* Adjust timeouts */ - lcp_close(0); /* Close connection */ + kill_link = 1; + if (conn_running) + /* Send the signal to the [dis]connector process(es) also */ + kill_my_pg(sig); } /* - * alrm - Catch SIGALRM signal. - * - * Indicates a timeout. + * chld - Catch SIGCHLD signal. + * Calls reap_kids to get status for any dead kids. */ -/*ARGSUSED*/ static void -alrm(sig, code, scp, addr) - int sig, code; - struct sigcontext *scp; - char *addr; +chld(sig) + int sig; { - struct itimerval itv; - struct callout *freep; - - MAINDEBUG((LOG_DEBUG, "Alarm")); - - /* - * Call and free first scheduled timeout and any that were scheduled - * for the same time. - */ - while (callout) { - freep = callout; /* Remove entry before calling */ - callout = freep->c_next; - (*freep->c_func)(freep->c_arg); - (void) free((char *) freep); - if (callout && callout->c_time) - break; - } - - /* - * Set a new itimer if there are more timeouts scheduled. - */ - if (callout) { - itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0; - itv.it_value.tv_usec = 0; - itv.it_value.tv_sec = callout->c_time; - MAINDEBUG((LOG_DEBUG, "Setting itimer for %d seconds.", - itv.it_value.tv_sec)); - if (setitimer(ITIMER_REAL, &itv, NULL)) { - syslog(LOG_ERR, "setitimer(ITIMER_REAL): %m"); - die(1); - } - if (gettimeofday(&schedtime, NULL)) { - syslog(LOG_ERR, "gettimeofday: %m"); - die(1); - } - } + reap_kids(); } /* - * io - Catch SIGIO signal. + * toggle_debug - Catch SIGUSR1 signal. * - * Indicates that incoming data is available. + * Toggle debug flag. */ /*ARGSUSED*/ static void -io(sig, code, scp, addr) - int sig, code; - struct sigcontext *scp; - char *addr; +toggle_debug(sig) + int sig; { - int len, i; - u_char *p; - u_short protocol; - fd_set fdset; - struct timeval notime; - int ready; - - MAINDEBUG((LOG_DEBUG, "IO signal received")); - adjtimeout(); /* Adjust timeouts */ - - /* we do this to see if the SIGIO handler is being invoked for input */ - /* ready, or for the socket buffer hitting the low-water mark. */ - - notime.tv_sec = 0; - notime.tv_usec = 0; - FD_ZERO(&fdset); - FD_SET(fd, &fdset); - - if ((ready = select(32, &fdset, (fd_set *) NULL, (fd_set *) NULL, - ¬ime)) == -1) { - syslog(LOG_ERR, "Error in io() select: %m"); - die(1); - } - - if (ready == 0) { - MAINDEBUG((LOG_DEBUG, "IO non-input ready SIGIO occured.")); - return; - } - - /* Yup, this is for real */ - for (;;) { /* Read all available packets */ - p = inpacket_buf; /* point to beginning of packet buffer */ - - len = read_packet(inpacket_buf); - if (len < 0) - return; - - if (len == 0) { - syslog(LOG_ERR, "End of file on fd!"); - die(1); - } - - if (len < DLLHEADERLEN) { - MAINDEBUG((LOG_INFO, "io(): Received short packet.")); - return; - } - - p += 2; /* Skip address and control */ - GETSHORT(protocol, p); - len -= DLLHEADERLEN; - - /* - * Toss all non-LCP packets unless LCP is OPEN. - */ - if (protocol != LCP && lcp_fsm[0].state != OPENED) { - MAINDEBUG((LOG_INFO, - "io(): Received non-LCP packet when LCP not open.")); - if (debug) - dumpbuffer(inpacket_buf, len + DLLHEADERLEN, LOG_INFO); - return; - } - - /* - * Upcall the proper protocol input routine. - */ - for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++) - if (prottbl[i].protocol == protocol) { - (*prottbl[i].input)(0, p, len); - break; - } - - if (i == sizeof (prottbl) / sizeof (struct protent)) { - syslog(LOG_WARNING, "input: Unknown protocol (%x) received!", - protocol); - lcp_sprotrej(0, p - DLLHEADERLEN, len + DLLHEADERLEN); - } + debug = !debug; + if (debug) { + setlogmask(LOG_UPTO(LOG_DEBUG)); + } else { + setlogmask(LOG_UPTO(LOG_WARNING)); } } -/* - * demuxprotrej - Demultiplex a Protocol-Reject. - */ -void -demuxprotrej(unit, protocol) - int unit; - u_short protocol; -{ - int i; - - /* - * Upcall the proper Protocol-Reject routine. - */ - for (i = 0; i < sizeof (prottbl) / sizeof (struct protent); i++) - if (prottbl[i].protocol == protocol) { - (*prottbl[i].protrej)(unit); - return; - } - - syslog(LOG_WARNING, - "demuxprotrej: Unrecognized Protocol-Reject for protocol %d!", - protocol); -} - /* - * incdebug - Catch SIGUSR1 signal. + * open_ccp - Catch SIGUSR2 signal. * - * Increment debug flag. + * Try to (re)negotiate compression. */ /*ARGSUSED*/ static void -incdebug(sig) +open_ccp(sig) int sig; { - syslog(LOG_INFO, "Debug turned ON, Level %d", debug); - setlogmask(LOG_UPTO(LOG_DEBUG)); - debug++; + open_ccp_flag = 1; } /* - * nodebug - Catch SIGUSR2 signal. - * - * Turn off debugging. + * bad_signal - We've caught a fatal signal. Clean up state and exit. */ -/*ARGSUSED*/ static void -nodebug(sig) +bad_signal(sig) int sig; { - setlogmask(LOG_UPTO(LOG_WARNING)); - debug = 0; + static int crashed = 0; + + if (crashed) + _exit(127); + crashed = 1; + syslog(LOG_ERR, "Fatal signal %d", sig); + if (conn_running) + kill_my_pg(SIGTERM); + die(1); } /* - * set_up_connection - run a program to initialize the serial connector + * device_script - run a program to connect or disconnect the + * serial device. */ -int -set_up_connection(program, in, out) +static int +device_script(program, in, out) char *program; int in, out; { int pid; int status; - sigset_t mask; - - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGHUP); - sigprocmask(SIG_BLOCK, &mask, &mask); + int errfd; + conn_running = 1; pid = fork(); - + if (pid < 0) { - syslog(LOG_ERR, "fork: %m"); + conn_running = 0; + syslog(LOG_ERR, "Failed to create child process: %m"); die(1); } - + if (pid == 0) { - setreuid(getuid(), getuid()); - setregid(getgid(), getgid()); - sigprocmask(SIG_SETMASK, &mask, NULL); - dup2(in, 0); - dup2(out, 1); + sys_close(); + closelog(); + if (in == out) { + if (in != 0) { + dup2(in, 0); + close(in); + } + dup2(0, 1); + } else { + if (out == 0) + out = dup(out); + if (in != 0) { + dup2(in, 0); + close(in); + } + if (out != 1) { + dup2(out, 1); + close(out); + } + } + if (nodetach == 0) { + close(2); + errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); + if (errfd >= 0 && errfd != 2) { + dup2(errfd, 2); + close(errfd); + } + } + setuid(getuid()); + setgid(getgid()); execl("/bin/sh", "sh", "-c", program, (char *)0); syslog(LOG_ERR, "could not exec /bin/sh: %m"); _exit(99); /* NOTREACHED */ } - while (waitpid(pid, &status, 0) != pid) { + while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) continue; - syslog(LOG_ERR, "waiting for connection process: %m"); + syslog(LOG_ERR, "error waiting for (dis)connection process: %m"); die(1); } - sigprocmask(SIG_SETMASK, &mask, NULL); + conn_running = 0; return (status == 0 ? 0 : -1); } /* - * Return user specified netmask. A value of zero means no netmask has - * been set. + * run-program - execute a program with given arguments, + * but don't wait for it. + * If the program can't be executed, logs an error unless + * must_exist is 0 and the program file doesn't exist. */ -/* ARGSUSED */ -u_long -GetMask(addr) - u_long addr; +int +run_program(prog, args, must_exist) + char *prog; + char **args; + int must_exist; { - return(netmask); + int pid; + + pid = fork(); + if (pid == -1) { + syslog(LOG_ERR, "Failed to create child process for %s: %m", prog); + return -1; + } + if (pid == 0) { + int new_fd; + + /* Leave the current location */ + (void) setsid(); /* No controlling tty. */ + (void) umask (S_IRWXG|S_IRWXO); + (void) chdir ("/"); /* no current directory. */ + setuid(geteuid()); + setgid(getegid()); + + /* Ensure that nothing of our device environment is inherited. */ + sys_close(); + closelog(); + close (0); + close (1); + close (2); + close (ttyfd); /* tty interface to the ppp device */ + + /* Don't pass handles to the PPP device, even by accident. */ + new_fd = open (_PATH_DEVNULL, O_RDWR); + if (new_fd >= 0) { + if (new_fd != 0) { + dup2 (new_fd, 0); /* stdin <- /dev/null */ + close (new_fd); + } + dup2 (0, 1); /* stdout -> /dev/null */ + dup2 (0, 2); /* stderr -> /dev/null */ + } + +#ifdef BSD + /* Force the priority back to zero if pppd is running higher. */ + if (setpriority (PRIO_PROCESS, 0, 0) < 0) + syslog (LOG_WARNING, "can't reset priority to 0: %m"); +#endif + + /* SysV recommends a second fork at this point. */ + + /* run the program; give it a null environment */ + execve(prog, args, script_env); + if (must_exist || errno != ENOENT) + syslog(LOG_WARNING, "Can't execute %s: %m", prog); + _exit(-1); + } + MAINDEBUG((LOG_DEBUG, "Script %s started; pid = %d", prog, pid)); + ++n_children; + return 0; } + /* - * dumpbuffer - print contents of a buffer in hex to standard output. + * reap_kids - get status from any dead child processes, + * and log a message for abnormal terminations. */ -void -dumpbuffer(buffer, size, level) - unsigned char *buffer; - int size; - int level; +static void +reap_kids() { - register int i; - char line[256], *p; - - printf("%d bytes:\n", size); - while (size > 0) - { - p = line; - sprintf(p, "%08lx: ", buffer); - p += 10; - - for (i = 0; i < 8; i++, p += 3) - if (size - i <= 0) - sprintf(p, "xx "); - else - sprintf(p, "%02x ", buffer[i]); - - for (i = 0; i < 8; i++) - if (size - i <= 0) - *p++ = 'x'; - else - *p++ = (' ' <= buffer[i] && buffer[i] <= '~') ? - buffer[i] : '.'; - - *p++ = 0; - buffer += 8; - size -= 8; - -/* syslog(level, "%s\n", line); */ - printf("%s\n", line); - fflush(stdout); + int pid, status; + + if (n_children == 0) + return; + if ((pid = waitpid(-1, &status, WNOHANG)) == -1) { + if (errno != ECHILD) + syslog(LOG_ERR, "Error waiting for child process: %m"); + return; + } + if (pid > 0) { + --n_children; + if (WIFSIGNALED(status)) { + syslog(LOG_WARNING, "Child process %d terminated with signal %d", + pid, WTERMSIG(status)); + } } } /* - * setdtr - control the DTR line on the serial port. - * This is called from die(), so it shouldn't call die(). + * log_packet - format a packet and log it. */ -setdtr(fd, on) -int fd, on; -{ - int modembits = TIOCM_DTR; - ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits); +char line[256]; /* line to be logged accumulated here */ +char *linep; + +void +log_packet(p, len, prefix, level) + u_char *p; + int len; + char *prefix; + int level; +{ + strcpy(line, prefix); + linep = line + strlen(line); + format_packet(p, len, pr_log, NULL); + if (linep != line) + syslog(level, "%s", line); } -#include +/* + * format_packet - make a readable representation of a packet, + * calling `printer(arg, format, ...)' to output it. + */ +void +format_packet(p, len, printer, arg) + u_char *p; + int len; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int i, n; + u_short proto; + u_char x; + struct protent *protp; + + if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { + p += 2; + GETSHORT(proto, p); + len -= PPP_HDRLEN; + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (proto == protp->protocol) + break; + if (protp != NULL) { + printer(arg, "[%s", protp->name); + n = (*protp->printpkt)(p, len, printer, arg); + printer(arg, "]"); + p += n; + len -= n; + } else { + printer(arg, "[proto=0x%x]", proto); + } + } -char line[256]; -char *p; + for (; len > 0; --len) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } +} -logf(level, fmt, va_alist) -int level; -char *fmt; -va_dcl +static void +pr_log __V((void *arg, char *fmt, ...)) { + int n; va_list pvar; char buf[256]; +#if __STDC__ + va_start(pvar, fmt); +#else + void *arg; + char *fmt; va_start(pvar); - vsprintf(buf, fmt, pvar); + arg = va_arg(pvar, void *); + fmt = va_arg(pvar, char *); +#endif + + n = vfmtmsg(buf, sizeof(buf), fmt, pvar); va_end(pvar); - p = line + strlen(line); - strcat(p, buf); + if (linep + n + 1 > line + sizeof(line)) { + syslog(LOG_DEBUG, "%s", line); + linep = line; + } + strcpy(linep, buf); + linep += n; +} - if (buf[strlen(buf)-1] == '\n') { - syslog(level, "%s", line); - line[0] = 0; +/* + * print_string - print a readable representation of a string using + * printer. + */ +void +print_string(p, len, printer, arg) + char *p; + int len; + void (*printer) __P((void *, char *, ...)); + void *arg; +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') + printer(arg, "\\"); + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + } + } } + printer(arg, "\""); } +/* + * novm - log an error message saying we ran out of memory, and die. + */ void novm(msg) char *msg; @@ -1236,3 +1352,327 @@ novm(msg) syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg); die(1); } + +/* + * fmtmsg - format a message into a buffer. Like sprintf except we + * also specify the length of the output buffer, and we handle + * %r (recursive format), %m (error message) and %I (IP address) formats. + * Doesn't do floating-point formats. + * Returns the number of chars put into buf. + */ +int +fmtmsg __V((char *buf, int buflen, char *fmt, ...)) +{ + va_list args; + int n; + +#if __STDC__ + va_start(args, fmt); +#else + char *buf; + int buflen; + char *fmt; + va_start(args); + buf = va_arg(args, char *); + buflen = va_arg(args, int); + fmt = va_arg(args, char *); +#endif + n = vfmtmsg(buf, buflen, fmt, args); + va_end(args); + return n; +} + +/* + * vfmtmsg - like fmtmsg, takes a va_list instead of a list of args. + */ +#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) + +int +vfmtmsg(buf, buflen, fmt, args) + char *buf; + int buflen; + char *fmt; + va_list args; +{ + int c, i, n; + int width, prec, fillch; + int base, len, neg, quoted; + unsigned long val = 0; + char *str, *f, *buf0; + unsigned char *p; + char num[32]; + time_t t; + static char hexchars[] = "0123456789abcdef"; + + buf0 = buf; + --buflen; + while (buflen > 0) { + for (f = fmt; *f != '%' && *f != 0; ++f) + ; + if (f > fmt) { + len = f - fmt; + if (len > buflen) + len = buflen; + memcpy(buf, fmt, len); + buf += len; + buflen -= len; + fmt = f; + } + if (*fmt == 0) + break; + c = *++fmt; + width = prec = 0; + fillch = ' '; + if (c == '0') { + fillch = '0'; + c = *++fmt; + } + if (c == '*') { + width = va_arg(args, int); + c = *++fmt; + } else { + while (isdigit(c)) { + width = width * 10 + c - '0'; + c = *++fmt; + } + } + if (c == '.') { + c = *++fmt; + if (c == '*') { + prec = va_arg(args, int); + c = *++fmt; + } else { + while (isdigit(c)) { + prec = prec * 10 + c - '0'; + c = *++fmt; + } + } + } + str = 0; + base = 0; + neg = 0; + ++fmt; + switch (c) { + case 'd': + i = va_arg(args, int); + if (i < 0) { + neg = 1; + val = -i; + } else + val = i; + base = 10; + break; + case 'o': + val = va_arg(args, unsigned int); + base = 8; + break; + case 'x': + val = va_arg(args, unsigned int); + base = 16; + break; + case 'p': + val = (unsigned long) va_arg(args, void *); + base = 16; + neg = 2; + break; + case 's': + str = va_arg(args, char *); + break; + case 'c': + num[0] = va_arg(args, int); + num[1] = 0; + str = num; + break; + case 'm': + str = strerror(errno); + break; + case 'I': + str = ip_ntoa(va_arg(args, u_int32_t)); + break; + case 'r': + f = va_arg(args, char *); +#ifndef __powerpc__ + n = vfmtmsg(buf, buflen + 1, f, va_arg(args, va_list)); +#else + /* On the powerpc, a va_list is an array of 1 structure */ + n = vfmtmsg(buf, buflen + 1, f, va_arg(args, void *)); +#endif + buf += n; + buflen -= n; + continue; + case 't': + time(&t); + str = ctime(&t); + str += 4; /* chop off the day name */ + str[15] = 0; /* chop off year and newline */ + break; + case 'v': /* "visible" string */ + case 'q': /* quoted string */ + quoted = c == 'q'; + p = va_arg(args, unsigned char *); + if (fillch == '0' && prec > 0) { + n = prec; + } else { + n = strlen((char *)p); + if (prec > 0 && prec < n) + n = prec; + } + while (n > 0 && buflen > 0) { + c = *p++; + --n; + if (!quoted && c >= 0x80) { + OUTCHAR('M'); + OUTCHAR('-'); + c -= 0x80; + } + if (quoted && (c == '"' || c == '\\')) + OUTCHAR('\\'); + if (c < 0x20 || (0x7f <= c && c < 0xa0)) { + if (quoted) { + OUTCHAR('\\'); + switch (c) { + case '\t': OUTCHAR('t'); break; + case '\n': OUTCHAR('n'); break; + case '\b': OUTCHAR('b'); break; + case '\f': OUTCHAR('f'); break; + default: + OUTCHAR('x'); + OUTCHAR(hexchars[c >> 4]); + OUTCHAR(hexchars[c & 0xf]); + } + } else { + if (c == '\t') + OUTCHAR(c); + else { + OUTCHAR('^'); + OUTCHAR(c ^ 0x40); + } + } + } else + OUTCHAR(c); + } + continue; + default: + *buf++ = '%'; + if (c != '%') + --fmt; /* so %z outputs %z etc. */ + --buflen; + continue; + } + if (base != 0) { + str = num + sizeof(num); + *--str = 0; + while (str > num + neg) { + *--str = hexchars[val % base]; + val = val / base; + if (--prec <= 0 && val == 0) + break; + } + switch (neg) { + case 1: + *--str = '-'; + break; + case 2: + *--str = 'x'; + *--str = '0'; + break; + } + len = num + sizeof(num) - 1 - str; + } else { + len = strlen(str); + if (prec > 0 && len > prec) + len = prec; + } + if (width > 0) { + if (width > buflen) + width = buflen; + if ((n = width - len) > 0) { + buflen -= n; + for (; n > 0; --n) + *buf++ = fillch; + } + } + if (len > buflen) + len = buflen; + memcpy(buf, str, len); + buf += len; + buflen -= len; + } + *buf = 0; + return buf - buf0; +} + +/* + * script_setenv - set an environment variable value to be used + * for scripts that we run (e.g. ip-up, auth-up, etc.) + */ +void +script_setenv(var, value) + char *var, *value; +{ + int vl = strlen(var); + int i; + char *p, *newstring; + + newstring = (char *) malloc(vl + strlen(value) + 2); + if (newstring == 0) + return; + strcpy(newstring, var); + newstring[vl] = '='; + strcpy(newstring+vl+1, value); + + /* check if this variable is already set */ + if (script_env != 0) { + for (i = 0; (p = script_env[i]) != 0; ++i) { + if (strncmp(p, var, vl) == 0 && p[vl] == '=') { + free(p); + script_env[i] = newstring; + return; + } + } + } else { + i = 0; + script_env = (char **) malloc(16 * sizeof(char *)); + if (script_env == 0) + return; + s_env_nalloc = 16; + } + + /* reallocate script_env with more space if needed */ + if (i + 1 >= s_env_nalloc) { + int new_n = i + 17; + char **newenv = (char **) realloc((void *)script_env, + new_n * sizeof(char *)); + if (newenv == 0) + return; + script_env = newenv; + s_env_nalloc = new_n; + } + + script_env[i] = newstring; + script_env[i+1] = 0; +} + +/* + * script_unsetenv - remove a variable from the environment + * for scripts. + */ +void +script_unsetenv(var) + char *var; +{ + int vl = strlen(var); + int i; + char *p; + + if (script_env == 0) + return; + for (i = 0; (p = script_env[i]) != 0; ++i) { + if (strncmp(p, var, vl) == 0 && p[vl] == '=') { + free(p); + while ((script_env[i] = script_env[i+1]) != 0) + ++i; + break; + } + } +}