X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Fmain.c;h=25076ca50d3aa59bfdc4a8b26264aeb8a764dd4c;hb=1156a926bc7cb9b19f1a00d29b26205f2b717f15;hp=1f6000b8b3eedb6a0c21a16ca73bb4b0f926bf2f;hpb=e8903340608548b0f92d1b0d0bf8144a43123df4;p=ppp.git diff --git a/pppd/main.c b/pppd/main.c index 1f6000b..25076ca 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -38,9 +38,35 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Copyright (c) 1999-2004 Paul Mackerras. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define RCSID "$Id: main.c,v 1.133 2004/02/02 03:40:12 paulus Exp $" +#define RCSID "$Id: main.c,v 1.143 2004/11/06 05:42:29 paulus Exp $" #include #include @@ -125,6 +151,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 +178,7 @@ int got_sigusr2; int got_sigterm; int got_sighup; +static sigset_t signals_handled; static int waiting; static sigjmp_buf sigjmp; @@ -171,6 +200,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 +238,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 +249,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)); @@ -625,16 +656,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); @@ -648,36 +684,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; @@ -692,19 +729,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; \ @@ -712,7 +748,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 */ @@ -837,12 +873,8 @@ detach() void reopen_log() { -#ifdef ULTRIX - openlog("pppd", LOG_PID); -#else openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); setlogmask(LOG_UPTO(LOG_INFO)); -#endif } /* @@ -1127,7 +1159,7 @@ void die(status) int status; { - print_link_stats(); + print_link_stats(); cleanup(); notify(exitnotify, status); syslog(LOG_INFO, "Exit."); @@ -1177,6 +1209,18 @@ print_link_stats() } } +/* + * 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. */ @@ -1193,6 +1237,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); @@ -1223,9 +1272,6 @@ timeout(func, arg, secs, usecs) { struct callout *newp, *p, **pp; - MAINDEBUG(("Timeout %p:%p in %d.%03d seconds.", func, arg, - secs, usecs/1000)); - /* * Allocate timeout. */ @@ -1264,8 +1310,6 @@ untimeout(func, arg) { struct callout **copp, *freep; - MAINDEBUG(("Untimeout %p:%p.", func, arg)); - /* * Find first matching timeout and remove it from the list. */ @@ -1338,6 +1382,7 @@ kill_my_pg(sig) { struct sigaction act, oldact; + sigemptyset(&act.sa_mask); /* unnecessary in fact */ act.sa_handler = SIG_IGN; act.sa_flags = 0; kill(0, sig); @@ -1368,7 +1413,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 */ @@ -1389,8 +1434,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); @@ -1472,34 +1517,83 @@ bad_signal(sig) * safe_fork - Create a child process. The child closes all the * file descriptors that we don't want to leak to a script. * The parent waits for the child to do this before returning. + * This also arranges for the specified fds to be dup'd to + * fds 0, 1, 2 in the child. */ pid_t -safe_fork() +safe_fork(int infd, int outfd, int errfd) { pid_t pid; - int pipefd[2]; + int fd, pipefd[2]; char buf[1]; + /* make sure fds 0, 1, 2 are occupied (probably not necessary) */ + while ((fd = dup(fd_devnull)) >= 0) { + if (fd > 2) { + close(fd); + break; + } + } + if (pipe(pipefd) == -1) pipefd[0] = pipefd[1] = -1; pid = fork(); - if (pid < 0) + if (pid < 0) { + error("fork failed: %m"); return -1; + } if (pid > 0) { + /* parent */ close(pipefd[1]); /* this read() blocks until the close(pipefd[1]) below */ complete_read(pipefd[0], buf, 1); close(pipefd[0]); return pid; } + + /* Executing in the child */ sys_close(); #ifdef USE_TDB tdb_close(pppdb); #endif + + /* make sure infd, outfd and errfd won't get tromped on below */ + if (infd == 1 || infd == 2) + infd = dup(infd); + if (outfd == 0 || outfd == 2) + outfd = dup(outfd); + if (errfd == 0 || errfd == 1) + errfd = dup(errfd); + + /* dup the in, out, err fds to 0, 1, 2 */ + if (infd != 0) + dup2(infd, 0); + if (outfd != 1) + dup2(outfd, 1); + if (errfd != 2) + dup2(errfd, 2); + + closelog(); + if (log_to_fd > 2) + close(log_to_fd); + if (the_channel->close) + (*the_channel->close)(); + else + close(devfd); /* some plugins don't have a close function */ + close(fd_ppp); + close(fd_devnull); + if (infd != 0) + close(infd); + if (outfd != 1) + close(outfd); + if (errfd != 2) + close(errfd); + notify(fork_notifier, 0); close(pipefd[0]); /* this close unblocks the read() call above in the parent */ close(pipefd[1]); + return 0; } @@ -1517,10 +1611,17 @@ device_script(program, in, out, dont_wait) int pid; int status = -1; int errfd; - int fd; + + if (log_to_fd >= 0) + errfd = log_to_fd; + else + errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); ++conn_running; - pid = safe_fork(); + pid = safe_fork(in, out, errfd); + + if (pid != 0 && log_to_fd < 0) + close(errfd); if (pid < 0) { --conn_running; @@ -1545,57 +1646,14 @@ device_script(program, in, out, dont_wait) /* here we are executing in the child */ - /* make sure fds 0, 1, 2 are occupied */ - while ((fd = dup(in)) >= 0) { - if (fd > 2) { - close(fd); - break; - } - } - - /* dup in and out to fds > 2 */ - { - int fd1 = in, fd2 = out, fd3 = log_to_fd; - - in = dup(in); - out = dup(out); - if (log_to_fd >= 0) { - errfd = dup(log_to_fd); - } else { - errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); - } - close(fd1); - close(fd2); - close(fd3); - } - - /* close fds 0 - 2 and any others we can think of */ - close(0); - close(1); - close(2); - if (the_channel->close) - (*the_channel->close)(); - closelog(); - close(fd_devnull); - - /* dup the in, out, err fds to 0, 1, 2 */ - dup2(in, 0); - close(in); - dup2(out, 1); - close(out); - if (errfd >= 0) { - dup2(errfd, 2); - close(errfd); - } - + setgid(getgid()); setuid(uid); if (getuid() != uid) { - error("setuid failed"); + fprintf(stderr, "pppd: setuid failed\n"); exit(1); } - setgid(getgid()); execl("/bin/sh", "sh", "-c", program, (char *)0); - error("could not exec /bin/sh: %m"); + perror("pppd: could not exec /bin/sh"); exit(99); /* NOTREACHED */ } @@ -1636,7 +1694,7 @@ run_program(prog, args, must_exist, done, arg) return 0; } - pid = safe_fork(); + pid = safe_fork(fd_devnull, fd_devnull, fd_devnull); if (pid == -1) { error("Failed to create child process for %s: %m", prog); return -1; @@ -1655,25 +1713,12 @@ run_program(prog, args, must_exist, done, arg) setuid(0); /* set real UID = root */ setgid(getegid()); - /* Ensure that nothing of our device environment is inherited. */ - closelog(); - if (the_channel->close) - (*the_channel->close)(); - - /* Don't pass handles to the PPP device, even by accident. */ - dup2(fd_devnull, 0); - dup2(fd_devnull, 1); - dup2(fd_devnull, 2); - close(fd_devnull); - #ifdef BSD /* Force the priority back to zero if pppd is running higher. */ if (setpriority (PRIO_PROCESS, 0, 0) < 0) warn("can't reset priority to 0: %m"); #endif - /* SysV recommends a second fork at this point. */ - /* run the program */ execve(prog, args, script_env); if (must_exist || errno != ENOENT) { @@ -1715,22 +1760,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;