X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Fmain.c;h=4da0b9af4bc73b2eaf9dea34abcea2ec18368938;hb=31b4bba68d46b38119fd8620ee09ff7f8831f4b5;hp=31828a788f48ae0960d00e3b594303876993ca4f;hpb=8821c36a7798fd1c16d85ce63bcf519618975800;p=ppp.git diff --git a/pppd/main.c b/pppd/main.c index 31828a7..4da0b9a 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.75 1999/04/12 06:44:42 paulus Exp $"; -#endif +#define RCSID "$Id: main.c,v 1.83 1999/08/13 06:46:15 paulus Exp $" #include #include @@ -58,10 +56,6 @@ static char rcsid[] = "$Id: main.c,v 1.75 1999/04/12 06:44:42 paulus Exp $"; #include "cbcp.h" #endif -#if defined(SUNOS4) -extern char *strerror(); -#endif - #ifdef IPX_CHANGE #include "ipxcp.h" #endif /* IPX_CHANGE */ @@ -69,6 +63,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 +72,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 +84,11 @@ 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 */ static int fd_ppp = -1; /* fd for talking PPP */ static int fd_loop; /* fd for getting demand-dial packets */ @@ -113,6 +113,7 @@ 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"; @@ -122,6 +123,7 @@ 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 */ @@ -143,6 +145,7 @@ 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)); @@ -215,10 +218,20 @@ main(argc, argv) struct protent *protp; struct stat statbuf; char numbuf[16]; - struct timeval now; phase = PHASE_INITIALIZE; - log_to_fd = -1; + + /* + * 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; @@ -248,31 +261,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. @@ -280,51 +307,68 @@ 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); } 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); @@ -342,7 +386,6 @@ main(argc, argv) */ if (!nodetach && !updetach) detach(); - pid = getpid(); p = getlogin(); if (p == NULL) { pw = getpwuid(uid); @@ -431,6 +474,8 @@ main(argc, argv) waiting = 0; + create_linkpidfile(); + /* * If we're doing dial-on-demand, set up the interface now. */ @@ -457,6 +502,8 @@ main(argc, argv) need_holdoff = 1; ttyfd = -1; real_ttyfd = -1; + status = EXIT_OK; + ++unsuccess; if (demand) { /* @@ -469,7 +516,7 @@ main(argc, argv) 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; @@ -500,6 +547,8 @@ main(argc, argv) info("Starting link"); } + phase = PHASE_SERIALCONN; + /* * Get a pty master/slave pair if the pty, notty, or record * options were specified. @@ -510,6 +559,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); @@ -518,7 +568,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; @@ -538,17 +589,19 @@ main(argc, argv) /* If the user specified the device name, become the user before opening it. */ int err; - if (!devnam_info.priv && !default_device) + if (!devnam_info.priv && !privopen) seteuid(uid); ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); err = errno; - if (!devnam_info.priv && !default_device) + if (!devnam_info.priv && !privopen) seteuid(0); if (ttyfd >= 0) break; errno = err; - if (err != EINTR) + if (err != EINTR) { error("Failed to open %s: %m", devnam); + status = EXIT_OPEN_FAILED; + } if (!persist || err != EINTR) goto fail; } @@ -567,7 +620,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 @@ -575,7 +628,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; } @@ -583,6 +637,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; @@ -613,9 +668,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 */ @@ -624,14 +677,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."); } - if (kill_link) - goto disconnect; - 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 */ @@ -644,8 +712,10 @@ 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; } @@ -663,8 +733,10 @@ main(argc, argv) /* set up the serial device as a ppp interface */ fd_ppp = establish_ppp(ttyfd); - if (fd_ppp < 0) + if (fd_ppp < 0) { + status = EXIT_FATAL_ERROR; goto disconnect; + } if (!demand) { @@ -681,6 +753,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); /* @@ -698,10 +774,11 @@ main(argc, argv) lcp_open(0); /* Start protocol */ open_ccp_flag = 0; add_fd(fd_ppp); + status = EXIT_NEGOTIATION_FAILED; for (phase = PHASE_ESTABLISH; 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; @@ -730,12 +807,9 @@ main(argc, argv) /* * 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); } @@ -784,11 +858,11 @@ 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; @@ -800,7 +874,7 @@ main(argc, argv) 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; @@ -823,6 +897,7 @@ main(argc, argv) } /* Wait for scripts to finish */ + /* XXX should have a timeout here */ while (n_children > 0) { if (debug) { struct subprocess *chp; @@ -833,7 +908,7 @@ main(argc, argv) reap_kids(1); } - die(0); + die(status); return 0; } @@ -859,15 +934,16 @@ detach() } setsid(); chdir("/"); - close(0); /* XXX we should make sure that none */ - close(1); /* of the fds we need are <= 2 */ + 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(); } /* @@ -896,14 +972,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); } /* @@ -936,6 +1035,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; @@ -1021,8 +1121,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(); @@ -1057,6 +1160,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 */ @@ -1212,6 +1339,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); @@ -1235,6 +1364,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); @@ -1311,7 +1441,7 @@ bad_signal(sig) kill_my_pg(SIGTERM); if (charshunt_pid) kill(charshunt_pid, SIGTERM); - die(1); + die(127); } @@ -1495,7 +1625,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; @@ -1548,9 +1678,12 @@ reap_kids(waitfor) 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)); @@ -1559,6 +1692,8 @@ reap_kids(waitfor) (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");