1 | |
2 | /* |
3 | * kPPP: A pppd Front End for the KDE project |
4 | * |
5 | * $Id$ |
6 | * |
7 | * Copyright (C) 1997,98 Bernd Johannes Wuebben, |
8 | * Mario Weilguni |
9 | * Copyright (C) 1998-2002 Harri Porten <porten@kde.org> |
10 | * |
11 | * |
12 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU Library General Public |
14 | * License as published by the Free Software Foundation; either |
15 | * version 2 of the License, or (at your option) any later version. |
16 | * |
17 | * This program is distributed in the hope that it will be useful, |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
20 | * Library General Public License for more details. |
21 | * |
22 | * You should have received a copy of the GNU Library General Public |
23 | * License along with this program; if not, write to the Free |
24 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
25 | */ |
26 | |
27 | /* A note to developers: |
28 | * |
29 | * Apart from the first dozen lines in main() the following code represents |
30 | * the setuid root part of kppp. So please be careful ! |
31 | * o restrain from using X, Qt or KDE library calls |
32 | * o check for possible buffer overflows |
33 | * o handle requests from the parent process with care. They might be forged. |
34 | * o be paranoid and think twice about everything you change. |
35 | */ |
36 | #include <kdefakes.h> |
37 | #include <config-kppp.h> |
38 | |
39 | #if defined(__osf__) || defined(__SVR4) |
40 | #define _POSIX_PII_SOCKET |
41 | extern "C" int sethostname(char *name, int name_len); |
42 | #if !defined(__osf__) |
43 | extern "C" int _Psendmsg(int, void*, int); |
44 | extern "C" int _Precvmsg(int, void*, int); |
45 | #endif |
46 | #endif |
47 | |
48 | #include "kpppconfig.h" |
49 | |
50 | #include <sys/types.h> |
51 | #include <sys/uio.h> |
52 | #include <sys/stat.h> |
53 | #include <sys/socket.h> |
54 | #include <sys/ioctl.h> |
55 | #include <sys/un.h> |
56 | #include <sys/wait.h> |
57 | #include <sys/param.h> |
58 | |
59 | |
60 | #include <netinet/in.h> |
61 | |
62 | #ifdef __FreeBSD__ |
63 | # include <sys/linker.h> // for kldload |
64 | #endif |
65 | |
66 | #ifndef HAVE_NET_IF_PPP_H |
67 | # if defined(__DragonFly__) |
68 | # include <net/ppp_layer/ppp_defs.h> |
69 | # include <net/if.h> |
70 | # include <net/ppp/if_ppp.h> |
71 | # elif defined HAVE_LINUX_IF_PPP_H |
72 | # include <linux/if_ppp.h> |
73 | # endif |
74 | #else |
75 | # include <net/ppp_defs.h> |
76 | # include <net/if.h> |
77 | # include <net/if_ppp.h> |
78 | #endif |
79 | |
80 | #include <errno.h> |
81 | #include <fcntl.h> |
82 | #include <regex.h> |
83 | #include <signal.h> |
84 | #include <stdio.h> |
85 | #include <stdlib.h> |
86 | #include <string.h> |
87 | #include <unistd.h> |
88 | #include <termios.h> |
89 | |
90 | #include "opener.h" |
91 | #include "devices.h" |
92 | |
93 | #ifdef HAVE_RESOLV_H |
94 | # include <arpa/nameser.h> |
95 | # include <resolv.h> |
96 | #endif |
97 | |
98 | #ifndef _PATH_RESCONF |
99 | #define _PATH_RESCONF "/etc/resolv.conf" |
100 | #endif |
101 | |
102 | #ifdef _XPG4_2 |
103 | extern "C" { |
104 | ssize_t recvmsg(int, struct msghdr *, int); |
105 | ssize_t sendmsg(int, const struct msghdr *, int); |
106 | } |
107 | #endif |
108 | |
109 | #define MY_ASSERT(x) if (!(x)) { \ |
110 | fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \ |
111 | exit(1); } |
112 | |
113 | #define MY_DEBUG |
114 | #ifndef MY_DEBUG |
115 | #define Debug(s) ((void)0); |
116 | #define Debug2(s, i) ((void)0); |
117 | #else |
118 | #define Debug(s) fprintf(stderr, (s "\n")); |
119 | #define Debug2(s, i) fprintf(stderr, (s), (i)); |
120 | #endif |
121 | |
122 | static void sighandler_child(int); |
123 | static pid_t pppdPid = -1; |
124 | static int pppdExitStatus = -1; |
125 | static int checkForInterface(); |
126 | |
127 | // processing will stop at first file that could be opened successfully |
128 | const char * const kppp_syslog[] = { "/var/log/syslog.ppp" , |
129 | "/var/log/syslog" , |
130 | "/var/log/messages" , |
131 | 0 }; |
132 | |
133 | Opener::Opener(int s) : socket(s), ttyfd(-1) { |
134 | lockfile[0] = '\0'; |
135 | signal(SIGUSR1, SIG_IGN); |
136 | signal(SIGTERM, SIG_IGN); |
137 | signal(SIGINT, SIG_IGN); |
138 | signal(SIGCHLD, sighandler_child); |
139 | mainLoop(); |
140 | } |
141 | |
142 | void Opener::mainLoop() { |
143 | |
144 | int len; |
145 | int fd = -1; |
146 | int flags, mode; |
147 | const char *device, * const *logFile; |
148 | union AllRequests request; |
149 | struct ResponseHeader response; |
150 | struct msghdr msg; |
151 | struct iovec iov; |
152 | |
153 | iov.iov_base = IOV_BASE_CAST &request; |
154 | iov.iov_len = sizeof(request); |
155 | |
156 | msg.msg_name = 0L; |
157 | msg.msg_namelen = 0; |
158 | msg.msg_iov = &iov; |
159 | msg.msg_iovlen = 1; |
160 | msg.msg_control = 0L; |
161 | msg.msg_controllen = 0; |
162 | |
163 | // loop forever |
164 | while(1) { |
165 | len = recvmsg(socket, &msg, 0); |
166 | if(len < 0) { |
167 | switch(errno) { |
168 | case EINTR: |
169 | Debug("Opener: interrupted system call, continuing" ); |
170 | break; |
171 | default: |
172 | perror("Opener: error reading from socket" ); |
173 | _exit(1); |
174 | } |
175 | } else { |
176 | switch(request.header.type) { |
177 | |
178 | case OpenDevice: |
179 | Debug("Opener: received OpenDevice" ); |
180 | MY_ASSERT(len == sizeof(struct OpenModemRequest)); |
181 | close(ttyfd); |
182 | device = deviceByIndex(request.modem.deviceNum); |
183 | response.status = 0; |
184 | if ((ttyfd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) == -1) { |
185 | Debug("error opening modem device !" ); |
186 | fd = open(DEVNULL, O_RDONLY); |
187 | response.status = -errno; |
188 | sendFD(fd, &response); |
189 | close(fd); |
190 | } else |
191 | sendFD(ttyfd, &response); |
192 | break; |
193 | |
194 | case OpenLock: |
195 | Debug("Opener: received OpenLock\n" ); |
196 | MY_ASSERT(len == sizeof(struct OpenLockRequest)); |
197 | flags = request.lock.flags; |
198 | MY_ASSERT(flags == O_RDONLY || flags == (O_WRONLY|O_TRUNC|O_CREAT)); |
199 | if(flags == (O_WRONLY|O_TRUNC|O_CREAT)) |
200 | mode = 0644; |
201 | else |
202 | mode = 0; |
203 | |
204 | device = deviceByIndex(request.lock.deviceNum); |
205 | MY_ASSERT(strlen(LOCK_DIR)+strlen(device) < MaxPathLen); |
206 | strlcpy(lockfile, LOCK_DIR"/LCK.." , MaxPathLen); |
207 | strlcat(lockfile, strrchr(device, '/') + 1, MaxPathLen ); |
208 | response.status = 0; |
209 | // TODO: |
210 | // struct stat st; |
211 | // if(stat(lockfile.data(), &st) == -1) { |
212 | // if(errno == EBADF) |
213 | // return -1; |
214 | // } else { |
215 | // // make sure that this is a regular file |
216 | // if(!S_ISREG(st.st_mode)) |
217 | // return -1; |
218 | // } |
219 | if ((fd = open(lockfile, flags, mode)) == -1) { |
220 | Debug("error opening lockfile!" ); |
221 | lockfile[0] = '\0'; |
222 | fd = open(DEVNULL, O_RDONLY); |
223 | response.status = -errno; |
224 | } else |
225 | fchown(fd, 0, 0); |
226 | sendFD(fd, &response); |
227 | close(fd); |
228 | break; |
229 | |
230 | case RemoveLock: |
231 | Debug("Opener: received RemoveLock" ); |
232 | MY_ASSERT(len == sizeof(struct RemoveLockRequest)); |
233 | close(ttyfd); |
234 | ttyfd = -1; |
235 | response.status = unlink(lockfile); |
236 | lockfile[0] = '\0'; |
237 | sendResponse(&response); |
238 | break; |
239 | |
240 | case OpenResolv: |
241 | Debug("Opener: received OpenResolv" ); |
242 | MY_ASSERT(len == sizeof(struct OpenResolvRequest)); |
243 | flags = request.resolv.flags; |
244 | response.status = 0; |
245 | if ((fd = open(_PATH_RESCONF, flags)) == -1) { |
246 | Debug("error opening resolv.conf!" ); |
247 | fd = open(DEVNULL, O_RDONLY); |
248 | response.status = -errno; |
249 | } |
250 | sendFD(fd, &response); |
251 | close(fd); |
252 | break; |
253 | |
254 | case OpenSysLog: |
255 | Debug("Opener: received OpenSysLog" ); |
256 | MY_ASSERT(len == sizeof(struct OpenLogRequest)); |
257 | response.status = 0; |
258 | logFile = &kppp_syslog[0]; |
259 | while (*logFile) { |
260 | if ((fd = open(*logFile, O_RDONLY)) >= 0) |
261 | break; |
262 | logFile++; |
263 | } |
264 | if (!*logFile) { |
265 | Debug("No success opening a syslog file !" ); |
266 | fd = open(DEVNULL, O_RDONLY); |
267 | response.status = -errno; |
268 | } |
269 | sendFD(fd, &response); |
270 | close(fd); |
271 | break; |
272 | |
273 | case SetSecret: |
274 | Debug("Opener: received SetSecret" ); |
275 | MY_ASSERT(len == sizeof(struct SetSecretRequest)); |
276 | response.status = !createAuthFile(request.secret.method, |
277 | request.secret.username, |
278 | request.secret.password); |
279 | sendResponse(&response); |
280 | break; |
281 | |
282 | case RemoveSecret: |
283 | Debug("Opener: received RemoveSecret" ); |
284 | MY_ASSERT(len == sizeof(struct RemoveSecretRequest)); |
285 | response.status = !removeAuthFile(request.remove.method); |
286 | sendResponse(&response); |
287 | break; |
288 | |
289 | case SetHostname: |
290 | Debug("Opener: received SetHostname" ); |
291 | MY_ASSERT(len == sizeof(struct SetHostnameRequest)); |
292 | response.status = 0; |
293 | if(sethostname(request.host.name, strlen(request.host.name))) |
294 | response.status = -errno; |
295 | sendResponse(&response); |
296 | break; |
297 | |
298 | case ExecPPPDaemon: |
299 | Debug("Opener: received ExecPPPDaemon" ); |
300 | MY_ASSERT(len == sizeof(struct ExecDaemonRequest)); |
301 | response.status = execpppd(request.daemon.arguments); |
302 | sendResponse(&response); |
303 | break; |
304 | |
305 | case KillPPPDaemon: |
306 | Debug("Opener: received KillPPPDaemon" ); |
307 | MY_ASSERT(len == sizeof(struct KillDaemonRequest)); |
308 | response.status = killpppd(); |
309 | sendResponse(&response); |
310 | break; |
311 | |
312 | case PPPDExitStatus: |
313 | Debug("Opener: received PPPDExitStatus" ); |
314 | MY_ASSERT(len == sizeof(struct PPPDExitStatusRequest)); |
315 | response.status = pppdExitStatus; |
316 | sendResponse(&response); |
317 | break; |
318 | |
319 | case Stop: |
320 | Debug("Opener: received STOP command" ); |
321 | _exit(0); |
322 | break; |
323 | |
324 | default: |
325 | Debug("Opener: unknown command type. Exiting ..." ); |
326 | _exit(1); |
327 | } |
328 | } // else |
329 | } |
330 | } |
331 | |
332 | |
333 | // |
334 | // Send an open fd over a UNIX socket pair |
335 | // |
336 | int Opener::(int fd, struct ResponseHeader *response) { |
337 | |
338 | struct { struct cmsghdr cmsg; int fd; } control; |
339 | struct msghdr msg; |
340 | struct iovec iov; |
341 | |
342 | msg.msg_name = 0L; |
343 | msg.msg_namelen = 0; |
344 | msg.msg_iov = &iov; |
345 | msg.msg_iovlen = 1; |
346 | |
347 | // Send data |
348 | iov.iov_base = IOV_BASE_CAST response; |
349 | iov.iov_len = sizeof(struct ResponseHeader); |
350 | |
351 | // Send a (duplicate of) the file descriptor |
352 | control.cmsg.cmsg_len = sizeof(struct cmsghdr) + sizeof(int); |
353 | control.cmsg.cmsg_level = SOL_SOCKET; |
354 | control.cmsg.cmsg_type = MY_SCM_RIGHTS; |
355 | |
356 | msg.msg_control = (char *) &control; |
357 | msg.msg_controllen = control.cmsg.cmsg_len; |
358 | |
359 | #ifdef CMSG_DATA |
360 | *((int *)CMSG_DATA(&control.cmsg)) = fd; |
361 | #else |
362 | *((int *) &control.cmsg.cmsg_data) = fd; |
363 | #endif |
364 | |
365 | if (sendmsg(socket, &msg, 0) < 0) { |
366 | perror("unable to send file descriptors" ); |
367 | return -1; |
368 | } |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | int Opener::(struct ResponseHeader *response) { |
374 | |
375 | struct msghdr msg; |
376 | struct iovec iov; |
377 | |
378 | msg.msg_name = 0L; |
379 | msg.msg_namelen = 0; |
380 | msg.msg_iov = &iov; |
381 | msg.msg_iovlen = 1; |
382 | msg.msg_control = 0L; |
383 | msg.msg_controllen = 0; |
384 | |
385 | // Send data |
386 | iov.iov_base = IOV_BASE_CAST response; |
387 | iov.iov_len = sizeof(struct ResponseHeader); |
388 | |
389 | if (sendmsg(socket, &msg, 0) < 0) { |
390 | perror("unable to send response" ); |
391 | return -1; |
392 | } |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | const char* Opener::deviceByIndex(int idx) { |
398 | |
399 | const char *device = 0L; |
400 | |
401 | for(int i = 0; devices[i]; i++) |
402 | if(i == idx) |
403 | device = devices[i]; |
404 | MY_ASSERT(device); |
405 | return device; |
406 | } |
407 | |
408 | bool Opener::createAuthFile(Auth method, char *username, char *password) { |
409 | const char *authfile, *oldName, *newName; |
410 | char line[100]; |
411 | char regexp[2*MaxStrLen+30]; |
412 | regex_t preg; |
413 | |
414 | if(!(authfile = authFile(method))) |
415 | return false; |
416 | |
417 | if(!(newName = authFile(method, New))) |
418 | return false; |
419 | |
420 | // look for username, "username" or 'username' |
421 | // if you modify this RE you have to adapt regexp's size above |
422 | snprintf(regexp, sizeof(regexp), "^[ \t]*%s[ \t]\\|^[ \t]*[\"\']%s[\"\']" , |
423 | username,username); |
424 | MY_ASSERT(regcomp(&preg, regexp, 0) == 0); |
425 | |
426 | // copy to new file pap- or chap-secrets |
427 | int old_umask = umask(0077); |
428 | FILE *fout = fopen(newName, "w" ); |
429 | if(fout) { |
430 | // copy old file |
431 | FILE *fin = fopen(authfile, "r" ); |
432 | if(fin) { |
433 | while(fgets(line, sizeof(line), fin)) { |
434 | if(regexec(&preg, line, 0, 0L, 0) == 0) |
435 | continue; |
436 | fputs(line, fout); |
437 | } |
438 | fclose(fin); |
439 | } |
440 | |
441 | // append user/pass pair |
442 | fprintf(fout, "\"%s\"\t*\t\"%s\"\n" , username, password); |
443 | fclose(fout); |
444 | } |
445 | |
446 | // restore umask |
447 | umask(old_umask); |
448 | |
449 | // free memory allocated by regcomp |
450 | regfree(&preg); |
451 | |
452 | if(!(oldName = authFile(method, Old))) |
453 | return false; |
454 | |
455 | // delete old file if any |
456 | unlink(oldName); |
457 | |
458 | rename(authfile, oldName); |
459 | rename(newName, authfile); |
460 | |
461 | return true; |
462 | } |
463 | |
464 | |
465 | bool Opener::removeAuthFile(Auth method) { |
466 | const char *authfile, *oldName; |
467 | |
468 | if(!(authfile = authFile(method))) |
469 | return false; |
470 | if(!(oldName = authFile(method, Old))) |
471 | return false; |
472 | |
473 | if(access(oldName, F_OK) == 0) { |
474 | unlink(authfile); |
475 | return (rename(oldName, authfile) == 0); |
476 | } else |
477 | return false; |
478 | } |
479 | |
480 | |
481 | const char* Opener::authFile(Auth method, int version) { |
482 | switch(method|version) { |
483 | case PAP|Original: |
484 | return PAP_AUTH_FILE; |
485 | break; |
486 | case PAP|New: |
487 | return PAP_AUTH_FILE".new" ; |
488 | break; |
489 | case PAP|Old: |
490 | return PAP_AUTH_FILE".old" ; |
491 | break; |
492 | case CHAP|Original: |
493 | return CHAP_AUTH_FILE; |
494 | break; |
495 | case CHAP|New: |
496 | return CHAP_AUTH_FILE".new" ; |
497 | break; |
498 | case CHAP|Old: |
499 | return CHAP_AUTH_FILE".old" ; |
500 | break; |
501 | default: |
502 | return 0L; |
503 | } |
504 | } |
505 | |
506 | |
507 | bool Opener::execpppd(const char *arguments) { |
508 | char buf[MAX_CMDLEN]; |
509 | char *args[MaxArgs]; |
510 | pid_t pgrpid; |
511 | |
512 | if(ttyfd<0) |
513 | return false; |
514 | |
515 | pppdExitStatus = -1; |
516 | |
517 | switch(pppdPid = fork()) |
518 | { |
519 | case -1: |
520 | fprintf(stderr,"In parent: fork() failed\n" ); |
521 | return false; |
522 | break; |
523 | |
524 | case 0: |
525 | // let's parse the arguments the user supplied into UNIX suitable form |
526 | // that is a list of pointers each pointing to exactly one word |
527 | strlcpy(buf, arguments, sizeof(buf)); |
528 | parseargs(buf, args); |
529 | // become a session leader and let /dev/ttySx |
530 | // be the controlling terminal. |
531 | pgrpid = setsid(); |
532 | #ifdef TIOCSCTTY |
533 | if(ioctl(ttyfd, TIOCSCTTY, 0)<0) |
534 | fprintf(stderr, "ioctl() failed.\n" ); |
535 | #elif defined (TIOCSPGRP) |
536 | if(ioctl(ttyfd, TIOCSPGRP, &pgrpid)<0) |
537 | fprintf(stderr, "ioctl() failed.\n" ); |
538 | #endif |
539 | if(tcsetpgrp(ttyfd, pgrpid)<0) |
540 | fprintf(stderr, "tcsetpgrp() failed.\n" ); |
541 | |
542 | dup2(ttyfd, 0); |
543 | dup2(ttyfd, 1); |
544 | |
545 | switch (checkForInterface()) { |
546 | case 1: |
547 | fprintf(stderr, "Cannot determine if kernel supports ppp.\n" ); |
548 | break; |
549 | case -1: |
550 | fprintf(stderr, "Kernel does not support ppp, oops.\n" ); |
551 | break; |
552 | case 0: |
553 | fprintf(stderr, "Kernel supports ppp alright.\n" ); |
554 | break; |
555 | } |
556 | |
557 | execve(pppdPath(), args, 0L); |
558 | _exit(0); |
559 | break; |
560 | |
561 | default: |
562 | Debug2("In parent: pppd pid %d\n" ,pppdPid); |
563 | close(ttyfd); |
564 | ttyfd = -1; |
565 | return true; |
566 | break; |
567 | } |
568 | } |
569 | |
570 | |
571 | bool Opener::killpppd()const { |
572 | if(pppdPid > 0) { |
573 | Debug2("In killpppd(): Sending SIGTERM to %d\n" , pppdPid); |
574 | if(kill(pppdPid, SIGTERM) < 0) { |
575 | Debug2("Error terminating %d. Sending SIGKILL\n" , pppdPid); |
576 | if(kill(pppdPid, SIGKILL) < 0) { |
577 | Debug2("Error killing %d\n" , pppdPid); |
578 | return false; |
579 | } |
580 | } |
581 | } |
582 | return true; |
583 | } |
584 | |
585 | |
586 | void Opener::parseargs(char* buf, char** args) { |
587 | int nargs = 0; |
588 | int quotes; |
589 | |
590 | while(nargs < MaxArgs-1 && *buf != '\0') { |
591 | |
592 | quotes = 0; |
593 | |
594 | // Strip whitespace. Use nulls, so that the previous argument is |
595 | // terminated automatically. |
596 | |
597 | while ((*buf == ' ' ) || (*buf == '\t' ) || (*buf == '\n' ) ) |
598 | *buf++ = '\0'; |
599 | |
600 | // detect begin of quoted argument |
601 | if (*buf == '"' || *buf == '\'') { |
602 | quotes = *buf; |
603 | *buf++ = '\0'; |
604 | } |
605 | |
606 | // save the argument |
607 | if(*buf != '\0') { |
608 | *args++ = buf; |
609 | nargs++; |
610 | } |
611 | |
612 | if (!quotes) |
613 | while ((*buf != '\0') && (*buf != '\n') && |
614 | (*buf != '\t') && (*buf != ' ')) |
615 | buf++; |
616 | else { |
617 | while ((*buf != '\0') && (*buf != quotes)) |
618 | buf++; |
619 | *buf++ = '\0'; |
620 | } |
621 | } |
622 | |
623 | *args = 0L; |
624 | } |
625 | |
626 | |
627 | const char* pppdPath() { |
628 | // wasting a few bytes |
629 | static char buffer[sizeof(PPPDSEARCHPATH)+sizeof(PPPDNAME)]; |
630 | static char *pppdPath = 0L; |
631 | char *p; |
632 | |
633 | if(pppdPath == 0L) { |
634 | const char *c = PPPDSEARCHPATH; |
635 | while(*c != '\0') { |
636 | while(*c == ':') |
637 | c++; |
638 | p = buffer; |
639 | while(*c != '\0' && *c != ':') |
640 | *p++ = *c++; |
641 | *p = '\0'; |
642 | strcat(p, "/" ); |
643 | strcat(p, PPPDNAME); |
644 | if(access(buffer, F_OK) == 0) |
645 | return (pppdPath = buffer); |
646 | } |
647 | } |
648 | |
649 | return pppdPath; |
650 | } |
651 | |
652 | int checkForInterface() |
653 | { |
654 | // I don't know if Linux needs more initialization to get the ioctl to |
655 | // work, pppd seems to hint it does. But BSD doesn't, and the following |
656 | // code should compile. |
657 | #if (defined(HAVE_NET_IF_PPP_H) || defined(HAVE_LINUX_IF_PPP_H)) && !defined(__SVR4) |
658 | int s, ok; |
659 | struct ifreq ifr; |
660 | // extern char *no_ppp_msg; |
661 | |
662 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
663 | return 1; /* can't tell */ |
664 | |
665 | strlcpy(ifr.ifr_name, "ppp0" , sizeof (ifr.ifr_name)); |
666 | ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0; |
667 | close(s); |
668 | |
669 | if (ok == -1) { |
670 | // This is ifdef'd FreeBSD, because FreeBSD is the only BSD that supports |
671 | // KLDs, the old LKM interface couldn't handle loading devices |
672 | // dynamically, and thus can't load ppp support on the fly |
673 | #ifdef __FreeBSD__ |
674 | // If we failed to load ppp support and don't have it already. |
675 | if (kldload("if_ppp" ) == -1) { |
676 | return -1; |
677 | } |
678 | return 0; |
679 | #else |
680 | return -1; |
681 | #endif |
682 | } |
683 | return 0; |
684 | #else |
685 | // We attempt to use the SunOS/SysVr4 method and stat /dev/ppp |
686 | struct stat buf; |
687 | |
688 | memset(&buf, 0, sizeof(buf)); |
689 | return stat("/dev/ppp" , &buf); |
690 | #endif |
691 | } |
692 | |
693 | |
694 | void sighandler_child(int) { |
695 | pid_t pid; |
696 | int status; |
697 | |
698 | signal(SIGCHLD, sighandler_child); |
699 | if(pppdPid>0) { |
700 | pid = waitpid(pppdPid, &status, WNOHANG); |
701 | if(pid != pppdPid) { |
702 | fprintf(stderr, "received SIGCHLD from unknown origin.\n" ); |
703 | } else { |
704 | Debug("It was pppd that died" ); |
705 | pppdPid = -1; |
706 | if((WIFEXITED(status))) { |
707 | pppdExitStatus = (WEXITSTATUS(status)); |
708 | Debug2("pppd exited with return value %d\n" , pppdExitStatus); |
709 | } else { |
710 | pppdExitStatus = 99; |
711 | Debug("pppd exited abnormally." ); |
712 | } |
713 | Debug2("Sending %i a SIGUSR1\n" , getppid()); |
714 | kill(getppid(), SIGUSR1); |
715 | } |
716 | } else |
717 | fprintf(stderr, "received unexpected SIGCHLD.\n" ); |
718 | } |
719 | |