aboutsummaryrefslogtreecommitdiffstats
path: root/src/ppp-watch.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ppp-watch.c')
-rw-r--r--src/ppp-watch.c764
1 files changed, 433 insertions, 331 deletions
diff --git a/src/ppp-watch.c b/src/ppp-watch.c
index d74935a1..6114215f 100644
--- a/src/ppp-watch.c
+++ b/src/ppp-watch.c
@@ -6,7 +6,7 @@
* one argument: the logical name of the device to bring up. Does not
* detach until the interface is up or has permanently failed to come up.
*
- * Copyright 1999 Red Hat, Inc.
+ * Copyright 1999-2001 Red Hat, Inc.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
@@ -75,198 +75,198 @@
#include <sys/wait.h>
#include <termios.h>
#include <net/if.h>
-
+#include <glib.h>
#include "shvar.h"
+#define IFCFGPREFIX "/etc/sysconfig/network-scripts/ifcfg-"
+#define IFUP_PPP "/etc/sysconfig/network-scripts/ifup-ppp"
+
static int theSigterm = 0;
static int theSigint = 0;
static int theSighup = 0;
static int theSigio = 0;
static int theSigchld = 0;
static int theSigalrm = 0;
+static int pipeArray[2];
// patch to respect the maxfail parameter to ppp
// Scott Sharkey <ssharkey@linux-no-limits.com>
static int dialCount = 0;
static int theChild;
-
-
-static void
-cleanExit(int exitCode);
-
-
+static void failureExit(int exitCode);
static void
-forward_signal(int signo) {
+interrupt_child(int signo) {
kill(theChild, SIGINT);
}
-
-
-
static void
set_signal(int signo, void (*handler)(int)) {
struct sigaction act;
-
act.sa_handler = handler;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
sigaction(signo, &act, NULL);
}
+/* Create a pipe, and fork off a child. This is the end of the road for
+ * the parent, which will wait for an exit status byte on the pipe (which
+ * is written by the child). */
+static void
+detach(char *device) {
+ pid_t childpid;
+ unsigned char exitCode;
+ int fd;
+ if (pipe(pipeArray) == -1)
+ exit(25);
-static void
-detach(int now, int parentExitCode, char *device) {
- static int pipeArray[2];
- char exitCode;
+ childpid = fork();
+ if (childpid == -1)
+ exit(26);
- if (now) {
- /* execute -- ignore errors in case called more than once */
- exitCode = parentExitCode;
- write(pipeArray[1], &exitCode, 1);
- close(pipeArray[1]);
+ if (childpid != 0) {
+ /* The parent only cares about notifications from the child. */
+ close (pipeArray[1]);
- } else {
- /* set up */
- int child;
-
- if (pipe(pipeArray)) exit (25);
-
- child = fork();
- if (child < 0) exit(26);
- if (child) {
- /* parent process */
- close (pipeArray[1]);
-
- /* forward likely signals to the main process; we will
- * react later
- */
- theChild = child;
- set_signal(SIGINT, forward_signal);
- set_signal(SIGTERM, forward_signal);
- set_signal(SIGHUP, forward_signal);
-
- while (read (pipeArray[0], &exitCode, 1) < 0) {
- switch (errno) {
- case EINTR: continue;
- default: exit (27); /* this will catch EIO in particular */
- }
+ /* Certain signals are meant for our child, the watcher process. */
+ theChild = childpid;
+ set_signal(SIGINT, interrupt_child);
+ set_signal(SIGTERM, interrupt_child);
+ set_signal(SIGHUP, interrupt_child);
+
+ /* Read the pipe until the child gives us an exit code as a byte. */
+ while (read (pipeArray[0], &exitCode, 1) == -1) {
+ switch (errno) {
+ case EINTR: continue;
+ default: exit (27); /* this will catch EIO in particular */
}
- switch (exitCode) {
- case 0:
- break;
+ }
+ switch (exitCode) {
+ case 0:
+ break;
case 33:
- fprintf(stderr, "%s already up, initiating redial\n", device);
- break;
+ fprintf(stderr, "%s already up, initiating redial\n", device);
+ break;
case 34:
- fprintf(stderr, "Failed to activate %s, retrying in the background\n", device);
- break;
+ fprintf(stderr, "Failed to activate %s, retrying in the background\n", device);
+ break;
default:
- fprintf(stderr, "Failed to activate %s with error %d\n", device, exitCode);
- break;
- }
- exit(exitCode);
-
- } else {
- int devnull;
-
- /* child process */
- close (pipeArray[0]);
- /* become a daemon */
- devnull = open("/dev/null", O_RDONLY);
- dup2(devnull,0);
- close(devnull);
- devnull = open("/dev/null", O_WRONLY);
- dup2(devnull,1);
- dup2(devnull,2);
- close(devnull);
- setsid();
- setpgid(0, 0);
- }
+ fprintf(stderr, "Failed to activate %s with error %d\n", device, exitCode);
+ break;
+ }
+ exit(exitCode);
}
-}
+ /* We're in the child process, which only writes the exit status
+ * of the pppd process to its parent (i.e., it reads nothing). */
+ close (pipeArray[0]);
+
+ /* Redirect stdio to /dev/null. */
+ fd = open("/dev/null", O_RDONLY);
+ dup2(fd, STDIN_FILENO);
+ close(fd);
+ fd = open("/dev/null", O_WRONLY);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+
+ /* Become session and process group leader. */
+ setsid();
+ setpgid(0, 0);
+}
+/* Do magic with the pid file (/var/run/pppwatch-$DEVICE.pid):
+ * Try to open it for writing. If it exists, send a SIGHUP to whatever PID
+ * is already listed in it and remove it. Repeat until we can open it.
+ * Write out our PID, and return. */
static void
doPidFile(char *device) {
- static char *pidFileName = NULL;
- char *pidFilePath;
- int fd; FILE *f;
+ static char pidFilePath[PATH_MAX] = "";
+ int fd = -1;
+ FILE *f = NULL;
pid_t pid = 0;
- if (pidFileName) {
- /* remove it */
- pidFilePath = alloca(strlen(pidFileName) + 25);
- sprintf(pidFilePath, "/var/run/pppwatch-%s.pid", pidFileName);
- unlink(pidFilePath); /* not much we can do in case of error... */
- }
-
- if (device) {
- /* create it */
- pidFileName = device;
- pidFilePath = alloca(strlen(pidFileName) + 25);
- sprintf(pidFilePath, "/var/run/pppwatch-%s.pid", pidFileName);
-restart:
- fd = open(pidFilePath, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL,
- S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH);
-
- if (fd == -1) {
- /* file already existed, or terrible things have happened... */
- fd = open(pidFilePath, O_RDONLY);
- if (fd == -1)
- cleanExit(36); /* terrible things have happened */
- /* already running, send a SIGHUP (we presume that they
- * are calling ifup for a reason, so they probably want
- * to redial) and then exit cleanly and let things go
- * on in the background
- */
- f = fdopen(fd, "r");
- if (!f) cleanExit(37);
- fscanf(f, "%d", &pid);
- fclose(f);
- if (pid) {
- if (kill(pid, SIGHUP)) {
- unlink(pidFilePath);
- goto restart;
- } else {
- /* reset pidFileName so we don't delete the current one */
- pidFileName = NULL;
- cleanExit(33);
- }
- }
+ if (device == NULL) {
+ /* Remove an existing pid file -- we're exiting. */
+ if(strlen(pidFilePath) > 0) {
+ unlink(pidFilePath);
}
+ } else {
+ /* Set up the name of the pid file, used only the first time. */
+ snprintf(pidFilePath, sizeof(pidFilePath), "/var/run/pppwatch-%s.pid",
+ device);
+
+ /* Create the pid file. */
+ do {
+ fd = open(pidFilePath, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_NOFOLLOW,
+ S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH);
+ if(fd == -1) {
+ /* Try to open the file for read. */
+ fd = open(pidFilePath, O_RDONLY);
+ if(fd == -1)
+ failureExit(36); /* This is not good. */
+
+ /* We're already running, send a SIGHUP (we presume that they
+ * are calling ifup for a reason, so they probably want to
+ * redial) and then exit cleanly and let things go on in the
+ * background. Muck with the filename so that we don't go
+ * deleting the pid file for the already-running instance.
+ */
+ f = fdopen(fd, "r");
+ if(f == NULL)
+ failureExit(37);
+
+ pid = 0;
+ fscanf(f, "%d", &pid);
+ fclose(f);
+
+ if(pid) {
+ /* Try to kill it. */
+ if (kill(pid, SIGHUP) == -1) {
+ /* No such pid, remove the bogus pid file. */
+ unlink(pidFilePath);
+ } else {
+ /* Got it. Don't mess with the pid file on
+ * our way out. */
+ memset(pidFilePath, '\0', sizeof(pidFilePath));
+ failureExit(33);
+ }
+ }
+ }
+ } while(fd == -1);
f = fdopen(fd, "w");
- if (!f)
- cleanExit(31);
+ if(f == NULL)
+ failureExit(31);
fprintf(f, "%d\n", getpid());
fclose(f);
}
}
-
-
-
-
-int
-fork_exec(int wait, char *path, char *arg1, char *arg2, char *arg3)
+/* Fork off and exec() a child process. If reap_child is non-zero,
+ * wait for the child to exit and return 0 if it ran successfully,
+ * otherwise return 0 right away and let the SIGCHLD handler deal. */
+static int
+fork_exec(gboolean reap, char *path, char *arg1, char *arg2, char *arg3)
{
- pid_t child;
+ pid_t childpid;
int status;
sigset_t sigs;
- if (!(child = fork())) {
- /* child */
+ childpid = fork();
+ if (childpid == -1)
+ exit(26);
- /* don't leave signals blocked for pppd */
+ if (childpid == 0) {
+ /* Do the exec magic. Prepare by clearing the signal mask for pppd. */
sigemptyset(&sigs);
sigprocmask(SIG_SETMASK, &sigs, NULL);
- if (!wait) {
- /* make sure that pppd is in its own process group */
+ if (!reap) {
+ /* Make sure that the pppd is the leader for its process group. */
setsid();
setpgid(0, 0);
}
@@ -276,8 +276,8 @@ fork_exec(int wait, char *path, char *arg1, char *arg2, char *arg3)
_exit (1);
}
- if (wait) {
- wait4 (child, &status, 0, NULL);
+ if (reap) {
+ waitpid (childpid, &status, 0);
if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
return 0;
} else {
@@ -288,174 +288,205 @@ fork_exec(int wait, char *path, char *arg1, char *arg2, char *arg3)
}
}
+/* Relay the pppd's exit code up to the parent -- can only be called once,
+ * because the parent exits as soon as it reads a byte. */
+static void
+relay_exitcode(unsigned char code)
+{
+ unsigned char exitCode;
+ exitCode = code;
+ write(pipeArray[1], &exitCode, 1);
+ close(pipeArray[1]);
+}
-
+/* Unregister with netreport, relay a status byte to the parent, clean up
+ * the pid file, and bail. */
static void
-cleanExit(int exitCode) {
- fork_exec(1, "/sbin/netreport", "-r", NULL, NULL);
- detach(1, exitCode, NULL);
+failureExit(int exitCode) {
+ fork_exec(TRUE, "/sbin/netreport", "-r", NULL, NULL);
+ relay_exitcode(exitCode);
doPidFile(NULL);
exit(exitCode);
}
-
-
+/* Keeps track of which signals we've seen so far. */
static void
-signal_handler (int signum) {
+signal_tracker (int signum) {
switch(signum) {
- case SIGTERM:
- theSigterm = 1; break;
- case SIGINT:
- theSigint = 1; break;
- case SIGHUP:
- theSighup = 1; break;
- case SIGIO:
- theSigio = 1; break;
- case SIGCHLD:
- theSigchld = 1; break;
- case SIGALRM:
- theSigalrm = 1; break;
+ case SIGTERM:
+ theSigterm = 1; break;
+ case SIGINT:
+ theSigint = 1; break;
+ case SIGHUP:
+ theSighup = 1; break;
+ case SIGIO:
+ theSigio = 1; break;
+ case SIGCHLD:
+ theSigchld = 1; break;
+ case SIGALRM:
+ theSigalrm = 1; break;
}
}
-
+/* Return a shvarFile for this interface, taking into account one level of
+ * inheritance (eeewww). */
static shvarFile *
-shvarfilesGet(char *interfaceName) {
- shvarFile *ifcfg;
- char *ifcfgName, *ifcfgParentName, *ifcfgParentDiff;
- static char ifcfgPrefix[] = "/etc/sysconfig/network-scripts/ifcfg-";
+shvarfilesGet(const char *interfaceName) {
+ shvarFile *ifcfg = NULL;
+ char ifcfgName[PATH_MAX];
+ char *ifcfgParentDiff = NULL;
- ifcfgName = alloca(sizeof(ifcfgPrefix)+strlen(interfaceName)+1);
- sprintf(ifcfgName, "%s%s", ifcfgPrefix, interfaceName);
+ /* Start with the basic configuration. */
+ snprintf(ifcfgName, sizeof(ifcfgName), "%s%s", IFCFGPREFIX, interfaceName);
ifcfg = svNewFile(ifcfgName);
- if (!ifcfg) return NULL;
+ if (ifcfg == NULL)
+ return NULL;
- /* Do we have a parent interface to inherit? */
- ifcfgParentDiff = strchr(interfaceName, '-');
+ /* Do we have a parent interface (i.e., for ppp0-blah, ppp0) to inherit? */
+ ifcfgParentDiff = strchr(ifcfgName + sizeof(IFCFGPREFIX), '-');
if (ifcfgParentDiff) {
- /* allocate more than enough memory... */
- ifcfgParentName = alloca(sizeof(ifcfgPrefix)+strlen(interfaceName)+1);
- strcpy(ifcfgParentName, ifcfgPrefix);
- strncat(ifcfgParentName, interfaceName, ifcfgParentDiff-interfaceName);
- ifcfg->parent = svNewFile(ifcfgParentName);
+ *ifcfgParentDiff = '\0';
+ ifcfg->parent = svNewFile(ifcfgName);
}
- /* don't keep the file descriptors around, they can become
- * stdout for children
- */
- close (ifcfg->fd); ifcfg->fd = 0;
+ /* This is very unclean, but we have to close the shvar descriptors in
+ * case they've been numbered STDOUT_FILENO or STDERR_FILENO, which would
+ * be disastrous if inherited by a child process. */
+ close (ifcfg->fd);
+ ifcfg->fd = 0;
+
if (ifcfg->parent) {
- close (ifcfg->parent->fd); ifcfg->parent->fd = 0;
+ close (ifcfg->parent->fd);
+ ifcfg->parent->fd = 0;
}
return ifcfg;
}
-
-
-static char *
-pppLogicalToPhysical(int *pppdPid, char *logicalName) {
- char *mapFileName;
- char buffer[20]; /* more than enough space for ppp<n> */
+/* Convert a logical interface name to a real one by reading the lock
+ * file created by pppd. */
+static void
+pppLogicalToPhysical(int *pppdPid, char *logicalName, char **physicalName) {
+ char mapFileName[PATH_MAX];
+ char buffer[20];
char *p, *q;
- int f, n;
+ int fd, n;
char *physicalDevice = NULL;
- mapFileName = alloca (strlen(logicalName)+20);
- sprintf(mapFileName, "/var/run/ppp-%s.pid", logicalName);
- if ((f = open(mapFileName, O_RDONLY)) >= 0) {
- n = read(f, buffer, 20);
+ snprintf(mapFileName, sizeof(mapFileName), "/var/run/ppp-%s.pid",
+ logicalName);
+ fd = open(mapFileName, O_RDONLY);
+ if (fd != -1) {
+ n = read(fd, buffer, sizeof(buffer));
+ close(fd);
if (n > 0) {
buffer[n] = '\0';
- /* get past pid */
- p = buffer; while (*p && *p != '\n') p++; *p = '\0'; p++;
- if (pppdPid) *pppdPid = atoi(buffer);
- /* get rid of \n */
- q = p; while (*q && *q != '\n' && q < buffer+n) q++; *q = '\0';
- if (*p) physicalDevice = strdup(p);
+ /* Split up the file at the first line break -- the PID is on the
+ * first line. */
+ if((p = strchr(buffer, '\n')) != NULL) {
+ *p = '\0';
+ p++;
+ if (pppdPid) {
+ *pppdPid = atoi(buffer);
+ }
+ /* The physical device name is on the second line. */
+ if((q = strchr(p, '\n')) != NULL) {
+ *q = '\0';
+ physicalDevice = strdup(p);
+ }
+ }
}
- close(f);
}
- return physicalDevice;
+ if (physicalDevice) {
+ if (physicalName) {
+ *physicalName = physicalDevice;
+ } else {
+ free(physicalDevice);
+ }
+ } else {
+ if (physicalName) {
+ *physicalName = NULL;
+ }
+ }
}
-
-static int
-interfaceStatus(char *device) {
+/* Return a boolean value indicating if the interface is up. If not, or
+ * if we don't know, return FALSE. */
+static gboolean
+interfaceIsUp(char *device) {
int sock = -1;
- int pfs[] = {AF_INET, AF_IPX, AF_AX25, AF_APPLETALK, 0};
+ int family[] = {PF_INET, PF_IPX, PF_AX25, PF_APPLETALK, 0};
int p = 0;
struct ifreq ifr;
- int retcode = 0;
+ gboolean retcode = FALSE;
- while ((sock == -1) && pfs[p]) {
- sock = socket(pfs[p++], SOCK_DGRAM, 0);
+ /* Create a socket suitable for doing routing ioctls. */
+ for (p = 0; (sock == -1) && family[p]; p++) {
+ sock = socket(family[p], SOCK_DGRAM, 0);
}
- if (sock == -1) return 0;
+ if (sock == -1)
+ return FALSE;
+ /* Populate the request structure for getting the interface's status. */
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, device, IFNAMSIZ);
+ strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
+ ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
- if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
- retcode = 0;
+ /* We return TRUE iff the ioctl succeeded and the interface is UP. */
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
+ retcode = FALSE;
} else if (ifr.ifr_flags & IFF_UP) {
- retcode = 1;
+ retcode = TRUE;
}
close(sock);
+
return retcode;
}
-
-/* very, very minimal hangup function. This is just to attempt to
- * hang up a device that should already be hung up, so it does not
- * need to be bulletproof.
- */
-void
+/* Very, very minimal hangup function. This just attempts to hang up a device
+ * that should already be hung up, so it does not need to be bulletproof. */
+static void
hangup(shvarFile *ifcfg) {
int fd;
- char *filename;
- struct termios ots, ts;
-
- filename = svGetValue(ifcfg, "MODEMPORT");
- if (!filename) return;
- fd = open(filename, O_RDWR|O_NOCTTY|O_NONBLOCK);
- if (fd == -1) goto clean;
- if (tcgetattr(fd, &ts)) goto clean;
- ots = ts;
- write(fd, "\r", 1); /* tickle modems that do not like dropped DTR */
- usleep(1000);
- cfsetospeed(&ts, B0);
- tcsetattr(fd, TCSANOW, &ts);
- usleep(100000);
- tcsetattr(fd, TCSANOW, &ots);
-
-clean:
- free(filename);
+ char *line;
+ struct termios original_ts, ts;
+
+ line = svGetValue(ifcfg, "MODEMPORT");
+ if (line == NULL)
+ return;
+
+ fd = open(line, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd != -1) {
+ if (tcgetattr(fd, &ts) != -1) {
+ original_ts = ts;
+ write(fd, "\r", 1); /* tickle modems that do not like dropped DTR */
+ usleep(1000);
+ cfsetospeed(&ts, B0);
+ tcsetattr(fd, TCSANOW, &ts);
+ usleep(100000);
+ tcsetattr(fd, TCSANOW, &original_ts);
+ }
+ close(fd);
+ }
+ free(line);
}
-
-
-
-
-
-
-
-
int
main(int argc, char **argv) {
- int status, waited;
+ int status;
+ pid_t waited;
char *device, *real_device, *physicalDevice = NULL;
- char *theBoot = NULL;
+ char *boot = NULL;
shvarFile *ifcfg;
sigset_t blockedsigs, unblockedsigs;
int pppdPid = 0;
int timeout = 30;
char *temp;
- int dieing = 0;
+ gboolean dying = FALSE;
int sendsig;
- int connectedOnce = 0;
+ gboolean connectedOnce = FALSE;
int maxfail = 0; // MAXFAIL Patch <ssharkey@linux-no-limits.com>
if (argc < 2) {
@@ -463,32 +494,39 @@ main(int argc, char **argv) {
exit(30);
}
- if (!strncmp(argv[1], "ifcfg-", 6)) {
+ if (strncmp(argv[1], "ifcfg-", 6) == 0) {
device = argv[1] + 6;
} else {
device = argv[1];
}
- detach(0, 0, device); /* prepare */
+ detach(device); /* Prepare a child process to monitor pppd. When we
+ return, we'll be in the child. */
- if (argc > 2 && !strcmp("boot", argv[2])) {
- theBoot = argv[2];
+ if ((argc > 2) && (strcmp("boot", argv[2]) == 0)) {
+ boot = argv[2];
}
ifcfg = shvarfilesGet(device);
- if (!ifcfg) cleanExit(28);
+ if (ifcfg == NULL)
+ failureExit(28);
real_device = svGetValue(ifcfg, "DEVICE");
- if (!real_device) real_device = device;
+ if (real_device == NULL)
+ real_device = device;
doPidFile(real_device);
- set_signal(SIGTERM, signal_handler);
- set_signal(SIGINT, signal_handler);
- set_signal(SIGHUP, signal_handler);
- set_signal(SIGIO, signal_handler);
- set_signal(SIGCHLD, signal_handler);
- if (theBoot) {
+ /* We'll want to know which signal interrupted our sleep below, so
+ * attach a signal handler to these. */
+ set_signal(SIGTERM, signal_tracker);
+ set_signal(SIGINT, signal_tracker);
+ set_signal(SIGHUP, signal_tracker);
+ set_signal(SIGIO, signal_tracker);
+ set_signal(SIGCHLD, signal_tracker);
+
+ /* We time out only if we're being run at boot-time. */
+ if (boot) {
temp = svGetValue(ifcfg, "BOOTTIMEOUT");
if (temp) {
timeout = atoi(temp);
@@ -497,35 +535,41 @@ main(int argc, char **argv) {
} else {
timeout = 30;
}
- set_signal(SIGALRM, signal_handler);
+ set_signal(SIGALRM, signal_tracker);
alarm(timeout);
}
- fork_exec(1, "/sbin/netreport", NULL, NULL, NULL);
+ /* Register us to get a signal when something changes. Yes, that's vague. */
+ fork_exec(TRUE, "/sbin/netreport", NULL, NULL, NULL);
+
+ /* Reset theSigchld, which should have been triggered by netreport. */
theSigchld = 0;
- /* don't set up the procmask until after we have received the netreport
- * signal
- */
+ /* We don't set up the procmask until after we have received the netreport
+ * signal. Do so now. */
sigemptyset(&blockedsigs);
sigaddset(&blockedsigs, SIGTERM);
sigaddset(&blockedsigs, SIGINT);
sigaddset(&blockedsigs, SIGHUP);
sigaddset(&blockedsigs, SIGIO);
sigaddset(&blockedsigs, SIGCHLD);
- if (theBoot) sigaddset(&blockedsigs, SIGALRM);
+ if (boot) {
+ sigaddset(&blockedsigs, SIGALRM);
+ }
sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
- /* prepare for sigsuspend later */
sigfillset(&unblockedsigs);
sigdelset(&unblockedsigs, SIGTERM);
sigdelset(&unblockedsigs, SIGINT);
sigdelset(&unblockedsigs, SIGHUP);
sigdelset(&unblockedsigs, SIGIO);
sigdelset(&unblockedsigs, SIGCHLD);
- if (theBoot) sigdelset(&unblockedsigs, SIGALRM);
+ if (boot) {
+ sigdelset(&unblockedsigs, SIGALRM);
+ }
+ sigprocmask(SIG_UNBLOCK, &unblockedsigs, NULL);
- fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device, theBoot);
+ /* Initialize the retry timeout using the RETRYTIMEOUT setting. */
temp = svGetValue(ifcfg, "RETRYTIMEOUT");
if (temp) {
timeout = atoi(temp);
@@ -534,71 +578,111 @@ main(int argc, char **argv) {
timeout = 30;
}
- while (1) {
- if (!theSigterm && !theSigint && !theSighup && !theSigio && !theSigchld && !theSigalrm)
+ /* Start trying to bring the interface up. */
+ fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
+
+ while (TRUE) {
+ /* Wait for a signal. */
+ if (!theSigterm &&
+ !theSigint &&
+ !theSighup &&
+ !theSigio &&
+ !theSigchld &&
+ !theSigalrm) {
sigsuspend(&unblockedsigs);
+ }
+ /* If we got SIGTERM or SIGINT, give up and hang up. */
if (theSigterm || theSigint) {
theSigterm = theSigint = 0;
- if (dieing) sendsig = SIGKILL;
- else sendsig = SIGTERM;
- dieing = 1;
+ /* If we've already tried to exit this way, use SIGKILL instead
+ * of SIGTERM, because pppd's just being stubborn. */
+ if (dying) {
+ sendsig = SIGKILL;
+ } else {
+ sendsig = SIGTERM;
+ }
+ dying = TRUE;
+
+ /* Get the pid of our child pppd. */
+ pppLogicalToPhysical(&pppdPid, real_device, NULL);
- if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
- physicalDevice = pppLogicalToPhysical(&pppdPid, real_device);
- if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
- if (!pppdPid) cleanExit(35);
+ /* We don't know what our child pid is. This is very confusing. */
+ if (!pppdPid) {
+ failureExit(35);
+ }
+
+ /* Die, pppd, die. */
kill(pppdPid, sendsig);
if (sendsig == SIGKILL) {
- kill(-pppdPid, SIGTERM); /* give it a chance to die nicely */
+ kill(-pppdPid, SIGTERM); /* Give it a chance to die nicely, then
+ kill its whole process group. */
usleep(2500000);
kill(-pppdPid, sendsig);
hangup(ifcfg);
- cleanExit(32);
+ failureExit(32);
}
}
+ /* If we got SIGHUP, reload and redial. */
if (theSighup) {
theSighup = 0;
- if (ifcfg->parent) svCloseFile(ifcfg->parent);
+
+ /* Free and reload the configuration structure. */
+ if (ifcfg->parent)
+ svCloseFile(ifcfg->parent);
svCloseFile(ifcfg);
ifcfg = shvarfilesGet(device);
- physicalDevice = pppLogicalToPhysical(&pppdPid, real_device);
- if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
+
+ /* Get the PID of our child pppd. */
+ pppLogicalToPhysical(&pppdPid, real_device, NULL);
kill(pppdPid, SIGTERM);
- /* redial when SIGCHLD arrives, even if !PERSIST */
- connectedOnce = 0;
- timeout = 0; /* redial immediately */
+
+ /* We'll redial when the SIGCHLD arrives, even if PERSIST is
+ * not set (the latter handled by clearing the "we've connected
+ * at least once" flag). */
+ connectedOnce = FALSE;
+
+ /* We don't want to delay before redialing, either, so cut
+ * the retry timeout to zero. */
+ timeout = 0;
}
+ /* If we got a SIGIO (from netreport, presumably), check if the
+ * interface is up and return zero (via our parent) if it is. */
if (theSigio) {
theSigio = 0;
- if (connectedOnce) {
- if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
- }
- physicalDevice = pppLogicalToPhysical(NULL, real_device);
+
+ pppLogicalToPhysical(NULL, real_device, &physicalDevice);
if (physicalDevice) {
- if (interfaceStatus(physicalDevice)) {
- /* device is up */
- detach(1, 0, NULL);
- connectedOnce = 1;
+ if (interfaceIsUp(physicalDevice)) {
+ /* The interface is up, so report a success to a parent if
+ * we have one. Any errors after this we just swallow. */
+ relay_exitcode(0);
+ connectedOnce = TRUE;
}
+ free(physicalDevice);
}
}
+ /* If we got a SIGCHLD, then pppd died (possibly because we killed it),
+ * and we need to restart it after timeout seconds. */
if (theSigchld) {
theSigchld = 0;
- waited = wait3(&status, 0, NULL);
- if (waited < 0) continue;
- /* now, we need to kill any children of pppd still in pppd's
+ /* Find its pid, which is also its process group ID. */
+ waited = waitpid(-1, &status, 0);
+ if (waited == -1) {
+ continue;
+ }
+
+ /* Now, we need to kill any children of pppd still in pppd's
* process group, in case they are hanging around.
* pppd is dead (we just waited for it) but there is no
* guarantee that its children are dead, and they will
* hold the modem if we do not get rid of them.
- * We have kept the old pid/pgrp around in pppdPid.
- */
+ * We have kept the old pid/pgrp around in pppdPid. */
if (pppdPid) {
kill(-pppdPid, SIGTERM); /* give it a chance to die nicely */
usleep(2500000);
@@ -607,52 +691,69 @@ main(int argc, char **argv) {
}
pppdPid = 0;
- if (!WIFEXITED(status)) cleanExit(29);
- if (dieing) cleanExit(WEXITSTATUS(status));
+ /* Bail if the child exitted abnormally or we were already
+ * signalled to kill it. */
+ if (!WIFEXITED(status)) {
+ failureExit(29);
+ }
+ if (dying) {
+ failureExit(WEXITSTATUS(status));
+ }
- /* error conditions from which we do not expect to recover
- * without user intervention -- do not fill up the logs.
- */
+ /* Error conditions from which we do not expect to recover
+ * without user intervention -- do not fill up the logs. */
switch (WEXITSTATUS(status)) {
case 1: case 2: case 3: case 4: case 6:
case 7: case 9: case 14: case 17:
- cleanExit(WEXITSTATUS(status));
+ failureExit(WEXITSTATUS(status));
break;
default:
break;
}
- /* We default to retrying the connect phase for backward compatibility. */
- if ((WEXITSTATUS(status) == 8) && !svTrueValue(ifcfg, "RETRYCONNECT", 1)) {
- cleanExit(WEXITSTATUS(status));
+ /* We default to retrying the connect phase for backward
+ * compatibility, unless RETRYCONNECT is false. */
+ if ((WEXITSTATUS(status) == 8) &&
+ !svTrueValue(ifcfg, "RETRYCONNECT", TRUE)) {
+ failureExit(WEXITSTATUS(status));
}
+ /* If we've never connected, or PERSIST is set, dial again, up
+ * to MAXFAIL times. */
if ((WEXITSTATUS(status) == 8) ||
- !connectedOnce || svTrueValue(ifcfg, "PERSIST", 0)) {
- if (!connectedOnce) {
- temp = svGetValue(ifcfg, "RETRYTIMEOUT");
- if (temp) {
- timeout = atoi(temp);
- free(temp);
- } else {
- timeout = 30;
- }
-
- } else {
- connectedOnce = 0;
- temp = svGetValue(ifcfg, "DISCONNECTTIMEOUT");
- if (temp) {
- timeout = atoi(temp);
- free(temp);
- } else {
- timeout = 2;
- }
- }
- sigprocmask(SIG_UNBLOCK, &blockedsigs, NULL);
- sleep(timeout);
- sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
- if (!theSigterm && !theSighup && !theSigio && !theSigchld && !theSigalrm)
- fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device, theBoot);
+ !connectedOnce ||
+ svTrueValue(ifcfg, "PERSIST", FALSE)) {
+ /* If we've been connected (i.e., if we didn't force a redial,
+ * but the connection went down) wait for DISCONNECTTIMEOUT
+ * seconds before redialing. */
+ if (connectedOnce) {
+ connectedOnce = FALSE;
+ temp = svGetValue(ifcfg, "DISCONNECTTIMEOUT");
+ if (temp) {
+ timeout = atoi(temp);
+ free(temp);
+ } else {
+ timeout = 2;
+ }
+ }
+ sigprocmask(SIG_UNBLOCK, &blockedsigs, NULL);
+ sleep(timeout);
+ sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
+ if (!theSigterm &&
+ !theSighup &&
+ !theSigio &&
+ !theSigchld &&
+ !theSigalrm) {
+ fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
+ }
+ /* Reinitialize the retry timeout. */
+ temp = svGetValue(ifcfg, "RETRYTIMEOUT");
+ if (temp) {
+ timeout = atoi(temp);
+ free(temp);
+ } else {
+ timeout = 30;
+ }
// Scott Sharkey <ssharkey@linux-no-limits.com>
// MAXFAIL Patch...
temp = svGetValue(ifcfg, "MAXFAIL");
@@ -665,20 +766,21 @@ main(int argc, char **argv) {
if ( maxfail != 0 ) {
dialCount++;
if ( dialCount < maxfail ) {
- fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device, theBoot);
+ fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
} else {
- cleanExit(WEXITSTATUS(status));
+ failureExit(WEXITSTATUS(status));
}
} else {
- fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device, theBoot);
+ fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
}
} else {
- cleanExit(WEXITSTATUS(status));
+ failureExit(WEXITSTATUS(status));
}
}
+ /* We timed out, and we're running at boot-time. */
if (theSigalrm) {
- detach(1, 34, NULL);
+ failureExit(34);
}
}
}