]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/pppd/main.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / pppd / main.c
1 /*
2  * main.c - Point-to-Point Protocol main module
3  *
4  * Copyright (c) 1989 Carnegie Mellon University.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are permitted
8  * provided that the above copyright notice and this paragraph are
9  * duplicated in all such forms and that any documentation,
10  * advertising materials, and other materials related to such
11  * distribution and use acknowledge that the software was developed
12  * by Carnegie Mellon University.  The name of the
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19
20 #ifndef lint
21 static char rcsid[] = "$FreeBSD$";
22 #endif
23
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <syslog.h>
33 #include <netdb.h>
34 #include <utmp.h>
35 #include <pwd.h>
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <sys/time.h>
40 #include <sys/resource.h>
41 #include <sys/stat.h>
42 #include <sys/socket.h>
43
44 #include "pppd.h"
45 #include "magic.h"
46 #include "fsm.h"
47 #include "lcp.h"
48 #include "ipcp.h"
49 #ifdef INET6
50 #include "ipv6cp.h"
51 #endif
52 #include "upap.h"
53 #include "chap.h"
54 #include "ccp.h"
55 #include "pathnames.h"
56 #include "patchlevel.h"
57
58 #ifdef CBCP_SUPPORT
59 #include "cbcp.h"
60 #endif
61
62 #if defined(SUNOS4)
63 extern char *strerror();
64 #endif
65
66 #ifdef IPX_CHANGE
67 #include "ipxcp.h"
68 #endif /* IPX_CHANGE */
69 #ifdef AT_CHANGE
70 #include "atcp.h"
71 #endif
72
73 /* interface vars */
74 char ifname[32];                /* Interface name */
75 int ifunit;                     /* Interface unit number */
76
77 char *progname;                 /* Name of this program */
78 char hostname[MAXNAMELEN];      /* Our hostname */
79 static char pidfilename[MAXPATHLEN];    /* name of pid file */
80 static char iffilename[MAXPATHLEN];     /* name of if file */
81 static char default_devnam[MAXPATHLEN]; /* name of default device */
82 static pid_t pid;               /* Our pid */
83 static uid_t uid;               /* Our real user-id */
84 time_t          etime,stime;    /* End and Start time */
85 int             minutes;        /* connection duration */
86 static int conn_running;        /* we have a [dis]connector running */
87
88 int ttyfd = -1;                 /* Serial port file descriptor */
89 mode_t tty_mode = -1;           /* Original access permissions to tty */
90 int baud_rate;                  /* Actual bits/second for serial device */
91 int hungup;                     /* terminal has been hung up */
92 int privileged;                 /* we're running as real uid root */
93 int need_holdoff;               /* need holdoff period before restarting */
94 int detached;                   /* have detached from terminal */
95
96 int phase;                      /* where the link is at */
97 int kill_link;
98 int open_ccp_flag;
99
100 char **script_env;              /* Env. variable values for scripts */
101 int s_env_nalloc;               /* # words avail at script_env */
102
103 u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
104 u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
105
106 static int n_children;          /* # child processes still running */
107
108 static int locked;              /* lock() has succeeded */
109
110 char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
111
112 /* Prototypes for procedures local to this file. */
113
114 static void create_pidfile __P((void));
115 static void cleanup __P((void));
116 static void close_tty __P((void));
117 static void get_input __P((void));
118 static void calltimeout __P((void));
119 static struct timeval *timeleft __P((struct timeval *));
120 static void kill_my_pg __P((int));
121 static void hup __P((int));
122 static void term __P((int));
123 static void chld __P((int));
124 static void toggle_debug __P((int));
125 static void open_ccp __P((int));
126 static void bad_signal __P((int));
127 static void holdoff_end __P((void *));
128 static int device_script __P((char *, int, int));
129 static void reap_kids __P((void));
130 static void pr_log __P((void *, char *, ...));
131
132 extern  char    *ttyname __P((int));
133 extern  char    *getlogin __P((void));
134 int main __P((int, char *[]));
135
136 #ifdef ultrix
137 #undef  O_NONBLOCK
138 #define O_NONBLOCK      O_NDELAY
139 #endif
140
141 #ifdef ULTRIX
142 #define setlogmask(x)
143 #endif
144
145 /*
146  * PPP Data Link Layer "protocol" table.
147  * One entry per supported protocol.
148  * The last entry must be NULL.
149  */
150 struct protent *protocols[] = {
151     &lcp_protent,
152     &pap_protent,
153     &chap_protent,
154 #ifdef CBCP_SUPPORT
155     &cbcp_protent,
156 #endif
157     &ipcp_protent,
158 #ifdef INET6
159     &ipv6cp_protent,
160 #endif
161     &ccp_protent,
162 #ifdef IPX_CHANGE
163     &ipxcp_protent,
164 #endif
165 #ifdef AT_CHANGE
166     &atcp_protent,
167 #endif
168     NULL
169 };
170
171 int
172 main(argc, argv)
173     int argc;
174     char *argv[];
175 {
176     int i, n, fdflags;
177     struct sigaction sa;
178     FILE *iffile;
179     char *p;
180     struct passwd *pw;
181     struct timeval timo;
182     sigset_t mask;
183     struct protent *protp;
184     struct stat statbuf;
185     int connect_attempts = 0;
186     char numbuf[16];
187
188     phase = PHASE_INITIALIZE;
189     p = ttyname(0);
190     if (p)
191         strcpy(devnam, p);
192     strcpy(default_devnam, devnam);
193
194     script_env = NULL;
195
196     /* Initialize syslog facilities */
197 #ifdef ULTRIX
198     openlog("pppd", LOG_PID);
199 #else
200     openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
201     setlogmask(LOG_UPTO(LOG_INFO));
202 #endif
203
204     if (gethostname(hostname, MAXNAMELEN) < 0 ) {
205         option_error("Couldn't get hostname: %m");
206         die(1);
207     }
208     hostname[MAXNAMELEN-1] = 0;
209
210     uid = getuid();
211     privileged = uid == 0;
212     sprintf(numbuf, "%d", uid);
213     script_setenv("UID", numbuf);
214
215     /*
216      * Initialize to the standard option set, then parse, in order,
217      * the system options file, the user's options file,
218      * the tty's options file, and the command line arguments.
219      */
220     for (i = 0; (protp = protocols[i]) != NULL; ++i)
221         (*protp->init)(0);
222
223     progname = *argv;
224
225     if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
226         || !options_from_user())
227         exit(1);
228     scan_args(argc-1, argv+1);  /* look for tty name on command line */
229     if (!options_for_tty()
230         || !parse_args(argc-1, argv+1))
231         exit(1);
232
233     /*
234      * Check that we are running as root.
235      */
236     if (geteuid() != 0) {
237         option_error("must be root to run %s, since it is not setuid-root",
238                      argv[0]);
239         die(1);
240     }
241
242     if (!ppp_available()) {
243         option_error(no_ppp_msg);
244         exit(1);
245     }
246
247     /*
248      * Check that the options given are valid and consistent.
249      */
250     sys_check_options();
251     auth_check_options();
252     for (i = 0; (protp = protocols[i]) != NULL; ++i)
253         if (protp->check_options != NULL)
254             (*protp->check_options)();
255     if (demand && connector == 0) {
256         option_error("connect script required for demand-dialling\n");
257         exit(1);
258     }
259
260     script_setenv("DEVICE", devnam);
261     sprintf(numbuf, "%d", baud_rate);
262     script_setenv("SPEED", numbuf);
263
264     /*
265      * If the user has specified the default device name explicitly,
266      * pretend they hadn't.
267      */
268     if (!default_device && strcmp(devnam, default_devnam) == 0)
269         default_device = 1;
270     if (default_device)
271         nodetach = 1;
272
273     /*
274      * Initialize system-dependent stuff and magic number package.
275      */
276     sys_init();
277     magic_init();
278     if (debug)
279         setlogmask(LOG_UPTO(LOG_DEBUG));
280
281     /*
282      * Detach ourselves from the terminal, if required,
283      * and identify who is running us.
284      */
285     if (nodetach == 0)
286         detach();
287     pid = getpid();
288     p = getlogin();
289     stime = time((time_t *) NULL);
290     if (p == NULL) {
291         pw = getpwuid(uid);
292         if (pw != NULL && pw->pw_name != NULL)
293             p = pw->pw_name;
294         else
295             p = "(unknown)";
296     }
297     syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
298            VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
299   
300     /*
301      * Compute mask of all interesting signals and install signal handlers
302      * for each.  Only one signal handler may be active at a time.  Therefore,
303      * all other signals should be masked when any handler is executing.
304      */
305     sigemptyset(&mask);
306     sigaddset(&mask, SIGHUP);
307     sigaddset(&mask, SIGINT);
308     sigaddset(&mask, SIGTERM);
309     sigaddset(&mask, SIGCHLD);
310
311 #define SIGNAL(s, handler)      { \
312         sa.sa_handler = handler; \
313         if (sigaction(s, &sa, NULL) < 0) { \
314             syslog(LOG_ERR, "Couldn't establish signal handler (%d): %m", s); \
315             die(1); \
316         } \
317     }
318
319     sa.sa_mask = mask;
320     sa.sa_flags = 0;
321     SIGNAL(SIGHUP, hup);                /* Hangup */
322     SIGNAL(SIGINT, term);               /* Interrupt */
323     SIGNAL(SIGTERM, term);              /* Terminate */
324     SIGNAL(SIGCHLD, chld);
325
326     SIGNAL(SIGUSR1, toggle_debug);      /* Toggle debug flag */
327     SIGNAL(SIGUSR2, open_ccp);          /* Reopen CCP */
328
329     /*
330      * Install a handler for other signals which would otherwise
331      * cause pppd to exit without cleaning up.
332      */
333     SIGNAL(SIGABRT, bad_signal);
334     SIGNAL(SIGALRM, bad_signal);
335     SIGNAL(SIGFPE, bad_signal);
336     SIGNAL(SIGILL, bad_signal);
337     SIGNAL(SIGPIPE, bad_signal);
338     SIGNAL(SIGQUIT, bad_signal);
339     SIGNAL(SIGSEGV, bad_signal);
340 #ifdef SIGBUS
341     SIGNAL(SIGBUS, bad_signal);
342 #endif
343 #ifdef SIGEMT
344     SIGNAL(SIGEMT, bad_signal);
345 #endif
346 #ifdef SIGPOLL
347     SIGNAL(SIGPOLL, bad_signal);
348 #endif
349 #ifdef SIGPROF
350     SIGNAL(SIGPROF, bad_signal);
351 #endif
352 #ifdef SIGSYS
353     SIGNAL(SIGSYS, bad_signal);
354 #endif
355 #ifdef SIGTRAP
356     SIGNAL(SIGTRAP, bad_signal);
357 #endif
358 #ifdef SIGVTALRM
359     SIGNAL(SIGVTALRM, bad_signal);
360 #endif
361 #ifdef SIGXCPU
362     SIGNAL(SIGXCPU, bad_signal);
363 #endif
364 #ifdef SIGXFSZ
365     SIGNAL(SIGXFSZ, bad_signal);
366 #endif
367
368     /*
369      * Apparently we can get a SIGPIPE when we call syslog, if
370      * syslogd has died and been restarted.  Ignoring it seems
371      * be sufficient.
372      */
373     signal(SIGPIPE, SIG_IGN);
374
375     /*
376      * If we're doing dial-on-demand, set up the interface now.
377      */
378     if (demand) {
379         /*
380          * Open the loopback channel and set it up to be the ppp interface.
381          */
382         open_ppp_loopback();
383
384         syslog(LOG_INFO, "Using interface ppp%d", ifunit);
385         (void) sprintf(ifname, "ppp%d", ifunit);
386         script_setenv("IFNAME", ifname);
387
388         create_pidfile();       /* write pid to file */
389
390         /*
391          * Configure the interface and mark it up, etc.
392          */
393         demand_conf();
394     }
395
396     for (;;) {
397
398         need_holdoff = 1;
399
400         if (demand) {
401             /*
402              * Don't do anything until we see some activity.
403              */
404             phase = PHASE_DORMANT;
405             kill_link = 0;
406             demand_unblock();
407             for (;;) {
408                 wait_loop_output(timeleft(&timo));
409                 calltimeout();
410                 if (kill_link) {
411                     if (!persist)
412                         die(0);
413                     kill_link = 0;
414                 }
415                 if (get_loop_output())
416                     break;
417                 reap_kids();
418             }
419
420             /*
421              * Now we want to bring up the link.
422              */
423             demand_block();
424             syslog(LOG_INFO, "Starting link");
425         }
426
427         /*
428          * Lock the device if we've been asked to.
429          */
430         if (lockflag && !default_device) {
431             if (lock(devnam) < 0)
432                 goto fail;
433             locked = 1;
434         }
435
436         /*
437          * Open the serial device and set it up to be the ppp interface.
438          * First we open it in non-blocking mode so we can set the
439          * various termios flags appropriately.  If we aren't dialling
440          * out and we want to use the modem lines, we reopen it later
441          * in order to wait for the carrier detect signal from the modem.
442          */
443         while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0)) < 0) {
444             if (errno != EINTR)
445                 syslog(LOG_ERR, "Failed to open %s: %m", devnam);
446             if (!persist || errno != EINTR)
447                 goto fail;
448         }
449         if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
450             || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
451             syslog(LOG_WARNING,
452                    "Couldn't reset non-blocking mode on device: %m");
453
454         hungup = 0;
455         kill_link = 0;
456
457         /*
458          * Do the equivalent of `mesg n' to stop broadcast messages.
459          */
460         if (fstat(ttyfd, &statbuf) < 0
461             || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
462             syslog(LOG_WARNING,
463                    "Couldn't restrict write permissions to %s: %m", devnam);
464         } else
465             tty_mode = statbuf.st_mode;
466
467         /* run connection script */
468         if (connector && connector[0]) {
469             MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector));
470
471             /*
472              * Set line speed, flow control, etc.
473              * On most systems we set CLOCAL for now so that we can talk
474              * to the modem before carrier comes up.  But this has the
475              * side effect that we might miss it if CD drops before we
476              * get to clear CLOCAL below.  On systems where we can talk
477              * successfully to the modem with CLOCAL clear and CD down,
478              * we can clear CLOCAL at this point.
479              */
480             set_up_tty(ttyfd, 1);
481
482             /* drop dtr to hang up in case modem is off hook */
483             if (!default_device && modem) {
484                 setdtr(ttyfd, FALSE);
485                 sleep(1);
486                 setdtr(ttyfd, TRUE);
487             }
488
489             if (device_script(connector, ttyfd, ttyfd) < 0) {
490                 syslog(LOG_ERR, "Connect script failed");
491                 setdtr(ttyfd, FALSE);
492                 connect_attempts++;
493                 goto fail;
494             }
495
496
497             syslog(LOG_INFO, "Serial connection established.");
498             sleep(1);           /* give it time to set up its terminal */
499         }
500
501         connect_attempts = 0;   /* we made it through ok */
502
503         /* set line speed, flow control, etc.; clear CLOCAL if modem option */
504         set_up_tty(ttyfd, 0);
505
506         /* reopen tty if necessary to wait for carrier */
507         if (connector == NULL && modem) {
508             while ((i = open(devnam, O_RDWR)) < 0) {
509                 if (errno != EINTR)
510                     syslog(LOG_ERR, "Failed to reopen %s: %m", devnam);
511                 if (!persist || errno != EINTR ||
512                         hungup || kill_link)
513                     goto fail;
514             }
515             close(i);
516         }
517
518         /* run welcome script, if any */
519         if (welcomer && welcomer[0]) {
520             if (device_script(welcomer, ttyfd, ttyfd) < 0)
521                 syslog(LOG_WARNING, "Welcome script failed");
522         }
523
524         /* set up the serial device as a ppp interface */
525         establish_ppp(ttyfd);
526
527         if (!demand) {
528             
529             syslog(LOG_INFO, "Using interface ppp%d", ifunit);
530             (void) sprintf(ifname, "ppp%d", ifunit);
531             
532             create_pidfile();   /* write pid to file */
533
534             /* write interface unit number to file */
535             for (n = strlen(devnam); n > 0 ; n--)
536                     if (devnam[n] == '/') { 
537                             n++;
538                             break;
539                     }
540             (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
541             if ((iffile = fopen(iffilename, "w")) != NULL) {
542                 fprintf(iffile, "ppp%d\n", ifunit);
543                 (void) fclose(iffile);
544             } else {
545                 syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
546                 iffilename[0] = 0;
547             }
548
549             script_setenv("IFNAME", ifname);
550         }
551
552         /*
553          * Start opening the connection and wait for
554          * incoming events (reply, timeout, etc.).
555          */
556         syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam);
557         stime = time((time_t *) NULL);
558         lcp_lowerup(0);
559         lcp_open(0);            /* Start protocol */
560         for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) {
561             wait_input(timeleft(&timo));
562             calltimeout();
563             get_input();
564             if (kill_link) {
565                 lcp_close(0, "User request");
566                 kill_link = 0;
567             }
568             if (open_ccp_flag) {
569                 if (phase == PHASE_NETWORK) {
570                     ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
571                     (*ccp_protent.open)(0);
572                 }
573                 open_ccp_flag = 0;
574             }
575             reap_kids();        /* Don't leave dead kids lying around */
576         }
577
578         /*
579          * If we may want to bring the link up again, transfer
580          * the ppp unit back to the loopback.  Set the
581          * real serial device back to its normal mode of operation.
582          */
583         clean_check();
584         if (demand)
585             restore_loop();
586         disestablish_ppp(ttyfd);
587
588         /*
589          * Run disconnector script, if requested.
590          * XXX we may not be able to do this if the line has hung up!
591          */
592         if (disconnector && !hungup) {
593             set_up_tty(ttyfd, 1);
594             if (device_script(disconnector, ttyfd, ttyfd) < 0) {
595                 syslog(LOG_WARNING, "disconnect script failed");
596             } else {
597                 syslog(LOG_INFO, "Serial link disconnected.");
598             }
599         }
600
601     fail:
602         if (ttyfd >= 0)
603             close_tty();
604         if (locked) {
605             unlock();
606             locked = 0;
607         }
608
609         if (!demand) {
610             if (pidfilename[0] != 0
611                 && unlink(pidfilename) < 0 && errno != ENOENT) 
612                 syslog(LOG_WARNING, "unable to delete pid file: %m");
613             pidfilename[0] = 0;
614
615             if (iffile)
616                     if (unlink(iffilename) < 0 && errno != ENOENT) 
617                             syslog(LOG_WARNING, "unable to delete if file: %m");
618             iffilename[0] = 0;
619         }
620
621         /* limit to retries? */
622         if (max_con_attempts)
623             if (connect_attempts >= max_con_attempts)
624                 break;
625
626         if (!persist)
627             die(1);
628
629         if (demand)
630             demand_discard();
631         if (holdoff > 0 && need_holdoff) {
632             phase = PHASE_HOLDOFF;
633             TIMEOUT(holdoff_end, NULL, holdoff);
634             do {
635                 wait_time(timeleft(&timo));
636                 calltimeout();
637                 if (kill_link) {
638                     if (!persist)
639                         die(0);
640                     kill_link = 0;
641                     phase = PHASE_DORMANT; /* allow signal to end holdoff */
642                 }
643                 reap_kids();
644             } while (phase == PHASE_HOLDOFF);
645         }
646     }
647
648     die(0);
649     return 0;
650 }
651
652 /*
653  * detach - detach us from the controlling terminal.
654  */
655 void
656 detach()
657 {
658     if (detached)
659         return;
660     if (daemon(0, 0) < 0) {
661         perror("Couldn't detach from controlling terminal");
662         die(1);
663     }
664     detached = 1;
665     pid = getpid();
666     /* update pid file if it has been written already */
667     if (pidfilename[0])
668         create_pidfile();
669 }
670
671 /*
672  * Create a file containing our process ID.
673  */
674 static void
675 create_pidfile()
676 {
677     FILE *pidfile;
678
679     (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
680     if ((pidfile = fopen(pidfilename, "w")) != NULL) {
681         fprintf(pidfile, "%d\n", pid);
682         (void) fclose(pidfile);
683     } else {
684         syslog(LOG_ERR, "Failed to create pid file %s: %m", pidfilename);
685         pidfilename[0] = 0;
686     }
687 }
688
689 /*
690  * holdoff_end - called via a timeout when the holdoff period ends.
691  */
692 static void
693 holdoff_end(arg)
694     void *arg;
695 {
696     phase = PHASE_DORMANT;
697 }
698
699 /*
700  * get_input - called when incoming data is available.
701  */
702 static void
703 get_input()
704 {
705     int len, i;
706     u_char *p;
707     u_short protocol;
708     struct protent *protp;
709
710     p = inpacket_buf;   /* point to beginning of packet buffer */
711
712     len = read_packet(inpacket_buf);
713     if (len < 0)
714         return;
715
716     if (len == 0) {
717         etime = time((time_t *) NULL);
718         minutes = (etime-stime)/60;
719         syslog(LOG_NOTICE, "Modem hangup, connected for %d minutes", (minutes >1) ? minutes : 1);
720         hungup = 1;
721         lcp_lowerdown(0);       /* serial link is no longer available */
722         link_terminated(0);
723         return;
724     }
725
726     if (debug /*&& (debugflags & DBG_INPACKET)*/)
727         log_packet(p, len, "rcvd ", LOG_DEBUG);
728
729     if (len < PPP_HDRLEN) {
730         MAINDEBUG((LOG_INFO, "io(): Received short packet."));
731         return;
732     }
733
734     p += 2;                             /* Skip address and control */
735     GETSHORT(protocol, p);
736     len -= PPP_HDRLEN;
737
738     /*
739      * Toss all non-LCP packets unless LCP is OPEN.
740      */
741     if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
742         MAINDEBUG((LOG_INFO,
743                    "get_input: Received non-LCP packet when LCP not open."));
744         return;
745     }
746
747     /*
748      * Until we get past the authentication phase, toss all packets
749      * except LCP, LQR and authentication packets.
750      */
751     if (phase <= PHASE_AUTHENTICATE
752         && !(protocol == PPP_LCP || protocol == PPP_LQR
753              || protocol == PPP_PAP || protocol == PPP_CHAP)) {
754         MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d",
755                    protocol, phase));
756         return;
757     }
758
759     /*
760      * Upcall the proper protocol input routine.
761      */
762     for (i = 0; (protp = protocols[i]) != NULL; ++i) {
763         if (protp->protocol == protocol && protp->enabled_flag) {
764             (*protp->input)(0, p, len);
765             return;
766         }
767         if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
768             && protp->datainput != NULL) {
769             (*protp->datainput)(0, p, len);
770             return;
771         }
772     }
773
774     if (debug)
775         syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol);
776     lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
777 }
778
779
780 /*
781  * quit - Clean up state and exit (with an error indication).
782  */
783 void 
784 quit()
785 {
786     die(1);
787 }
788
789 /*
790  * die - like quit, except we can specify an exit status.
791  */
792 void
793 die(status)
794     int status;
795 {
796     cleanup();
797     syslog(LOG_INFO, "Exit.");
798     exit(status);
799 }
800
801 /*
802  * cleanup - restore anything which needs to be restored before we exit
803  */
804 /* ARGSUSED */
805 static void
806 cleanup()
807 {
808     sys_cleanup();
809
810     if (ttyfd >= 0)
811         close_tty();
812
813     if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) 
814         syslog(LOG_WARNING, "unable to delete pid file: %m");
815     pidfilename[0] = 0;
816
817     if (locked)
818         unlock();
819 }
820
821 /*
822  * close_tty - restore the terminal device and close it.
823  */
824 static void
825 close_tty()
826 {
827     disestablish_ppp(ttyfd);
828
829     /* drop dtr to hang up */
830     if (modem) {
831         setdtr(ttyfd, FALSE);
832         /*
833          * This sleep is in case the serial port has CLOCAL set by default,
834          * and consequently will reassert DTR when we close the device.
835          */
836         sleep(1);
837     }
838
839     restore_tty(ttyfd);
840
841     if (tty_mode != (mode_t) -1)
842         fchmod(ttyfd, tty_mode);
843
844     close(ttyfd);
845     ttyfd = -1;
846 }
847
848
849 struct  callout {
850     struct timeval      c_time;         /* time at which to call routine */
851     void                *c_arg;         /* argument to routine */
852     void                (*c_func) __P((void *)); /* routine */
853     struct              callout *c_next;
854 };
855
856 static struct callout *callout = NULL;  /* Callout list */
857 static struct timeval timenow;          /* Current time */
858
859 /*
860  * timeout - Schedule a timeout.
861  *
862  * Note that this timeout takes the number of seconds, NOT hz (as in
863  * the kernel).
864  */
865 void
866 timeout(func, arg, time)
867     void (*func) __P((void *));
868     void *arg;
869     int time;
870 {
871     struct callout *newp, *p, **pp;
872   
873     MAINDEBUG((LOG_DEBUG, "Timeout %lx:%lx in %d seconds.",
874                (long) func, (long) arg, time));
875   
876     /*
877      * Allocate timeout.
878      */
879     if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) {
880         syslog(LOG_ERR, "Out of memory in timeout()!");
881         die(1);
882     }
883     newp->c_arg = arg;
884     newp->c_func = func;
885     gettimeofday(&timenow, NULL);
886     newp->c_time.tv_sec = timenow.tv_sec + time;
887     newp->c_time.tv_usec = timenow.tv_usec;
888   
889     /*
890      * Find correct place and link it in.
891      */
892     for (pp = &callout; (p = *pp); pp = &p->c_next)
893         if (newp->c_time.tv_sec < p->c_time.tv_sec
894             || (newp->c_time.tv_sec == p->c_time.tv_sec
895                 && newp->c_time.tv_usec < p->c_time.tv_sec))
896             break;
897     newp->c_next = p;
898     *pp = newp;
899 }
900
901
902 /*
903  * untimeout - Unschedule a timeout.
904  */
905 void
906 untimeout(func, arg)
907     void (*func) __P((void *));
908     void *arg;
909 {
910     struct callout **copp, *freep;
911   
912     MAINDEBUG((LOG_DEBUG, "Untimeout %lx:%lx.", (long) func, (long) arg));
913   
914     /*
915      * Find first matching timeout and remove it from the list.
916      */
917     for (copp = &callout; (freep = *copp); copp = &freep->c_next)
918         if (freep->c_func == func && freep->c_arg == arg) {
919             *copp = freep->c_next;
920             (void) free((char *) freep);
921             break;
922         }
923 }
924
925
926 /*
927  * calltimeout - Call any timeout routines which are now due.
928  */
929 static void
930 calltimeout()
931 {
932     struct callout *p;
933
934     while (callout != NULL) {
935         p = callout;
936
937         if (gettimeofday(&timenow, NULL) < 0) {
938             syslog(LOG_ERR, "Failed to get time of day: %m");
939             die(1);
940         }
941         if (!(p->c_time.tv_sec < timenow.tv_sec
942               || (p->c_time.tv_sec == timenow.tv_sec
943                   && p->c_time.tv_usec <= timenow.tv_usec)))
944             break;              /* no, it's not time yet */
945
946         callout = p->c_next;
947         (*p->c_func)(p->c_arg);
948
949         free((char *) p);
950     }
951 }
952
953
954 /*
955  * timeleft - return the length of time until the next timeout is due.
956  */
957 static struct timeval *
958 timeleft(tvp)
959     struct timeval *tvp;
960 {
961     if (callout == NULL)
962         return NULL;
963
964     gettimeofday(&timenow, NULL);
965     tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
966     tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
967     if (tvp->tv_usec < 0) {
968         tvp->tv_usec += 1000000;
969         tvp->tv_sec -= 1;
970     }
971     if (tvp->tv_sec < 0)
972         tvp->tv_sec = tvp->tv_usec = 0;
973
974     return tvp;
975 }
976
977
978 /*
979  * kill_my_pg - send a signal to our process group, and ignore it ourselves.
980  */
981 static void
982 kill_my_pg(sig)
983     int sig;
984 {
985     struct sigaction act, oldact;
986
987     act.sa_handler = SIG_IGN;
988     act.sa_flags = 0;
989     kill(0, sig);
990     sigaction(sig, &act, &oldact);
991     sigaction(sig, &oldact, NULL);
992 }
993
994
995 /*
996  * hup - Catch SIGHUP signal.
997  *
998  * Indicates that the physical layer has been disconnected.
999  * We don't rely on this indication; if the user has sent this
1000  * signal, we just take the link down.
1001  */
1002 static void
1003 hup(sig)
1004     int sig;
1005 {
1006     syslog(LOG_INFO, "Hangup (SIGHUP)");
1007     kill_link = 1;
1008     if (conn_running)
1009         /* Send the signal to the [dis]connector process(es) also */
1010         kill_my_pg(sig);
1011 }
1012
1013
1014 /*
1015  * term - Catch SIGTERM signal and SIGINT signal (^C/del).
1016  *
1017  * Indicates that we should initiate a graceful disconnect and exit.
1018  */
1019 /*ARGSUSED*/
1020 static void
1021 term(sig)
1022     int sig;
1023 {
1024     syslog(LOG_INFO, "Terminating on signal %d.", sig);
1025     persist = 0;                /* don't try to restart */
1026     kill_link = 1;
1027     if (conn_running)
1028         /* Send the signal to the [dis]connector process(es) also */
1029         kill_my_pg(sig);
1030 }
1031
1032
1033 /*
1034  * chld - Catch SIGCHLD signal.
1035  * Calls reap_kids to get status for any dead kids.
1036  */
1037 static void
1038 chld(sig)
1039     int sig;
1040 {
1041     reap_kids();
1042 }
1043
1044
1045 /*
1046  * toggle_debug - Catch SIGUSR1 signal.
1047  *
1048  * Toggle debug flag.
1049  */
1050 /*ARGSUSED*/
1051 static void
1052 toggle_debug(sig)
1053     int sig;
1054 {
1055     debug = !debug;
1056     if (debug) {
1057         setlogmask(LOG_UPTO(LOG_DEBUG));
1058     } else {
1059         setlogmask(LOG_UPTO(LOG_WARNING));
1060     }
1061 }
1062
1063
1064 /*
1065  * open_ccp - Catch SIGUSR2 signal.
1066  *
1067  * Try to (re)negotiate compression.
1068  */
1069 /*ARGSUSED*/
1070 static void
1071 open_ccp(sig)
1072     int sig;
1073 {
1074     open_ccp_flag = 1;
1075 }
1076
1077
1078 /*
1079  * bad_signal - We've caught a fatal signal.  Clean up state and exit.
1080  */
1081 static void
1082 bad_signal(sig)
1083     int sig;
1084 {
1085     static int crashed = 0;
1086
1087     if (crashed)
1088         _exit(127);
1089     crashed = 1;
1090     syslog(LOG_ERR, "Fatal signal %d", sig);
1091     if (conn_running)
1092         kill_my_pg(SIGTERM);
1093     die(1);
1094 }
1095
1096
1097 /*
1098  * device_script - run a program to connect or disconnect the
1099  * serial device.
1100  */
1101 static int
1102 device_script(program, in, out)
1103     char *program;
1104     int in, out;
1105 {
1106     int pid;
1107     int status;
1108     int errfd;
1109
1110     conn_running = 1;
1111     pid = fork();
1112
1113     if (pid < 0) {
1114         conn_running = 0;
1115         syslog(LOG_ERR, "Failed to create child process: %m");
1116         die(1);
1117     }
1118
1119     if (pid == 0) {
1120         sys_close();
1121         closelog();
1122         if (in == out) {
1123             if (in != 0) {
1124                 dup2(in, 0);
1125                 close(in);
1126             }
1127             dup2(0, 1);
1128         } else {
1129             if (out == 0)
1130                 out = dup(out);
1131             if (in != 0) {
1132                 dup2(in, 0);
1133                 close(in);
1134             }
1135             if (out != 1) {
1136                 dup2(out, 1);
1137                 close(out);
1138             }
1139         }
1140         if (nodetach == 0) {
1141             close(2);
1142             errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
1143             if (errfd >= 0 && errfd != 2) {
1144                 dup2(errfd, 2);
1145                 close(errfd);
1146             }
1147         }
1148         setuid(getuid());
1149         setgid(getgid());
1150         execl("/bin/sh", "sh", "-c", program, (char *)0);
1151         syslog(LOG_ERR, "could not exec /bin/sh: %m");
1152         _exit(99);
1153         /* NOTREACHED */
1154     }
1155
1156     while (waitpid(pid, &status, 0) < 0) {
1157         if (errno == EINTR)
1158             continue;
1159         syslog(LOG_ERR, "error waiting for (dis)connection process: %m");
1160         die(1);
1161     }
1162     conn_running = 0;
1163
1164     return (status == 0 ? 0 : -1);
1165 }
1166
1167
1168 /*
1169  * run-program - execute a program with given arguments,
1170  * but don't wait for it.
1171  * If the program can't be executed, logs an error unless
1172  * must_exist is 0 and the program file doesn't exist.
1173  */
1174 int
1175 run_program(prog, args, must_exist)
1176     char *prog;
1177     char **args;
1178     int must_exist;
1179 {
1180     int pid;
1181
1182     pid = fork();
1183     if (pid == -1) {
1184         syslog(LOG_ERR, "Failed to create child process for %s: %m", prog);
1185         return -1;
1186     }
1187     if (pid == 0) {
1188         int new_fd;
1189
1190         /* Leave the current location */
1191         (void) setsid();    /* No controlling tty. */
1192         (void) umask (S_IRWXG|S_IRWXO);
1193         (void) chdir ("/"); /* no current directory. */
1194         setuid(geteuid());
1195         setgid(getegid());
1196
1197         /* Ensure that nothing of our device environment is inherited. */
1198         sys_close();
1199         closelog();
1200         close (0);
1201         close (1);
1202         close (2);
1203         close (ttyfd);  /* tty interface to the ppp device */
1204
1205         /* Don't pass handles to the PPP device, even by accident. */
1206         new_fd = open (_PATH_DEVNULL, O_RDWR);
1207         if (new_fd >= 0) {
1208             if (new_fd != 0) {
1209                 dup2  (new_fd, 0); /* stdin <- /dev/null */
1210                 close (new_fd);
1211             }
1212             dup2 (0, 1); /* stdout -> /dev/null */
1213             dup2 (0, 2); /* stderr -> /dev/null */
1214         }
1215
1216 #ifdef BSD
1217         /* Force the priority back to zero if pppd is running higher. */
1218         if (setpriority (PRIO_PROCESS, 0, 0) < 0)
1219             syslog (LOG_WARNING, "can't reset priority to 0: %m"); 
1220 #endif
1221
1222         /* SysV recommends a second fork at this point. */
1223
1224         /* run the program; give it a null environment */
1225         execve(prog, args, script_env);
1226         if (must_exist || errno != ENOENT)
1227             syslog(LOG_WARNING, "Can't execute %s: %m", prog);
1228         _exit(-1);
1229     }
1230     MAINDEBUG((LOG_DEBUG, "Script %s started; pid = %d", prog, pid));
1231     ++n_children;
1232     return 0;
1233 }
1234
1235
1236 /*
1237  * reap_kids - get status from any dead child processes,
1238  * and log a message for abnormal terminations.
1239  */
1240 static void
1241 reap_kids()
1242 {
1243     int pid, status;
1244
1245     if (n_children == 0)
1246         return;
1247     if ((pid = waitpid(-1, &status, WNOHANG)) == -1) {
1248         if (errno != ECHILD)
1249             syslog(LOG_ERR, "Error waiting for child process: %m");
1250         return;
1251     }
1252     if (pid > 0) {
1253         --n_children;
1254         if (WIFSIGNALED(status)) {
1255             syslog(LOG_WARNING, "Child process %d terminated with signal %d",
1256                    pid, WTERMSIG(status));
1257         }
1258     }
1259 }
1260
1261
1262 /*
1263  * log_packet - format a packet and log it.
1264  */
1265
1266 char line[256];                 /* line to be logged accumulated here */
1267 char *linep;
1268
1269 void
1270 log_packet(p, len, prefix, level)
1271     u_char *p;
1272     int len;
1273     char *prefix;
1274     int level;
1275 {
1276     strcpy(line, prefix);
1277     linep = line + strlen(line);
1278     format_packet(p, len, pr_log, NULL);
1279     if (linep != line)
1280         syslog(level, "%s", line);
1281 }
1282
1283 /*
1284  * format_packet - make a readable representation of a packet,
1285  * calling `printer(arg, format, ...)' to output it.
1286  */
1287 void
1288 format_packet(p, len, printer, arg)
1289     u_char *p;
1290     int len;
1291     void (*printer) __P((void *, char *, ...));
1292     void *arg;
1293 {
1294     int i, n;
1295     u_short proto;
1296     u_char x;
1297     struct protent *protp;
1298
1299     if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
1300         p += 2;
1301         GETSHORT(proto, p);
1302         len -= PPP_HDRLEN;
1303         for (i = 0; (protp = protocols[i]) != NULL; ++i)
1304             if (proto == protp->protocol)
1305                 break;
1306         if (protp != NULL) {
1307             printer(arg, "[%s", protp->name);
1308             n = (*protp->printpkt)(p, len, printer, arg);
1309             printer(arg, "]");
1310             p += n;
1311             len -= n;
1312         } else {
1313             printer(arg, "[proto=0x%x]", proto);
1314         }
1315     }
1316
1317     for (; len > 0; --len) {
1318         GETCHAR(x, p);
1319         printer(arg, " %.2x", x);
1320     }
1321 }
1322
1323 static void
1324 pr_log __V((void *arg, char *fmt, ...))
1325 {
1326     int n;
1327     va_list pvar;
1328     char buf[256];
1329
1330 #if __STDC__
1331     va_start(pvar, fmt);
1332 #else
1333     void *arg;
1334     char *fmt;
1335     va_start(pvar);
1336     arg = va_arg(pvar, void *);
1337     fmt = va_arg(pvar, char *);
1338 #endif
1339
1340     n = vfmtmsg(buf, sizeof(buf), fmt, pvar);
1341     va_end(pvar);
1342
1343     if (linep + n + 1 > line + sizeof(line)) {
1344         syslog(LOG_DEBUG, "%s", line);
1345         linep = line;
1346     }
1347     strcpy(linep, buf);
1348     linep += n;
1349 }
1350
1351 /*
1352  * print_string - print a readable representation of a string using
1353  * printer.
1354  */
1355 void
1356 print_string(p, len, printer, arg)
1357     char *p;
1358     int len;
1359     void (*printer) __P((void *, char *, ...));
1360     void *arg;
1361 {
1362     int c;
1363
1364     printer(arg, "\"");
1365     for (; len > 0; --len) {
1366         c = *p++;
1367         if (' ' <= c && c <= '~') {
1368             if (c == '\\' || c == '"')
1369                 printer(arg, "\\");
1370             printer(arg, "%c", c);
1371         } else {
1372             switch (c) {
1373             case '\n':
1374                 printer(arg, "\\n");
1375                 break;
1376             case '\r':
1377                 printer(arg, "\\r");
1378                 break;
1379             case '\t':
1380                 printer(arg, "\\t");
1381                 break;
1382             default:
1383                 printer(arg, "\\%.3o", c);
1384             }
1385         }
1386     }
1387     printer(arg, "\"");
1388 }
1389
1390 /*
1391  * novm - log an error message saying we ran out of memory, and die.
1392  */
1393 void
1394 novm(msg)
1395     char *msg;
1396 {
1397     syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg);
1398     die(1);
1399 }
1400
1401 /*
1402  * fmtmsg - format a message into a buffer.  Like sprintf except we
1403  * also specify the length of the output buffer, and we handle
1404  * %r (recursive format), %m (error message) and %I (IP address) formats.
1405  * Doesn't do floating-point formats.
1406  * Returns the number of chars put into buf.
1407  */
1408 int
1409 fmtmsg __V((char *buf, int buflen, char *fmt, ...))
1410 {
1411     va_list args;
1412     int n;
1413
1414 #if __STDC__
1415     va_start(args, fmt);
1416 #else
1417     char *buf;
1418     int buflen;
1419     char *fmt;
1420     va_start(args);
1421     buf = va_arg(args, char *);
1422     buflen = va_arg(args, int);
1423     fmt = va_arg(args, char *);
1424 #endif
1425     n = vfmtmsg(buf, buflen, fmt, args);
1426     va_end(args);
1427     return n;
1428 }
1429
1430 /*
1431  * vfmtmsg - like fmtmsg, takes a va_list instead of a list of args.
1432  */
1433 #define OUTCHAR(c)      (buflen > 0? (--buflen, *buf++ = (c)): 0)
1434
1435 int
1436 vfmtmsg(buf, buflen, fmt, args)
1437     char *buf;
1438     int buflen;
1439     char *fmt;
1440     va_list args;
1441 {
1442     int c, i, n;
1443     int width, prec, fillch;
1444     int base, len, neg, quoted;
1445     unsigned long val = 0;
1446     char *str, *f, *buf0;
1447     unsigned char *p;
1448     char num[32];
1449     time_t t;
1450     static char hexchars[] = "0123456789abcdef";
1451
1452     buf0 = buf;
1453     --buflen;
1454     while (buflen > 0) {
1455         for (f = fmt; *f != '%' && *f != 0; ++f)
1456             ;
1457         if (f > fmt) {
1458             len = f - fmt;
1459             if (len > buflen)
1460                 len = buflen;
1461             memcpy(buf, fmt, len);
1462             buf += len;
1463             buflen -= len;
1464             fmt = f;
1465         }
1466         if (*fmt == 0)
1467             break;
1468         c = *++fmt;
1469         width = prec = 0;
1470         fillch = ' ';
1471         if (c == '0') {
1472             fillch = '0';
1473             c = *++fmt;
1474         }
1475         if (c == '*') {
1476             width = va_arg(args, int);
1477             c = *++fmt;
1478         } else {
1479             while (isdigit(c)) {
1480                 width = width * 10 + c - '0';
1481                 c = *++fmt;
1482             }
1483         }
1484         if (c == '.') {
1485             c = *++fmt;
1486             if (c == '*') {
1487                 prec = va_arg(args, int);
1488                 c = *++fmt;
1489             } else {
1490                 while (isdigit(c)) {
1491                     prec = prec * 10 + c - '0';
1492                     c = *++fmt;
1493                 }
1494             }
1495         }
1496         str = 0;
1497         base = 0;
1498         neg = 0;
1499         ++fmt;
1500         switch (c) {
1501         case 'd':
1502             i = va_arg(args, int);
1503             if (i < 0) {
1504                 neg = 1;
1505                 val = -i;
1506             } else
1507                 val = i;
1508             base = 10;
1509             break;
1510         case 'o':
1511             val = va_arg(args, unsigned int);
1512             base = 8;
1513             break;
1514         case 'x':
1515             val = va_arg(args, unsigned int);
1516             base = 16;
1517             break;
1518         case 'p':
1519             val = (unsigned long) va_arg(args, void *);
1520             base = 16;
1521             neg = 2;
1522             break;
1523         case 's':
1524             str = va_arg(args, char *);
1525             break;
1526         case 'c':
1527             num[0] = va_arg(args, int);
1528             num[1] = 0;
1529             str = num;
1530             break;
1531         case 'm':
1532             str = strerror(errno);
1533             break;
1534         case 'I':
1535             str = ip_ntoa(va_arg(args, u_int32_t));
1536             break;
1537         case 'r':
1538             f = va_arg(args, char *);
1539 #if !defined(__powerpc__) && !defined(__amd64__)
1540             n = vfmtmsg(buf, buflen + 1, f, va_arg(args, va_list));
1541 #else
1542             /* On the powerpc, a va_list is an array of 1 structure */
1543             n = vfmtmsg(buf, buflen + 1, f, va_arg(args, void *));
1544 #endif
1545             buf += n;
1546             buflen -= n;
1547             continue;
1548         case 't':
1549             time(&t);
1550             str = ctime(&t);
1551             str += 4;           /* chop off the day name */
1552             str[15] = 0;        /* chop off year and newline */
1553             break;
1554         case 'v':               /* "visible" string */
1555         case 'q':               /* quoted string */
1556             quoted = c == 'q';
1557             p = va_arg(args, unsigned char *);
1558             if (fillch == '0' && prec > 0) {
1559                 n = prec;
1560             } else {
1561                 n = strlen((char *)p);
1562                 if (prec > 0 && prec < n)
1563                     n = prec;
1564             }
1565             while (n > 0 && buflen > 0) {
1566                 c = *p++;
1567                 --n;
1568                 if (!quoted && c >= 0x80) {
1569                     OUTCHAR('M');
1570                     OUTCHAR('-');
1571                     c -= 0x80;
1572                 }
1573                 if (quoted && (c == '"' || c == '\\'))
1574                     OUTCHAR('\\');
1575                 if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
1576                     if (quoted) {
1577                         OUTCHAR('\\');
1578                         switch (c) {
1579                         case '\t':      OUTCHAR('t');   break;
1580                         case '\n':      OUTCHAR('n');   break;
1581                         case '\b':      OUTCHAR('b');   break;
1582                         case '\f':      OUTCHAR('f');   break;
1583                         default:
1584                             OUTCHAR('x');
1585                             OUTCHAR(hexchars[c >> 4]);
1586                             OUTCHAR(hexchars[c & 0xf]);
1587                         }
1588                     } else {
1589                         if (c == '\t')
1590                             OUTCHAR(c);
1591                         else {
1592                             OUTCHAR('^');
1593                             OUTCHAR(c ^ 0x40);
1594                         }
1595                     }
1596                 } else
1597                     OUTCHAR(c);
1598             }
1599             continue;
1600         default:
1601             *buf++ = '%';
1602             if (c != '%')
1603                 --fmt;          /* so %z outputs %z etc. */
1604             --buflen;
1605             continue;
1606         }
1607         if (base != 0) {
1608             str = num + sizeof(num);
1609             *--str = 0;
1610             while (str > num + neg) {
1611                 *--str = hexchars[val % base];
1612                 val = val / base;
1613                 if (--prec <= 0 && val == 0)
1614                     break;
1615             }
1616             switch (neg) {
1617             case 1:
1618                 *--str = '-';
1619                 break;
1620             case 2:
1621                 *--str = 'x';
1622                 *--str = '0';
1623                 break;
1624             }
1625             len = num + sizeof(num) - 1 - str;
1626         } else {
1627             len = strlen(str);
1628             if (prec > 0 && len > prec)
1629                 len = prec;
1630         }
1631         if (width > 0) {
1632             if (width > buflen)
1633                 width = buflen;
1634             if ((n = width - len) > 0) {
1635                 buflen -= n;
1636                 for (; n > 0; --n)
1637                     *buf++ = fillch;
1638             }
1639         }
1640         if (len > buflen)
1641             len = buflen;
1642         memcpy(buf, str, len);
1643         buf += len;
1644         buflen -= len;
1645     }
1646     *buf = 0;
1647     return buf - buf0;
1648 }
1649
1650 /*
1651  * script_setenv - set an environment variable value to be used
1652  * for scripts that we run (e.g. ip-up, auth-up, etc.)
1653  */
1654 void
1655 script_setenv(var, value)
1656     char *var, *value;
1657 {
1658     int vl = strlen(var);
1659     int i;
1660     char *p, *newstring;
1661
1662     newstring = (char *) malloc(vl + strlen(value) + 2);
1663     if (newstring == 0)
1664         return;
1665     strcpy(newstring, var);
1666     newstring[vl] = '=';
1667     strcpy(newstring+vl+1, value);
1668
1669     /* check if this variable is already set */
1670     if (script_env != 0) {
1671         for (i = 0; (p = script_env[i]) != 0; ++i) {
1672             if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
1673                 free(p);
1674                 script_env[i] = newstring;
1675                 return;
1676             }
1677         }
1678     } else {
1679         i = 0;
1680         script_env = (char **) malloc(16 * sizeof(char *));
1681         if (script_env == 0)
1682             return;
1683         s_env_nalloc = 16;
1684     }
1685
1686     /* reallocate script_env with more space if needed */
1687     if (i + 1 >= s_env_nalloc) {
1688         int new_n = i + 17;
1689         char **newenv = (char **) realloc((void *)script_env,
1690                                           new_n * sizeof(char *));
1691         if (newenv == 0)
1692             return;
1693         script_env = newenv;
1694         s_env_nalloc = new_n;
1695     }
1696
1697     script_env[i] = newstring;
1698     script_env[i+1] = 0;
1699 }
1700
1701 /*
1702  * script_unsetenv - remove a variable from the environment
1703  * for scripts.
1704  */
1705 void
1706 script_unsetenv(var)
1707     char *var;
1708 {
1709     int vl = strlen(var);
1710     int i;
1711     char *p;
1712
1713     if (script_env == 0)
1714         return;
1715     for (i = 0; (p = script_env[i]) != 0; ++i) {
1716         if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
1717             free(p);
1718             while ((script_env[i] = script_env[i+1]) != 0)
1719                 ++i;
1720             break;
1721         }
1722     }
1723 }