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