X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fmain.c;h=4de4b994156c801566415b34693dc6ee59bb0ff1;hp=ba5e6aeb3fb3f43c36a01a1ce0d9ae3cfb419b0e;hb=3943299d6eea2ce36d9e32d83b5d8aa168e6f68c;hpb=f409b9f7b070fc7e0d1839f09d813503d7224df6 diff --git a/pppd/main.c b/pppd/main.c index ba5e6ae..4de4b99 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -17,9 +17,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static char rcsid[] = "$Id: main.c,v 1.66 1999/03/24 05:05:24 paulus Exp $"; -#endif +#define RCSID "$Id: main.c,v 1.87 1999/11/15 03:55:37 paulus Exp $" #include #include @@ -48,6 +46,9 @@ static char rcsid[] = "$Id: main.c,v 1.66 1999/03/24 05:05:24 paulus Exp $"; #include "fsm.h" #include "lcp.h" #include "ipcp.h" +#ifdef INET6 +#include "ipv6cp.h" +#endif #include "upap.h" #include "chap.h" #include "ccp.h" @@ -58,10 +59,6 @@ static char rcsid[] = "$Id: main.c,v 1.66 1999/03/24 05:05:24 paulus Exp $"; #include "cbcp.h" #endif -#if defined(SUNOS4) -extern char *strerror(); -#endif - #ifdef IPX_CHANGE #include "ipxcp.h" #endif /* IPX_CHANGE */ @@ -69,6 +66,8 @@ extern char *strerror(); #include "atcp.h" #endif +static const char rcsid[] = RCSID; + /* interface vars */ char ifname[32]; /* Interface name */ int ifunit; /* Interface unit number */ @@ -76,8 +75,8 @@ int ifunit; /* Interface unit number */ char *progname; /* Name of this program */ char hostname[MAXNAMELEN]; /* Our hostname */ static char pidfilename[MAXPATHLEN]; /* name of pid file */ +static char linkpidfile[MAXPATHLEN]; /* name of linkname pid file */ static char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */ -static pid_t pid; /* Our pid */ static uid_t uid; /* Our real user-id */ static int conn_running; /* we have a [dis]connector running */ @@ -88,7 +87,13 @@ 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 log_to_fd; /* send log messages to this fd too */ +struct stat devstat; /* result of stat() on devnam */ +int prepass = 0; /* doing prepass to find device name */ +int devnam_fixed; /* set while in options.ttyxx file */ +volatile int status; /* exit status for pppd */ +int unsuccess; /* # unsuccessful connection attempts */ +int (*holdoff_hook) __P((void)) = NULL; +int (*new_phase_hook) __P((int)) = NULL; static int fd_ppp = -1; /* fd for talking PPP */ static int fd_loop; /* fd for getting demand-dial packets */ @@ -110,8 +115,10 @@ 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 got_sigchld; /* set if we have received a SIGCHLD */ static int locked; /* lock() has succeeded */ +static int privopen; /* don't lock, open device as root */ char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n"; @@ -121,13 +128,29 @@ int ngroups; /* How many groups valid in groups */ static struct timeval start_time; /* Time when link was started. */ struct pppd_stats link_stats; +int link_connect_time; int link_stats_valid; static int charshunt_pid; /* Process ID for charshunt */ +/* + * We maintain a list of child process pids and + * functions to call when they exit. + */ +struct subprocess { + pid_t pid; + char *prog; + void (*done) __P((void *)); + void *arg; + struct subprocess *next; +}; + +static struct subprocess *children; + /* Prototypes for procedures local to this file. */ static void create_pidfile __P((void)); +static void create_linkpidfile __P((void)); static void cleanup __P((void)); static void close_tty __P((void)); static void get_input __P((void)); @@ -142,18 +165,13 @@ 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, int)); -static void reap_kids __P((int waitfor)); -static void pr_log __P((void *, char *, ...)); -static void logit __P((int, char *, va_list)); -static void vslp_printer __P((void *, char *, ...)); -static void format_packet __P((u_char *, int, void (*) (void *, char *, ...), - void *)); +static int reap_kids __P((int waitfor)); static void record_child __P((int, char *, void (*) (void *), void *)); static int start_charshunt __P((int, int)); static void charshunt_done __P((void *)); static void charshunt __P((int, int, char *)); -static int record_write(FILE *, int code, u_char *buf, int nb, - struct timeval *); +static int record_write __P((FILE *, int code, u_char *buf, int nb, + struct timeval *)); extern char *ttyname __P((int)); extern char *getlogin __P((void)); @@ -181,6 +199,9 @@ struct protent *protocols[] = { &cbcp_protent, #endif &ipcp_protent, +#ifdef INET6 + &ipv6cp_protent, +#endif &ccp_protent, #ifdef IPX_CHANGE &ipxcp_protent, @@ -196,7 +217,7 @@ main(argc, argv) int argc; char *argv[]; { - int i, fdflags; + int i, fdflags, t; struct sigaction sa; char *p; struct passwd *pw; @@ -205,20 +226,25 @@ main(argc, argv) struct protent *protp; struct stat statbuf; char numbuf[16]; - struct timeval now; - phase = PHASE_INITIALIZE; - log_to_fd = -1; + new_phase(PHASE_INITIALIZE); + + /* + * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else. + * This way we can close 0, 1, 2 in detach() without clobbering + * a fd that we are using. + */ + if ((i = open("/dev/null", O_RDWR)) >= 0) { + while (0 <= i && i <= 2) + i = dup(i); + if (i >= 0) + close(i); + } 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)); -#endif + reopen_log(); if (gethostname(hostname, MAXNAMELEN) < 0 ) { option_error("Couldn't get hostname: %m"); @@ -226,6 +252,9 @@ main(argc, argv) } hostname[MAXNAMELEN-1] = 0; + /* make sure we don't create world or group writable files. */ + umask(umask(0777) | 022); + uid = getuid(); privileged = uid == 0; slprintf(numbuf, sizeof(numbuf), "%d", uid); @@ -233,6 +262,12 @@ main(argc, argv) ngroups = getgroups(NGROUPS_MAX, groups); + /* + * Initialize magic number generator now so that protocols may + * use magic numbers in initialization. + */ + magic_init(); + /* * Initialize to the standard option set, then parse, in order, * the system options file, the user's options file, @@ -243,31 +278,45 @@ main(argc, argv) progname = *argv; + prepass = 0; if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1) || !options_from_user()) - exit(1); - using_pty = notty || ptycommand != NULL; - scan_args(argc-1, argv+1); /* look for tty name on command line */ + exit(EXIT_OPTION_ERROR); + + /* scan command line and options files to find device name */ + prepass = 1; + parse_args(argc-1, argv+1); + prepass = 0; /* * Work out the device name, if it hasn't already been specified. */ - if (!using_pty) { - p = isatty(0)? ttyname(0): NULL; - if (p != NULL) { - if (default_device) - strlcpy(devnam, p, sizeof(devnam)); - else if (strcmp(devnam, p) == 0) - default_device = 1; + using_pty = notty || ptycommand != NULL; + if (!using_pty && default_device) { + char *p; + if (!isatty(0) || (p = ttyname(0)) == NULL) { + option_error("no device specified and stdin is not a tty"); + exit(EXIT_OPTION_ERROR); } + strlcpy(devnam, p, sizeof(devnam)); + if (stat(devnam, &devstat) < 0) + fatal("Couldn't stat default device %s: %m", devnam); } /* * Parse the tty options file and the command line. + * The per-tty options file should not change + * ptycommand, notty or devnam. */ - if (!options_for_tty() - || !parse_args(argc-1, argv+1)) - exit(1); + devnam_fixed = 1; + if (!using_pty) { + if (!options_for_tty()) + exit(EXIT_OPTION_ERROR); + } + + devnam_fixed = 0; + if (!parse_args(argc-1, argv+1)) + exit(EXIT_OPTION_ERROR); /* * Check that we are running as root. @@ -275,61 +324,78 @@ main(argc, argv) if (geteuid() != 0) { option_error("must be root to run %s, since it is not setuid-root", argv[0]); - exit(1); + exit(EXIT_NOT_ROOT); } if (!ppp_available()) { option_error(no_ppp_msg); - exit(1); + exit(EXIT_NO_KERNEL_SUPPORT); } /* * Check that the options given are valid and consistent. */ if (!sys_check_options()) - exit(1); + exit(EXIT_OPTION_ERROR); 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 is required for demand-dialling\n"); - exit(1); + exit(EXIT_OPTION_ERROR); } + /* default holdoff to 0 if no connect script has been given */ + if (connector == 0 && !holdoff_specified) + holdoff = 0; if (using_pty) { if (!default_device) { option_error("%s option precludes specifying device name", notty? "notty": "pty"); - exit(1); + exit(EXIT_OPTION_ERROR); } if (ptycommand != NULL && notty) { option_error("pty option is incompatible with notty option"); - exit(1); + exit(EXIT_OPTION_ERROR); } default_device = notty; lockflag = 0; modem = 0; + if (notty && log_to_fd <= 1) + log_to_fd = -1; } else { - if (devnam[0] == 0) { - option_error("no device specified and stdin is not a tty"); - exit(1); + /* + * If the user has specified a device which is the same as + * the one on stdin, pretend they didn't specify any. + * If the device is already open read/write on stdin, + * we assume we don't need to lock it, and we can open it as root. + */ + if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) + && statbuf.st_rdev == devstat.st_rdev) { + default_device = 1; + fdflags = fcntl(0, F_GETFL); + if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR) + privopen = 1; } } if (default_device) nodetach = 1; - else - log_to_fd = 1; /* default to stdout */ + + /* + * Don't send log messages to the serial port, it tends to + * confuse the peer. :-) + */ + if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0 + && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) + log_to_fd = -1; script_setenv("DEVICE", devnam); - slprintf(numbuf, sizeof(numbuf), "%d", baud_rate); - script_setenv("SPEED", numbuf); /* - * Initialize system-dependent stuff and magic number package. + * Initialize system-dependent stuff. */ sys_init(); - magic_init(); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); @@ -339,7 +405,6 @@ main(argc, argv) */ if (!nodetach && !updetach) detach(); - pid = getpid(); p = getlogin(); if (p == NULL) { pw = getpwuid(uid); @@ -350,7 +415,8 @@ main(argc, argv) } syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d", VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid); - + script_setenv("PPPLOGNAME", p); + /* * Compute mask of all interesting signals and install signal handlers * for each. Only one signal handler may be active at a time. Therefore, @@ -427,6 +493,8 @@ main(argc, argv) waiting = 0; + create_linkpidfile(); + /* * If we're doing dial-on-demand, set up the interface now. */ @@ -453,19 +521,21 @@ main(argc, argv) need_holdoff = 1; ttyfd = -1; real_ttyfd = -1; + status = EXIT_OK; + ++unsuccess; if (demand) { /* * Don't do anything until we see some activity. */ kill_link = 0; - phase = PHASE_DORMANT; + new_phase(PHASE_DORMANT); demand_unblock(); add_fd(fd_loop); for (;;) { if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &mask, NULL); - if (kill_link) { + if (kill_link || got_sigchld) { sigprocmask(SIG_UNBLOCK, &mask, NULL); } else { waiting = 1; @@ -482,7 +552,8 @@ main(argc, argv) } if (get_loop_output()) break; - reap_kids(0); + if (got_sigchld) + reap_kids(0); } remove_fd(fd_loop); if (kill_link && !persist) @@ -495,6 +566,8 @@ main(argc, argv) info("Starting link"); } + new_phase(PHASE_SERIALCONN); + /* * Get a pty master/slave pair if the pty, notty, or record * options were specified. @@ -505,6 +578,7 @@ main(argc, argv) if (ptycommand != NULL || notty || record_file != NULL) { if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) { error("Couldn't allocate pseudo-tty"); + status = EXIT_FATAL_ERROR; goto fail; } set_up_tty(pty_slave, 1); @@ -513,7 +587,8 @@ main(argc, argv) /* * Lock the device if we've been asked to. */ - if (lockflag && !default_device) { + status = EXIT_LOCK_FAILED; + if (lockflag && !privopen) { if (lock(devnam) < 0) goto fail; locked = 1; @@ -532,16 +607,21 @@ main(argc, argv) for (;;) { /* If the user specified the device name, become the user before opening it. */ - if (!devnam_info.priv) + int err; + if (!devnam_info.priv && !privopen) seteuid(uid); ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); - if (!devnam_info.priv) + err = errno; + if (!devnam_info.priv && !privopen) seteuid(0); if (ttyfd >= 0) break; - if (errno != EINTR) + errno = err; + if (err != EINTR) { error("Failed to open %s: %m", devnam); - if (!persist || errno != EINTR) + status = EXIT_OPEN_FAILED; + } + if (!persist || err != EINTR) goto fail; } if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 @@ -559,7 +639,7 @@ main(argc, argv) /* * Set line speed, flow control, etc. - * If we have a non-null connection script, + * If we have a non-null connection or initializer script, * 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 @@ -567,7 +647,8 @@ main(argc, argv) * successfully to the modem with CLOCAL clear and CD down, * we could clear CLOCAL at this point. */ - set_up_tty(ttyfd, (connector != NULL && connector[0] != 0)); + set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0) + || initializer != NULL)); real_ttyfd = ttyfd; } @@ -575,6 +656,7 @@ main(argc, argv) * If the notty and/or record option was specified, * start up the character shunt now. */ + status = EXIT_PTYCMD_FAILED; if (ptycommand != NULL) { if (record_file != NULL) { int ipipe[2], opipe[2], ok; @@ -605,9 +687,7 @@ main(argc, argv) } /* run connection script */ - if (connector && connector[0]) { - MAINDEBUG(("Connecting with <%s>", connector)); - + if ((connector && connector[0]) || initializer) { if (real_ttyfd != -1) { if (!default_device && modem) { setdtr(real_ttyfd, 0); /* in case modem is off hook */ @@ -616,12 +696,29 @@ main(argc, argv) } } - if (device_script(connector, ttyfd, ttyfd, 0) < 0) { - error("Connect script failed"); - goto fail; + if (initializer && initializer[0]) { + if (device_script(initializer, ttyfd, ttyfd, 0) < 0) { + error("Initializer script failed"); + status = EXIT_INIT_FAILED; + goto fail; + } + if (kill_link) + goto disconnect; + + info("Serial port initialized."); } - info("Serial connection established."); + if (connector && connector[0]) { + if (device_script(connector, ttyfd, ttyfd, 0) < 0) { + error("Connect script failed"); + status = EXIT_CONNECT_FAILED; + goto fail; + } + if (kill_link) + goto disconnect; + + info("Serial connection established."); + } /* set line speed, flow control, etc.; clear CLOCAL if modem option */ @@ -634,14 +731,19 @@ main(argc, argv) for (;;) { if ((i = open(devnam, O_RDWR)) >= 0) break; - if (errno != EINTR) + if (errno != EINTR) { error("Failed to reopen %s: %m", devnam); + status = EXIT_OPEN_FAILED; + } if (!persist || errno != EINTR || hungup || kill_link) goto fail; } close(i); } + slprintf(numbuf, sizeof(numbuf), "%d", baud_rate); + script_setenv("SPEED", numbuf); + /* run welcome script, if any */ if (welcomer && welcomer[0]) { if (device_script(welcomer, ttyfd, ttyfd, 0) < 0) @@ -650,8 +752,10 @@ main(argc, argv) /* set up the serial device as a ppp interface */ fd_ppp = establish_ppp(ttyfd); - if (fd_ppp < 0) - goto fail; + if (fd_ppp < 0) { + status = EXIT_FATAL_ERROR; + goto disconnect; + } if (!demand) { @@ -668,6 +772,10 @@ main(argc, argv) */ notice("Connect: %s <--> %s", ifname, ppp_devnam); gettimeofday(&start_time, NULL); + link_stats_valid = 0; + script_unsetenv("CONNECT_TIME"); + script_unsetenv("BYTES_SENT"); + script_unsetenv("BYTES_RCVD"); lcp_lowerup(0); /* @@ -685,10 +793,12 @@ main(argc, argv) lcp_open(0); /* Start protocol */ open_ccp_flag = 0; add_fd(fd_ppp); - for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) { + status = EXIT_NEGOTIATION_FAILED; + new_phase(PHASE_ESTABLISH); + while (phase != PHASE_DEAD) { if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &mask, NULL); - if (kill_link || open_ccp_flag) { + if (kill_link || open_ccp_flag || got_sigchld) { sigprocmask(SIG_UNBLOCK, &mask, NULL); } else { waiting = 1; @@ -704,28 +814,38 @@ main(argc, argv) kill_link = 0; } if (open_ccp_flag) { - if (phase == PHASE_NETWORK) { + if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) { ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ (*ccp_protent.open)(0); } open_ccp_flag = 0; } - reap_kids(0); /* Don't leave dead kids lying around */ + if (got_sigchld) + reap_kids(0); /* Don't leave dead kids lying around */ } /* * Print connect time and statistics. */ - if (gettimeofday(&now, NULL) >= 0) { - int t = now.tv_sec - start_time.tv_sec; - t = (t + 5) / 6; /* now in 1/10ths of minutes */ - info("Connect time %d.%d minutes.", t/10, t%10); - } if (link_stats_valid) { + int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */ + info("Connect time %d.%d minutes.", t/10, t%10); info("Sent %d bytes, received %d bytes.", link_stats.bytes_out, link_stats.bytes_in); } + /* + * Delete pid file before disestablishing ppp. Otherwise it + * can happen that another pppd gets the same unit and then + * we delete its pid file. + */ + if (!demand) { + if (pidfilename[0] != 0 + && unlink(pidfilename) < 0 && errno != ENOENT) + warn("unable to delete pid file %s: %m", pidfilename); + pidfilename[0] = 0; + } + /* * If we may want to bring the link up again, transfer * the ppp unit back to the loopback. Set the @@ -737,12 +857,16 @@ main(argc, argv) restore_loop(); disestablish_ppp(ttyfd); fd_ppp = -1; + if (!hungup) + lcp_lowerdown(0); /* * Run disconnector script, if requested. * XXX we may not be able to do this if the line has hung up! */ + disconnect: if (disconnector && !hungup) { + new_phase(PHASE_DISCONNECT); if (real_ttyfd >= 0) set_up_tty(real_ttyfd, 1); if (device_script(disconnector, ttyfd, ttyfd, 0) < 0) { @@ -751,8 +875,6 @@ main(argc, argv) info("Serial link disconnected."); } } - if (!hungup) - lcp_lowerdown(0); fail: if (pty_master >= 0) @@ -769,23 +891,26 @@ main(argc, argv) if (!demand) { if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) - warn("unable to delete pid file: %m"); + warn("unable to delete pid file %s: %m", pidfilename); pidfilename[0] = 0; } - if (!persist) + if (!persist || (maxfail > 0 && unsuccess >= maxfail)) break; kill_link = 0; if (demand) demand_discard(); - if (holdoff > 0 && need_holdoff) { - phase = PHASE_HOLDOFF; - TIMEOUT(holdoff_end, NULL, holdoff); + t = need_holdoff? holdoff: 0; + if (holdoff_hook) + t = (*holdoff_hook)(); + if (t > 0) { + new_phase(PHASE_HOLDOFF); + TIMEOUT(holdoff_end, NULL, t); do { if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &mask, NULL); - if (kill_link) { + if (kill_link || got_sigchld) { sigprocmask(SIG_UNBLOCK, &mask, NULL); } else { waiting = 1; @@ -797,9 +922,10 @@ main(argc, argv) calltimeout(); if (kill_link) { kill_link = 0; - phase = PHASE_DORMANT; /* allow signal to end holdoff */ + new_phase(PHASE_DORMANT); /* allow signal to end holdoff */ } - reap_kids(0); + if (got_sigchld) + reap_kids(0); } while (phase == PHASE_HOLDOFF); if (!persist) break; @@ -807,10 +933,19 @@ main(argc, argv) } /* Wait for scripts to finish */ - while (n_children > 0) - reap_kids(1); + /* XXX should have a timeout here */ + while (n_children > 0) { + if (debug) { + struct subprocess *chp; + dbglog("Waiting for %d child processes...", n_children); + for (chp = children; chp != NULL; chp = chp->next) + dbglog(" script %s, pid %d", chp->prog, chp->pid); + } + if (reap_kids(1) < 0) + break; + } - die(0); + die(status); return 0; } @@ -820,18 +955,46 @@ main(argc, argv) void detach() { + int pid; + if (detached) return; - if (daemon(0, 0) < 0) { - perror("Couldn't detach from controlling terminal"); - die(1); + if ((pid = fork()) < 0) { + error("Couldn't detach (fork failed: %m)"); + die(1); /* or just return? */ } + if (pid != 0) { + /* parent */ + if (locked) + relock(pid); + exit(0); /* parent dies */ + } + setsid(); + chdir("/"); + close(0); + close(1); + close(2); detached = 1; log_to_fd = -1; - pid = getpid(); - /* update pid file if it has been written already */ + /* update pid files if they have been written already */ if (pidfilename[0]) create_pidfile(); + if (linkpidfile[0]) + create_linkpidfile(); +} + +/* + * reopen_log - (re)open our connection to syslog. + */ +void +reopen_log() +{ +#ifdef ULTRIX + openlog("pppd", LOG_PID); +#else + openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); + setlogmask(LOG_UPTO(LOG_INFO)); +#endif } /* @@ -846,14 +1009,37 @@ create_pidfile() slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid", _PATH_VARRUN, ifname); if ((pidfile = fopen(pidfilename, "w")) != NULL) { - fprintf(pidfile, "%d\n", pid); + fprintf(pidfile, "%d\n", getpid()); (void) fclose(pidfile); } else { error("Failed to create pid file %s: %m", pidfilename); pidfilename[0] = 0; } - slprintf(numbuf, sizeof(numbuf), "%d", pid); + slprintf(numbuf, sizeof(numbuf), "%d", getpid()); script_setenv("PPPD_PID", numbuf); + if (linkpidfile[0]) + create_linkpidfile(); +} + +static void +create_linkpidfile() +{ + FILE *pidfile; + + if (linkname[0] == 0) + return; + slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid", + _PATH_VARRUN, linkname); + if ((pidfile = fopen(linkpidfile, "w")) != NULL) { + fprintf(pidfile, "%d\n", getpid()); + if (pidfilename[0]) + fprintf(pidfile, "%s\n", ifname); + (void) fclose(pidfile); + } else { + error("Failed to create pid file %s: %m", linkpidfile); + linkpidfile[0] = 0; + } + script_setenv("LINKNAME", linkname); } /* @@ -863,7 +1049,7 @@ static void holdoff_end(arg) void *arg; { - phase = PHASE_DORMANT; + new_phase(PHASE_DORMANT); } /* @@ -886,6 +1072,7 @@ get_input() if (len == 0) { notice("Modem hangup"); hungup = 1; + status = EXIT_HANGUP; lcp_lowerdown(0); /* serial link is no longer available */ link_terminated(0); return; @@ -943,6 +1130,17 @@ get_input() lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN); } +/* + * new_phase - signal the start of a new phase of pppd's operation. + */ +void +new_phase(p) + int p; +{ + phase = p; + if (new_phase_hook) + (*new_phase_hook)(p); +} /* * die - clean up state and exit with the specified status. @@ -971,8 +1169,11 @@ cleanup() close_tty(); if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) - warn("unable to delete pid file: %m"); + warn("unable to delete pid file %s: %m", pidfilename); pidfilename[0] = 0; + if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT) + warn("unable to delete pid file %s: %m", linkpidfile); + linkpidfile[0] = 0; if (locked) unlock(); @@ -1007,6 +1208,30 @@ close_tty() real_ttyfd = -1; } +/* + * update_link_stats - get stats at link termination. + */ +void +update_link_stats(u) + int u; +{ + struct timeval now; + char numbuf[32]; + + if (!get_ppp_stats(u, &link_stats) + || gettimeofday(&now, NULL) < 0) + return; + link_connect_time = now.tv_sec - start_time.tv_sec; + link_stats_valid = 1; + + slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time); + script_setenv("CONNECT_TIME", numbuf); + slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out); + script_setenv("BYTES_SENT", numbuf); + slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in); + script_setenv("BYTES_RCVD", numbuf); +} + struct callout { struct timeval c_time; /* time at which to call routine */ @@ -1076,7 +1301,7 @@ untimeout(func, arg) for (copp = &callout; (freep = *copp); copp = &freep->c_next) if (freep->c_func == func && freep->c_arg == arg) { *copp = freep->c_next; - (void) free((char *) freep); + free((char *) freep); break; } } @@ -1162,6 +1387,8 @@ hup(sig) { info("Hangup (SIGHUP)"); kill_link = 1; + if (status != EXIT_HANGUP) + status = EXIT_USER_REQUEST; if (conn_running) /* Send the signal to the [dis]connector process(es) also */ kill_my_pg(sig); @@ -1185,6 +1412,7 @@ term(sig) info("Terminating on signal %d.", sig); persist = 0; /* don't try to restart */ kill_link = 1; + status = EXIT_USER_REQUEST; if (conn_running) /* Send the signal to the [dis]connector process(es) also */ kill_my_pg(sig); @@ -1197,12 +1425,13 @@ term(sig) /* * chld - Catch SIGCHLD signal. - * Calls reap_kids to get status for any dead kids. + * Sets a flag so we will call reap_kids in the mainline. */ static void chld(sig) int sig; { + got_sigchld = 1; if (waiting) siglongjmp(sigjmp, 1); } @@ -1260,7 +1489,7 @@ bad_signal(sig) kill_my_pg(SIGTERM); if (charshunt_pid) kill(charshunt_pid, SIGTERM); - die(1); + die(127); } @@ -1275,7 +1504,7 @@ device_script(program, in, out, dont_wait) int dont_wait; { int pid; - int status = 0; + int status = -1; int errfd; ++conn_running; @@ -1314,12 +1543,9 @@ device_script(program, in, out, dont_wait) if (out == 0) out = dup(out); dup2(in, 0); - if (in != out) - close(in); } if (out != 1) { dup2(out, 1); - close(out); } if (real_ttyfd > 2) close(real_ttyfd); @@ -1341,6 +1567,7 @@ device_script(program, in, out, dont_wait) if (dont_wait) { record_child(pid, program, NULL, NULL); + status = 0; } else { while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) @@ -1354,20 +1581,6 @@ device_script(program, in, out, dont_wait) } -/* - * We maintain a list of child process pids and - * functions to call when they exit. - */ -struct subprocess { - pid_t pid; - char *prog; - void (*done) __P((void *)); - void *arg; - struct subprocess *next; -}; - -struct subprocess *children; - /* * run-program - execute a program with given arguments, * but don't wait for it. @@ -1452,12 +1665,7 @@ run_program(prog, args, must_exist, done, arg) if (must_exist || errno != ENOENT) { /* have to reopen the log, there's nowhere else for the message to go. */ -#ifdef ULTRIX - openlog("pppd", LOG_PID); -#else - openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); - setlogmask(LOG_UPTO(LOG_INFO)); -#endif + reopen_log(); syslog(LOG_ERR, "Can't execute %s: %m", prog); closelog(); } @@ -1465,7 +1673,7 @@ run_program(prog, args, must_exist, done, arg) } if (debug) - dbglog("Script %s started; pid = %d", prog, pid); + dbglog("Script %s started (pid %d)", prog, pid); record_child(pid, prog, done, arg); return pid; @@ -1505,205 +1713,45 @@ record_child(pid, prog, done, arg) * reap_kids - get status from any dead child processes, * and log a message for abnormal terminations. */ -static void +static int reap_kids(waitfor) int waitfor; { int pid, status; struct subprocess *chp, **prevp; + got_sigchld = 0; if (n_children == 0) - return; + return 0; while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1 && pid != 0) { --n_children; - for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) - if (chp->pid == pid) + for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) { + if (chp->pid == pid) { + *prevp = chp->next; break; + } + } if (WIFSIGNALED(status)) { warn("Child process %s (pid %d) terminated with signal %d", (chp? chp->prog: "??"), pid, WTERMSIG(status)); } else if (debug) - dbglog("process %d (%s) finished, status = 0x%x", - pid, (chp? chp->prog: "??"), status); + dbglog("Script %s finished (pid %d), status = 0x%x", + (chp? chp->prog: "??"), pid, status); if (chp && chp->done) (*chp->done)(chp->arg); + if (chp) + free(chp); } - if (pid == -1 && errno != ECHILD && errno != EINTR) - error("Error waiting for child process: %m"); -} - - -/* - * log_packet - format a packet and log it. - */ - -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; -{ - strlcpy(line, prefix, sizeof(line)); - linep = line + strlen(line); - format_packet(p, len, pr_log, NULL); - if (linep != line) - syslog(level, "%s", line); -} - -/* - * format_packet - make a readable representation of a packet, - * calling `printer(arg, format, ...)' to output it. - */ -static 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; - 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 { - for (i = 0; (protp = protocols[i]) != NULL; ++i) - if (proto == (protp->protocol & ~0x8000)) - break; - if (protp != 0 && protp->data_name != 0) { - printer(arg, "[%s data]", protp->data_name); - if (len > 8) - printer(arg, "%.8B ...", p); - else - printer(arg, "%.*B", len, p); - len = 0; - } else - printer(arg, "[proto=0x%x]", proto); - } - } - - if (len > 32) - printer(arg, "%.32B ...", p); - else - printer(arg, "%.*B", len, p); -} - -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); - arg = va_arg(pvar, void *); - fmt = va_arg(pvar, char *); -#endif - - n = vslprintf(buf, sizeof(buf), fmt, pvar); - va_end(pvar); - - if (linep + n + 1 > line + sizeof(line)) { - syslog(LOG_DEBUG, "%s", line); - linep = line; + if (pid == -1) { + if (errno == ECHILD) + return -1; + if (errno != EINTR) + error("Error waiting for child process: %m"); } - strlcpy(linep, buf, line + sizeof(line) - linep); - linep += n; -} - -/* - * vslp_printer - used in processing a %P format - */ -struct buffer_info { - char *ptr; - int len; -}; - -static void -vslp_printer __V((void *arg, char *fmt, ...)) -{ - int n; - va_list pvar; - struct buffer_info *bi; - -#if __STDC__ - va_start(pvar, fmt); -#else - void *arg; - char *fmt; - va_start(pvar); - arg = va_arg(pvar, void *); - fmt = va_arg(pvar, char *); -#endif - - bi = (struct buffer_info *) arg; - n = vslprintf(bi->ptr, bi->len, fmt, pvar); - va_end(pvar); - - bi->ptr += n; - bi->len -= n; + return 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. @@ -1715,282 +1763,6 @@ novm(msg) fatal("Virtual memory exhausted allocating %s\n", msg); } -/* - * slprintf - 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), %v (visible string), - * %q (quoted string), %t (current time) and %I (IP address) formats. - * Doesn't do floating-point formats. - * Returns the number of chars put into buf. - */ -int -slprintf __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 = vslprintf(buf, buflen, fmt, args); - va_end(args); - return n; -} - -/* - * vslprintf - like slprintf, takes a va_list instead of a list of args. - */ -#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) - -int -vslprintf(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; - u_int32_t ip; - static char hexchars[] = "0123456789abcdef"; - struct buffer_info bufinfo; - - 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': - 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': - ip = va_arg(args, u_int32_t); - ip = ntohl(ip); - slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff, - (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); - str = num; - break; - case 'r': - f = va_arg(args, char *); -#ifndef __powerpc__ - n = vslprintf(buf, buflen + 1, f, va_arg(args, va_list)); -#else - /* On the powerpc, a va_list is an array of 1 structure */ - n = vslprintf(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; - case 'P': /* print PPP packet */ - bufinfo.ptr = buf; - bufinfo.len = buflen + 1; - p = va_arg(args, unsigned char *); - n = va_arg(args, int); - format_packet(p, n, vslp_printer, &bufinfo); - buf = bufinfo.ptr; - buflen = bufinfo.len - 1; - continue; - case 'B': - p = va_arg(args, unsigned char *); - for (n = prec; n > 0; --n) { - c = *p++; - if (fillch == ' ') - OUTCHAR(' '); - OUTCHAR(hexchars[(c >> 4) & 0xf]); - OUTCHAR(hexchars[c & 0xf]); - } - 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.) @@ -2018,6 +1790,7 @@ script_setenv(var, value) } } } else { + /* no space allocated for script env. ptrs. yet */ i = 0; script_env = (char **) malloc(16 * sizeof(char *)); if (script_env == 0) @@ -2064,188 +1837,6 @@ script_unsetenv(var) } } -/* - * strlcpy - like strcpy/strncpy, doesn't overflow destination buffer, - * always leaves destination null-terminated (for len > 0). - */ -size_t -strlcpy(dest, src, len) - char *dest; - const char *src; - size_t len; -{ - size_t ret = strlen(src); - - if (len != 0) { - if (ret < len) - strcpy(dest, src); - else { - strncpy(dest, src, len - 1); - dest[len-1] = 0; - } - } - return ret; -} - -/* - * strlcat - like strcat/strncat, doesn't overflow destination buffer, - * always leaves destination null-terminated (for len > 0). - */ -size_t -strlcat(dest, src, len) - char *dest; - const char *src; - size_t len; -{ - size_t dlen = strlen(dest); - - return dlen + strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0)); -} - -/* - * logit - does the hard work for fatal et al. - */ -static void -logit(level, fmt, args) - int level; - char *fmt; - va_list args; -{ - int n; - char buf[256]; - - n = vslprintf(buf, sizeof(buf), fmt, args); - syslog(level, "%s", buf); - if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) { - if (buf[n-1] != '\n') - buf[n++] = '\n'; - if (write(log_to_fd, buf, n) != n) - log_to_fd = -1; - } -} - -/* - * fatal - log an error message and die horribly. - */ -void -fatal __V((char *fmt, ...)) -{ - va_list pvar; - -#if __STDC__ - va_start(pvar, fmt); -#else - char *fmt; - va_start(pvar); - fmt = va_arg(pvar, char *); -#endif - - logit(LOG_ERR, fmt, pvar); - va_end(pvar); - - die(1); /* as promised */ -} - -/* - * error - log an error message. - */ -void -error __V((char *fmt, ...)) -{ - va_list pvar; - -#if __STDC__ - va_start(pvar, fmt); -#else - char *fmt; - va_start(pvar); - fmt = va_arg(pvar, char *); -#endif - - logit(LOG_ERR, fmt, pvar); - va_end(pvar); -} - -/* - * warn - log a warning message. - */ -void -warn __V((char *fmt, ...)) -{ - va_list pvar; - -#if __STDC__ - va_start(pvar, fmt); -#else - char *fmt; - va_start(pvar); - fmt = va_arg(pvar, char *); -#endif - - logit(LOG_WARNING, fmt, pvar); - va_end(pvar); -} - -/* - * notice - log a notice-level message. - */ -void -notice __V((char *fmt, ...)) -{ - va_list pvar; - -#if __STDC__ - va_start(pvar, fmt); -#else - char *fmt; - va_start(pvar); - fmt = va_arg(pvar, char *); -#endif - - logit(LOG_NOTICE, fmt, pvar); - va_end(pvar); -} - -/* - * info - log an informational message. - */ -void -info __V((char *fmt, ...)) -{ - va_list pvar; - -#if __STDC__ - va_start(pvar, fmt); -#else - char *fmt; - va_start(pvar); - fmt = va_arg(pvar, char *); -#endif - - logit(LOG_INFO, fmt, pvar); - va_end(pvar); -} - -/* - * dbglog - log a debug message. - */ -void -dbglog __V((char *fmt, ...)) -{ - va_list pvar; - -#if __STDC__ - va_start(pvar, fmt); -#else - char *fmt; - va_start(pvar); - fmt = va_arg(pvar, char *); -#endif - - logit(LOG_DEBUG, fmt, pvar); - va_end(pvar); -} - /* * start_charshunt - create a child process to run the character shunt. */ @@ -2379,8 +1970,16 @@ charshunt(ifd, ofd, record_file) nibuf = nobuf = 0; ibufp = obufp = NULL; pty_readable = stdin_readable = 1; - gettimeofday(&lasttime, NULL); nfds = (ofd > pty_master? ofd: pty_master) + 1; + if (recordf != NULL) { + gettimeofday(&lasttime, NULL); + putc(7, recordf); /* put start marker */ + putc(lasttime.tv_sec >> 24, recordf); + putc(lasttime.tv_sec >> 16, recordf); + putc(lasttime.tv_sec >> 8, recordf); + putc(lasttime.tv_sec, recordf); + lasttime.tv_usec = 0; + } while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) { FD_ZERO(&ready); @@ -2496,8 +2095,8 @@ record_write(f, code, buf, nb, tp) int diff; gettimeofday(&now, NULL); - diff = (now.tv_sec - tp->tv_sec) * 10 - + (now.tv_usec - tp->tv_usec) / 100000; + now.tv_usec /= 100000; /* actually 1/10 s, not usec now */ + diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec); if (diff > 0) { if (diff > 255) { putc(5, f);