From 8592783058509b5493a42f3b0432fcbaab55c994 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 12 May 1999 06:19:49 +0000 Subject: [PATCH] exit with an appropriate value to indicate what happened look in included options files for device name before processing options.ttyname file don't send log output to the serial port cope with user specifying a symlink to the device on stdin add logfd and nologfd options insist that the device is a character device --- pppd/auth.c | 7 ++- pppd/cbcp.c | 5 +- pppd/lcp.c | 3 +- pppd/main.c | 134 ++++++++++++++++++++++++++++++++-------------- pppd/options.c | 52 +++++++++++++----- pppd/patchlevel.h | 6 +-- pppd/pppd.h | 29 +++++++++- 7 files changed, 176 insertions(+), 60 deletions(-) diff --git a/pppd/auth.c b/pppd/auth.c index 53b4ef5..de3ae64 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -33,7 +33,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: auth.c,v 1.51 1999/04/12 06:24:44 paulus Exp $"; +static char rcsid[] = "$Id: auth.c,v 1.52 1999/05/12 06:19:46 paulus Exp $"; #endif #include @@ -367,6 +367,7 @@ link_established(unit) if (!wo->neg_upap || !null_login(unit)) { warn("peer refused to authenticate: terminating link"); lcp_close(unit, "peer refused to authenticate"); + status = EXIT_PEER_AUTH_FAILED; return; } } @@ -460,6 +461,7 @@ auth_peer_fail(unit, protocol) * Authentication failure: take the link down */ lcp_close(unit, "Authentication failed"); + status = EXIT_PEER_AUTH_FAILED; } /* @@ -562,6 +564,7 @@ np_up(unit, proto) * At this point we consider that the link has come up successfully. */ need_holdoff = 0; + status = EXIT_OK; if (idle_time_limit > 0) TIMEOUT(check_idle, NULL, idle_time_limit); @@ -625,6 +628,7 @@ check_idle(arg) /* link is idle: shut it down. */ notice("Terminating connection due to lack of activity."); lcp_close(0, "Link inactive"); + status = EXIT_IDLE_TIMEOUT; } else { TIMEOUT(check_idle, NULL, idle_time_limit - itime); } @@ -639,6 +643,7 @@ connect_time_expired(arg) { info("Connect time expired"); lcp_close(0, "Connect time expired"); /* Close connection */ + status = EXIT_CONNECT_TIME; } /* diff --git a/pppd/cbcp.c b/pppd/cbcp.c index 64472f5..34ba6f7 100644 --- a/pppd/cbcp.c +++ b/pppd/cbcp.c @@ -19,7 +19,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: cbcp.c,v 1.6 1999/03/16 22:54:38 paulus Exp $"; +static char rcsid[] = "$Id: cbcp.c,v 1.7 1999/05/12 06:19:46 paulus Exp $"; #endif #include @@ -439,6 +439,8 @@ cbcp_recvack(us, pckt, len) if (address[0]) dbglog("peer will call: %s", address); } + if (type == CB_CONF_NO) + return; } cbcp_up(us); @@ -451,4 +453,5 @@ cbcp_up(us) { persist = 0; lcp_close(0, "Call me back, please"); + status = EXIT_CALLBACK; } diff --git a/pppd/lcp.c b/pppd/lcp.c index 73f310d..dbf100d 100644 --- a/pppd/lcp.c +++ b/pppd/lcp.c @@ -18,7 +18,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: lcp.c,v 1.38 1999/04/16 11:35:43 paulus Exp $"; +static char rcsid[] = "$Id: lcp.c,v 1.39 1999/05/12 06:19:47 paulus Exp $"; #endif /* @@ -1805,6 +1805,7 @@ void LcpLinkFailure (f) info("No response to %d echo-requests", lcp_echos_pending); notice("Serial link appears to be disconnected."); lcp_close(f->unit, "Peer not responding"); + status = EXIT_PEER_DEAD; } } diff --git a/pppd/main.c b/pppd/main.c index e67e18c..cd795fa 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -18,7 +18,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: main.c,v 1.77 1999/04/28 02:45:44 paulus Exp $"; +static char rcsid[] = "$Id: main.c,v 1.78 1999/05/12 06:19:47 paulus Exp $"; #endif #include @@ -58,10 +58,6 @@ static char rcsid[] = "$Id: main.c,v 1.77 1999/04/28 02:45:44 paulus Exp $"; #include "cbcp.h" #endif -#if defined(SUNOS4) -extern char *strerror(); -#endif - #ifdef IPX_CHANGE #include "ipxcp.h" #endif /* IPX_CHANGE */ @@ -87,7 +83,9 @@ 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 */ +volatile int status; /* exit status for pppd */ static int fd_ppp = -1; /* fd for talking PPP */ static int fd_loop; /* fd for getting demand-dial packets */ @@ -112,6 +110,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"; @@ -217,7 +216,6 @@ main(argc, argv) struct timeval now; phase = PHASE_INITIALIZE; - log_to_fd = -1; /* * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else. @@ -259,31 +257,56 @@ 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); + if (!using_pty) { + int save_defdev = default_device; + + default_device = 1; + if (!options_for_tty()) + exit(EXIT_OPTION_ERROR); + if (notty || ptycommand != NULL) { + option_error("%s option may not be used in per-tty options file", + notty? "notty": "pty"); + exit(EXIT_OPTION_ERROR); + } + if (!default_device) { + option_error("per-tty options file may not specify device name"); + exit(EXIT_OPTION_ERROR); + } + default_device = save_defdev; + } + + if (!parse_args(argc-1, argv+1)) + exit(EXIT_OPTION_ERROR); /* * Check that we are running as root. @@ -291,51 +314,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); @@ -467,6 +507,7 @@ main(argc, argv) need_holdoff = 1; ttyfd = -1; real_ttyfd = -1; + status = EXIT_OK; if (demand) { /* @@ -520,6 +561,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); @@ -528,7 +570,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; @@ -548,17 +591,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; } @@ -593,6 +638,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; @@ -636,6 +682,7 @@ main(argc, argv) if (device_script(connector, ttyfd, ttyfd, 0) < 0) { error("Connect script failed"); + status = EXIT_CONNECT_FAILED; goto fail; } if (kill_link) @@ -654,8 +701,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; } @@ -673,8 +722,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) { @@ -708,6 +759,7 @@ 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); @@ -843,7 +895,7 @@ main(argc, argv) reap_kids(1); } - die(0); + die(status); return 0; } @@ -945,6 +997,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; @@ -1221,6 +1274,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); @@ -1244,6 +1299,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); @@ -1320,7 +1376,7 @@ bad_signal(sig) kill_my_pg(SIGTERM); if (charshunt_pid) kill(charshunt_pid, SIGTERM); - die(1); + die(127); } diff --git a/pppd/options.c b/pppd/options.c index f9c9aa5..4976376 100644 --- a/pppd/options.c +++ b/pppd/options.c @@ -18,7 +18,7 @@ */ #ifndef lint -static char rcsid[] = "$Id: options.c,v 1.57 1999/04/12 06:24:47 paulus Exp $"; +static char rcsid[] = "$Id: options.c,v 1.58 1999/05/12 06:19:48 paulus Exp $"; #endif #include @@ -90,8 +90,11 @@ bool notty = 0; /* Stdin/out is not a tty */ char *record_file = NULL; /* File to record chars sent/received */ int using_pty = 0; bool sync_serial = 0; /* Device is synchronous serial device */ +int log_to_fd = 1; /* send log messages to this fd too */ extern option_t auth_options[]; +extern struct stat devstat; +extern int prepass; /* Doing pre-pass to find device name */ struct option_info connector_info; struct option_info disconnector_info; @@ -108,7 +111,7 @@ pcap_t pc; /* Fake struct pcap so we can compile expr */ /* * Prototypes */ -static int setdevname __P((char *, int)); +static int setdevname __P((char *)); static int setipaddr __P((char *)); static int setspeed __P((char *)); static int noopt __P((char **)); @@ -167,9 +170,9 @@ option_t general_options[] = { OPT_A2INFO | OPT_PRIVFIX, &welcomer_info }, { "pty", o_string, &ptycommand, "Script to run on pseudo-tty master side", - OPT_A2INFO | OPT_PRIVFIX, &ptycommand_info }, + OPT_A2INFO | OPT_PRIVFIX | OPT_PREPASS, &ptycommand_info }, { "notty", o_bool, ¬ty, - "Input/output is not a tty", 1 }, + "Input/output is not a tty", OPT_PREPASS | 1 }, { "record", o_string, &record_file, "Record characters sent/received to file" }, { "maxconnect", o_int, &maxconnect, @@ -197,9 +200,9 @@ option_t general_options[] = { { "local", o_bool, &modem, "Don't use modem control lines" }, { "file", o_special, readfile, - "Take options from a file" }, + "Take options from a file", OPT_PREPASS }, { "call", o_special, callfile, - "Take options from a privileged file" }, + "Take options from a privileged file", OPT_PREPASS }, { "persist", o_bool, &persist, "Keep on reopening connection after close", 1 }, { "nopersist", o_bool, &persist, @@ -214,6 +217,11 @@ option_t general_options[] = { "Show brief listing of options" }, { "sync", o_bool, &sync_serial, "Use synchronous HDLC serial encoding", 1 }, + { "logfd", o_int, &log_to_fd, + "Send log messages to this file descriptor" }, + { "nologfd", o_int, &log_to_fd, + "Don't send log messages to any file descriptor", + OPT_NOARG | OPT_VAL(-1) }, #ifdef PPP_FILTER { "pdebug", o_int, &dflag, @@ -251,6 +259,8 @@ See pppd(8) for more options.\n\ /* * parse_args - parse a string of arguments from the command line. + * If prepass is true, we are scanning for the device name and only + * processing a few options, so error messages are suppressed. */ int parse_args(argc, argv) @@ -288,7 +298,7 @@ parse_args(argc, argv) /* * Maybe a tty name, speed or IP address? */ - if ((ret = setdevname(arg, 0)) == 0 + if ((ret = setdevname(arg)) == 0 && (ret = setspeed(arg)) == 0 && (ret = setipaddr(arg)) == 0) { option_error("unrecognized option '%s'", arg); @@ -301,6 +311,7 @@ parse_args(argc, argv) return 1; } +#if 0 /* * scan_args - scan the command line arguments to get the tty name, * if specified. Also checks whether the notty or pty option was given. @@ -334,6 +345,7 @@ scan_args(argc, argv) (void) setdevname(arg, 1); } } +#endif /* * options_from_file - Read a string of options from a file, @@ -401,7 +413,7 @@ options_from_file(filename, must_exist, check_prot, priv) /* * Maybe a tty name, speed or IP address? */ - if ((i = setdevname(cmd, 0)) == 0 + if ((i = setdevname(cmd)) == 0 && (i = setspeed(cmd)) == 0 && (i = setipaddr(cmd)) == 0) { option_error("In file %s: unrecognized option '%s'", @@ -515,6 +527,9 @@ process_option(opt, argv) char *sv; int (*parser) __P((char **)); + if (prepass && (opt->flags & OPT_PREPASS) == 0) + return 1; + if ((opt->flags & OPT_PRIV) && !privileged_option) { option_error("using the %s option requires root privilege", opt->name); return 0; @@ -696,6 +711,10 @@ option_error __V((char *fmt, ...)) va_start(args); fmt = va_arg(args, char *); #endif + if (prepass) { + va_end(args); + return; + } vslprintf(buf, sizeof(buf), fmt, args); va_end(args); if (phase == PHASE_INITIALIZE) @@ -1177,9 +1196,8 @@ setspeed(arg) * setdevname - Set the device name. */ static int -setdevname(cp, quiet) +setdevname(cp) char *cp; - int quiet; { struct stat statbuf; char dev[MAXPATHLEN]; @@ -1194,22 +1212,26 @@ setdevname(cp, quiet) } /* - * Check if there is a device by this name. + * Check if there is a character device by this name. */ if (stat(cp, &statbuf) < 0) { - if (errno == ENOENT || quiet) + if (errno == ENOENT) return 0; option_error("Couldn't stat %s: %m", cp); return -1; } + if (!S_ISCHR(statbuf.st_mode)) { + option_error("%s is not a character device", cp); + return -1; + } if (devnam_info.priv && !privileged_option) { - if (!quiet) - option_error("device name cannot be overridden"); + option_error("device name cannot be overridden"); return -1; } strlcpy(devnam, cp, sizeof(devnam)); + devstat = statbuf; default_device = 0; devnam_info.priv = privileged_option; devnam_info.source = option_source; @@ -1235,6 +1257,8 @@ setipaddr(arg) */ if ((colon = strchr(arg, ':')) == NULL) return 0; + if (prepass) + return 1; /* * If colon first character, then no local addr. diff --git a/pppd/patchlevel.h b/pppd/patchlevel.h index 1260d54..8fad1de 100644 --- a/pppd/patchlevel.h +++ b/pppd/patchlevel.h @@ -1,6 +1,6 @@ -/* $Id: patchlevel.h,v 1.40 1999/04/16 11:49:30 paulus Exp $ */ +/* $Id: patchlevel.h,v 1.41 1999/05/12 06:19:48 paulus Exp $ */ #define PATCHLEVEL 8 #define VERSION "2.3" -#define IMPLEMENTATION "alpha" -#define DATE "16 April 1999" +#define IMPLEMENTATION "" +#define DATE "12 May 1999" diff --git a/pppd/pppd.h b/pppd/pppd.h index 5dece10..fdb4aae 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -16,7 +16,7 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: pppd.h,v 1.38 1999/04/12 06:24:47 paulus Exp $ + * $Id: pppd.h,v 1.39 1999/05/12 06:19:49 paulus Exp $ */ /* @@ -40,6 +40,7 @@ #include #define __V(x) (va_alist) va_dcl #define const +#define volatile #endif /* @@ -96,6 +97,7 @@ typedef struct { #define OPT_A2COPY 0x200000 /* addr2 -> second location to rcv value */ #define OPT_ENABLE 0x400000 /* use *addr2 as enable for option */ #define OPT_PRIVFIX 0x800000 /* can't be overridden if noauth */ +#define OPT_PREPASS 0x1000000/* do this opt in pre-pass to find device */ #define OPT_VAL(x) ((x) & OPT_VALUE) @@ -147,6 +149,7 @@ extern int link_stats_valid; /* set if link_stats is valid */ extern int using_pty; /* using pty as device (notty or pty opt.) */ extern int log_to_fd; /* logging to this fd as well as syslog */ extern char *no_ppp_msg; /* message to print if ppp not in kernel */ +extern volatile int status; /* exit status for pppd */ /* * Variables set by command-line options. @@ -491,7 +494,31 @@ extern struct option_info ptycommand_info; PUTCHAR(PPP_UI, p); \ PUTSHORT(t, p); } +/* + * Exit status values. + */ +#define EXIT_OK 0 +#define EXIT_FATAL_ERROR 1 +#define EXIT_OPTION_ERROR 2 +#define EXIT_NOT_ROOT 3 +#define EXIT_NO_KERNEL_SUPPORT 4 +#define EXIT_USER_REQUEST 5 +#define EXIT_LOCK_FAILED 6 +#define EXIT_OPEN_FAILED 7 +#define EXIT_CONNECT_FAILED 8 +#define EXIT_PTYCMD_FAILED 9 +#define EXIT_NEGOTIATION_FAILED 10 +#define EXIT_PEER_AUTH_FAILED 11 +#define EXIT_IDLE_TIMEOUT 12 +#define EXIT_CONNECT_TIME 13 +#define EXIT_CALLBACK 14 +#define EXIT_PEER_DEAD 15 +#define EXIT_HANGUP 16 +/* + * Debug macros. Slightly useful for finding bugs in pppd, not particularly + * useful for finding out why your connection isn't being established. + */ #ifdef DEBUGALL #define DEBUGMAIN 1 #define DEBUGFSM 1 -- 2.39.2