]> git.pld-linux.org Git - projects/rc-scripts.git/blame - src/ppp-watch.c
- todo from arekm
[projects/rc-scripts.git] / src / ppp-watch.c
CommitLineData
458f14b7
AM
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 *
00196ec7 9 * Copyright 1999-2003 Red Hat, Inc.
458f14b7
AM
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>
de1fc6ce 78#include <glib.h>
458f14b7
AM
79#include "shvar.h"
80
ec9b27c1 81#define IFCFGPREFIX "/etc/sysconfig/interfaces/ifcfg-"
de1fc6ce
JR
82#define IFUP_PPP "/etc/sysconfig/network-scripts/ifup-ppp"
83
458f14b7
AM
84static int theSigterm = 0;
85static int theSigint = 0;
86static int theSighup = 0;
87static int theSigio = 0;
88static int theSigchld = 0;
89static int theSigalrm = 0;
de1fc6ce 90static int pipeArray[2];
458f14b7 91
de1fc6ce
JR
92// patch to respect the maxfail parameter to ppp
93// Scott Sharkey <ssharkey@linux-no-limits.com>
94static int dialCount = 0;
458f14b7 95static int theChild;
de1fc6ce 96static void failureExit(int exitCode);
458f14b7
AM
97
98static void
de1fc6ce 99interrupt_child(int signo) {
458f14b7
AM
100 kill(theChild, SIGINT);
101}
102
458f14b7
AM
103static void
104set_signal(int signo, void (*handler)(int)) {
105 struct sigaction act;
458f14b7
AM
106 act.sa_handler = handler;
107 act.sa_flags = SA_RESTART;
108 sigemptyset(&act.sa_mask);
109 sigaction(signo, &act, NULL);
110}
111
de1fc6ce
JR
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;
458f14b7 120
de1fc6ce
JR
121 if (pipe(pipeArray) == -1)
122 exit(25);
458f14b7 123
de1fc6ce
JR
124 childpid = fork();
125 if (childpid == -1)
126 exit(26);
458f14b7 127
de1fc6ce
JR
128 if (childpid != 0) {
129 /* The parent only cares about notifications from the child. */
130 close (pipeArray[1]);
458f14b7 131
de1fc6ce
JR
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 */
458f14b7 143 }
de1fc6ce
JR
144 }
145 switch (exitCode) {
146 case 0:
147 break;
458f14b7 148 case 33:
de1fc6ce
JR
149 fprintf(stderr, "%s already up, initiating redial\n", device);
150 break;
458f14b7 151 case 34:
de1fc6ce
JR
152 fprintf(stderr, "Failed to activate %s, retrying in the background\n", device);
153 break;
458f14b7 154 default:
de1fc6ce
JR
155 fprintf(stderr, "Failed to activate %s with error %d\n", device, exitCode);
156 break;
157 }
158 exit(exitCode);
458f14b7 159 }
458f14b7 160
de1fc6ce
JR
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
00196ec7
AM
165 /* Don't leak this into programs we call. */
166 fcntl(pipeArray[1], F_SETFD, FD_CLOEXEC);
167
de1fc6ce
JR
168 /* Redirect stdio to /dev/null. */
169 fd = open("/dev/null", O_RDONLY);
170 dup2(fd, STDIN_FILENO);
171 close(fd);
458f14b7 172
de1fc6ce
JR
173 fd = open("/dev/null", O_WRONLY);
174 dup2(fd, STDOUT_FILENO);
175 dup2(fd, STDERR_FILENO);
176 close(fd);
458f14b7 177
de1fc6ce
JR
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. */
458f14b7
AM
187static void
188doPidFile(char *device) {
de1fc6ce
JR
189 static char pidFilePath[PATH_MAX] = "";
190 int fd = -1;
191 FILE *f = NULL;
458f14b7
AM
192 pid_t pid = 0;
193
de1fc6ce
JR
194 if (device == NULL) {
195 /* Remove an existing pid file -- we're exiting. */
196 if(strlen(pidFilePath) > 0) {
197 unlink(pidFilePath);
458f14b7 198 }
de1fc6ce
JR
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);
458f14b7
AM
242
243 f = fdopen(fd, "w");
de1fc6ce
JR
244 if(f == NULL)
245 failureExit(31);
458f14b7
AM
246 fprintf(f, "%d\n", getpid());
247 fclose(f);
248 }
249}
250
de1fc6ce
JR
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. */
254static int
255fork_exec(gboolean reap, char *path, char *arg1, char *arg2, char *arg3)
458f14b7 256{
de1fc6ce 257 pid_t childpid;
458f14b7
AM
258 int status;
259
260 sigset_t sigs;
261
de1fc6ce
JR
262 childpid = fork();
263 if (childpid == -1)
264 exit(26);
458f14b7 265
de1fc6ce
JR
266 if (childpid == 0) {
267 /* Do the exec magic. Prepare by clearing the signal mask for pppd. */
458f14b7
AM
268 sigemptyset(&sigs);
269 sigprocmask(SIG_SETMASK, &sigs, NULL);
270
de1fc6ce
JR
271 if (!reap) {
272 /* Make sure that the pppd is the leader for its process group. */
458f14b7
AM
273 setsid();
274 setpgid(0, 0);
275 }
276
277 execl(path, path, arg1, arg2, arg3, NULL);
278 perror(path);
279 _exit (1);
280 }
281
de1fc6ce
JR
282 if (reap) {
283 waitpid (childpid, &status, 0);
458f14b7
AM
284 if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
285 return 0;
286 } else {
287 return 1;
288 }
289 } else {
290 return 0;
291 }
292}
293
de1fc6ce
JR
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. */
296static void
297relay_exitcode(unsigned char code)
298{
299 unsigned char exitCode;
300 exitCode = code;
301 write(pipeArray[1], &exitCode, 1);
302 close(pipeArray[1]);
303}
458f14b7 304
de1fc6ce
JR
305/* Unregister with netreport, relay a status byte to the parent, clean up
306 * the pid file, and bail. */
458f14b7 307static void
de1fc6ce
JR
308failureExit(int exitCode) {
309 fork_exec(TRUE, "/sbin/netreport", "-r", NULL, NULL);
310 relay_exitcode(exitCode);
458f14b7
AM
311 doPidFile(NULL);
312 exit(exitCode);
313}
314
de1fc6ce 315/* Keeps track of which signals we've seen so far. */
458f14b7 316static void
de1fc6ce 317signal_tracker (int signum) {
458f14b7 318 switch(signum) {
de1fc6ce
JR
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;
458f14b7
AM
331 }
332}
333
de1fc6ce
JR
334/* Return a shvarFile for this interface, taking into account one level of
335 * inheritance (eeewww). */
458f14b7 336static shvarFile *
de1fc6ce
JR
337shvarfilesGet(const char *interfaceName) {
338 shvarFile *ifcfg = NULL;
339 char ifcfgName[PATH_MAX];
340 char *ifcfgParentDiff = NULL;
458f14b7 341
de1fc6ce
JR
342 /* Start with the basic configuration. */
343 snprintf(ifcfgName, sizeof(ifcfgName), "%s%s", IFCFGPREFIX, interfaceName);
458f14b7 344 ifcfg = svNewFile(ifcfgName);
de1fc6ce
JR
345 if (ifcfg == NULL)
346 return NULL;
458f14b7 347
de1fc6ce
JR
348 /* Do we have a parent interface (i.e., for ppp0-blah, ppp0) to inherit? */
349 ifcfgParentDiff = strchr(ifcfgName + sizeof(IFCFGPREFIX), '-');
458f14b7 350 if (ifcfgParentDiff) {
de1fc6ce
JR
351 *ifcfgParentDiff = '\0';
352 ifcfg->parent = svNewFile(ifcfgName);
458f14b7
AM
353 }
354
de1fc6ce
JR
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
458f14b7 361 if (ifcfg->parent) {
de1fc6ce
JR
362 close (ifcfg->parent->fd);
363 ifcfg->parent->fd = 0;
458f14b7
AM
364 }
365
366 return ifcfg;
367}
368
de1fc6ce
JR
369/* Convert a logical interface name to a real one by reading the lock
370 * file created by pppd. */
371static void
372pppLogicalToPhysical(int *pppdPid, char *logicalName, char **physicalName) {
373 char mapFileName[PATH_MAX];
374 char buffer[20];
458f14b7 375 char *p, *q;
de1fc6ce 376 int fd, n;
458f14b7
AM
377 char *physicalDevice = NULL;
378
de1fc6ce
JR
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);
458f14b7
AM
385 if (n > 0) {
386 buffer[n] = '\0';
de1fc6ce
JR
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 }
458f14b7 401 }
458f14b7
AM
402 }
403
de1fc6ce
JR
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 }
458f14b7
AM
415}
416
de1fc6ce
JR
417/* Return a boolean value indicating if the interface is up. If not, or
418 * if we don't know, return FALSE. */
419static gboolean
420interfaceIsUp(char *device) {
421 int sock = -1;
422 int family[] = {PF_INET, PF_IPX, PF_AX25, PF_APPLETALK, 0};
458f14b7
AM
423 int p = 0;
424 struct ifreq ifr;
de1fc6ce 425 gboolean retcode = FALSE;
458f14b7 426
de1fc6ce
JR
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);
458f14b7 430 }
de1fc6ce
JR
431 if (sock == -1)
432 return FALSE;
458f14b7 433
de1fc6ce 434 /* Populate the request structure for getting the interface's status. */
458f14b7 435 memset(&ifr, 0, sizeof(ifr));
de1fc6ce
JR
436 strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
437 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
458f14b7 438
00196ec7 439 /* We return TRUE iff the ioctl succeeded and the interface is UP. */
de1fc6ce
JR
440 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
441 retcode = FALSE;
458f14b7 442 } else if (ifr.ifr_flags & IFF_UP) {
de1fc6ce 443 retcode = TRUE;
458f14b7
AM
444 }
445
446 close(sock);
de1fc6ce 447
458f14b7
AM
448 return retcode;
449}
450
de1fc6ce
JR
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. */
453static void
458f14b7
AM
454hangup(shvarFile *ifcfg) {
455 int fd;
de1fc6ce
JR
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);
458f14b7
AM
477}
478
458f14b7
AM
479int
480main(int argc, char **argv) {
de1fc6ce
JR
481 int status;
482 pid_t waited;
458f14b7 483 char *device, *real_device, *physicalDevice = NULL;
de1fc6ce 484 char *boot = NULL;
458f14b7 485 shvarFile *ifcfg;
de1fc6ce 486 sigset_t blockedsigs, unblockedsigs;
458f14b7
AM
487 int pppdPid = 0;
488 int timeout = 30;
489 char *temp;
de1fc6ce 490 gboolean dying = FALSE;
458f14b7 491 int sendsig;
de1fc6ce
JR
492 gboolean connectedOnce = FALSE;
493 int maxfail = 0; // MAXFAIL Patch <ssharkey@linux-no-limits.com>
458f14b7
AM
494
495 if (argc < 2) {
00196ec7 496 fprintf (stderr, "usage: ppp-watch <interface-name> [boot]\n");
458f14b7
AM
497 exit(30);
498 }
499
de1fc6ce 500 if (strncmp(argv[1], "ifcfg-", 6) == 0) {
458f14b7
AM
501 device = argv[1] + 6;
502 } else {
503 device = argv[1];
504 }
505
de1fc6ce
JR
506 detach(device); /* Prepare a child process to monitor pppd. When we
507 return, we'll be in the child. */
458f14b7 508
de1fc6ce
JR
509 if ((argc > 2) && (strcmp("boot", argv[2]) == 0)) {
510 boot = argv[2];
458f14b7
AM
511 }
512
513 ifcfg = shvarfilesGet(device);
de1fc6ce
JR
514 if (ifcfg == NULL)
515 failureExit(28);
458f14b7
AM
516
517 real_device = svGetValue(ifcfg, "DEVICE");
de1fc6ce
JR
518 if (real_device == NULL)
519 real_device = device;
458f14b7
AM
520
521 doPidFile(real_device);
522
de1fc6ce
JR
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);
458f14b7
AM
543 }
544
de1fc6ce
JR
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. */
458f14b7
AM
549 theSigchld = 0;
550
de1fc6ce
JR
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. */
458f14b7
AM
576 temp = svGetValue(ifcfg, "RETRYTIMEOUT");
577 if (temp) {
578 timeout = atoi(temp);
579 free(temp);
580 } else {
581 timeout = 30;
582 }
583
de1fc6ce
JR
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 }
458f14b7 597
de1fc6ce 598 /* If we got SIGTERM or SIGINT, give up and hang up. */
458f14b7
AM
599 if (theSigterm || theSigint) {
600 theSigterm = theSigint = 0;
601
de1fc6ce
JR
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. */
00196ec7 612 pppLogicalToPhysical(&pppdPid, device, NULL);
de1fc6ce
JR
613
614 /* We don't know what our child pid is. This is very confusing. */
615 if (!pppdPid) {
616 failureExit(35);
617 }
458f14b7 618
de1fc6ce 619 /* Die, pppd, die. */
458f14b7
AM
620 kill(pppdPid, sendsig);
621 if (sendsig == SIGKILL) {
de1fc6ce
JR
622 kill(-pppdPid, SIGTERM); /* Give it a chance to die nicely, then
623 kill its whole process group. */
458f14b7
AM
624 usleep(2500000);
625 kill(-pppdPid, sendsig);
626 hangup(ifcfg);
de1fc6ce 627 failureExit(32);
458f14b7
AM
628 }
629 }
630
de1fc6ce 631 /* If we got SIGHUP, reload and redial. */
458f14b7
AM
632 if (theSighup) {
633 theSighup = 0;
de1fc6ce
JR
634
635 /* Free and reload the configuration structure. */
636 if (ifcfg->parent)
637 svCloseFile(ifcfg->parent);
458f14b7
AM
638 svCloseFile(ifcfg);
639 ifcfg = shvarfilesGet(device);
de1fc6ce
JR
640
641 /* Get the PID of our child pppd. */
00196ec7 642 pppLogicalToPhysical(&pppdPid, device, NULL);
458f14b7 643 kill(pppdPid, SIGTERM);
de1fc6ce
JR
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;
458f14b7
AM
653 }
654
de1fc6ce
JR
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. */
458f14b7
AM
657 if (theSigio) {
658 theSigio = 0;
de1fc6ce 659
00196ec7 660 pppLogicalToPhysical(NULL, device, &physicalDevice);
458f14b7 661 if (physicalDevice) {
de1fc6ce
JR
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;
458f14b7 667 }
de1fc6ce 668 free(physicalDevice);
458f14b7
AM
669 }
670 }
671
de1fc6ce
JR
672 /* If we got a SIGCHLD, then pppd died (possibly because we killed it),
673 * and we need to restart it after timeout seconds. */
458f14b7
AM
674 if (theSigchld) {
675 theSigchld = 0;
458f14b7 676
de1fc6ce
JR
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
458f14b7
AM
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.
de1fc6ce 688 * We have kept the old pid/pgrp around in pppdPid. */
458f14b7
AM
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
de1fc6ce
JR
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 }
458f14b7 705
de1fc6ce
JR
706 /* Error conditions from which we do not expect to recover
707 * without user intervention -- do not fill up the logs. */
458f14b7
AM
708 switch (WEXITSTATUS(status)) {
709 case 1: case 2: case 3: case 4: case 6:
710 case 7: case 9: case 14: case 17:
de1fc6ce 711 failureExit(WEXITSTATUS(status));
458f14b7
AM
712 break;
713 default:
714 break;
715 }
716
00196ec7
AM
717 /* PGB 08/20/02: We no longer retry connecting MAXFAIL
718 times on a failed connect script unless RETRYCONNECT is
719 true. */
de1fc6ce 720 if ((WEXITSTATUS(status) == 8) &&
00196ec7 721 !svTrueValue(ifcfg, "RETRYCONNECT", FALSE)) {
de1fc6ce
JR
722 failureExit(WEXITSTATUS(status));
723 }
724
725 /* If we've never connected, or PERSIST is set, dial again, up
726 * to MAXFAIL times. */
458f14b7 727 if ((WEXITSTATUS(status) == 8) ||
de1fc6ce
JR
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 &&
00196ec7 747 !theSigint &&
de1fc6ce
JR
748 !theSighup &&
749 !theSigio &&
750 !theSigchld &&
751 !theSigalrm) {
752 fork_exec(FALSE, IFUP_PPP, "daemon", device, boot);
753 }
754 /* Reinitialize the retry timeout. */
458f14b7
AM
755 temp = svGetValue(ifcfg, "RETRYTIMEOUT");
756 if (temp) {
757 timeout = atoi(temp);
758 free(temp);
759 } else {
760 timeout = 30;
761 }
de1fc6ce
JR
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;
458f14b7 770 }
de1fc6ce
JR
771 if ( maxfail != 0 ) {
772 dialCount++;
00196ec7 773 if ( dialCount >= maxfail )
de1fc6ce 774 failureExit(WEXITSTATUS(status));
de1fc6ce 775 }
458f14b7 776 } else {
de1fc6ce 777 failureExit(WEXITSTATUS(status));
458f14b7
AM
778 }
779 }
780
de1fc6ce 781 /* We timed out, and we're running at boot-time. */
458f14b7 782 if (theSigalrm) {
de1fc6ce 783 failureExit(34);
458f14b7
AM
784 }
785 }
786}
This page took 0.171973 seconds and 4 git commands to generate.