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