]> TLD Linux GIT Repositories - rc-scripts.git/blob - src/ppp-watch.c
- from PLD
[rc-scripts.git] / src / ppp-watch.c
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 syncronous.  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/interfaces/ifcfg-"
82 #define IFUP_PPP "/lib/rc-scripts/ifup-ppp"
83
84 static int theSigterm = 0;
85 static int theSigint = 0;
86 static int theSighup = 0;
87 static int theSigio = 0;
88 static int theSigchld = 0;
89 static int theSigalrm = 0;
90 static int pipeArray[2];
91
92 // patch to respect the maxfail parameter to ppp
93 // Scott Sharkey <ssharkey@linux-no-limits.com>
94 static int dialCount = 0;
95 static int theChild;
96 static void failureExit(int exitCode);
97
98 static void
99 interrupt_child(int signo) {
100     kill(theChild, SIGINT);
101 }
102
103 static void
104 set_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). */
115 static void
116 detach(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             case 33:
149                 fprintf(stderr, "%s already up, initiating redial\n", device);
150                 break;
151             case 34:
152                 fprintf(stderr, "Failed to activate %s, retrying in the background\n", device);
153                 break;
154             default:
155                 fprintf(stderr, "Failed to activate %s with error %d\n", device, exitCode);
156                 break;
157          }
158          exit(exitCode);
159     }
160
161     /* We're in the child process, which only writes the exit status
162      * of the pppd process to its parent (i.e., it reads nothing). */
163     close (pipeArray[0]);
164
165     /* Don't leak this into programs we call. */
166     fcntl(pipeArray[1], F_SETFD, FD_CLOEXEC);
167
168     /* Redirect stdio to /dev/null. */
169     fd = open("/dev/null", O_RDONLY);
170     dup2(fd, STDIN_FILENO);
171     close(fd);
172
173     fd = open("/dev/null", O_WRONLY);
174     dup2(fd, STDOUT_FILENO);
175     dup2(fd, STDERR_FILENO);
176     close(fd);
177
178     /* Become session and process group leader. */
179     setsid();
180     setpgid(0, 0);
181 }
182
183 /* Do magic with the pid file (/var/run/pppwatch-$DEVICE.pid):
184  * Try to open it for writing.  If it exists, send a SIGHUP to whatever PID
185  * is already listed in it and remove it.  Repeat until we can open it.
186  * Write out our PID, and return. */
187 static void
188 doPidFile(char *device) {
189     static char pidFilePath[PATH_MAX] = "";
190     int fd = -1;
191     FILE *f = NULL;
192     pid_t pid = 0;
193
194     if (device == NULL) {
195         /* Remove an existing pid file -- we're exiting. */
196         if(strlen(pidFilePath) > 0) {
197             unlink(pidFilePath);
198         }
199     } else  {
200         /* Set up the name of the pid file, used only the first time. */
201         snprintf(pidFilePath, sizeof(pidFilePath), "/var/run/pppwatch-%s.pid",
202                  device);
203
204         /* Create the pid file. */
205         do {
206             fd = open(pidFilePath, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_NOFOLLOW,
207                       S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH);
208             if(fd == -1) {
209                 /* Try to open the file for read. */
210                 fd = open(pidFilePath, O_RDONLY);
211                 if(fd == -1)
212                     failureExit(36); /* This is not good. */
213
214                 /* We're already running, send a SIGHUP (we presume that they
215                  * are calling ifup for a reason, so they probably want to
216                  * redial) and then exit cleanly and let things go on in the
217                  * background.  Muck with the filename so that we don't go
218                  * deleting the pid file for the already-running instance.
219                  */
220                 f = fdopen(fd, "r");
221                 if(f == NULL)
222                     failureExit(37);
223
224                 pid = 0;
225                 fscanf(f, "%d", &pid);
226                 fclose(f);
227
228                 if(pid) {
229                     /* Try to kill it. */
230                     if (kill(pid, SIGHUP) == -1) {
231                         /* No such pid, remove the bogus pid file. */
232                         unlink(pidFilePath);
233                     } else {
234                         /* Got it.  Don't mess with the pid file on
235                          * our way out. */
236                         memset(pidFilePath, '\0', sizeof(pidFilePath));
237                         failureExit(33);
238                     }
239                 }
240             }
241         } while(fd == -1);
242
243         f = fdopen(fd, "w");
244         if(f == NULL)
245             failureExit(31);
246         fprintf(f, "%d\n", getpid());
247         fclose(f);
248     }
249 }
250
251 /* Fork off and exec() a child process.  If reap_child is non-zero,
252  * wait for the child to exit and return 0 if it ran successfully,
253  * otherwise return 0 right away and let the SIGCHLD handler deal. */
254 static int
255 fork_exec(gboolean reap, char *path, char *arg1, char *arg2, char *arg3)
256 {
257     pid_t childpid;
258     int status;
259
260     sigset_t sigs;
261
262     childpid = fork();
263     if (childpid == -1)
264         exit(26);
265
266     if (childpid == 0) {
267         /* Do the exec magic.  Prepare by clearing the signal mask for pppd. */
268         sigemptyset(&sigs);
269         sigprocmask(SIG_SETMASK, &sigs, NULL);
270
271         if (!reap) {
272             /* Make sure that the pppd is the leader for its process group. */
273             setsid();
274             setpgid(0, 0);
275         }
276
277         execl(path, path, arg1, arg2, arg3, NULL);
278         perror(path);
279         _exit (1);
280     }
281
282     if (reap) {
283         waitpid (childpid, &status, 0);
284         if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
285             return 0;
286         } else {
287             return 1;
288         }
289     } else {
290         return 0;
291     }
292 }
293
294 /* Relay the pppd's exit code up to the parent -- can only be called once,
295  * because the parent exits as soon as it reads a byte. */
296 static void
297 relay_exitcode(unsigned char code)
298 {
299     unsigned char exitCode;
300     exitCode = code;
301     write(pipeArray[1], &exitCode, 1);
302     close(pipeArray[1]);
303 }
304
305 /* Unregister with netreport, relay a status byte to the parent, clean up
306  * the pid file, and bail. */
307 static void
308 failureExit(int exitCode) {
309     fork_exec(TRUE, "/sbin/netreport", "-r", NULL, NULL);
310     relay_exitcode(exitCode);
311     doPidFile(NULL);
312     exit(exitCode);
313 }
314
315 /* Keeps track of which signals we've seen so far. */
316 static void
317 signal_tracker (int signum) {
318     switch(signum) {
319         case SIGTERM:
320             theSigterm = 1; break;
321         case SIGINT:
322             theSigint = 1; break;
323         case SIGHUP:
324             theSighup = 1; break;
325         case SIGIO:
326             theSigio = 1; break;
327         case SIGCHLD:
328             theSigchld = 1; break;
329         case SIGALRM:
330             theSigalrm = 1; break;
331     }
332 }
333
334 /* Return a shvarFile for this interface, taking into account one level of
335  * inheritance (eeewww). */
336 static shvarFile *
337 shvarfilesGet(const char *interfaceName) {
338     shvarFile *ifcfg = NULL;
339     char ifcfgName[PATH_MAX];
340     char *ifcfgParentDiff = NULL;
341
342     /* Start with the basic configuration. */
343     snprintf(ifcfgName, sizeof(ifcfgName), "%s%s", IFCFGPREFIX, interfaceName);
344     ifcfg = svNewFile(ifcfgName);
345     if (ifcfg == NULL)
346             return NULL;
347
348     /* Do we have a parent interface (i.e., for ppp0-blah, ppp0) to inherit? */
349     ifcfgParentDiff = strchr(ifcfgName + sizeof(IFCFGPREFIX), '-');
350     if (ifcfgParentDiff) {
351         *ifcfgParentDiff = '\0';
352         ifcfg->parent = svNewFile(ifcfgName);
353     }
354
355     /* This is very unclean, but we have to close the shvar descriptors in
356      * case they've been numbered STDOUT_FILENO or STDERR_FILENO, which would
357      * be disastrous if inherited by a child process. */
358     close (ifcfg->fd);
359     ifcfg->fd = 0;
360
361     if (ifcfg->parent) {
362         close (ifcfg->parent->fd);
363         ifcfg->parent->fd = 0;
364     }
365
366     return ifcfg;
367 }
368
369 /* Convert a logical interface name to a real one by reading the lock
370  * file created by pppd. */
371 static void
372 pppLogicalToPhysical(int *pppdPid, char *logicalName, char **physicalName) {
373     char mapFileName[PATH_MAX];
374     char buffer[20];
375     char *p, *q;
376     int fd, n;
377     char *physicalDevice = NULL;
378
379     snprintf(mapFileName, sizeof(mapFileName), "/var/run/ppp-%s.pid",
380              logicalName);
381     fd = open(mapFileName, O_RDONLY);
382     if (fd != -1) {
383         n = read(fd, buffer, sizeof(buffer));
384         close(fd);
385         if (n > 0) {
386             buffer[n] = '\0';
387             /* Split up the file at the first line break -- the PID is on the
388              * first line. */
389             if((p = strchr(buffer, '\n')) != NULL) {
390                 *p = '\0';
391                 p++;
392                 if (pppdPid) {
393                     *pppdPid = atoi(buffer);
394                 }
395                 /* The physical device name is on the second line. */
396                 if((q = strchr(p, '\n')) != NULL) {
397                     *q = '\0';
398                     physicalDevice = strdup(p);
399                 }
400             }
401         }
402     }
403
404     if (physicalDevice) {
405         if (physicalName) {
406             *physicalName = physicalDevice;
407         } else {
408             free(physicalDevice);
409         }
410     } else {
411         if (physicalName) {
412             *physicalName = NULL;
413         }
414     }
415 }
416
417 /* Return a boolean value indicating if the interface is up.  If not, or
418  * if we don't know, return FALSE. */
419 static gboolean
420 interfaceIsUp(char *device) {
421     int sock = -1;
422     int family[] = {PF_INET, PF_IPX, PF_AX25, PF_APPLETALK, 0};
423     int p = 0;
424     struct ifreq ifr;
425     gboolean retcode = FALSE;
426
427     /* Create a socket suitable for doing routing ioctls. */
428     for (p = 0; (sock == -1) && family[p]; p++) {
429         sock = socket(family[p], SOCK_DGRAM, 0);
430     }
431     if (sock == -1)
432         return FALSE;
433
434     /* Populate the request structure for getting the interface's status. */
435     memset(&ifr, 0, sizeof(ifr));
436     strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
437     ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
438
439     /* We return TRUE iff the ioctl succeeded and the interface is UP. */
440     if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
441         retcode = FALSE;
442     } else if (ifr.ifr_flags & IFF_UP) {
443         retcode = TRUE;
444     }
445
446     close(sock);
447
448     return retcode;
449 }
450
451 /* Very, very minimal hangup function.  This just attempts to hang up a device
452  * that should already be hung up, so it does not need to be bulletproof.  */
453 static void
454 hangup(shvarFile *ifcfg) {
455     int fd;
456     char *line;
457     struct termios original_ts, ts;
458
459     line = svGetValue(ifcfg, "MODEMPORT");
460     if (line == NULL)
461         return;
462
463     fd = open(line, O_RDWR | O_NOCTTY | O_NONBLOCK);
464     if (fd != -1) {
465         if (tcgetattr(fd, &ts) != -1) {
466             original_ts = ts;
467             write(fd, "\r", 1); /* tickle modems that do not like dropped DTR */
468             usleep(1000);
469             cfsetospeed(&ts, B0);
470             tcsetattr(fd, TCSANOW, &ts);
471             usleep(100000);
472             tcsetattr(fd, TCSANOW, &original_ts);
473         }
474         close(fd);
475     }
476     free(line);
477 }
478
479 int
480 main(int argc, char **argv) {
481     int status;
482     pid_t waited;
483     char *device, *real_device, *physicalDevice = NULL;
484     char *boot = NULL;
485     shvarFile *ifcfg;
486     sigset_t blockedsigs, unblockedsigs;
487     int pppdPid = 0;
488     int timeout = 30;
489     char *temp;
490     gboolean dying = FALSE;
491     int sendsig;
492     gboolean connectedOnce = FALSE;
493     int maxfail = 0;            // MAXFAIL Patch <ssharkey@linux-no-limits.com>
494
495     if (argc < 2) {
496         fprintf (stderr, "usage: ppp-watch <interface-name> [boot]\n");
497         exit(30);
498     }
499
500     if (strncmp(argv[1], "ifcfg-", 6) == 0) {
501         device = argv[1] + 6;
502     } else {
503         device = argv[1];
504     }
505
506     detach(device); /* Prepare a child process to monitor pppd.  When we
507                        return, we'll be in the child. */
508
509     if ((argc > 2) && (strcmp("boot", argv[2]) == 0)) {
510         boot = argv[2];
511     }
512
513     ifcfg = shvarfilesGet(device);
514     if (ifcfg == NULL)
515         failureExit(28);
516
517     real_device = svGetValue(ifcfg, "DEVICE");
518     if (real_device == NULL)
519         real_device = device;
520
521     doPidFile(real_device);
522
523     /* We'll want to know which signal interrupted our sleep below, so
524      * attach a signal handler to these. */
525     set_signal(SIGTERM, signal_tracker);
526     set_signal(SIGINT, signal_tracker);
527     set_signal(SIGHUP, signal_tracker);
528     set_signal(SIGIO, signal_tracker);
529     set_signal(SIGCHLD, signal_tracker);
530
531     /* We time out only if we're being run at boot-time. */
532     if (boot) {
533         temp = svGetValue(ifcfg, "BOOTTIMEOUT");
534         if (temp) {
535             timeout = atoi(temp);
536             if (timeout < 1) timeout = 1;
537             free(temp);
538         } else {
539             timeout = 30;
540         }
541         set_signal(SIGALRM, signal_tracker);
542         alarm(timeout);
543     }
544
545     /* Register us to get a signal when something changes. Yes, that's vague. */
546     fork_exec(TRUE, "/sbin/netreport", NULL, NULL, NULL);
547
548     /* Reset theSigchld, which should have been triggered by netreport. */
549     theSigchld = 0;
550
551     /* We don't set up the procmask until after we have received the netreport
552      * signal.  Do so now. */
553     sigemptyset(&blockedsigs);
554     sigaddset(&blockedsigs, SIGTERM);
555     sigaddset(&blockedsigs, SIGINT);
556     sigaddset(&blockedsigs, SIGHUP);
557     sigaddset(&blockedsigs, SIGIO);
558     sigaddset(&blockedsigs, SIGCHLD);
559     if (boot) {
560         sigaddset(&blockedsigs, SIGALRM);
561     }
562     sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
563
564     sigfillset(&unblockedsigs);
565     sigdelset(&unblockedsigs, SIGTERM);
566     sigdelset(&unblockedsigs, SIGINT);
567     sigdelset(&unblockedsigs, SIGHUP);
568     sigdelset(&unblockedsigs, SIGIO);
569     sigdelset(&unblockedsigs, SIGCHLD);
570     if (boot) {
571         sigdelset(&unblockedsigs, SIGALRM);
572     }
573     sigprocmask(SIG_UNBLOCK, &unblockedsigs, NULL);
574
575     /* Initialize the retry timeout using the RETRYTIMEOUT setting. */
576     temp = svGetValue(ifcfg, "RETRYTIMEOUT");
577     if (temp) {
578         timeout = atoi(temp);
579         free(temp);
580     } else {
581         timeout = 30;
582     }
583
584     /* Start trying to bring the interface up. */
585     fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
586
587     while (TRUE) {
588         /* Wait for a signal. */
589         if (!theSigterm &&
590             !theSigint &&
591             !theSighup &&
592             !theSigio &&
593             !theSigchld &&
594             !theSigalrm) {
595             sigsuspend(&unblockedsigs);
596         }
597
598         /* If we got SIGTERM or SIGINT, give up and hang up. */
599         if (theSigterm || theSigint) {
600             theSigterm = theSigint = 0;
601
602             /* If we've already tried to exit this way, use SIGKILL instead
603              * of SIGTERM, because pppd's just being stubborn. */
604             if (dying) {
605                 sendsig = SIGKILL;
606             } else {
607                 sendsig = SIGTERM;
608             }
609             dying = TRUE;
610
611             /* Get the pid of our child pppd. */
612             pppLogicalToPhysical(&pppdPid, device, NULL);
613
614             /* We don't know what our child pid is.  This is very confusing. */
615             if (!pppdPid) {
616                 failureExit(35);
617             }
618
619             /* Die, pppd, die. */
620             kill(pppdPid, sendsig);
621             if (sendsig == SIGKILL) {
622                 kill(-pppdPid, SIGTERM); /* Give it a chance to die nicely, then
623                                             kill its whole process group. */
624                 usleep(2500000);
625                 kill(-pppdPid, sendsig);
626                 hangup(ifcfg);
627                 failureExit(32);
628             }
629         }
630
631         /* If we got SIGHUP, reload and redial. */
632         if (theSighup) {
633             theSighup = 0;
634
635             /* Free and reload the configuration structure. */
636             if (ifcfg->parent)
637                 svCloseFile(ifcfg->parent);
638             svCloseFile(ifcfg);
639             ifcfg = shvarfilesGet(device);
640
641             /* Get the PID of our child pppd. */
642             pppLogicalToPhysical(&pppdPid, device, NULL);
643             kill(pppdPid, SIGTERM);
644
645             /* We'll redial when the SIGCHLD arrives, even if PERSIST is
646              * not set (the latter handled by clearing the "we've connected
647              * at least once" flag). */
648             connectedOnce = FALSE;
649
650             /* We don't want to delay before redialing, either, so cut
651              * the retry timeout to zero. */
652             timeout = 0;
653         }
654
655         /* If we got a SIGIO (from netreport, presumably), check if the
656          * interface is up and return zero (via our parent) if it is. */
657         if (theSigio) {
658             theSigio = 0;
659
660             pppLogicalToPhysical(NULL, device, &physicalDevice);
661             if (physicalDevice) {
662                 if (interfaceIsUp(physicalDevice)) {
663                     /* The interface is up, so report a success to a parent if
664                      * we have one.  Any errors after this we just swallow. */
665                     relay_exitcode(0);
666                     connectedOnce = TRUE;
667                 }
668                 free(physicalDevice);
669             }
670         }
671
672         /* If we got a SIGCHLD, then pppd died (possibly because we killed it),
673          * and we need to restart it after timeout seconds. */
674         if (theSigchld) {
675             theSigchld = 0;
676
677             /* Find its pid, which is also its process group ID. */
678             waited = waitpid(-1, &status, 0);
679             if (waited == -1) {
680                 continue;
681             }
682
683             /* Now, we need to kill any children of pppd still in pppd's
684              * process group, in case they are hanging around.
685              * pppd is dead (we just waited for it) but there is no
686              * guarantee that its children are dead, and they will
687              * hold the modem if we do not get rid of them.
688              * We have kept the old pid/pgrp around in pppdPid.  */
689             if (pppdPid) {
690                 kill(-pppdPid, SIGTERM); /* give it a chance to die nicely */
691                 usleep(2500000);
692                 kill(-pppdPid, SIGKILL);
693                 hangup(ifcfg);
694             }
695             pppdPid = 0;
696
697             /* Bail if the child exitted abnormally or we were already
698              * signalled to kill it. */
699             if (!WIFEXITED(status)) {
700                 failureExit(29);
701             }
702             if (dying) {
703                 failureExit(WEXITSTATUS(status));
704             }
705
706             /* Error conditions from which we do not expect to recover
707              * without user intervention -- do not fill up the logs.  */
708             switch (WEXITSTATUS(status)) {
709             case 1: case 2: case 3: case 4: case 6:
710             case 7: case 9: case 14: case 17:
711                 failureExit(WEXITSTATUS(status));
712                 break;
713             default:
714                 break;
715             }
716
717             /* PGB 08/20/02: We no longer retry connecting MAXFAIL
718                times on a failed connect script unless RETRYCONNECT is
719                true. */
720             if ((WEXITSTATUS(status) == 8) &&
721                 !svTrueValue(ifcfg, "RETRYCONNECT", FALSE)) {
722                 failureExit(WEXITSTATUS(status));
723             }
724
725             /* If we've never connected, or PERSIST is set, dial again, up
726              * to MAXFAIL times. */
727             if ((WEXITSTATUS(status) == 8) ||
728                 !connectedOnce ||
729                 svTrueValue(ifcfg, "PERSIST", FALSE)) {
730                 /* If we've been connected (i.e., if we didn't force a redial,
731                  * but the connection went down) wait for DISCONNECTTIMEOUT
732                  * seconds before redialing. */
733                 if (connectedOnce) {
734                     connectedOnce = FALSE;
735                     temp = svGetValue(ifcfg, "DISCONNECTTIMEOUT");
736                     if (temp) {
737                         timeout = atoi(temp);
738                         free(temp);
739                     } else {
740                         timeout = 2;
741                     }
742                 }
743                 sigprocmask(SIG_UNBLOCK, &blockedsigs, NULL);
744                 sleep(timeout);
745                 sigprocmask(SIG_BLOCK, &blockedsigs, NULL);
746                 if (!theSigterm &&
747                     !theSigint &&
748                     !theSighup &&
749                     !theSigio &&
750                     !theSigchld &&
751                     !theSigalrm) {
752                     fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
753                 }
754                 /* Reinitialize the retry timeout. */
755                 temp = svGetValue(ifcfg, "RETRYTIMEOUT");
756                 if (temp) {
757                     timeout = atoi(temp);
758                     free(temp);
759                 } else {
760                     timeout = 30;
761                 }
762 // Scott Sharkey <ssharkey@linux-no-limits.com>
763 // MAXFAIL Patch...
764                 temp = svGetValue(ifcfg, "MAXFAIL");
765                 if (temp) {
766                     maxfail = atoi(temp);
767                     free(temp);
768                 } else {
769                     maxfail = 0;
770                 }
771                 if ( maxfail != 0 ) {
772                     dialCount++;
773                     if ( dialCount >= maxfail )
774                         failureExit(WEXITSTATUS(status));
775                 } 
776             } else {
777                 failureExit(WEXITSTATUS(status));
778             }
779         }
780
781         /* We timed out, and we're running at boot-time. */
782         if (theSigalrm) {
783             failureExit(34);
784         }
785     }
786 }