* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define RCSID "$Id: main.c,v 1.142 2004/11/04 10:05:23 paulus Exp $"
+#define RCSID "$Id: main.c,v 1.145 2004/11/12 10:30:51 paulus Exp $"
#include <stdio.h>
#include <ctype.h>
void (*snoop_send_hook) __P((unsigned char *p, int len)) = NULL;
static int conn_running; /* we have a [dis]connector running */
-static int devfd; /* fd of underlying device */
-static int fd_ppp = -1; /* fd for talking PPP */
static int fd_loop; /* fd for getting demand-dial packets */
-static int fd_devnull; /* fd for /dev/null */
+int fd_devnull; /* fd for /dev/null */
+int devfd = -1; /* fd of underlying device */
+int fd_ppp = -1; /* fd for talking PPP */
int phase; /* where the link is at */
int kill_link;
int open_ccp_flag;
int error_count;
+bool bundle_eof;
+bool bundle_terminating;
+
/*
* We maintain a list of child process pids and
* functions to call when they exit.
if (dryrun)
die(0);
- /*
- * Initialize system-dependent stuff.
- */
- sys_init();
-
/* Make sure fds 0, 1, 2 are open to somewhere. */
fd_devnull = open(_PATH_DEVNULL, O_RDWR);
if (fd_devnull < 0)
fd_devnull = i;
}
+ /*
+ * Initialize system-dependent stuff.
+ */
+ sys_init();
+
#ifdef USE_TDB
pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644);
if (pppdb != NULL) {
* Configure the interface and mark it up, etc.
*/
demand_conf();
- create_linkpidfile(getpid());
}
do_callback = 0;
for (;;) {
+ bundle_eof = 0;
+ bundle_terminating = 0;
listen_time = 0;
need_holdoff = 1;
devfd = -1;
info("Starting link");
}
- new_phase(PHASE_SERIALCONN);
-
- devfd = the_channel->connect();
- if (devfd < 0)
- goto fail;
-
- /* set up the serial device as a ppp interface */
-#ifdef USE_TDB
- tdb_writelock(pppdb);
-#endif
- fd_ppp = the_channel->establish_ppp(devfd);
- if (fd_ppp < 0) {
-#ifdef USE_TDB
- tdb_writeunlock(pppdb);
-#endif
- status = EXIT_FATAL_ERROR;
- goto disconnect;
- }
- /* create the pid file, now that we've obtained a ppp interface */
- if (!demand)
- create_linkpidfile(getpid());
-
- if (!demand && ifunit >= 0)
- set_ifunit(1);
-#ifdef USE_TDB
- tdb_writeunlock(pppdb);
-#endif
-
- /*
- * Start opening the connection and wait for
- * incoming events (reply, timeout, etc.).
- */
- if (ifunit >= 0)
- notice("Connect: %s <--> %s", ifname, ppp_devnam);
- else
- notice("Starting negotiation on %s", ppp_devnam);
gettimeofday(&start_time, NULL);
script_unsetenv("CONNECT_TIME");
script_unsetenv("BYTES_SENT");
script_unsetenv("BYTES_RCVD");
- lcp_lowerup(0);
- add_fd(fd_ppp);
lcp_open(0); /* Start protocol */
- status = EXIT_NEGOTIATION_FAILED;
- new_phase(PHASE_ESTABLISH);
while (phase != PHASE_DEAD) {
handle_events();
get_input();
- if (kill_link)
+ if (kill_link) {
+ bundle_terminating = 1;
lcp_close(0, "User request");
+ if (phase == PHASE_MASTER)
+ mp_bundle_terminated();
+ }
if (open_ccp_flag) {
if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
}
}
- print_link_stats();
-
- /*
- * 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
- * real serial device back to its normal mode of operation.
- */
- remove_fd(fd_ppp);
- clean_check();
- the_channel->disestablish_ppp(devfd);
- fd_ppp = -1;
- if (!hungup)
- lcp_lowerdown(0);
- if (!demand)
- script_unsetenv("IFNAME");
-
- /*
- * Run disconnector script, if requested.
- * XXX we may not be able to do this if the line has hung up!
- */
- disconnect:
- new_phase(PHASE_DISCONNECT);
- if (the_channel->disconnect)
- the_channel->disconnect();
-
- fail:
- if (the_channel->cleanup)
- (*the_channel->cleanup)();
-
- if (!demand) {
- if (pidfilename[0] != 0
- && unlink(pidfilename) < 0 && errno != ENOENT)
- warn("unable to delete pid file %s: %m", pidfilename);
- pidfilename[0] = 0;
- }
-
if (!persist || (maxfail > 0 && unsuccess >= maxfail))
break;
void
reopen_log()
{
-#ifdef ULTRIX
- openlog("pppd", LOG_PID);
-#else
openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
setlogmask(LOG_UPTO(LOG_INFO));
-#endif
}
/*
}
}
-static void
+void
create_linkpidfile(pid)
int pid;
{
}
}
+/*
+ * remove_pidfile - remove our pid files
+ */
+void remove_pidfiles()
+{
+ if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+ 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;
+}
+
/*
* holdoff_end - called via a timeout when the holdoff period ends.
*/
return;
if (len == 0) {
+ if (bundle_eof && multilink_master) {
+ notice("Last channel has disconnected");
+ mp_bundle_terminated();
+ return;
+ }
notice("Modem hangup");
hungup = 1;
status = EXIT_HANGUP;
die(status)
int status;
{
- print_link_stats();
+ if (!doing_multilink || multilink_master)
+ print_link_stats();
cleanup();
notify(exitnotify, status);
syslog(LOG_INFO, "Exit.");
the_channel->disestablish_ppp(devfd);
if (the_channel->cleanup)
(*the_channel->cleanup)();
-
- if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
- 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;
+ remove_pidfiles();
#ifdef USE_TDB
if (pppdb != NULL)
* 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;
}
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;
/* 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)();
- else
- close(devfd); /* some plugins don't have a close function */
- 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 */
}
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;
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) {
vlen = 0;
for (i = 0; (p = script_env[i]) != 0; ++i)
vlen += strlen(p) + 1;
- vbuf = malloc(vlen);
+ vbuf = malloc(vlen + 1);
if (vbuf == 0)
novm("database entry");
q = vbuf;