X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Fmain.c;h=6915baeb0e3bc39c606cbc3b1576c53e9dbebfc5;hb=0f9a7931d88adb8df9d962997c7cd92969f1d80d;hp=86315662b0611ba6b7d62434ee8b380e9c10396b;hpb=81131c3cca1cb450d8ff67213182d9d06dc5d9ad;p=ppp.git diff --git a/pppd/main.c b/pppd/main.c index 8631566..6915bae 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -40,7 +40,7 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define RCSID "$Id: main.c,v 1.130 2003/12/17 08:56:05 kad Exp $" +#define RCSID "$Id: main.c,v 1.140 2004/11/04 09:56:26 paulus Exp $" #include #include @@ -125,6 +125,8 @@ int do_callback; /* != 0 if we should do callback next */ int doing_callback; /* != 0 if we are doing callback */ int ppp_session_number; /* Session number, for channels with such a concept (eg PPPoE) */ +int childwait_done; /* have timed out waiting for children */ + #ifdef USE_TDB TDB_CONTEXT *pppdb; /* database for storing status etc. */ #endif @@ -150,6 +152,7 @@ int got_sigusr2; int got_sigterm; int got_sighup; +static sigset_t signals_handled; static int waiting; static sigjmp_buf sigjmp; @@ -171,6 +174,7 @@ int ngroups; /* How many groups valid in groups */ static struct timeval start_time; /* Time when link was started. */ +static struct pppd_stats old_link_stats; struct pppd_stats link_stats; unsigned link_connect_time; int link_stats_valid; @@ -208,7 +212,8 @@ static void toggle_debug __P((int)); static void open_ccp __P((int)); static void bad_signal __P((int)); static void holdoff_end __P((void *)); -static int reap_kids __P((int waitfor)); +static int reap_kids __P((void)); +static void childwait_end __P((void *)); #ifdef USE_TDB static void update_db_entry __P((void)); @@ -218,7 +223,7 @@ static void cleanup_db __P((void)); #endif static void handle_events __P((void)); -static void print_link_stats __P((void)); +void print_link_stats __P((void)); extern char *ttyname __P((int)); extern char *getlogin __P((void)); @@ -589,7 +594,8 @@ main(argc, argv) */ disconnect: new_phase(PHASE_DISCONNECT); - the_channel->disconnect(); + if (the_channel->disconnect) + the_channel->disconnect(); fail: if (the_channel->cleanup) @@ -624,16 +630,21 @@ main(argc, argv) } /* Wait for scripts to finish */ - /* XXX should have a timeout here */ - while (n_children > 0) { + reap_kids(); + if (n_children > 0) { + if (child_wait > 0) + TIMEOUT(childwait_end, NULL, child_wait); 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; + while (n_children > 0 && !childwait_done) { + handle_events(); + if (kill_link && !childwait_done) + childwait_end(NULL); + } } die(status); @@ -647,36 +658,37 @@ static void handle_events() { struct timeval timo; - sigset_t mask; kill_link = open_ccp_flag = 0; if (sigsetjmp(sigjmp, 1) == 0) { - sigprocmask(SIG_BLOCK, &mask, NULL); + sigprocmask(SIG_BLOCK, &signals_handled, NULL); if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) { - sigprocmask(SIG_UNBLOCK, &mask, NULL); + sigprocmask(SIG_UNBLOCK, &signals_handled, NULL); } else { waiting = 1; - sigprocmask(SIG_UNBLOCK, &mask, NULL); + sigprocmask(SIG_UNBLOCK, &signals_handled, NULL); wait_input(timeleft(&timo)); } } waiting = 0; calltimeout(); if (got_sighup) { + info("Hangup (SIGHUP)"); kill_link = 1; got_sighup = 0; if (status != EXIT_HANGUP) status = EXIT_USER_REQUEST; } if (got_sigterm) { + info("Terminating on signal %d", got_sigterm); kill_link = 1; persist = 0; status = EXIT_USER_REQUEST; got_sigterm = 0; } if (got_sigchld) { - reap_kids(0); /* Don't leave dead kids lying around */ got_sigchld = 0; + reap_kids(); /* Don't leave dead kids lying around */ } if (got_sigusr2) { open_ccp_flag = 1; @@ -691,19 +703,18 @@ static void setup_signals() { struct sigaction sa; - sigset_t mask; /* * Compute mask of all interesting signals and install signal handlers * for each. Only one signal handler may be active at a time. Therefore, * all other signals should be masked when any handler is executing. */ - sigemptyset(&mask); - sigaddset(&mask, SIGHUP); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGUSR2); + sigemptyset(&signals_handled); + sigaddset(&signals_handled, SIGHUP); + sigaddset(&signals_handled, SIGINT); + sigaddset(&signals_handled, SIGTERM); + sigaddset(&signals_handled, SIGCHLD); + sigaddset(&signals_handled, SIGUSR2); #define SIGNAL(s, handler) do { \ sa.sa_handler = handler; \ @@ -711,7 +722,7 @@ setup_signals() fatal("Couldn't establish signal handler (%d): %m", s); \ } while (0) - sa.sa_mask = mask; + sa.sa_mask = signals_handled; sa.sa_flags = 0; SIGNAL(SIGHUP, hup); /* Hangup */ SIGNAL(SIGINT, term); /* Interrupt */ @@ -1126,7 +1137,7 @@ void die(status) int status; { - print_link_stats(); + print_link_stats(); cleanup(); notify(exitnotify, status); syslog(LOG_INFO, "Exit."); @@ -1172,9 +1183,22 @@ print_link_stats() info("Connect time %d.%d minutes.", t/10, t%10); info("Sent %u bytes, received %u bytes.", link_stats.bytes_out, link_stats.bytes_in); + link_stats_valid = 0; } } +/* + * reset_link_stats - "reset" stats when link goes up. + */ +void +reset_link_stats(u) + int u; +{ + if (!get_ppp_stats(u, &old_link_stats)) + return; + gettimeofday(&start_time, NULL); +} + /* * update_link_stats - get stats at link termination. */ @@ -1191,6 +1215,11 @@ update_link_stats(u) link_connect_time = now.tv_sec - start_time.tv_sec; link_stats_valid = 1; + link_stats.bytes_in -= old_link_stats.bytes_in; + link_stats.bytes_out -= old_link_stats.bytes_out; + link_stats.pkts_in -= old_link_stats.pkts_in; + link_stats.pkts_out -= old_link_stats.pkts_out; + slprintf(numbuf, sizeof(numbuf), "%u", link_connect_time); script_setenv("CONNECT_TIME", numbuf, 0); slprintf(numbuf, sizeof(numbuf), "%u", link_stats.bytes_out); @@ -1328,6 +1357,7 @@ timeleft(tvp) /* * kill_my_pg - send a signal to our process group, and ignore it ourselves. + * We assume that sig is currently blocked. */ static void kill_my_pg(sig) @@ -1335,10 +1365,22 @@ kill_my_pg(sig) { struct sigaction act, oldact; + sigemptyset(&act.sa_mask); /* unnecessary in fact */ act.sa_handler = SIG_IGN; act.sa_flags = 0; - sigaction(sig, &act, &oldact); kill(0, sig); + /* + * The kill() above made the signal pending for us, as well as + * the rest of our process group, but we don't want it delivered + * to us. It is blocked at the moment. Setting it to be ignored + * will cause the pending signal to be discarded. If we did the + * kill() after setting the signal to be ignored, it is unspecified + * (by POSIX) whether the signal is immediately discarded or left + * pending, and in fact Linux would leave it pending, and so it + * would be delivered after the current signal handler exits, + * leading to an infinite loop. + */ + sigaction(sig, &act, &oldact); sigaction(sig, &oldact, NULL); } @@ -1354,7 +1396,7 @@ static void hup(sig) int sig; { - info("Hangup (SIGHUP)"); + /* can't log a message here, it can deadlock */ got_sighup = 1; if (conn_running) /* Send the signal to the [dis]connector process(es) also */ @@ -1375,8 +1417,8 @@ static void term(sig) int sig; { - info("Terminating on signal %d.", sig); - got_sigterm = 1; + /* can't log a message here, it can deadlock */ + got_sigterm = sig; if (conn_running) /* Send the signal to the [dis]connector process(es) also */ kill_my_pg(sig); @@ -1561,6 +1603,8 @@ device_script(program, in, out, dont_wait) close(2); if (the_channel->close) (*the_channel->close)(); + else + close(devfd); /* some plugins don't have a close function */ closelog(); close(fd_devnull); @@ -1701,22 +1745,37 @@ record_child(pid, prog, done, arg) } } +/* + * childwait_end - we got fed up waiting for the child processes to + * exit, send them all a SIGTERM. + */ +static void +childwait_end(arg) + void *arg; +{ + struct subprocess *chp; + + for (chp = children; chp != NULL; chp = chp->next) { + if (debug) + dbglog("sending SIGTERM to process %d", chp->pid); + kill(chp->pid, SIGTERM); + } + childwait_done = 1; +} /* * reap_kids - get status from any dead child processes, * and log a message for abnormal terminations. */ static int -reap_kids(waitfor) - int waitfor; +reap_kids() { int pid, status; struct subprocess *chp, **prevp; if (n_children == 0) return 0; - while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1 - && pid != 0) { + while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) { for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) { if (chp->pid == pid) { --n_children;