]> git.pld-linux.org Git - projects/rc-scripts.git/blame - src/ppp-watch.c
- fixed lang.csh
[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 *
9 * Copyright 1999 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
79#include "shvar.h"
80
81static int theSigterm = 0;
82static int theSigint = 0;
83static int theSighup = 0;
84static int theSigio = 0;
85static int theSigchld = 0;
86static int theSigalrm = 0;
87
88static int theChild;
89
90
91static void
92cleanExit(int exitCode);
93
94
95
96static void
97forward_signal(int signo) {
98 kill(theChild, SIGINT);
99}
100
101
102
103
104static void
105set_signal(int signo, void (*handler)(int)) {
106 struct sigaction act;
107
108 act.sa_handler = handler;
109 act.sa_flags = SA_RESTART;
110 sigemptyset(&act.sa_mask);
111 sigaction(signo, &act, NULL);
112}
113
114
115
116static void
117detach(int now, int parentExitCode, char *device) {
118 static int pipeArray[2];
119 char exitCode;
120
121 if (now) {
122 /* execute -- ignore errors in case called more than once */
123 exitCode = parentExitCode;
124 write(pipeArray[1], &exitCode, 1);
125 close(pipeArray[1]);
126
127 } else {
128 /* set up */
129 int child;
130
131 if (pipe(pipeArray)) exit (25);
132
133 child = fork();
134 if (child < 0) exit(26);
135 if (child) {
136 /* parent process */
137 close (pipeArray[1]);
138
139 /* forward likely signals to the main process; we will
140 * react later
141 */
142 theChild = child;
143 set_signal(SIGINT, forward_signal);
144 set_signal(SIGTERM, forward_signal);
145 set_signal(SIGHUP, forward_signal);
146
147 while (read (pipeArray[0], &exitCode, 1) < 0) {
148 switch (errno) {
149 case EINTR: continue;
150 default: exit (27); /* this will catch EIO in particular */
151 }
152 }
153 switch (exitCode) {
154 case 0:
155 break;
156 case 33:
157 fprintf(stderr, "%s already up, initiating redial\n", device);
158 break;
159 case 34:
160 fprintf(stderr, "Failed to activate %s, retrying in the background\n", device);
161 break;
162 default:
163 fprintf(stderr, "Failed to activate %s with error %d\n", device, exitCode);
164 break;
165 }
166 exit(exitCode);
167
168 } else {
169 /* child process */
170 close (pipeArray[0]);
171 /* become a daemon */
172 close (0);
173 close (1);
174 close (2);
175 setsid();
176 setpgid(0, 0);
177 }
178 }
179}
180
181
182
183static void
184doPidFile(char *device) {
185 static char *pidFileName = NULL;
186 char *pidFilePath;
187 int fd; FILE *f;
188 pid_t pid = 0;
189
190 if (pidFileName) {
191 /* remove it */
192 pidFilePath = alloca(strlen(pidFileName) + 25);
193 sprintf(pidFilePath, "/var/run/pppwatch-%s.pid", pidFileName);
194 unlink(pidFilePath); /* not much we can do in case of error... */
195 }
196
197 if (device) {
198 /* create it */
199 pidFileName = device;
200 pidFilePath = alloca(strlen(pidFileName) + 25);
201 sprintf(pidFilePath, "/var/run/pppwatch-%s.pid", pidFileName);
202restart:
203 fd = open(pidFilePath, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL,
204 S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH);
205
206 if (fd == -1) {
207 /* file already existed, or terrible things have happened... */
208 fd = open(pidFilePath, O_RDONLY);
209 if (fd == -1)
210 cleanExit(36); /* terrible things have happened */
211 /* already running, send a SIGHUP (we presume that they
212 * are calling ifup for a reason, so they probably want
213 * to redial) and then exit cleanly and let things go
214 * on in the background
215 */
216 f = fdopen(fd, "r");
217 if (!f) cleanExit(37);
218 fscanf(f, "%d", &pid);
219 fclose(f);
220 if (pid) {
221 if (kill(pid, SIGHUP)) {
222 unlink(pidFilePath);
223 goto restart;
224 } else {
225 cleanExit(33);
226 }
227 }
228 }
229
230 f = fdopen(fd, "w");
231 if (!f)
232 cleanExit(31);
233 fprintf(f, "%d\n", getpid());
234 fclose(f);
235 }
236}
237
238
239
240
241
242int
243fork_exec(int wait, char *path, char *arg1, char *arg2, char *arg3)
244{
245 pid_t child;
246 int status;
247
248 sigset_t sigs;
249
250 if (!(child = fork())) {
251 /* child */
252
253 /* don't leave signals blocked for pppd */
254 sigemptyset(&sigs);
255 sigprocmask(SIG_SETMASK, &sigs, NULL);
256
257 if (!wait) {
258 /* make sure that pppd is in its own process group */
259 setsid();
260 setpgid(0, 0);
261 }
262
263 execl(path, path, arg1, arg2, arg3, NULL);
264 perror(path);
265 _exit (1);
266 }
267
268 if (wait) {
269 wait4 (child, &status, 0, NULL);
270 if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
271 return 0;
272 } else {
273 return 1;
274 }
275 } else {
276 return 0;
277 }
278}
279
280
281
282static void
283cleanExit(int exitCode) {
284 fork_exec(1, "/sbin/netreport", "-r", NULL, NULL);
285 detach(1, exitCode, NULL);
286 doPidFile(NULL);
287 exit(exitCode);
288}
289
290
291
292static void
293signal_handler (int signum) {
294 switch(signum) {
295 case SIGTERM:
296 theSigterm = 1; break;
297 case SIGINT:
298 theSigint = 1; break;
299 case SIGHUP:
300 theSighup = 1; break;
301 case SIGIO:
302 theSigio = 1; break;
303 case SIGCHLD:
304 theSigchld = 1; break;
305 case SIGALRM:
306 theSigalrm = 1; break;
307 }
308}
309
310
311static shvarFile *
312shvarfilesGet(char *interfaceName) {
313 shvarFile *ifcfg;
314 char *ifcfgName, *ifcfgParentName, *ifcfgParentDiff;
315 static char ifcfgPrefix[] = "/etc/sysconfig/network-scripts/ifcfg-";
316
317 ifcfgName = alloca(sizeof(ifcfgPrefix)+strlen(interfaceName)+1);
318 sprintf(ifcfgName, "%s%s", ifcfgPrefix, interfaceName);
319 ifcfg = svNewFile(ifcfgName);
320 if (!ifcfg) return NULL;
321
322 /* Do we have a parent interface to inherit? */
323 ifcfgParentDiff = strchr(interfaceName, '-');
324 if (ifcfgParentDiff) {
325 /* allocate more than enough memory... */
326 ifcfgParentName = alloca(sizeof(ifcfgPrefix)+strlen(interfaceName)+1);
327 strcpy(ifcfgParentName, ifcfgPrefix);
328 strncat(ifcfgParentName, interfaceName, ifcfgParentDiff-interfaceName);
329 ifcfg->parent = svNewFile(ifcfgParentName);
330 }
331
332 /* don't keep the file descriptors around, they can become
333 * stdout for children
334 */
335 close (ifcfg->fd); ifcfg->fd = 0;
336 if (ifcfg->parent) {
337 close (ifcfg->parent->fd); ifcfg->parent->fd = 0;
338 }
339
340 return ifcfg;
341}
342
343
344
345static char *
346pppLogicalToPhysical(int *pppdPid, char *logicalName) {
347 char *mapFileName;
348 char buffer[20]; /* more than enough space for ppp<n> */
349 char *p, *q;
350 int f, n;
351 char *physicalDevice = NULL;
352
353 mapFileName = alloca (strlen(logicalName)+20);
354 sprintf(mapFileName, "/var/run/ppp-%s.pid", logicalName);
355 if ((f = open(mapFileName, O_RDONLY)) >= 0) {
356 n = read(f, buffer, 20);
357 if (n > 0) {
358 buffer[n] = '\0';
359 /* get past pid */
360 p = buffer; while (*p && *p != '\n') p++; *p = '\0'; p++;
361 if (pppdPid) *pppdPid = atoi(buffer);
362 /* get rid of \n */
363 q = p; while (*q && *q != '\n' && q < buffer+n) q++; *q = '\0';
364 if (*p) physicalDevice = strdup(p);
365 }
366 close(f);
367 }
368
369 return physicalDevice;
370}
371
372
373static int
374interfaceStatus(char *device) {
375 int sock = 0;
376 int pfs[] = {AF_INET, AF_IPX, AF_AX25, AF_APPLETALK, 0};
377 int p = 0;
378 struct ifreq ifr;
379 int retcode = 0;
380
381 while (!sock && pfs[p]) {
382 sock = socket(pfs[p++], SOCK_DGRAM, 0);
383 }
384 if (!sock) return 0;
385
386 memset(&ifr, 0, sizeof(ifr));
387 strncpy(ifr.ifr_name, device, IFNAMSIZ);
388
389 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
390 retcode = 0;
391 } else if (ifr.ifr_flags & IFF_UP) {
392 retcode = 1;
393 }
394
395 close(sock);
396 return retcode;
397}
398
399
400/* very, very minimal hangup function. This is just to attempt to
401 * hang up a device that should already be hung up, so it does not
402 * need to be bulletproof.
403 */
404void
405hangup(shvarFile *ifcfg) {
406 int fd;
407 char *filename;
408 struct termios ots, ts;
409
410 filename = svGetValue(ifcfg, "MODEMPORT");
411 if (!filename) return;
412 fd = open(filename, O_RDWR|O_NOCTTY|O_NONBLOCK);
413 if (fd == -1) goto clean;
414 if (tcgetattr(fd, &ts)) goto clean;
415 ots = ts;
416 write(fd, "\r", 1); /* tickle modems that do not like dropped DTR */
417 usleep(1000);
418 cfsetospeed(&ts, B0);
419 tcsetattr(fd, TCSANOW, &ts);
420 usleep(100000);
421 tcsetattr(fd, TCSANOW, &ots);
422
423clean:
424 free(filename);
425}
426
427
428
429
430
431
432
433
434
435int
436main(int argc, char **argv) {
437 int status, waited;
438 char *device, *real_device, *physicalDevice = NULL;
439 char *theBoot = NULL;
440 shvarFile *ifcfg;
441 sigset_t sigs;
442 int pppdPid = 0;
443 int timeout = 30;
444 char *temp;
445 struct timeval tv;
446 int dieing = 0;
447 int sendsig;
448 int connectedOnce = 0;
449
450 if (argc < 2) {
451 fprintf (stderr, "usage: ppp-watch [ifcfg-]<logical-name> [boot]\n");
452 exit(30);
453 }
454
455 if (!strncmp(argv[1], "ifcfg-", 6)) {
456 device = argv[1] + 6;
457 } else {
458 device = argv[1];
459 }
460
461 detach(0, 0, device); /* prepare */
462
463 if (argc > 2 && !strcmp("boot", argv[2])) {
464 theBoot = argv[2];
465 }
466
467 ifcfg = shvarfilesGet(device);
468 if (!ifcfg) cleanExit(28);
469
470 real_device = svGetValue(ifcfg, "DEVICE");
471 if (!real_device) real_device = device;
472
473 doPidFile(real_device);
474
475 set_signal(SIGTERM, signal_handler);
476 set_signal(SIGINT, signal_handler);
477 set_signal(SIGHUP, signal_handler);
478 set_signal(SIGIO, signal_handler);
479 set_signal(SIGCHLD, signal_handler);
480 if (theBoot) {
481 set_signal(SIGALRM, signal_handler);
482 alarm(30);
483 }
484
485 fork_exec(1, "/sbin/netreport", NULL, NULL, NULL);
486 theSigchld = 0;
487
488 /* don't set up the procmask until after we have received the netreport
489 * signal
490 */
491 sigemptyset(&sigs);
492 sigaddset(&sigs, SIGTERM);
493 sigaddset(&sigs, SIGINT);
494 sigaddset(&sigs, SIGHUP);
495 sigaddset(&sigs, SIGIO);
496 sigaddset(&sigs, SIGCHLD);
497 if (theBoot) sigaddset(&sigs, SIGALRM);
498 sigprocmask(SIG_BLOCK, &sigs, NULL);
499
500 /* prepare for sigsuspend later */
501 sigfillset(&sigs);
502 sigdelset(&sigs, SIGTERM);
503 sigdelset(&sigs, SIGINT);
504 sigdelset(&sigs, SIGHUP);
505 sigdelset(&sigs, SIGIO);
506 sigdelset(&sigs, SIGCHLD);
507 if (theBoot) sigdelset(&sigs, SIGALRM);
508
509 fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device, theBoot);
510 temp = svGetValue(ifcfg, "RETRYTIMEOUT");
511 if (temp) {
512 timeout = atoi(temp);
513 free(temp);
514 } else {
515 timeout = 30;
516 }
517
518 while (1) {
519 sigsuspend(&sigs);
520
521 if (theSigterm || theSigint) {
522 theSigterm = theSigint = 0;
523
524 if (dieing) sendsig = SIGKILL;
525 else sendsig = SIGTERM;
526 dieing = 1;
527
528 if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
529 physicalDevice = pppLogicalToPhysical(&pppdPid, real_device);
530 if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
531 if (!pppdPid) cleanExit(35);
532 kill(pppdPid, sendsig);
533 if (sendsig == SIGKILL) {
534 kill(-pppdPid, SIGTERM); /* give it a chance to die nicely */
535 usleep(2500000);
536 kill(-pppdPid, sendsig);
537 hangup(ifcfg);
538 cleanExit(32);
539 }
540 }
541
542 if (theSighup) {
543 theSighup = 0;
544 if (ifcfg->parent) svCloseFile(ifcfg->parent);
545 svCloseFile(ifcfg);
546 ifcfg = shvarfilesGet(device);
547 physicalDevice = pppLogicalToPhysical(&pppdPid, real_device);
548 if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
549 kill(pppdPid, SIGTERM);
550 /* redial when SIGCHLD arrives, even if !PERSIST */
551 connectedOnce = 0;
552 timeout = 0; /* redial immediately */
553 }
554
555 if (theSigio) {
556 theSigio = 0;
557 if (connectedOnce) {
558 if (physicalDevice) { free(physicalDevice); physicalDevice = NULL; }
559 temp = svGetValue(ifcfg, "DISCONNECTTIMEOUT");
560 if (temp) {
561 timeout = atoi(temp);
562 free(temp);
563 } else {
564 timeout = 2;
565 }
566 }
567 physicalDevice = pppLogicalToPhysical(NULL, real_device);
568 if (physicalDevice) {
569 if (interfaceStatus(physicalDevice)) {
570 /* device is up */
571 detach(1, 0, NULL);
572 connectedOnce = 1;
573 }
574 }
575 }
576
577 if (theSigchld) {
578 theSigchld = 0;
579 waited = wait3(&status, 0, NULL);
580 if (waited < 0) continue;
581
582 /* now, we need to kill any children of pppd still in pppd's
583 * process group, in case they are hanging around.
584 * pppd is dead (we just waited for it) but there is no
585 * guarantee that its children are dead, and they will
586 * hold the modem if we do not get rid of them.
587 * We have kept the old pid/pgrp around in pppdPid.
588 */
589 if (pppdPid) {
590 kill(-pppdPid, SIGTERM); /* give it a chance to die nicely */
591 usleep(2500000);
592 kill(-pppdPid, SIGKILL);
593 hangup(ifcfg);
594 }
595 pppdPid = 0;
596
597 if (!WIFEXITED(status)) cleanExit(29);
598 if (dieing) cleanExit(WEXITSTATUS(status));
599
600 /* error conditions from which we do not expect to recover
601 * without user intervention -- do not fill up the logs.
602 */
603 switch (WEXITSTATUS(status)) {
604 case 1: case 2: case 3: case 4: case 6:
605 case 7: case 9: case 14: case 17:
606 cleanExit(WEXITSTATUS(status));
607 break;
608 default:
609 break;
610 }
611
612 if ((WEXITSTATUS(status) == 8) ||
613 !connectedOnce || svTrueValue(ifcfg, "PERSIST", 0)) {
614 temp = svGetValue(ifcfg, "RETRYTIMEOUT");
615 if (temp) {
616 timeout = atoi(temp);
617 free(temp);
618 } else {
619 timeout = 30;
620 }
621 if (connectedOnce) {
622 memset(&tv, 0, sizeof(tv));
623 tv.tv_sec = timeout;
624 select(0, NULL, NULL, NULL, &tv);
625 }
626 fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device, theBoot);
627 } else {
628 cleanExit(WEXITSTATUS(status));
629 }
630 }
631
632 if (theSigalrm) {
633 detach(1, 34, NULL);
634 }
635 }
636}
This page took 0.095413 seconds and 4 git commands to generate.