X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fmain.c;h=954fc2b6b977df84133bee29ffa5125095af7011;hp=7eee19215e5439dc6aea01d3cb5cb01d92c728e3;hb=baf120b604ced444e816f4b362ccca4b123b0fcf;hpb=10d17d19d3f7fe61e3b1f46a97198c521b535589 diff --git a/pppd/main.c b/pppd/main.c index 7eee192..954fc2b 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.79 1999/05/13 00:35:23 paulus Exp $"; -#endif +#define RCSID "$Id: main.c,v 1.89 2000/01/18 19:49:52 masputra Exp $" #include #include @@ -48,6 +46,9 @@ static char rcsid[] = "$Id: main.c,v 1.79 1999/05/13 00:35:23 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" @@ -65,6 +66,8 @@ static char rcsid[] = "$Id: main.c,v 1.79 1999/05/13 00:35:23 paulus Exp $"; #include "atcp.h" #endif +static const char rcsid[] = RCSID; + /* interface vars */ char ifname[32]; /* Interface name */ int ifunit; /* Interface unit number */ @@ -72,6 +75,7 @@ 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 uid_t uid; /* Our real user-id */ static int conn_running; /* we have a [dis]connector running */ @@ -85,7 +89,15 @@ int need_holdoff; /* need holdoff period before restarting */ int detached; /* have detached from terminal */ 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 do_callback; /* != 0 if we should do callback next */ +int doing_callback; /* != 0 if we are doing callback */ +char *callback_script; /* script for doing callback */ + +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 */ @@ -142,6 +154,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)); @@ -156,7 +169,7 @@ 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 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 *)); @@ -190,6 +203,9 @@ struct protent *protocols[] = { &cbcp_protent, #endif &ipcp_protent, +#ifdef INET6 + &ipv6cp_protent, +#endif &ccp_protent, #ifdef IPX_CHANGE &ipxcp_protent, @@ -205,9 +221,9 @@ main(argc, argv) int argc; char *argv[]; { - int i, fdflags; + int i, fdflags, t; struct sigaction sa; - char *p; + char *p, *connector; struct passwd *pw; struct timeval timo; sigset_t mask; @@ -215,7 +231,7 @@ main(argc, argv) struct stat statbuf; char numbuf[16]; - phase = PHASE_INITIALIZE; + new_phase(PHASE_INITIALIZE); /* * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else. @@ -240,6 +256,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); @@ -247,6 +266,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, @@ -287,24 +312,13 @@ main(argc, argv) * The per-tty options file should not change * ptycommand, notty or devnam. */ + devnam_fixed = 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; } + devnam_fixed = 0; if (!parse_args(argc-1, argv+1)) exit(EXIT_OPTION_ERROR); @@ -331,10 +345,13 @@ main(argc, argv) for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->check_options != NULL) (*protp->check_options)(); - if (demand && connector == 0) { + if (demand && connect_script == 0) { option_error("connect script is required for demand-dialling\n"); exit(EXIT_OPTION_ERROR); } + /* default holdoff to 0 if no connect script has been given */ + if (connect_script == 0 && !holdoff_specified) + holdoff = 0; if (using_pty) { if (!default_device) { @@ -380,10 +397,9 @@ main(argc, argv) script_setenv("DEVICE", devnam); /* - * Initialize system-dependent stuff and magic number package. + * Initialize system-dependent stuff. */ sys_init(); - magic_init(); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); @@ -481,6 +497,8 @@ main(argc, argv) waiting = 0; + create_linkpidfile(); + /* * If we're doing dial-on-demand, set up the interface now. */ @@ -502,19 +520,23 @@ main(argc, argv) demand_conf(); } + do_callback = 0; for (;;) { need_holdoff = 1; ttyfd = -1; real_ttyfd = -1; status = EXIT_OK; + ++unsuccess; + doing_callback = do_callback; + do_callback = 0; - if (demand) { + if (demand && !doing_callback) { /* * 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 (;;) { @@ -551,6 +573,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. @@ -586,6 +610,7 @@ main(argc, argv) */ hungup = 0; kill_link = 0; + connector = doing_callback? callback_script: connect_script; if (devnam[0] != 0) { for (;;) { /* If the user specified the device name, become the @@ -622,7 +647,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 @@ -630,7 +655,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; } @@ -669,10 +695,9 @@ main(argc, argv) } /* run connection script */ - if (connector && connector[0]) { - MAINDEBUG(("Connecting with <%s>", connector)); - + if ((connector && connector[0]) || initializer) { if (real_ttyfd != -1) { + /* XXX do this if doing_callback == CALLBACK_DIALIN? */ if (!default_device && modem) { setdtr(real_ttyfd, 0); /* in case modem is off hook */ sleep(1); @@ -680,20 +705,37 @@ main(argc, argv) } } - if (device_script(connector, ttyfd, ttyfd, 0) < 0) { - error("Connect script failed"); - status = EXIT_CONNECT_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 */ if (real_ttyfd != -1) set_up_tty(real_ttyfd, 0); + + if (doing_callback == CALLBACK_DIALIN) + connector = NULL; } /* reopen tty if necessary to wait for carrier */ @@ -753,18 +795,19 @@ main(argc, argv) * time for something from the peer. This can avoid bouncing * our packets off his tty before he has it set up. */ - if (connector != NULL || ptycommand != NULL) { + add_fd(fd_ppp); + if (connect_delay != 0 && (connector != NULL || ptycommand != NULL)) { struct timeval t; - t.tv_sec = 1; - t.tv_usec = 0; + t.tv_sec = connect_delay / 1000; + t.tv_usec = connect_delay % 1000; wait_input(&t); } lcp_open(0); /* Start protocol */ open_ccp_flag = 0; - add_fd(fd_ppp); status = EXIT_NEGOTIATION_FAILED; - for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) { + new_phase(PHASE_ESTABLISH); + while (phase != PHASE_DEAD) { if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &mask, NULL); if (kill_link || open_ccp_flag || got_sigchld) { @@ -783,7 +826,7 @@ 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); } @@ -803,6 +846,18 @@ main(argc, argv) 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 @@ -822,10 +877,11 @@ main(argc, argv) * XXX we may not be able to do this if the line has hung up! */ disconnect: - if (disconnector && !hungup) { + if (disconnect_script && !hungup) { + new_phase(PHASE_DISCONNECT); if (real_ttyfd >= 0) set_up_tty(real_ttyfd, 1); - if (device_script(disconnector, ttyfd, ttyfd, 0) < 0) { + if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) { warn("disconnect script failed"); } else { info("Serial link disconnected."); @@ -847,19 +903,22 @@ 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); @@ -875,7 +934,7 @@ 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 */ } if (got_sigchld) reap_kids(0); @@ -886,6 +945,7 @@ main(argc, argv) } /* Wait for scripts to finish */ + /* XXX should have a timeout here */ while (n_children > 0) { if (debug) { struct subprocess *chp; @@ -893,7 +953,8 @@ main(argc, argv) for (chp = children; chp != NULL; chp = chp->next) dbglog(" script %s, pid %d", chp->prog, chp->pid); } - reap_kids(1); + if (reap_kids(1) < 0) + break; } die(status); @@ -927,9 +988,11 @@ detach() close(2); detached = 1; log_to_fd = -1; - /* 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(); } /* @@ -966,6 +1029,29 @@ create_pidfile() } 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); } /* @@ -975,7 +1061,91 @@ static void holdoff_end(arg) void *arg; { - phase = PHASE_DORMANT; + new_phase(PHASE_DORMANT); +} + +/* List of protocol names, to make our messages a little more informative. */ +struct protocol_list { + u_short proto; + const char *name; +} protocol_list[] = { + { 0x21, "IP" }, + { 0x23, "OSI Network Layer" }, + { 0x25, "Xerox NS IDP" }, + { 0x27, "DECnet Phase IV" }, + { 0x29, "Appletalk" }, + { 0x2b, "Novell IPX" }, + { 0x2d, "VJ compressed TCP/IP" }, + { 0x2f, "VJ uncompressed TCP/IP" }, + { 0x31, "Bridging PDU" }, + { 0x33, "Stream Protocol ST-II" }, + { 0x35, "Banyan Vines" }, + { 0x39, "AppleTalk EDDP" }, + { 0x3b, "AppleTalk SmartBuffered" }, + { 0x3d, "Multi-Link" }, + { 0x3f, "NETBIOS Framing" }, + { 0x41, "Cisco Systems" }, + { 0x43, "Ascom Timeplex" }, + { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" }, + { 0x47, "DCA Remote Lan" }, + { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" }, + { 0x4b, "SNA over 802.2" }, + { 0x4d, "SNA" }, + { 0x4f, "IP6 Header Compression" }, + { 0x6f, "Stampede Bridging" }, + { 0xfb, "single-link compression" }, + { 0xfd, "1st choice compression" }, + { 0x0201, "802.1d Hello Packets" }, + { 0x0203, "IBM Source Routing BPDU" }, + { 0x0205, "DEC LANBridge100 Spanning Tree" }, + { 0x0231, "Luxcom" }, + { 0x0233, "Sigma Network Systems" }, + { 0x8021, "Internet Protocol Control Protocol" }, + { 0x8023, "OSI Network Layer Control Protocol" }, + { 0x8025, "Xerox NS IDP Control Protocol" }, + { 0x8027, "DECnet Phase IV Control Protocol" }, + { 0x8029, "Appletalk Control Protocol" }, + { 0x802b, "Novell IPX Control Protocol" }, + { 0x8031, "Bridging NCP" }, + { 0x8033, "Stream Protocol Control Protocol" }, + { 0x8035, "Banyan Vines Control Protocol" }, + { 0x803d, "Multi-Link Control Protocol" }, + { 0x803f, "NETBIOS Framing Control Protocol" }, + { 0x8041, "Cisco Systems Control Protocol" }, + { 0x8043, "Ascom Timeplex" }, + { 0x8045, "Fujitsu LBLB Control Protocol" }, + { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" }, + { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" }, + { 0x804b, "SNA over 802.2 Control Protocol" }, + { 0x804d, "SNA Control Protocol" }, + { 0x804f, "IP6 Header Compression Control Protocol" }, + { 0x006f, "Stampede Bridging Control Protocol" }, + { 0x80fb, "Single Link Compression Control Protocol" }, + { 0x80fd, "Compression Control Protocol" }, + { 0xc021, "Link Control Protocol" }, + { 0xc023, "Password Authentication Protocol" }, + { 0xc025, "Link Quality Report" }, + { 0xc027, "Shiva Password Authentication Protocol" }, + { 0xc029, "CallBack Control Protocol (CBCP)" }, + { 0xc081, "Container Control Protocol" }, + { 0xc223, "Challenge Handshake Authentication Protocol" }, + { 0xc281, "Proprietary Authentication Protocol" }, + { 0, NULL }, +}; + +/* + * protocol_name - find a name for a PPP protocol. + */ +const char * +protocol_name(proto) + int proto; +{ + struct protocol_list *lp; + + for (lp = protocol_list; lp->proto != 0; ++lp) + if (proto == lp->proto) + return lp->name; + return NULL; } /* @@ -1051,11 +1221,27 @@ get_input() } } - if (debug) - warn("Unsupported protocol (0x%x) received", protocol); + if (debug) { + const char *pname = protocol_name(protocol); + if (pname != NULL) + warn("Unsupported protocol '%s' (0x%x) received", pname, protocol); + else + warn("Unsupported protocol 0x%x received", protocol); + } 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. @@ -1084,8 +1270,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(); @@ -1213,7 +1402,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; } } @@ -1625,7 +1814,7 @@ 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; { @@ -1634,7 +1823,7 @@ reap_kids(waitfor) got_sigchld = 0; if (n_children == 0) - return; + return 0; while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1 && pid != 0) { --n_children; @@ -1655,8 +1844,13 @@ reap_kids(waitfor) if (chp) free(chp); } - if (pid == -1 && errno != ECHILD && errno != EINTR) - error("Error waiting for child process: %m"); + if (pid == -1) { + if (errno == ECHILD) + return -1; + if (errno != EINTR) + error("Error waiting for child process: %m"); + } + return 0; } @@ -1697,6 +1891,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)