source: projects/initscripts/tags/initscripts-8.91.3/src/ppp-watch.c @ 1108

Revision 1108, 23.8 KB checked in by daisuke, 13 years ago (diff)

import initscripts-8.90.6 from internal cvs repository

Line 
1/*
2 * ppp-watch.c
3 *
4 * Bring up a PPP connection and Do The Right Thing[tm] to make bringing
5 * the connection up or down with ifup and ifdown synchronous.  Takes
6 * one argument: the logical name of the device to bring up.  Does not
7 * detach until the interface is up or has permanently failed to come up.
8 *
9 * Copyright 1999-2003 Red Hat, Inc.
10 *
11 * This is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/* Algorithm:
28 *   fork
29 *   if child:
30 *     Register with netreport.  (now exit implies deregister first)
31 *     fork/exec ifup-ppp daemon <interface>
32 *   else:
33 *     while (1):
34 *       sigsuspend()
35 *       if SIGTERM or SIGINT:
36 *         kill pppd pgrp
37 *         exit
38 *       if SIGHUP:
39 *         reload ifcfg files
40 *         kill pppd pgrp
41 *         wait for SIGCHLD to redial
42 *       if SIGIO:
43 *         if no physical device found: continue
44 *         elif physical device is down:
45 *           wait for pppd to exit to redial if appropriate
46 *         else: (physical device is up)
47 *           detach; continue
48 *       if SIGCHLD: (pppd exited)
49 *         wait()
50 *         if pppd exited:
51 *           if PERSIST: redial
52 *           else: exit
53 *         else: (pppd was killed)
54 *           exit
55 *
56 *
57 * When ppp-watch itself dies for reasons of its own, it uses a return code
58 * higher than 25 so as not to clash with pppd return codes, which, as of
59 * this writing, range from 0 to 19.
60 */
61
62#include <unistd.h>
63#include <errno.h>
64#include <fcntl.h>
65#include <signal.h>
66#include <string.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <sys/ioctl.h>
70#include <sys/time.h>
71#include <sys/types.h>
72#include <sys/resource.h>
73#include <sys/socket.h>
74#include <sys/stat.h>
75#include <sys/wait.h>
76#include <termios.h>
77#include <net/if.h>
78#include <glib.h>
79#include "shvar.h"
80
81#define IFCFGPREFIX "/etc/sysconfig/network-scripts/ifcfg-"
82#define IFUP_PPP "/etc/sysconfig/network-scripts/ifup-ppp"
83
84static int theSigterm = 0;
85static int theSigint = 0;
86static int theSighup = 0;
87static int theSigio = 0;
88static int theSigchld = 0;
89static int theSigalrm = 0;
90static int pipeArray[2];
91
92// patch to respect the maxfail parameter to ppp
93// Scott Sharkey <ssharkey@linux-no-limits.com>
94static int dialCount = 0;
95static pid_t theChild;
96static void failureExit(int exitCode);
97
98static void
99interrupt_child(int signo) {
100    kill(theChild, SIGINT);
101}
102
103static void
104set_signal(int signo, void (*handler)(int)) {
105    struct sigaction act;
106    act.sa_handler = handler;
107    act.sa_flags = SA_RESTART;
108    sigemptyset(&act.sa_mask);
109    sigaction(signo, &act, NULL);
110}
111
112/* Create a pipe, and fork off a child.  This is the end of the road for
113 * the parent, which will wait for an exit status byte on the pipe (which
114 * is written by the child). */
115static void
116detach(char *device) {
117    pid_t childpid;
118    unsigned char exitCode;
119    int fd;
120
121    if (pipe(pipeArray) == -1)
122        exit(25);
123
124    childpid = fork();
125    if (childpid == -1)
126        exit(26);
127
128    if (childpid != 0) {
129        /* The parent only cares about notifications from the child. */
130        close (pipeArray[1]);
131
132        /* Certain signals are meant for our child, the watcher process. */
133        theChild = childpid;
134        set_signal(SIGINT, interrupt_child);
135        set_signal(SIGTERM, interrupt_child);
136        set_signal(SIGHUP, interrupt_child);
137
138        /* Read the pipe until the child gives us an exit code as a byte. */
139        while (read (pipeArray[0], &exitCode, 1) == -1) {
140            switch (errno) {
141                case EINTR: continue;
142                default: exit (27); /* this will catch EIO in particular */
143            }
144        }
145        switch (exitCode) {
146        case 0:
147            break;
148           
149        case 1:
150            fprintf(stderr, "Device %s: An immediately fatal error of some kind  occurred,  such  as  an "
151                    "essential system call failing, or running out of virtual memory.\n", device);
152            break;
153         
154        case 2:
155            fprintf(stderr, "Device %s: An error was detected in processing the options given, such as "
156                    "two mutually exclusive options being used.\n", device);
157            break;
158         
159        case 3:
160            fprintf(stderr, "Device %s: Pppd is not setuid-root and the invoking user is not root.\n", device);
161            break;
162         
163        case 4:
164            fprintf(stderr, "Device %s: The  kernel  does  not  support PPP, for example, the PPP kernel "
165                    "driver is not included or cannot be loaded.\n", device);
166            break;
167         
168        case 5:
169            fprintf(stderr, "Device %s: Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP signal.\n", device);
170            break;
171         
172        case 6:
173            fprintf(stderr, "Device %s: The serial port could not be locked.\n", device);
174            break;
175         
176        case 7:
177            fprintf(stderr, "Device %s: The serial port could not be opened.\n", device);
178            break;
179         
180        case 8:
181            fprintf(stderr, "Device %s: The connect script failed (maybe wrong password or wrong phone number).\n", device);
182            break;
183         
184        case 9:
185            fprintf(stderr, "Device %s: The command specified as the argument to the pty option could not be run.\n", device);
186            break;
187         
188        case 10:
189            fprintf(stderr, "Device %s: The PPP negotiation failed, that is, it didn't reach the point "
190                    "where at least one network protocol (e.g. IP) was running.\n", device);
191            break;
192         
193        case 11:
194            fprintf(stderr, "Device %s: The peer system failed (or refused) to authenticate itself.\n", device);
195            break;
196         
197        case 12:
198            fprintf(stderr, "Device %s: The link was established successfully and terminated because it was idle.\n", device);
199            break;
200         
201        case 13:
202            fprintf(stderr, "Device %s: The link was established successfully and terminated because the connect time limit was reached.\n", device);
203            break;
204
205        case 14:
206            fprintf(stderr, "Device %s: Callback was negotiated and an incoming  call should arrive shortly.\n", device);
207            break;
208
209        case 15:
210            fprintf(stderr, "Device %s: The link was terminated because the peer is not responding to echo requests.\n", device);
211            break;
212
213        case 16:
214            fprintf(stderr, "Device %s: The link was terminated by the modem hanging up.\n", device);
215            break;
216
217        case 18:
218            fprintf(stderr, "Device %s: The init script failed (returned a non-zero exit status).\n", device);
219            break;
220
221        case 19:
222            fprintf(stderr, "Device %s: We failed to authenticate ourselves to the peer.\n", device);
223            break;
224
225        case 33:
226            fprintf(stderr, "%s already up, initiating redial\n", device);
227            break;
228        case 34:
229            fprintf(stderr, "Failed to activate %s, retrying in the background\n", device);
230            break;
231        default:
232            fprintf(stderr, "Failed to activate %s with error %d\n", device, exitCode);
233            break;
234        }
235        exit(exitCode);
236    }
237
238    /* We're in the child process, which only writes the exit status
239     * of the pppd process to its parent (i.e., it reads nothing). */
240    close (pipeArray[0]);
241
242    /* Don't leak this into programs we call. */
243    fcntl(pipeArray[1], F_SETFD, FD_CLOEXEC);
244
245    /* Redirect stdio to /dev/null. */
246    fd = open("/dev/null", O_RDONLY);
247    dup2(fd, STDIN_FILENO);
248    close(fd);
249
250    fd = open("/dev/null", O_WRONLY);
251    dup2(fd, STDOUT_FILENO);
252    dup2(fd, STDERR_FILENO);
253    close(fd);
254
255    /* Become session and process group leader. */
256    setsid();
257    setpgid(0, 0);
258}
259
260/* Do magic with the pid file (/var/run/pppwatch-$DEVICE.pid):
261 * Try to open it for writing.  If it exists, send a SIGHUP to whatever PID
262 * is already listed in it and remove it.  Repeat until we can open it.
263 * Write out our PID, and return. */
264static void
265doPidFile(char *device) {
266    static char pidFilePath[PATH_MAX] = "";
267    int fd = -1;
268    FILE *f = NULL;
269    pid_t pid = 0;
270
271    if (device == NULL) {
272        /* Remove an existing pid file -- we're exiting. */
273        if(strlen(pidFilePath) > 0) {
274            unlink(pidFilePath);
275        }
276    } else  {
277        /* Set up the name of the pid file, used only the first time. */
278        snprintf(pidFilePath, sizeof(pidFilePath), "/var/run/pppwatch-%s.pid",
279                 device);
280
281        /* Create the pid file. */
282        do {
283            fd = open(pidFilePath, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_NOFOLLOW,
284                      S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH);
285            if(fd == -1) {
286                /* Try to open the file for read. */
287                fd = open(pidFilePath, O_RDONLY);
288                if(fd == -1)
289                    failureExit(36); /* This is not good. */
290
291                /* We're already running, send a SIGHUP (we presume that they
292                 * are calling ifup for a reason, so they probably want to
293                 * redial) and then exit cleanly and let things go on in the
294                 * background.  Muck with the filename so that we don't go
295                 * deleting the pid file for the already-running instance.
296                 */
297                f = fdopen(fd, "r");
298                if(f == NULL)
299                    failureExit(37);
300
301                pid = 0;
302                fscanf(f, "%d", &pid);
303                fclose(f);
304
305                if(pid) {
306                    /* Try to kill it. */
307                    if (kill(pid, SIGHUP) == -1) {
308                        /* No such pid, remove the bogus pid file. */
309                        unlink(pidFilePath);
310                    } else {
311                        /* Got it.  Don't mess with the pid file on
312                         * our way out. */
313                        memset(pidFilePath, '\0', sizeof(pidFilePath));
314                        failureExit(33);
315                    }
316                }
317            }
318        } while(fd == -1);
319
320        f = fdopen(fd, "w");
321        if(f == NULL)
322            failureExit(31);
323        fprintf(f, "%d\n", getpid());
324        fclose(f);
325    }
326}
327
328/* Fork off and exec() a child process.  If reap_child is non-zero,
329 * wait for the child to exit and return 0 if it ran successfully,
330 * otherwise return 0 right away and let the SIGCHLD handler deal. */
331static int
332fork_exec(gboolean reap, char *path, char *arg1, char *arg2, char *arg3)
333{
334    pid_t childpid;
335    int status;
336
337    sigset_t sigs;
338
339    childpid = fork();
340    if (childpid == -1)
341        exit(26);
342
343    if (childpid == 0) {
344        /* Do the exec magic.  Prepare by clearing the signal mask for pppd. */
345        sigemptyset(&sigs);
346        sigprocmask(SIG_SETMASK, &sigs, NULL);
347
348        if (!reap) {
349            /* Make sure that the pppd is the leader for its process group. */
350            setsid();
351            setpgid(0, 0);
352        }
353
354        execl(path, path, arg1, arg2, arg3, NULL);
355        perror(path);
356        _exit (1);
357    }
358
359    if (reap) {
360        waitpid (childpid, &status, 0);
361        if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
362            return 0;
363        } else {
364            return 1;
365        }
366    } else {
367        return 0;
368    }
369}
370
371/* Relay the pppd's exit code up to the parent -- can only be called once,
372 * because the parent exits as soon as it reads a byte. */
373static void
374relay_exitcode(unsigned char code)
375{
376    unsigned char exitCode;
377    exitCode = code;
378    write(pipeArray[1], &exitCode, 1);
379    close(pipeArray[1]);
380}
381
382/* Unregister with netreport, relay a status byte to the parent, clean up
383 * the pid file, and bail. */
384static void
385failureExit(int exitCode) {
386    fork_exec(TRUE, "/sbin/netreport", "-r", NULL, NULL);
387    relay_exitcode(exitCode);
388    doPidFile(NULL);
389    exit(exitCode);
390}
391
392/* Keeps track of which signals we've seen so far. */
393static void
394signal_tracker (int signum) {
395    switch(signum) {
396        case SIGTERM:
397            theSigterm = 1; break;
398        case SIGINT:
399            theSigint = 1; break;
400        case SIGHUP:
401            theSighup = 1; break;
402        case SIGIO:
403            theSigio = 1; break;
404        case SIGCHLD:
405            theSigchld = 1; break;
406        case SIGALRM:
407            theSigalrm = 1; break;
408    }
409}
410
411/* Return a shvarFile for this interface, taking into account one level of
412 * inheritance (eeewww). */
413static shvarFile *
414shvarfilesGet(const char *interfaceName) {
415    shvarFile *ifcfg = NULL;
416    char ifcfgName[PATH_MAX];
417    char *ifcfgParentDiff = NULL;
418
419    /* Start with the basic configuration. */
420    snprintf(ifcfgName, sizeof(ifcfgName), "%s%s", IFCFGPREFIX, interfaceName);
421    ifcfg = svNewFile(ifcfgName);
422    if (ifcfg == NULL)
423            return NULL;
424
425    /* Do we have a parent interface (i.e., for ppp0-blah, ppp0) to inherit? */
426    ifcfgParentDiff = strchr(ifcfgName + sizeof(IFCFGPREFIX), '-');
427    if (ifcfgParentDiff) {
428        *ifcfgParentDiff = '\0';
429        ifcfg->parent = svNewFile(ifcfgName);
430    }
431
432    /* This is very unclean, but we have to close the shvar descriptors in
433     * case they've been numbered STDOUT_FILENO or STDERR_FILENO, which would
434     * be disastrous if inherited by a child process. */
435    close (ifcfg->fd);
436    ifcfg->fd = 0;
437
438    if (ifcfg->parent) {
439        close (ifcfg->parent->fd);
440        ifcfg->parent->fd = 0;
441    }
442
443    return ifcfg;
444}
445
446/* Convert a logical interface name to a real one by reading the lock
447 * file created by pppd. */
448static void
449pppLogicalToPhysical(int *pppdPid, char *logicalName, char **physicalName) {
450    char mapFileName[PATH_MAX];
451    char buffer[20];
452    char *p, *q;
453    int fd, n;
454    char *physicalDevice = NULL;
455
456    snprintf(mapFileName, sizeof(mapFileName), "/var/run/ppp-%s.pid",
457             logicalName);
458    fd = open(mapFileName, O_RDONLY);
459    if (fd != -1) {
460        n = read(fd, buffer, sizeof(buffer) - 1);
461        close(fd);
462        if (n > 0) {
463            buffer[n] = '\0';
464            /* Split up the file at the first line break -- the PID is on the
465             * first line. */
466            if((p = strchr(buffer, '\n')) != NULL) {
467                *p = '\0';
468                p++;
469                if (pppdPid) {
470                    *pppdPid = atoi(buffer);
471                }
472                /* The physical device name is on the second line. */
473                if((q = strchr(p, '\n')) != NULL) {
474                    *q = '\0';
475                    physicalDevice = strdup(p);
476                }
477            }
478        }
479    }
480
481    if (physicalDevice) {
482        if (physicalName) {
483            *physicalName = physicalDevice;
484        } else {
485            free(physicalDevice);
486        }
487    } else {
488        if (physicalName) {
489            *physicalName = NULL;
490        }
491    }
492}
493
494/* Return a boolean value indicating if the interface is up.  If not, or
495 * if we don't know, return FALSE. */
496static gboolean
497interfaceIsUp(char *device) {
498    int sock = -1;
499    int family[] = {PF_INET, PF_IPX, PF_AX25, PF_APPLETALK, 0};
500    int p = 0;
501    struct ifreq ifr;
502    gboolean retcode = FALSE;
503
504    /* Create a socket suitable for doing routing ioctls. */
505    for (p = 0; (sock == -1) && family[p]; p++) {
506        sock = socket(family[p], SOCK_DGRAM, 0);
507    }
508    if (sock == -1)
509        return FALSE;
510
511    /* Populate the request structure for getting the interface's status. */
512    memset(&ifr, 0, sizeof(ifr));
513    strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
514    ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
515
516    /* We return TRUE iff the ioctl succeeded and the interface is UP. */
517    if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
518        retcode = FALSE;
519    } else if (ifr.ifr_flags & IFF_UP) {
520        retcode = TRUE;
521    }
522
523    close(sock);
524
525    return retcode;
526}
527
528/* Very, very minimal hangup function.  This just attempts to hang up a device
529 * that should already be hung up, so it does not need to be bulletproof.  */
530static void
531hangup(shvarFile *ifcfg) {
532    int fd;
533    char *line;
534    struct termios original_ts, ts;
535
536    line = svGetValue(ifcfg, "MODEMPORT");
537    if (line == NULL)
538        return;
539
540    fd = open(line, O_RDWR | O_NOCTTY | O_NONBLOCK);
541    if (fd != -1) {
542        if (tcgetattr(fd, &ts) != -1) {
543            original_ts = ts;
544            write(fd, "\r", 1); /* tickle modems that do not like dropped DTR */
545            usleep(1000);
546            cfsetospeed(&ts, B0);
547            tcsetattr(fd, TCSANOW, &ts);
548            usleep(100000);
549            tcsetattr(fd, TCSANOW, &original_ts);
550        }
551        close(fd);
552    }
553    free(line);
554}
555
556int
557main(int argc, char **argv) {
558    int status;
559    pid_t waited;
560    char *device, *real_device, *physicalDevice = NULL;
561    char *boot = NULL;
562    shvarFile *ifcfg;
563    sigset_t blockedsigs, unblockedsigs;
564    int pppdPid = 0;
565    int timeout = 30;
566    char *temp;
567    gboolean dying = FALSE;
568    int sendsig;
569    gboolean connectedOnce = FALSE;
570    int maxfail = 0;            // MAXFAIL Patch <ssharkey@linux-no-limits.com>
571
572    if (argc < 2) {
573        fprintf (stderr, "usage: ppp-watch <interface-name> [boot]\n");
574        exit(30);
575    }
576
577    if (strncmp(argv[1], "ifcfg-", 6) == 0) {
578        device = argv[1] + 6;
579    } else {
580        device = argv[1];
581    }
582
583    detach(device); /* Prepare a child process to monitor pppd.  When we
584                       return, we'll be in the child. */
585
586    if ((argc > 2) && (strcmp("boot", argv[2]) == 0)) {
587        boot = argv[2];
588    }
589
590    ifcfg = shvarfilesGet(device);
591    if (ifcfg == NULL)
592        failureExit(28);
593
594    real_device = svGetValue(ifcfg, "DEVICE");
595    if (real_device == NULL)
596        real_device = device;
597
598    doPidFile(real_device);
599
600    /* We'll want to know which signal interrupted our sleep below, so
601     * attach a signal handler to these. */
602    set_signal(SIGTERM, signal_tracker);
603    set_signal(SIGINT, signal_tracker);
604    set_signal(SIGHUP, signal_tracker);
605    set_signal(SIGIO, signal_tracker);
606    set_signal(SIGCHLD, signal_tracker);
607
608    /* We time out only if we're being run at boot-time. */
609    if (boot) {
610        temp = svGetValue(ifcfg, "BOOTTIMEOUT");
611        if (temp) {
612            timeout = atoi(temp);
613            if (timeout < 1) timeout = 1;
614            free(temp);
615        } else {
616            timeout = 30;
617        }
618        set_signal(SIGALRM, signal_tracker);
619        alarm(timeout);
620    }
621
622    /* Register us to get a signal when something changes. Yes, that's vague. */
623    fork_exec(TRUE, "/sbin/netreport", NULL, NULL, NULL);
624
625    /* Reset theSigchld, which should have been triggered by netreport. */
626    theSigchld = 0;
627
628    /* We don't set up the procmask until after we have received the netreport
629     * signal.  Do so now. */
630    sigemptyset(&blockedsigs);
631    sigaddset(&blockedsigs, SIGTERM);
632    sigaddset(&blockedsigs, SIGINT);
633    sigaddset(&blockedsigs, SIGHUP);
634    sigaddset(&blockedsigs, SIGIO);
635    sigaddset(&blockedsigs, SIGCHLD);
636    if (boot) {
637        sigaddset(&blockedsigs, SIGALRM);
638    }
639    sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
640
641    sigfillset(&unblockedsigs);
642    sigdelset(&unblockedsigs, SIGTERM);
643    sigdelset(&unblockedsigs, SIGINT);
644    sigdelset(&unblockedsigs, SIGHUP);
645    sigdelset(&unblockedsigs, SIGIO);
646    sigdelset(&unblockedsigs, SIGCHLD);
647    if (boot) {
648        sigdelset(&unblockedsigs, SIGALRM);
649    }
650    sigprocmask(SIG_UNBLOCK, &unblockedsigs, NULL);
651
652    /* Initialize the retry timeout using the RETRYTIMEOUT setting. */
653    temp = svGetValue(ifcfg, "RETRYTIMEOUT");
654    if (temp) {
655        timeout = atoi(temp);
656        free(temp);
657    } else {
658        timeout = 30;
659    }
660
661    /* Start trying to bring the interface up. */
662    fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
663
664    while (TRUE) {
665        /* Wait for a signal. */
666        if (!theSigterm &&
667            !theSigint &&
668            !theSighup &&
669            !theSigio &&
670            !theSigchld &&
671            !theSigalrm) {
672            sigsuspend(&unblockedsigs);
673        }
674
675        /* If we got SIGTERM or SIGINT, give up and hang up. */
676        if (theSigterm || theSigint) {
677            theSigterm = theSigint = 0;
678
679            /* If we've already tried to exit this way, use SIGKILL instead
680             * of SIGTERM, because pppd's just being stubborn. */
681            if (dying) {
682                sendsig = SIGKILL;
683            } else {
684                sendsig = SIGTERM;
685            }
686            dying = TRUE;
687
688            /* Get the pid of our child pppd. */
689            pppLogicalToPhysical(&pppdPid, device, NULL);
690
691            /* We don't know what our child pid is.  This is very confusing. */
692            if (!pppdPid) {
693                failureExit(35);
694            }
695
696            /* Die, pppd, die. */
697            kill(pppdPid, sendsig);
698            if (sendsig == SIGKILL) {
699                kill(-pppdPid, SIGTERM); /* Give it a chance to die nicely, then
700                                            kill its whole process group. */
701                usleep(2500000);
702                kill(-pppdPid, sendsig);
703                hangup(ifcfg);
704                failureExit(32);
705            }
706        }
707
708        /* If we got SIGHUP, reload and redial. */
709        if (theSighup) {
710            theSighup = 0;
711
712            /* Free and reload the configuration structure. */
713            if (ifcfg->parent)
714                svCloseFile(ifcfg->parent);
715            svCloseFile(ifcfg);
716            ifcfg = shvarfilesGet(device);
717
718            /* Get the PID of our child pppd. */
719            pppLogicalToPhysical(&pppdPid, device, NULL);
720            kill(pppdPid, SIGTERM);
721
722            /* We'll redial when the SIGCHLD arrives, even if PERSIST is
723             * not set (the latter handled by clearing the "we've connected
724             * at least once" flag). */
725            connectedOnce = FALSE;
726
727            /* We don't want to delay before redialing, either, so cut
728             * the retry timeout to zero. */
729            timeout = 0;
730        }
731
732        /* If we got a SIGIO (from netreport, presumably), check if the
733         * interface is up and return zero (via our parent) if it is. */
734        if (theSigio) {
735            theSigio = 0;
736
737            pppLogicalToPhysical(NULL, device, &physicalDevice);
738            if (physicalDevice) {
739                if (interfaceIsUp(physicalDevice)) {
740                    /* The interface is up, so report a success to a parent if
741                     * we have one.  Any errors after this we just swallow. */
742                    relay_exitcode(0);
743                    connectedOnce = TRUE;
744                    alarm(0);
745                }
746                free(physicalDevice);
747            }
748        }
749
750        /* If we got a SIGCHLD, then pppd died (possibly because we killed it),
751         * and we need to restart it after timeout seconds. */
752        if (theSigchld) {
753            theSigchld = 0;
754
755            /* Find its pid, which is also its process group ID. */
756            waited = waitpid(-1, &status, 0);
757            if (waited == -1) {
758                continue;
759            }
760
761            /* Now, we need to kill any children of pppd still in pppd's
762             * process group, in case they are hanging around.
763             * pppd is dead (we just waited for it) but there is no
764             * guarantee that its children are dead, and they will
765             * hold the modem if we do not get rid of them.
766             * We have kept the old pid/pgrp around in pppdPid.  */
767            if (pppdPid) {
768                kill(-pppdPid, SIGTERM); /* give it a chance to die nicely */
769                usleep(2500000);
770                kill(-pppdPid, SIGKILL);
771                hangup(ifcfg);
772            }
773            pppdPid = 0;
774
775            /* Bail if the child exitted abnormally or we were already
776             * signalled to kill it. */
777            if (!WIFEXITED(status)) {
778                failureExit(29);
779            }
780            if (dying) {
781                failureExit(WEXITSTATUS(status));
782            }
783
784            /* Error conditions from which we do not expect to recover
785             * without user intervention -- do not fill up the logs.  */
786            switch (WEXITSTATUS(status)) {
787            case 1: case 2: case 3: case 4: case 6:
788            case 7: case 9: case 14: case 17:
789                failureExit(WEXITSTATUS(status));
790                break;
791            default:
792                break;
793            }
794
795            /* PGB 08/20/02: We no longer retry connecting MAXFAIL
796               times on a failed connect script unless RETRYCONNECT is
797               true. */
798            if ((WEXITSTATUS(status) == 8) &&
799                !svTrueValue(ifcfg, "RETRYCONNECT", FALSE)) {
800                failureExit(WEXITSTATUS(status));
801            }
802
803            /* If we've never connected, or PERSIST is set, dial again, up
804             * to MAXFAIL times. */
805            if ((WEXITSTATUS(status) == 8) ||
806                !connectedOnce ||
807                svTrueValue(ifcfg, "PERSIST", FALSE)) {
808                /* If we've been connected (i.e., if we didn't force a redial,
809                 * but the connection went down) wait for DISCONNECTTIMEOUT
810                 * seconds before redialing. */
811                if (connectedOnce) {
812                    connectedOnce = FALSE;
813                    temp = svGetValue(ifcfg, "DISCONNECTTIMEOUT");
814                    if (temp) {
815                        timeout = atoi(temp);
816                        free(temp);
817                    } else {
818                        timeout = 2;
819                    }
820                }
821                sigprocmask(SIG_UNBLOCK, &blockedsigs, NULL);
822                sleep(timeout);
823                sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
824                if (!theSigterm &&
825                    !theSigint &&
826                    !theSighup &&
827                    !theSigio &&
828                    !theSigchld &&
829                    !theSigalrm) {
830                    fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
831                }
832                /* Reinitialize the retry timeout. */
833                temp = svGetValue(ifcfg, "RETRYTIMEOUT");
834                if (temp) {
835                    timeout = atoi(temp);
836                    free(temp);
837                } else {
838                    timeout = 30;
839                }
840// Scott Sharkey <ssharkey@linux-no-limits.com>
841// MAXFAIL Patch...
842                temp = svGetValue(ifcfg, "MAXFAIL");
843                if (temp) {
844                    maxfail = atoi(temp);
845                    free(temp);
846                } else {
847                    maxfail = 0;
848                }
849                if ( maxfail != 0 ) {
850                    dialCount++;
851                    if ( dialCount >= maxfail )
852                        failureExit(WEXITSTATUS(status));
853                } 
854            } else {
855                failureExit(WEXITSTATUS(status));
856            }
857        }
858
859        /* We timed out, and we're running at boot-time. */
860        if (theSigalrm) {
861            failureExit(34);
862        }
863    }
864}
Note: See TracBrowser for help on using the repository browser.