main.c revision 1.44
1/* $OpenBSD: main.c,v 1.44 2004/02/09 19:59:15 canacar 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#ifndef lint 46#if 0 47static char rcsid[] = "Id: main.c,v 1.49 1998/05/05 05:24:17 paulus Exp $"; 48#else 49static char rcsid[] = "$OpenBSD: main.c,v 1.44 2004/02/09 19:59:15 canacar Exp $"; 50#endif 51#endif 52 53#include <stdio.h> 54#include <ctype.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58#include <signal.h> 59#include <errno.h> 60#include <fcntl.h> 61#include <syslog.h> 62#include <netdb.h> 63#include <utmp.h> 64#include <pwd.h> 65#include <sys/param.h> 66#include <sys/types.h> 67#include <sys/wait.h> 68#include <sys/time.h> 69#include <sys/resource.h> 70#include <sys/stat.h> 71#include <sys/socket.h> 72#include <net/if.h> 73 74#include "pppd.h" 75#include "magic.h" 76#include "fsm.h" 77#include "lcp.h" 78#include "ipcp.h" 79#include "upap.h" 80#include "chap.h" 81#include "ccp.h" 82#include "pathnames.h" 83#include "patchlevel.h" 84 85#ifdef CBCP_SUPPORT 86#include "cbcp.h" 87#endif 88 89#if defined(SUNOS4) 90extern char *strerror(); 91#endif 92 93#ifdef IPX_CHANGE 94#include "ipxcp.h" 95#endif /* IPX_CHANGE */ 96#ifdef AT_CHANGE 97#include "atcp.h" 98#endif 99 100/* interface vars */ 101char ifname[IFNAMSIZ]; /* Interface name */ 102int ifunit; /* Interface unit number */ 103 104char *progname; /* Name of this program */ 105char hostname[MAXHOSTNAMELEN]; /* Our hostname */ 106static char pidfilename[MAXPATHLEN]; /* name of pid file */ 107static char default_devnam[MAXPATHLEN]; /* name of default device */ 108static pid_t pid; /* Our pid */ 109static uid_t uid; /* Our real user-id */ 110static int conn_running; /* we have a [dis]connector running */ 111static int crashed = 0; 112 113int ttyfd = -1; /* Serial port file descriptor */ 114mode_t tty_mode = -1; /* Original access permissions to tty */ 115int baud_rate; /* Actual bits/second for serial device */ 116int hungup; /* terminal has been hung up */ 117int privileged; /* we're running as real uid root */ 118int need_holdoff; /* need holdoff period before restarting */ 119int detached; /* have detached from terminal */ 120 121int phase; /* where the link is at */ 122int kill_link; 123int open_ccp_flag; 124 125char **script_env; /* Env. variable values for scripts */ 126int s_env_nalloc; /* # words avail at script_env */ 127 128u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */ 129u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */ 130 131static int n_children; /* # child processes still running */ 132 133static int locked; /* lock() has succeeded */ 134 135char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n"; 136 137/* Prototypes for procedures local to this file. */ 138 139static void create_pidfile(void); 140static void cleanup(void); 141static void close_tty(void); 142static void get_input(void); 143static void calltimeout(void); 144static struct timeval *timeleft(struct timeval *); 145static void kill_my_pg(int); 146static void hup(int); 147static void term(int); 148static void chld(int); 149static void toggle_debug(int); 150static void open_ccp(int); 151static void bad_signal(int); 152static void holdoff_end(void *); 153static int device_script(char *, int, int); 154static void reap_kids(void); 155static void pr_log(void *, char *, ...); 156 157extern char *ttyname(int); 158extern char *getlogin(void); 159int main(int, char *[]); 160 161#ifdef ultrix 162#undef O_NONBLOCK 163#define O_NONBLOCK O_NDELAY 164#endif 165 166#ifdef ULTRIX 167#define setlogmask(x) 168#endif 169 170/* 171 * PPP Data Link Layer "protocol" table. 172 * One entry per supported protocol. 173 * The last entry must be NULL. 174 */ 175struct protent *protocols[] = { 176 &lcp_protent, 177 &pap_protent, 178 &chap_protent, 179#ifdef CBCP_SUPPORT 180 &cbcp_protent, 181#endif 182 &ipcp_protent, 183 &ccp_protent, 184#ifdef IPX_CHANGE 185 &ipxcp_protent, 186#endif 187#ifdef AT_CHANGE 188 &atcp_protent, 189#endif 190 NULL 191}; 192 193int 194main(argc, argv) 195 int argc; 196 char *argv[]; 197{ 198 int i, fdflags; 199 struct sigaction sa; 200 char *p; 201 struct passwd *pw; 202 struct timeval timo; 203 sigset_t mask; 204 struct protent *protp; 205 struct stat statbuf; 206 char numbuf[16]; 207 208 phase = PHASE_INITIALIZE; 209 p = ttyname(0); 210 if (p) 211 strlcpy(devnam, p, MAXPATHLEN); 212 strlcpy(default_devnam, devnam, sizeof default_devnam); 213 214 script_env = NULL; 215 216 /* Initialize syslog facilities */ 217#ifdef ULTRIX 218 openlog("pppd", LOG_PID); 219#else 220 openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); 221 setlogmask(LOG_UPTO(LOG_INFO)); 222#endif 223 224 if (gethostname(hostname, sizeof hostname) < 0 ) { 225 option_error("Couldn't get hostname: %m"); 226 die(1); 227 } 228 229 uid = getuid(); 230 privileged = uid == 0; 231 snprintf(numbuf, sizeof numbuf, "%u", uid); 232 script_setenv("UID", numbuf); 233 234 /* 235 * Initialize to the standard option set, then parse, in order, 236 * the system options file, the user's options file, 237 * the tty's options file, and the command line arguments. 238 */ 239 for (i = 0; (protp = protocols[i]) != NULL; ++i) 240 (*protp->init)(0); 241 242 progname = *argv; 243 244 if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1) 245 || !options_from_user()) 246 exit(1); 247 scan_args(argc-1, argv+1); /* look for tty name on command line */ 248 if (!options_for_tty() 249 || !parse_args(argc-1, argv+1)) 250 exit(1); 251 252 /* 253 * Check that we are running as root. 254 */ 255 if (geteuid() != 0) { 256 option_error("must be root to run %s, since it is not setuid-root", 257 argv[0]); 258 die(1); 259 } 260 261 if (!ppp_available()) { 262 option_error(no_ppp_msg); 263 exit(1); 264 } 265 266 /* 267 * Check that the options given are valid and consistent. 268 */ 269 sys_check_options(); 270 auth_check_options(); 271 for (i = 0; (protp = protocols[i]) != NULL; ++i) 272 if (protp->check_options != NULL) 273 (*protp->check_options)(); 274 if (demand && connector == 0) { 275 option_error("connect script required for demand-dialling\n"); 276 exit(1); 277 } 278 279 script_setenv("DEVICE", devnam); 280 snprintf(numbuf, sizeof numbuf, "%d", baud_rate); 281 script_setenv("SPEED", numbuf); 282 283 /* 284 * If the user has specified the default device name explicitly, 285 * pretend they hadn't. 286 */ 287 if (!default_device && strcmp(devnam, default_devnam) == 0) 288 default_device = 1; 289 if (default_device) 290 nodetach = 1; 291 292 /* 293 * Initialize system-dependent stuff and magic number package. 294 */ 295 sys_init(); 296 magic_init(); 297 if (debug) 298 setlogmask(LOG_UPTO(LOG_DEBUG)); 299 300 /* 301 * Detach ourselves from the terminal, if required, 302 * and identify who is running us. 303 */ 304 if (nodetach == 0) 305 detach(); 306 pid = getpid(); 307 p = getlogin(); 308 if (p == NULL) { 309 pw = getpwuid(uid); 310 if (pw != NULL && pw->pw_name != NULL) 311 p = pw->pw_name; 312 else 313 p = "(unknown)"; 314 } 315 syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %u", 316 VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid); 317 318 /* 319 * Compute mask of all interesting signals and install signal handlers 320 * for each. Only one signal handler may be active at a time. Therefore, 321 * all other signals should be masked when any handler is executing. 322 */ 323 sigemptyset(&mask); 324 sigaddset(&mask, SIGHUP); 325 sigaddset(&mask, SIGINT); 326 sigaddset(&mask, SIGTERM); 327 sigaddset(&mask, SIGCHLD); 328 329#define SIGNAL(s, handler) { \ 330 sa.sa_handler = handler; \ 331 if (sigaction(s, &sa, NULL) < 0) { \ 332 syslog(LOG_ERR, "Couldn't establish signal handler (%d): %m", s); \ 333 die(1); \ 334 } \ 335 } 336 337 sa.sa_mask = mask; 338 sa.sa_flags = 0; 339 SIGNAL(SIGHUP, hup); /* Hangup */ 340 SIGNAL(SIGINT, term); /* Interrupt */ 341 SIGNAL(SIGTERM, term); /* Terminate */ 342 SIGNAL(SIGCHLD, chld); 343 344 SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */ 345 SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */ 346 347 /* 348 * Install a handler for other signals which would otherwise 349 * cause pppd to exit without cleaning up. 350 */ 351 SIGNAL(SIGABRT, bad_signal); 352 SIGNAL(SIGALRM, bad_signal); 353 SIGNAL(SIGFPE, bad_signal); 354 SIGNAL(SIGILL, bad_signal); 355 SIGNAL(SIGPIPE, bad_signal); 356 SIGNAL(SIGQUIT, bad_signal); 357#if SIGSEGV_CHECK 358 SIGNAL(SIGSEGV, bad_signal); 359#endif 360#ifdef SIGBUS 361 SIGNAL(SIGBUS, bad_signal); 362#endif 363#ifdef SIGEMT 364 SIGNAL(SIGEMT, bad_signal); 365#endif 366#ifdef SIGPOLL 367 SIGNAL(SIGPOLL, bad_signal); 368#endif 369#ifdef SIGPROF 370 SIGNAL(SIGPROF, bad_signal); 371#endif 372#ifdef SIGSYS 373 SIGNAL(SIGSYS, bad_signal); 374#endif 375#ifdef SIGTRAP 376 SIGNAL(SIGTRAP, bad_signal); 377#endif 378#ifdef SIGVTALRM 379 SIGNAL(SIGVTALRM, bad_signal); 380#endif 381#ifdef SIGXCPU 382 SIGNAL(SIGXCPU, bad_signal); 383#endif 384#ifdef SIGXFSZ 385 SIGNAL(SIGXFSZ, bad_signal); 386#endif 387 388 /* 389 * Apparently we can get a SIGPIPE when we call syslog, if 390 * syslogd has died and been restarted. Ignoring it seems 391 * be sufficient. 392 */ 393 signal(SIGPIPE, SIG_IGN); 394 395 /* 396 * If we're doing dial-on-demand, set up the interface now. 397 */ 398 if (demand) { 399 /* 400 * Open the loopback channel and set it up to be the ppp interface. 401 */ 402 open_ppp_loopback(); 403 404 syslog(LOG_INFO, "Using interface ppp%d", ifunit); 405 (void) snprintf(ifname, sizeof ifname, "ppp%d", ifunit); 406 script_setenv("IFNAME", ifname); 407 408 create_pidfile(); /* write pid to file */ 409 410 /* 411 * Configure the interface and mark it up, etc. 412 */ 413 demand_conf(); 414 } 415 416 for (;;) { 417 418 need_holdoff = 1; 419 420 if (demand) { 421 /* 422 * Don't do anything until we see some activity. 423 */ 424 phase = PHASE_DORMANT; 425 kill_link = 0; 426 demand_unblock(); 427 for (;;) { 428 wait_loop_output(timeleft(&timo)); 429 calltimeout(); 430 if (kill_link) { 431 if (!persist) 432 die(0); 433 kill_link = 0; 434 } 435 if (get_loop_output()) 436 break; 437 reap_kids(); 438 } 439 440 /* 441 * Now we want to bring up the link. 442 */ 443 demand_drop(); 444 syslog(LOG_INFO, "Starting link"); 445 } 446 447 /* 448 * Lock the device if we've been asked to. 449 */ 450 if (lockflag && !default_device) { 451 if (lock(devnam) < 0) 452 goto fail; 453 locked = 1; 454 } 455 456 /* 457 * Open the serial device and set it up to be the ppp interface. 458 * First we open it in non-blocking mode so we can set the 459 * various termios flags appropriately. If we aren't dialling 460 * out and we want to use the modem lines, we reopen it later 461 * in order to wait for the carrier detect signal from the modem. 462 */ 463 while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0)) < 0) { 464 if (errno != EINTR) 465 syslog(LOG_ERR, "Failed to open %s: %m", devnam); 466 if (!persist || errno != EINTR) 467 goto fail; 468 } 469 if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 470 || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) 471 syslog(LOG_WARNING, 472 "Couldn't reset non-blocking mode on device: %m"); 473 474 hungup = 0; 475 kill_link = 0; 476 477 /* 478 * Do the equivalent of `mesg n' to stop broadcast messages. 479 */ 480 if (fstat(ttyfd, &statbuf) < 0 481 || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { 482 syslog(LOG_WARNING, 483 "Couldn't restrict write permissions to %s: %m", devnam); 484 } else 485 tty_mode = statbuf.st_mode; 486 487 /* run connection script */ 488 if (connector && connector[0]) { 489 MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector)); 490 491 /* 492 * Set line speed, flow control, etc. 493 * On most systems we set CLOCAL for now so that we can talk 494 * to the modem before carrier comes up. But this has the 495 * side effect that we might miss it if CD drops before we 496 * get to clear CLOCAL below. On systems where we can talk 497 * successfully to the modem with CLOCAL clear and CD down, 498 * we can clear CLOCAL at this point. 499 */ 500 set_up_tty(ttyfd, (modem_chat == 0)); 501 502 /* drop dtr to hang up in case modem is off hook */ 503 if (!default_device && modem) { 504 setdtr(ttyfd, FALSE); 505 sleep(1); 506 setdtr(ttyfd, TRUE); 507 } 508 509 if (device_script(connector, ttyfd, ttyfd) < 0) { 510 syslog(LOG_ERR, "Connect script failed"); 511 setdtr(ttyfd, FALSE); 512 goto fail; 513 } 514 515 syslog(LOG_INFO, "Serial connection established."); 516 sleep(1); /* give it time to set up its terminal */ 517 } 518 519 set_up_tty(ttyfd, 0); 520 521 /* reopen tty if necessary to wait for carrier */ 522 if (connector == NULL && modem) { 523 while ((i = open(devnam, O_RDWR)) < 0) { 524 if (errno != EINTR) 525 syslog(LOG_ERR, "Failed to reopen %s: %m", devnam); 526 if (!persist || errno != EINTR || hungup || kill_link) 527 goto fail; 528 } 529 close(i); 530 } 531 532 /* run welcome script, if any */ 533 if (welcomer && welcomer[0]) { 534 if (device_script(welcomer, ttyfd, ttyfd) < 0) 535 syslog(LOG_WARNING, "Welcome script failed"); 536 } 537 538 /* set up the serial device as a ppp interface */ 539 establish_ppp(ttyfd); 540 541 if (!demand) { 542 543 syslog(LOG_INFO, "Using interface ppp%d", ifunit); 544 (void) snprintf(ifname, sizeof ifname, "ppp%d", ifunit); 545 script_setenv("IFNAME", ifname); 546 547 create_pidfile(); /* write pid to file */ 548 } 549 550 /* 551 * Start opening the connection and wait for 552 * incoming events (reply, timeout, etc.). 553 */ 554 syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam); 555 lcp_lowerup(0); 556 lcp_open(0); /* Start protocol */ 557 for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) { 558 wait_input(timeleft(&timo)); 559 calltimeout(); 560 get_input(); 561 if (kill_link) { 562 lcp_close(0, "User request"); 563 kill_link = 0; 564 } 565 if (open_ccp_flag) { 566 if (phase == PHASE_NETWORK) { 567 ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ 568 (*ccp_protent.open)(0); 569 } 570 open_ccp_flag = 0; 571 } 572 reap_kids(); /* Don't leave dead kids lying around */ 573 } 574 575 /* 576 * If we may want to bring the link up again, transfer 577 * the ppp unit back to the loopback. Set the 578 * real serial device back to its normal mode of operation. 579 */ 580 clean_check(); 581 if (demand) 582 restore_loop(); 583 disestablish_ppp(ttyfd); 584 585 /* 586 * Run disconnector script, if requested. 587 * XXX we may not be able to do this if the line has hung up! 588 */ 589 if (disconnector && !hungup) { 590 set_up_tty(ttyfd, 1); 591 if (device_script(disconnector, ttyfd, ttyfd) < 0) { 592 syslog(LOG_WARNING, "disconnect script failed"); 593 } else { 594 syslog(LOG_INFO, "Serial link disconnected."); 595 } 596 } 597 598 fail: 599 if (ttyfd >= 0) 600 close_tty(); 601 if (locked) { 602 unlock(); 603 locked = 0; 604 } 605 606 if (!demand) { 607 if (pidfilename[0] != 0 608 && unlink(pidfilename) < 0 && errno != ENOENT) 609 syslog(LOG_WARNING, "unable to delete pid file: %m"); 610 pidfilename[0] = 0; 611 } 612 613 if (!persist) 614 die(1); 615 616 if (holdoff > 0 && need_holdoff) { 617 phase = PHASE_HOLDOFF; 618 TIMEOUT(holdoff_end, NULL, holdoff); 619 do { 620 wait_time(timeleft(&timo)); 621 calltimeout(); 622 if (kill_link) { 623 if (!persist) 624 die(0); 625 kill_link = 0; 626 phase = PHASE_DORMANT; /* allow signal to end holdoff */ 627 } 628 reap_kids(); 629 } while (phase == PHASE_HOLDOFF); 630 } 631 } 632 633 die(0); 634 return 0; 635} 636 637/* 638 * detach - detach us from the controlling terminal. 639 */ 640void 641detach() 642{ 643 if (detached) 644 return; 645 if (daemon(0, 0) < 0) { 646 perror("Couldn't detach from controlling terminal"); 647 die(1); 648 } 649 detached = 1; 650 pid = getpid(); 651 /* update pid file if it has been written already */ 652 if (pidfilename[0]) 653 create_pidfile(); 654} 655 656/* 657 * Create a file containing our process ID. 658 */ 659static void 660create_pidfile() 661{ 662 FILE *pidfile; 663 664 (void) snprintf(pidfilename, sizeof pidfilename, 665 "%s%s.pid", _PATH_VARRUN, ifname); 666 if ((pidfile = fopen(pidfilename, "w")) != NULL) { 667 fprintf(pidfile, "%ld\n", (long)pid); 668 (void) fclose(pidfile); 669 } else { 670 syslog(LOG_ERR, "Failed to create pid file %s: %m", pidfilename); 671 pidfilename[0] = 0; 672 } 673} 674 675/* 676 * holdoff_end - called via a timeout when the holdoff period ends. 677 */ 678static void 679holdoff_end(arg) 680 void *arg; 681{ 682 phase = PHASE_DORMANT; 683} 684 685/* 686 * get_input - called when incoming data is available. 687 */ 688static void 689get_input() 690{ 691 int len, i; 692 u_char *p; 693 u_short protocol; 694 struct protent *protp; 695 696 p = inpacket_buf; /* point to beginning of packet buffer */ 697 698 len = read_packet(inpacket_buf); 699 if (len < 0) 700 return; 701 702 if (len == 0) { 703 syslog(LOG_NOTICE, "Modem hangup"); 704 hungup = 1; 705 lcp_lowerdown(0); /* serial link is no longer available */ 706 link_terminated(0); 707 return; 708 } 709 710 if (debug /*&& (debugflags & DBG_INPACKET)*/) 711 log_packet(p, len, "rcvd ", LOG_DEBUG); 712 713 if (len < PPP_HDRLEN) { 714 MAINDEBUG((LOG_INFO, "io(): Received short packet.")); 715 return; 716 } 717 718 p += 2; /* Skip address and control */ 719 GETSHORT(protocol, p); 720 len -= PPP_HDRLEN; 721 722 /* 723 * Toss all non-LCP packets unless LCP is OPEN. 724 */ 725 if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) { 726 MAINDEBUG((LOG_INFO, 727 "get_input: Received non-LCP packet when LCP not open.")); 728 return; 729 } 730 731 /* 732 * Until we get past the authentication phase, toss all packets 733 * except LCP, LQR and authentication packets. 734 */ 735 if (phase <= PHASE_AUTHENTICATE 736 && !(protocol == PPP_LCP || protocol == PPP_LQR 737 || protocol == PPP_PAP || protocol == PPP_CHAP)) { 738 MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d", 739 protocol, phase)); 740 return; 741 } 742 743 /* 744 * Upcall the proper protocol input routine. 745 */ 746 for (i = 0; (protp = protocols[i]) != NULL; ++i) { 747 if (protp->protocol == protocol && protp->enabled_flag) { 748 (*protp->input)(0, p, len); 749 return; 750 } 751 if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag 752 && protp->datainput != NULL) { 753 (*protp->datainput)(0, p, len); 754 return; 755 } 756 } 757 758 if (debug) 759 syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol); 760 lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN); 761} 762 763 764/* 765 * quit - Clean up state and exit (with an error indication). 766 */ 767void 768quit() 769{ 770 die(1); 771} 772 773/* 774 * die - like quit, except we can specify an exit status. 775 */ 776void 777die(status) 778 int status; 779{ 780 struct syslog_data sdata = SYSLOG_DATA_INIT; 781 782 cleanup(); 783 syslog_r(LOG_INFO, &sdata, "Exit."); 784 _exit(status); 785} 786 787/* 788 * cleanup - restore anything which needs to be restored before we exit 789 */ 790/* ARGSUSED */ 791static void 792cleanup() 793{ 794 sys_cleanup(); 795 796 if (ttyfd >= 0) 797 close_tty(); 798 799 if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) 800 syslog(LOG_WARNING, "unable to delete pid file: %m"); 801 pidfilename[0] = 0; 802 803 if (locked) 804 unlock(); 805} 806 807/* 808 * close_tty - restore the terminal device and close it. 809 */ 810static void 811close_tty() 812{ 813 disestablish_ppp(ttyfd); 814 815 /* drop dtr to hang up */ 816 if (modem) { 817 setdtr(ttyfd, FALSE); 818 /* 819 * This sleep is in case the serial port has CLOCAL set by default, 820 * and consequently will reassert DTR when we close the device. 821 */ 822 sleep(1); 823 } 824 825 restore_tty(ttyfd); 826 827 if (tty_mode != (mode_t) -1) 828 fchmod(ttyfd, tty_mode); 829 830 close(ttyfd); 831 ttyfd = -1; 832} 833 834 835struct callout { 836 struct timeval c_time; /* time at which to call routine */ 837 void *c_arg; /* argument to routine */ 838 void (*c_func)(void *); /* routine */ 839 struct callout *c_next; 840}; 841 842static struct callout *callout = NULL; /* Callout list */ 843static struct timeval timenow; /* Current time */ 844 845/* 846 * timeout - Schedule a timeout. 847 * 848 * Note that this timeout takes the number of seconds, NOT hz (as in 849 * the kernel). 850 */ 851void 852timeout(func, arg, time) 853 void (*func)(void *); 854 void *arg; 855 int time; 856{ 857 struct callout *newp, *p, **pp; 858 859 MAINDEBUG((LOG_DEBUG, "Timeout %lx:%lx in %d seconds.", 860 (long) func, (long) arg, time)); 861 862 /* 863 * Allocate timeout. 864 */ 865 if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) { 866 syslog(LOG_ERR, "Out of memory in timeout()!"); 867 die(1); 868 } 869 newp->c_arg = arg; 870 newp->c_func = func; 871 gettimeofday(&timenow, NULL); 872 newp->c_time.tv_sec = timenow.tv_sec + time; 873 newp->c_time.tv_usec = timenow.tv_usec; 874 875 /* 876 * Find correct place and link it in. 877 */ 878 for (pp = &callout; (p = *pp); pp = &p->c_next) 879 if (newp->c_time.tv_sec < p->c_time.tv_sec 880 || (newp->c_time.tv_sec == p->c_time.tv_sec 881 && newp->c_time.tv_usec < p->c_time.tv_sec)) 882 break; 883 newp->c_next = p; 884 *pp = newp; 885} 886 887 888/* 889 * untimeout - Unschedule a timeout. 890 */ 891void 892untimeout(func, arg) 893 void (*func)(void *); 894 void *arg; 895{ 896 struct callout **copp, *freep; 897 898 MAINDEBUG((LOG_DEBUG, "Untimeout %lx:%lx.", (long) func, (long) arg)); 899 900 /* 901 * Find first matching timeout and remove it from the list. 902 */ 903 for (copp = &callout; (freep = *copp); copp = &freep->c_next) 904 if (freep->c_func == func && freep->c_arg == arg) { 905 *copp = freep->c_next; 906 (void) free((char *) freep); 907 break; 908 } 909} 910 911 912/* 913 * calltimeout - Call any timeout routines which are now due. 914 */ 915static void 916calltimeout() 917{ 918 struct callout *p; 919 920 while (callout != NULL) { 921 p = callout; 922 923 if (gettimeofday(&timenow, NULL) < 0) { 924 syslog(LOG_ERR, "Failed to get time of day: %m"); 925 die(1); 926 } 927 if (!(p->c_time.tv_sec < timenow.tv_sec 928 || (p->c_time.tv_sec == timenow.tv_sec 929 && p->c_time.tv_usec <= timenow.tv_usec))) 930 break; /* no, it's not time yet */ 931 932 callout = p->c_next; 933 (*p->c_func)(p->c_arg); 934 935 free((char *) p); 936 } 937} 938 939 940/* 941 * timeleft - return the length of time until the next timeout is due. 942 */ 943static struct timeval * 944timeleft(tvp) 945 struct timeval *tvp; 946{ 947 if (callout == NULL) 948 return NULL; 949 950 gettimeofday(&timenow, NULL); 951 tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec; 952 tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec; 953 if (tvp->tv_usec < 0) { 954 tvp->tv_usec += 1000000; 955 tvp->tv_sec -= 1; 956 } 957 if (tvp->tv_sec < 0) 958 tvp->tv_sec = tvp->tv_usec = 0; 959 960 return tvp; 961} 962 963 964/* 965 * kill_my_pg - send a signal to our process group, and ignore it ourselves. 966 */ 967static void 968kill_my_pg(sig) 969 int sig; 970{ 971 struct sigaction act, oldact; 972 973 act.sa_handler = SIG_IGN; 974 act.sa_flags = 0; 975 kill(0, sig); 976 sigaction(sig, &act, &oldact); 977 sigaction(sig, &oldact, NULL); 978} 979 980 981/* 982 * hup - Catch SIGHUP signal. 983 * 984 * Indicates that the physical layer has been disconnected. 985 * We don't rely on this indication; if the user has sent this 986 * signal, we just take the link down. 987 */ 988static void 989hup(sig) 990 int sig; 991{ 992 int save_errno = errno; 993 struct syslog_data sdata = SYSLOG_DATA_INIT; 994 995 if (crashed) 996 _exit(127); 997 syslog_r(LOG_INFO, &sdata, "Hangup (SIGHUP)"); 998 kill_link = 1; 999 if (conn_running) 1000 /* Send the signal to the [dis]connector process(es) also */ 1001 kill_my_pg(sig); 1002 errno = save_errno; 1003} 1004 1005 1006/* 1007 * term - Catch SIGTERM signal and SIGINT signal (^C/del). 1008 * 1009 * Indicates that we should initiate a graceful disconnect and exit. 1010 */ 1011/*ARGSUSED*/ 1012static void 1013term(sig) 1014 int sig; 1015{ 1016 int save_errno = errno; 1017 struct syslog_data sdata = SYSLOG_DATA_INIT; 1018 1019 if (crashed) 1020 _exit(127); 1021 syslog_r(LOG_INFO, &sdata, "Terminating on signal %d.", sig); 1022 persist = 0; /* don't try to restart */ 1023 kill_link = 1; 1024 if (conn_running) 1025 /* Send the signal to the [dis]connector process(es) also */ 1026 kill_my_pg(sig); 1027 errno = save_errno; 1028} 1029 1030 1031/* 1032 * chld - Catch SIGCHLD signal. 1033 * Calls reap_kids to get status for any dead kids. 1034 */ 1035static void 1036chld(sig) 1037 int sig; 1038{ 1039 int save_errno = errno; 1040 1041 reap_kids(); /* XXX somewhat unsafe */ 1042 errno = save_errno; 1043} 1044 1045 1046/* 1047 * toggle_debug - Catch SIGUSR1 signal. 1048 * 1049 * Toggle debug flag. 1050 */ 1051/*ARGSUSED*/ 1052static void 1053toggle_debug(sig) 1054 int sig; 1055{ 1056 debug = !debug; 1057 if (debug) { 1058 setlogmask(LOG_UPTO(LOG_DEBUG)); /* XXX safe, but wrong */ 1059 } else { 1060 setlogmask(LOG_UPTO(LOG_WARNING)); /* XXX safe, but wrong */ 1061 } 1062} 1063 1064 1065/* 1066 * open_ccp - Catch SIGUSR2 signal. 1067 * 1068 * Try to (re)negotiate compression. 1069 */ 1070/*ARGSUSED*/ 1071static void 1072open_ccp(sig) 1073 int sig; 1074{ 1075 open_ccp_flag = 1; 1076} 1077 1078 1079/* 1080 * bad_signal - We've caught a fatal signal. Clean up state and exit. 1081 */ 1082static void 1083bad_signal(sig) 1084 int sig; 1085{ 1086 struct syslog_data sdata = SYSLOG_DATA_INIT; 1087 1088 if (crashed) 1089 _exit(127); 1090 crashed = 1; 1091 syslog_r(LOG_ERR, &sdata, "Fatal signal %d", sig); 1092 if (conn_running) 1093 kill_my_pg(SIGTERM); 1094 die(1); /* XXX unsafe! */ 1095} 1096 1097 1098/* 1099 * device_script - run a program to connect or disconnect the 1100 * serial device. 1101 */ 1102static int 1103device_script(program, in, out) 1104 char *program; 1105 int in, out; 1106{ 1107 pid_t pid; 1108 int status; 1109 int errfd; 1110 1111 conn_running = 1; 1112 pid = fork(); 1113 1114 if (pid < 0) { 1115 conn_running = 0; 1116 syslog(LOG_ERR, "Failed to create child process: %m"); 1117 die(1); 1118 } 1119 1120 if (pid == 0) { 1121 sys_close(); 1122 closelog(); 1123 if (in == out) { 1124 if (in != 0) { 1125 dup2(in, 0); 1126 close(in); 1127 } 1128 dup2(0, 1); 1129 } else { 1130 if (out == 0) 1131 out = dup(out); 1132 if (in != 0) { 1133 dup2(in, 0); 1134 close(in); 1135 } 1136 if (out != 1) { 1137 dup2(out, 1); 1138 close(out); 1139 } 1140 } 1141 if (nodetach == 0) { 1142 close(2); 1143 errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); 1144 if (errfd >= 0 && errfd != 2) { 1145 dup2(errfd, 2); 1146 close(errfd); 1147 } 1148 } 1149 /* revoke privs */ 1150 seteuid(getuid()); 1151 setuid(getuid()); 1152 setegid(getgid()); 1153 setgid(getgid()); 1154 execl("/bin/sh", "sh", "-c", program, (char *)0); 1155 syslog(LOG_ERR, "could not exec /bin/sh: %m"); 1156 _exit(99); 1157 /* NOTREACHED */ 1158 } 1159 1160 while (waitpid(pid, &status, 0) < 0) { 1161 if (errno == EINTR) 1162 continue; 1163 syslog(LOG_ERR, "error waiting for (dis)connection process: %m"); 1164 die(1); 1165 } 1166 conn_running = 0; 1167 1168 return (status == 0 ? 0 : -1); 1169} 1170 1171 1172/* 1173 * run-program - execute a program with given arguments, 1174 * but don't wait for it. 1175 * If the program can't be executed, logs an error unless 1176 * must_exist is 0 and the program file doesn't exist. 1177 */ 1178int 1179run_program(prog, args, must_exist) 1180 char *prog; 1181 char **args; 1182 int must_exist; 1183{ 1184 pid_t pid; 1185 1186 pid = fork(); 1187 if (pid == -1) { 1188 syslog(LOG_ERR, "Failed to create child process for %s: %m", prog); 1189 return -1; 1190 } 1191 if (pid == 0) { 1192 int new_fd; 1193 1194 /* Leave the current location */ 1195 (void) setsid(); /* No controlling tty. */ 1196 (void) umask (S_IRWXG|S_IRWXO); 1197 (void) chdir ("/"); /* no current directory. */ 1198 setuid(geteuid()); 1199 setgid(getegid()); 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 **) malloc(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 1673/* 1674 * script_unsetenv - remove a variable from the environment 1675 * for scripts. 1676 */ 1677void 1678script_unsetenv(var) 1679 char *var; 1680{ 1681 int vl = strlen(var); 1682 int i; 1683 char *p; 1684 1685 if (script_env == 0) 1686 return; 1687 for (i = 0; (p = script_env[i]) != 0; ++i) { 1688 if (strncmp(p, var, vl) == 0 && p[vl] == '=') { 1689 free(p); 1690 while ((script_env[i] = script_env[i+1]) != 0) 1691 ++i; 1692 break; 1693 } 1694 } 1695} 1696