]>
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 | * | |
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 |
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; | |
de1fc6ce | 90 | static int pipeArray[2]; |
458f14b7 | 91 | |
de1fc6ce JR |
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; |
de1fc6ce | 96 | static void failureExit(int exitCode); |
458f14b7 AM |
97 | |
98 | static void | |
de1fc6ce | 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 | ||
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). */ | |
115 | static void | |
116 | detach(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 |
187 | static void |
188 | doPidFile(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. */ | |
254 | static int | |
255 | fork_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. */ | |
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 | } | |
458f14b7 | 304 | |
de1fc6ce JR |
305 | /* Unregister with netreport, relay a status byte to the parent, clean up |
306 | * the pid file, and bail. */ | |
458f14b7 | 307 | static void |
de1fc6ce JR |
308 | failureExit(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 | 316 | static void |
de1fc6ce | 317 | signal_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 | 336 | static shvarFile * |
de1fc6ce JR |
337 | shvarfilesGet(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. */ | |
371 | static void | |
372 | pppLogicalToPhysical(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. */ | |
419 | static gboolean | |
420 | interfaceIsUp(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. */ | |
453 | static void | |
458f14b7 AM |
454 | hangup(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 |
479 | int |
480 | main(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 | } |