main.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* main.c - Point-to-Point Protocol main module
*
* Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, provided that the above copyright
* notice appears in all copies.
*
* SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
*
* Copyright (c) 1989 Carnegie Mellon University.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by Carnegie Mellon University. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <netdb.h>
#include <pwd.h>
#include <setjmp.h>
#include <sys/resource.h>
#include "pppd.h"
#include "magic.h"
#include "fsm.h"
#include "lcp.h"
#include "ipcp.h"
#ifdef INET6
#include "ipv6cp.h"
#endif
#include "upap.h"
#include "chap.h"
#include "ccp.h"
#include "pathnames.h"
#include "patchlevel.h"
#ifdef HAVE_MULTILINK
#include "tdb.h"
#endif
#ifdef CBCP_SUPPORT
#include "cbcp.h"
#endif
#ifdef IPX_CHANGE
#include "ipxcp.h"
#endif /* IPX_CHANGE */
#ifdef AT_CHANGE
#include "atcp.h"
#endif
#endif
/* interface vars */
char *progname; /* Name of this program */
static int conn_running; /* we have a [dis]connector running */
int ttyfd; /* Serial port file descriptor */
bool hungup; /* terminal has been hung up */
bool privileged; /* we're running as real uid root */
bool need_holdoff; /* need holdoff period before restarting */
bool detached; /* have detached from terminal */
bool prepass = 0; /* doing prepass to find device name */
int devnam_fixed; /* set while in options.ttyxx file */
volatile int status; /* exit status for pppd */
int unsuccess; /* # unsuccessful connection attempts */
int do_callback; /* != 0 if we should do callback next */
int doing_callback; /* != 0 if we are doing callback */
char *callback_script; /* script for doing callback */
#ifdef HAVE_MULTILINK
char db_key[32];
#endif
/*
* For plug-in usage:
*
* holdoff_hook - Can be used to change the demand-dial hold-off
* time dynamically. This is normally set by the
* "holdoff" option, and is 30 seconds by default.
*
* new_phase_hook - This is called for each change in the PPP
* phase (per RFC 1661). This can be used to log
* progress.
*
* check_options_hook - This is called before doing sys_init()
* and allows the plugin to verify the selected options.
*
* updown_script_hook - This is called with the proposed
* command-line arguments for any of the
*
* device_pipe_hook - If this is set, then an extra fd (3) is
* passed to the connect/disconnect script. This extra
* fd is the write side of a pipe, and the read side is
* passed to this routine. This can be used to pass
* arbitrary data from the script back to pppd.
*/
static int fd_loop; /* fd for getting demand-dial packets */
static int pty_master; /* fd for master side of pty */
static int real_ttyfd; /* fd for actual serial port (not pty) */
int phase; /* where the link is at */
int kill_link;
int open_ccp_flag;
static int waiting; /* for input from peer or timer expiration */
static sigjmp_buf sigjmp;
char **script_env; /* Env. variable values for scripts */
int s_env_nalloc; /* # words avail at script_env */
static int n_children; /* # child processes still running */
static bool got_sigchld; /* set if we have received a SIGCHLD */
static bool locked; /* lock() has succeeded */
static bool privopen; /* don't lock, open device as root */
char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
int ngroups; /* How many groups valid in groups */
struct pppd_stats link_stats;
int link_connect_time;
bool link_stats_valid;
extern option_t general_options[];
extern option_t auth_options[];
/*
* We maintain a list of child process pids and
* functions to call when they exit.
*/
struct subprocess {
char *prog;
void *arg;
struct subprocess *next;
};
static struct subprocess *children;
/* Prototypes for procedures local to this file. */
static void setup_signals __P((void));
static void create_pidfile __P((void));
static void create_linkpidfile __P((void));
static void calltimeout __P((void));
static void kill_my_pg __P((int));
static void toggle_debug __P((int));
static void bad_signal __P((int));
static void holdoff_end __P((void *));
static int device_script __P((char *, int, int, int, char *));
static int open_socket __P((char *));
static int start_charshunt __P((int, int));
static void charshunt_done __P((void *, int));
struct timeval *));
static void final_reap __P((void));
#ifdef HAVE_MULTILINK
static void update_db_entry __P((void));
static void add_db_key __P((const char *));
static void delete_db_key __P((const char *));
static void cleanup_db __P((void));
#endif
#ifdef ultrix
#define O_NONBLOCK O_NDELAY
#endif
#ifdef ULTRIX
#define setlogmask(x) 0
#endif
/* Backward compatibility for Linux */
#ifndef RECMARK_TIMESTART
#define RECMARK_STARTSEND 1
#define RECMARK_STARTRECV 2
#define RECMARK_ENDSEND 3
#define RECMARK_ENDRECV 4
#define RECMARK_TIMEDELTA32 5
#define RECMARK_TIMEDELTA8 6
#define RECMARK_TIMESTART 7
#endif
/*
* PPP Data Link Layer "protocol" table.
* One entry per supported protocol.
* The last entry must be NULL.
*/
#ifdef CBCP_SUPPORT
#endif
#ifdef INET6
#endif
#ifdef IPX_CHANGE
#endif
#ifdef AT_CHANGE
#endif
};
int
int argc;
char *argv[];
{
int i, fdflags, t;
char *p, *connector;
char numbuf[16];
ifname[0] = '\0';
/*
* This way we can close 0, 1, 2 in detach() without clobbering
* a fd that we are using.
*/
while (0 <= i && i <= 2)
i = dup(i);
if (i >= 0)
(void) close(i);
}
script_env = NULL;
/* Initialize syslog facilities */
reopen_log();
option_error("Couldn't get hostname: %m");
exit(1);
}
/* make sure we don't create world or group writable files. */
privileged = (uid == 0);
/*
* Initialize magic number generator now so that protocols may
* use magic numbers in initialization.
*/
magic_init();
prepass = 0;
/*
* Initialize to the standard option set, then parse, in order, the
* system options file, the user's options file, the tty's options file,
* and the command line arguments. At last, install the options declared
* by each protocol into the extra_option list.
*/
}
}
/*
* Install "generic" options into the extra_options list.
*/
/* Install any system-specific options (or remove unusable ones) */
sys_options();
|| !options_from_user())
/* scan command line and options files to find device name */
prepass = 1;
prepass = 0;
/*
* Work out the device name, if it hasn't already been specified.
*/
char *p;
option_error("no device specified and stdin is not a tty");
}
}
/*
* Parse the tty options file and the command line.
* The per-tty options file should not change
* ptycommand, pty_socket, notty or devnam.
*/
devnam_fixed = 1;
if (!using_pty && !direct_tty) {
if (!options_for_tty())
}
devnam_fixed = 0;
/*
* Check that we are running as root.
*/
if (geteuid() != 0) {
option_error("must be root to run %s, since it is not setuid-root",
argv[0]);
}
if (!ppp_available()) {
}
/*
* Check that the options given are valid and consistent.
*/
if (!sys_check_options())
#ifdef HAVE_MULTILINK
#endif
(*protp->check_options)();
option_error("connect script is required for demand-dialling\n");
}
option_error("updetach cannot be used with %s",
}
/* default holdoff to 0 if no connect script has been given */
holdoff = 0;
if (using_pty || direct_tty) {
if (!default_device) {
option_error("%s option precludes specifying device name",
}
option_error("pty option is incompatible with notty option");
}
direct_tty)) {
option_error("socket option is incompatible with pty and notty");
}
lockflag = 0;
modem = 0;
log_to_fd = -1;
} else {
/*
* If the user has specified a device which is the same as
* the one on stdin, pretend they didn't specify any.
* we assume we don't need to lock it, and we can open it as root.
*/
default_device = 1;
privopen = 1;
}
}
if (default_device)
nodetach = 1;
/*
* Don't send log messages to the serial port, it tends to
* confuse the peer. :-)
*/
log_to_fd = -1;
early_log = 0;
if (debug)
/*
* Initialize system-dependent stuff.
*/
if (check_options_hook != NULL &&
}
#ifdef HAVE_MULTILINK
} else {
if (multilink) {
warn("Warning: disabling multilink");
multilink = 0;
}
}
#endif
/*
* Detach ourselves from the terminal, if required, and identify
* who is running us. Printing to stderr stops here unless
* nodetach or updetach is set.
*/
detach();
p = getlogin();
if (p == NULL) {
else
p = "(unknown)";
}
script_setenv("PPPLOGNAME", p, 0);
if (devnam[0] != '\0')
waiting = 0;
/*
* If we're doing dial-on-demand, set up the interface now.
*/
if (demand) {
/*
* Open the loopback channel and set it up to be the ppp interface.
*/
#ifdef HAVE_MULTILINK
(void) tdb_writelock(pppdb);
#endif
set_ifunit(1);
fd_loop = open_ppp_loopback();
#ifdef HAVE_MULTILINK
(void) tdb_writeunlock(pppdb);
#endif
/*
* Configure the interface and mark it up, etc.
*/
demand_conf();
}
do_callback = 0;
for (;;) {
need_holdoff = 1;
ttyfd = -1;
real_ttyfd = -1;
++unsuccess;
do_callback = 0;
if (demand && !doing_callback) {
/*
* Don't do anything until we see some activity.
*/
kill_link = 0;
for (;;) {
if (kill_link || got_sigchld) {
} else {
waiting = 1;
}
}
waiting = 0;
calltimeout();
if (kill_link) {
if (!persist)
break;
kill_link = 0;
}
if (get_loop_output())
break;
if (got_sigchld)
(void) reap_kids(0);
}
break;
/*
* Now we want to bring up the link.
*/
demand_block();
info("Starting link");
}
/*
* or record options were specified.
*/
pty_master = -1;
pty_slave = -1;
error("Couldn't allocate pseudo-tty");
goto fail;
}
}
/*
* Lock the device if we've been asked to.
*/
goto fail;
locked = 1;
}
/*
* Open the serial device and set it up to be the ppp interface.
* First we open it in non-blocking mode so we can set the
* various termios flags appropriately. If we aren't dialling
* out and we want to use the modem lines, we reopen it later
* in order to wait for the carrier detect signal from the modem.
*/
hungup = 0;
kill_link = 0;
if (direct_tty) {
ttyfd = 0;
} else if (devnam[0] != '\0') {
for (;;) {
/* If the user specified the device name, become the
user before opening it. */
int err;
if ((ttyfd = sys_extra_fd()) < 0)
(void) seteuid(0);
if (ttyfd >= 0)
break;
}
goto fail;
}
warn("Couldn't reset non-blocking mode on device: %m");
/*
* Do the equivalent of `mesg n' to stop broadcast messages.
*/
} else
/*
* Set line speed, flow control, etc.
* If we have a non-null connection or initializer script,
* on most systems we set CLOCAL for now so that we can talk
* to the modem before carrier comes up. But this has the
* side effect that we might miss it if CD drops before we
* get to clear CLOCAL below. On systems where we can talk
* successfully to the modem with CLOCAL clear and CD down,
* we could clear CLOCAL at this point.
*/
|| initializer != NULL));
real_ttyfd = ttyfd;
}
/*
* start up the character shunt now.
*/
if (ptycommand != NULL) {
if (record_file != NULL) {
fatal("Couldn't create pipes for record option: %m");
dbglog("starting charshunt for pty option");
if (!ok)
goto fail;
} else {
"pty") < 0)
goto fail;
(void) close(pty_master);
pty_master = -1;
}
} else if (pty_socket != NULL) {
if (fd < 0)
goto fail;
dbglog("starting charshunt for socket option");
goto fail;
} else if (notty) {
dbglog("starting charshunt for notty option");
if (!start_charshunt(0, 1))
goto fail;
} else if (record_file != NULL) {
dbglog("starting charshunt for record option");
goto fail;
}
/* run connection script */
if (real_ttyfd != -1) {
/* XXX do this if doing_callback == CALLBACK_DIALIN? */
(void) sleep(1);
}
}
error("Initializer script failed");
goto fail;
}
if (kill_link)
goto disconnect;
info("Serial port initialized.");
}
error("Connect script failed");
goto fail;
}
if (kill_link)
goto disconnect;
info("Serial connection established.");
}
/*
* Clear CLOCAL if modem option -- we now have carrier
* established, and we should respect loss of carrier.
*/
if (real_ttyfd != -1)
set_up_tty(real_ttyfd, 0);
if (doing_callback == CALLBACK_DIALIN)
}
/* reopen tty if necessary to wait for carrier */
for (;;) {
break;
}
goto fail;
}
(void) close(i);
}
/* run welcome script, if any */
warn("Welcome script failed");
}
/* set up the serial device as a ppp interface */
#ifdef HAVE_MULTILINK
(void) tdb_writelock(pppdb);
#endif
if (fd_ppp < 0) {
#ifdef HAVE_MULTILINK
(void) tdb_writeunlock(pppdb);
#endif
goto disconnect;
}
set_ifunit(1);
#ifdef HAVE_MULTILINK
(void) tdb_writeunlock(pppdb);
#endif
/*
* Start opening the connection and wait for
* incoming events (reply, timeout, etc.).
*/
link_stats_valid = 0;
script_unsetenv("CONNECT_TIME");
script_unsetenv("BYTES_SENT");
script_unsetenv("BYTES_RCVD");
lcp_lowerup(0);
/* Mostly for accounting purposes */
/*
* If we are initiating this connection, wait for a short
* time for something from the peer. This can avoid bouncing
* our packets off his tty before he has it set up.
*/
struct timeval t;
wait_input(&t);
}
lcp_open(0); /* Start protocol */
open_ccp_flag = 0;
while (phase != PHASE_DEAD) {
} else {
waiting = 1;
}
}
waiting = 0;
calltimeout();
get_input();
if (kill_link) {
lcp_close(0, "User request");
kill_link = 0;
}
if (open_ccp_flag) {
/* Uncloak ourselves. */
(*ccp_protent.open)(0);
}
open_ccp_flag = 0;
}
if (got_sigchld)
(void) reap_kids(0); /* Don't leave dead kids lying around */
}
/*
* Print connect time and statistics.
*/
if (link_stats_valid) {
" packets).",
}
/*
* Delete pid file before disestablishing ppp. Otherwise it
* can happen that another pppd gets the same unit and then
* we delete its pid file.
*/
if (!demand) {
if (pidfilename[0] != '\0'
pidfilename[0] = '\0';
}
/*
* If we may want to bring the link up again, transfer
* the ppp unit back to the loopback. Set the
* real serial device back to its normal mode of operation.
*/
clean_check();
if (demand)
restore_loop();
fd_ppp = -1;
if (!hungup)
lcp_lowerdown(0);
if (!demand)
script_unsetenv("IFNAME");
/*
* Run disconnector script, if requested.
* XXX we may not be able to do this if the line has hung up!
*/
!hungup) {
if (real_ttyfd >= 0)
"disconnect") < 0) {
warn("disconnect script failed");
} else {
info("Serial link disconnected.");
}
}
fail:
if (pty_master >= 0)
(void) close(pty_master);
if (pty_slave >= 0) {
pty_slave = -1;
}
if (real_ttyfd >= 0)
close_tty();
if (locked) {
locked = 0;
unlock();
}
if (!demand) {
if (pidfilename[0] != '\0'
pidfilename[0] = '\0';
}
break;
kill_link = 0;
if (demand)
t = need_holdoff? holdoff: 0;
if (holdoff_hook != NULL)
t = (*holdoff_hook)();
if (t > 0) {
do {
if (kill_link || got_sigchld) {
} else {
waiting = 1;
}
}
waiting = 0;
calltimeout();
if (kill_link) {
kill_link = 0;
}
if (got_sigchld)
(void) reap_kids(0);
} while (phase == PHASE_HOLDOFF);
if (!persist)
break;
}
}
/* Wait for scripts to finish */
final_reap();
return (0);
}
/*
* setup_signals - initialize signal handling.
*/
static void
{
/*
* Compute mask of all interesting signals and install signal handlers
* for each. Only one signal handler may be active at a time. Therefore,
* all other signals should be masked when any handler is executing.
*/
(void) sigemptyset(&main_sigmask);
fatal("Couldn't establish signal handler (%d): %m", s); \
} else ((void)0)
/*
* Install a handler for other signals which would otherwise
* cause pppd to exit without cleaning up.
*/
#ifndef DEBUG
#endif
#ifdef SIGBUS
#endif
#ifdef SIGEMT
#endif
#ifdef SIGPOLL
#endif
#ifdef SIGPROF
#endif
#ifdef SIGSYS
#endif
#ifdef SIGTRAP
#endif
#ifdef SIGVTALRM
#endif
#ifdef SIGXCPU
#endif
#ifdef SIGXFSZ
#endif
/*
* Apparently we can get a SIGPIPE when we call syslog, if
* syslogd has died and been restarted. Ignoring it seems
* be sufficient.
*/
}
/*
* set_ifunit - do things we need to do once we know which ppp
* unit we are using.
*/
void
int iskey;
{
sys_ifname();
if (iskey) {
create_pidfile(); /* write pid to file */
}
}
/*
* detach - detach us from the controlling terminal.
*/
void
detach()
{
char numbuf[16];
if (detached)
return;
error("Couldn't detach (fork failed: %m)");
}
/* parent */
if (locked)
exit(0); /* parent dies */
}
(void) setsid();
/*
* Fork again to relinquish session leadership. This is needed
* to prevent the daemon from acquiring controlling terminal.
*/
error("Couldn't detach (second fork failed: %m)");
}
/* parent */
if (locked)
exit(0); /* parent dies */
}
(void) chdir("/");
(void) close(0);
(void) close(1);
(void) close(2);
detached = 1;
if (!log_to_file && !log_to_specific_fd)
log_to_fd = -1;
/* update pid files if they have been written already */
if (pidfilename[0] != '\0')
if (linkpidfile[0] != '\0')
}
/*
* reopen_log - (re)open our connection to syslog.
*/
void
{
#ifdef ULTRIX
#else
#endif
}
/*
* Create a file containing our process ID.
*/
static void
{
} else {
pidfilename[0] = '\0';
}
}
static void
{
if (linkname[0] == '\0')
return;
if (ifname[0] != '\0')
} else {
linkpidfile[0] = '\0';
}
}
/*
* holdoff_end - called via a timeout when the holdoff period ends.
*/
/*ARGSUSED*/
static void
void *arg;
{
}
/* List of protocol names, to make our messages a little more informative. */
struct protocol_list {
const char *name;
} protocol_list[] = {
{ 0x21, "IP" },
{ 0x23, "OSI Network Layer" },
{ 0x25, "Xerox NS IDP" },
{ 0x27, "DECnet Phase IV" },
{ 0x29, "Appletalk" },
{ 0x2b, "Novell IPX" },
{ 0x31, "Bridging PDU" },
{ 0x33, "Stream Protocol ST-II" },
{ 0x35, "Banyan Vines" },
{ 0x39, "AppleTalk EDDP" },
{ 0x3b, "AppleTalk SmartBuffered" },
{ 0x3d, "Multilink" },
{ 0x3f, "NetBIOS Frame" },
{ 0x41, "Cisco LAN Extension" },
{ 0x43, "Ascom Timeplex" },
{ 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" },
{ 0x47, "DCA Remote Lan" },
{ 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
{ 0x4b, "SNA over 802.2" },
{ 0x4d, "SNA" },
{ 0x4f, "IP6 Header Compression" },
{ 0x51, "KNX Bridging" },
{ 0x53, "Encrypted" },
{ 0x55, "per-link encrypted" },
{ 0x57, "IPv6" },
{ 0x59, "PPP Muxing" },
{ 0x6f, "Stampede Bridging" },
{ 0x73, "MP+" },
{ 0xc1, "STMF" },
{ 0xfb, "per-link compressed" },
{ 0xfd, "compressed datagram" },
{ 0x0201, "802.1d Hello Packets" },
{ 0x0203, "IBM Source Routing BPDU" },
{ 0x0205, "DEC LANBridge100 Spanning Tree" },
{ 0x0207, "Cisco Discovery Protocol" },
{ 0x0231, "Luxcom" },
{ 0x0233, "Sigma Network Systems" },
{ 0x0235, "Apple Client Server Protocol" },
{ 0x0281, "MPLS Unicast" },
{ 0x0283, "MPLS Multicast" },
{ 0x0285, "IEEE p1284.4" },
{ 0x0287, "ETSI TETRA TNP1" },
{ 0x4021, "Stacker LZS" },
{ 0x8021, "Internet Protocol Control Protocol" },
{ 0x8023, "OSI Network Layer Control Protocol" },
{ 0x8025, "Xerox NS IDP Control Protocol" },
{ 0x8027, "DECnet Phase IV Control Protocol" },
{ 0x8029, "Appletalk Control Protocol" },
{ 0x802b, "Novell IPX Control Protocol" },
{ 0x8031, "Bridging Control Protocol" },
{ 0x8033, "Stream Protocol Control Protocol" },
{ 0x8035, "Banyan Vines Control Protocol" },
{ 0x803f, "NetBIOS Frames Control Protocol" },
{ 0x8041, "Cisco LAN Extension Control Protocol" },
{ 0x8043, "Ascom Timeplex Control Protocol" },
{ 0x8045, "Fujitsu LBLB Control Protocol" },
{ 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
{ 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
{ 0x804b, "SNA over 802.2 Control Protocol" },
{ 0x804d, "SNA Control Protocol" },
{ 0x8051, "KNX Bridging Control Protocol" },
{ 0x8053, "Encryption Control Protocol" },
{ 0x8055, "Per-link Encryption Control Protocol" },
{ 0x8057, "IPv6 Control Protocol" },
{ 0x806f, "Stampede Bridging Control Protocol" },
{ 0x80c1, "STMF Control Protocol" },
{ 0x80fb, "Per-link Compression Control Protocol" },
{ 0x80fd, "Compression Control Protocol" },
{ 0x8207, "Cisco Discovery Control Protocol" },
{ 0x8235, "Apple Client Server Control Protocol" },
{ 0x8281, "MPLS Control Protocol" },
{ 0x8287, "ETSI TETRA TNP1 Control Protocol" },
{ 0xc021, "Link Control Protocol" },
{ 0xc023, "Password Authentication Protocol" },
{ 0xc025, "Link Quality Report" },
{ 0xc027, "Shiva Password Authentication Protocol" },
{ 0xc029, "CallBack Control Protocol (CBCP)" },
{ 0xc02b, "Bandwidth Allocation Control Protocol" },
{ 0xc02d, "BAP" },
{ 0xc081, "Container Control Protocol" },
{ 0xc223, "Challenge Handshake Authentication Protocol" },
{ 0xc227, "Extensible Authentication Protocol" },
{ 0xc281, "Funk Proprietary Authentication Protocol" },
{ 0, NULL },
};
/*
* protocol_name - find a name for a PPP protocol.
*/
const char *
int proto;
{
struct protocol_list *lp;
return (NULL);
}
static const char *phase_names[] = { PHASE__NAMES };
const char *
int pval;
{
static char buf[32];
return ((const char *)buf);
}
return (phase_names[pval]);
}
/*
* get_input - called when incoming data is available.
*/
static void
{
int len, i;
u_char *p;
const char *pname;
p = inpacket_buf; /* point to beginning of packet buffer */
if (len < 0)
return;
if (len == 0) {
notice("Modem hangup");
hungup = 1;
lcp_lowerdown(0); /* serial link is no longer available */
link_terminated(0);
return;
}
if (debug /*&& (debugflags & DBG_INPACKET)*/)
if (len < PPP_HDRLEN) {
return;
}
p += 2; /* Skip address and control */
len -= PPP_HDRLEN;
/*
* Toss all non-LCP packets unless LCP is in Opened state and
* discard non-authentication protocols if we're not yet
* authenticated.
*/
(phase <= PHASE_AUTHENTICATE &&
dbglog("Discarded proto 0x%x in %s phase",
else
dbglog("Discarded %s (0x%x) in %s phase",
return;
}
/*
* Upcall the proper protocol input routine.
*/
return;
}
return;
}
}
if (debug) {
else
}
}
/*
* new_phase - signal the start of a new phase of pppd's operation.
*/
void
new_phase(p)
int p;
{
if (new_phase_hook != NULL)
(*new_phase_hook)(p, phase);
phase = p;
}
/*
* die - clean up state and exit with the specified status.
*/
void
int status;
{
cleanup();
if (phase != PHASE_EXIT) {
}
}
/*
* cleanup - restore anything which needs to be restored before we exit
*/
static void
cleanup()
{
sys_cleanup(); /* XXX: Need to check if this is okay after close_tty */
if (fd_ppp >= 0) {
fd_ppp = -1;
}
if (real_ttyfd >= 0)
close_tty();
pidfilename[0] = '\0';
linkpidfile[0] = '\0';
if (locked) {
locked = 0;
unlock();
}
#ifdef HAVE_MULTILINK
cleanup_db();
}
#endif
}
/*
* close_tty - restore the terminal device and close it.
*/
static void
{
int fd = real_ttyfd;
real_ttyfd = -1;
/* drop dtr to hang up */
if (!default_device && modem) {
/*
* This sleep is in case the serial port has CLOCAL set by default,
* and consequently will reassert DTR when we close the device.
*/
(void) sleep(1);
}
/* XXX if devnam is a symlink, this will change the link */
}
}
}
}
/*
* update_link_stats - get stats at link termination.
*/
void
int u;
{
char numbuf[32];
} else {
link_connect_time = 0;
}
if (get_ppp_stats(u, &link_stats)) {
link_stats_valid = 1;
}
}
struct callout {
void *c_arg; /* argument to routine */
};
/*
* timeout - Schedule a timeout.
*
* Note that this timeout takes the number of seconds, NOT hz (as in
* the kernel).
*/
void
void *arg;
int time;
{
/*
* Allocate timeout.
*/
novm("callout structure for timeout.");
/*
* Find correct place and link it in.
*/
break;
}
/*
* untimeout - Unschedule a timeout.
*/
void
void *arg;
{
/*
* Find first matching timeout and remove it from the list.
*/
break;
}
}
/*
* calltimeout - Call any timeout routines which are now due.
*/
static void
{
struct callout *p;
p = callout;
fatal("Failed to get time of day: %m");
break; /* no, it's not time yet */
free((char *) p);
}
}
/*
* timeleft - return the length of time until the next timeout is due.
*/
static struct timeval *
{
return (NULL);
}
return (tvp);
}
/*
* kill_my_pg - send a signal to our process group, and ignore it ourselves.
*/
static void
int sig;
{
(void) sigemptyset(&mask);
/*
* Ignore signal 'sig' temporarily, before finally re-activating the
* original handler. We need to do it in the following sequence, since
* otherwise the signal handler for 'sig' will be called forever.
*/
}
/*
* Send signal 'sig' to all processes whose process group ID is equal
* to the process group ID of the sender.
*/
}
}
/*
* hup - Catch SIGHUP signal.
*
* Indicates that the physical layer has been disconnected.
* We don't rely on this indication; if the user has sent this
* signal, we just take the link down.
*/
static void
int sig;
{
info("Hangup (SIGHUP)");
kill_link = 1;
if (status != EXIT_HANGUP)
if (conn_running > 0)
/* Send the signal to the [dis]connector process(es) also */
if (charshunt_pid)
if (waiting)
}
/*
*
* Indicates that we should initiate a graceful disconnect and exit.
*/
/*ARGSUSED*/
static void
int sig;
{
persist = 0; /* don't try to restart */
kill_link = 1;
if (conn_running > 0)
/* Send the signal to the [dis]connector process(es) also */
if (charshunt_pid)
if (waiting)
}
/*
* chld - Catch SIGCHLD signal.
* Sets a flag so we will call reap_kids in the mainline.
*/
/*ARGSUSED*/
static void
int sig;
{
got_sigchld = 1;
if (waiting)
}
/*
* toggle_debug - Catch SIGUSR1 signal.
*
* Toggle debug flag.
*/
/*ARGSUSED*/
static void
int sig;
{
if (debug) {
print_ncpstate(0, NULL);
dbglog("debug logging disabled");
debug = 0;
} else {
dbglog("debug logging enabled");
print_ncpstate(0, NULL);
debug = 1;
}
}
/*
* open_ccp - Catch SIGUSR2 signal.
*
* Try to (re)negotiate compression.
*/
/*ARGSUSED*/
static void
int sig;
{
open_ccp_flag = 1;
if (waiting)
}
/*
* bad_signal - We've caught a fatal signal. Clean up state and exit.
*/
static void
int sig;
{
static int crashed = 0;
if (crashed)
_exit(127);
crashed = 1;
if (conn_running > 0)
if (charshunt_pid)
die(127);
}
/*
* device_script - run a program to talk to the serial device
* (e.g. to run the connector or disconnector script).
*/
static int
char *program;
int dont_wait;
char *optname;
{
int status = -1;
int errfd;
int envpipe[2];
error("Cannot create pipe for child: %m");
return (-1);
}
++conn_running;
--conn_running;
error("Failed to create child process: %m");
return (-1);
}
sys_close();
closelog();
if (envpipe[0] >= 0) {
}
if (in == 2) {
/* aargh!!! */
} else if (out == 2) {
}
if (log_to_fd >= 0) {
if (log_to_fd != 2) {
error("dup2(log_to_fd, STDERR) failed: %m");
}
} else {
(void) close(2);
error("dup2(errfd, STDERR) failed: %m");
}
}
if (in != 0) {
if (out == 0)
error("dup2(in, STDIN) failed: %m");
}
if (out != 1) {
error("dup2(out, STDOUT) failed: %m");
}
error("dup2(pipe, pipeout) failed: %m");
if (real_ttyfd > 2)
(void) close(real_ttyfd);
if (pty_master > 2)
(void) close(pty_master);
if (pty_slave > 2) {
pty_slave = -1;
}
error("setuid failed");
exit(1);
}
if (script_env != NULL) {
while (*script_env != NULL) {
script_env++;
}
}
exit(99);
/* NOTREACHED */
}
if (debug)
if (dont_wait) {
status = 0;
} else {
if (envpipe[0] >= 0) {
(*device_pipe_hook)(envpipe[0]);
}
continue;
fatal("error waiting for (dis)connection process: %m");
}
if (envpipe[0] >= 0)
--conn_running;
}
return (status == 0 ? 0 : -1);
}
/*
* run-program - execute a program with given arguments,
* but don't wait for it.
* If the program can't be executed, logs an error unless
* must_exist is 0 and the program file doesn't exist.
* Returns -1 if it couldn't fork, 0 if the file doesn't exist
* or isn't an executable plain file, or the process ID of the child.
* If done != NULL, (*done)(arg, int) will be called later (within
* reap_kids) if this routine returns value > 0.
*/
char *prog;
char **args;
int must_exist;
void *arg;
{
int retv;
/*
* First check if the file exists and is executable.
* We don't use access() because that would use the
* real user-id, which might not be root, and the script
* might be accessible only to root.
*/
return (0);
}
if (updown_script_hook != NULL) {
if (retv == -1) {
return (-1);
}
}
return (-1);
}
int new_fd;
/* Leave the current location */
(void) setsid(); /* No controlling tty. */
(void) setuid(0); /* set real UID = root */
/* Ensure that nothing of our device environment is inherited. */
sys_close();
closelog();
(void) close(0);
(void) close(1);
(void) close(2);
if (real_ttyfd >= 0)
(void) close(real_ttyfd);
/* Don't pass handles to the PPP device, even by accident. */
if (new_fd >= 0) {
if (new_fd != 0) {
}
}
#ifdef BSD
/* Force the priority back to zero if pppd is running higher. */
if (setpriority (PRIO_PROCESS, 0, 0) < 0)
warn("can't reset priority to 0: %m");
#endif
/* SysV recommends a second fork at this point. */
/* run the program */
/* have to reopen the log, there's nowhere else
for the message to go. */
reopen_log();
closelog();
}
_exit(-1);
}
if (debug)
return (pid);
}
/*
* record_child - add a child process to the list for reap_kids
* to use.
*/
static void
char *prog;
void *arg;
{
struct subprocess *chp;
++n_children;
} else {
}
}
/*
* reap_kids - get status from any dead child processes,
* and log a message for abnormal terminations.
*/
static int
int waitfor;
{
int status, i;
got_sigchld = 0;
if (n_children == 0)
return (0);
/*CONSTANTCONDITION*/
while (1) {
if (pid == 0) {
break; /* return 0 */
} else if (pid == -1) {
continue;
error("Error waiting for child process: %m");
return (-1);
} else {
--n_children;
break;
}
}
warn("Child process %s (pid %d) %s with signal %d (%s)",
i, signal_name(i));
} else if (debug) {
dbglog("Child process %s finished (pid %d), status = %d",
}
}
}
return (0);
}
/*
* infanticide - timeout while waiting for child process.
*/
/*ARGSUSED*/
static void
int sig;
{
struct subprocess *chp;
static int runcount = 0;
if (runcount < 2) {
} else {
/* Quit and hope for the best. */
n_children = 0;
}
runcount++;
}
/*
* Perform final wait before exiting.
*/
static void
{
struct subprocess *chp;
if (n_children > 0 && debug) {
}
while (n_children > 0) {
(void) alarm(7);
if (reap_kids(1) < 0)
break;
}
(void) alarm(0);
}
/*
* novm - log an error message saying we ran out of memory, and die.
*/
void
char *msg;
{
}
/*
* script_setenv - set an environment variable value to be used
* for scripts that we run (e.g. ip-up, auth-up, etc.)
*/
void
const char *var;
const char *value;
int iskey;
{
int i;
char *p, *newstring;
/*
* XXX: Can we assert that a tdb write lock is held here ? It appears that
* Linux's use of tdb is not safe.
*/
novm("script environment string");
return;
}
/* check if this variable is already set */
if (script_env != NULL) {
for (i = 0; (p = script_env[i]) != NULL; ++i) {
#ifdef HAVE_MULTILINK
delete_db_key(p);
#endif
free(p-1);
script_env[i] = newstring;
#ifdef HAVE_MULTILINK
#endif
return;
}
}
} else {
/* no space allocated for script env. ptrs. yet */
i = 0;
if (script_env == NULL) {
novm("script environment variable.");
return;
}
s_env_nalloc = 16;
}
/* reallocate script_env with more space if needed */
if (i + 1 >= s_env_nalloc) {
int new_n = i + 17;
new_n * sizeof(char *));
novm("expanded script environment variable.");
return;
}
script_env = newenv;
}
script_env[i] = newstring;
#ifdef HAVE_MULTILINK
if (iskey)
}
#endif
}
/*
* script_unsetenv - remove a variable from the environment
* for scripts.
*/
void
const char *var;
{
int i;
char *p;
/*
* XXX: Can we assert that a tdb write lock is held here ? It appears that
* Linux's use of tdb is not safe.
*/
if (script_env == NULL)
return;
for (i = 0; (p = script_env[i]) != NULL; ++i) {
#ifdef HAVE_MULTILINK
delete_db_key(p);
#endif
free(p-1);
++i;
break;
}
}
#ifdef HAVE_MULTILINK
#endif
}
/*
* script_getenv - find a variable in the script environment.
*/
const char *
const char *var;
{
int i;
char *p;
if (script_env == NULL)
return (NULL);
for (i = 0; (p = script_env[i]) != NULL; ++i) {
return ((const char *)p+vl+1);
}
return (NULL);
}
#ifdef HAVE_MULTILINK
/*
* update_db_entry - update our entry in the database.
*/
static void
{
int vlen, i;
char *p, *q, *vbuf;
if (script_env == NULL)
return;
/*
* vlen needs to be initialized as 1, or otherwise, the last string
* is truncated by slprintf.
*/
vlen = 1;
for (i = 0; (p = script_env[i]) != NULL; ++i)
novm("database entry");
q = vbuf;
for (i = 0; (p = script_env[i]) != NULL; ++i)
}
/*
* add_db_key - add a key that we can use to look up our database entry.
*/
static void
const char *str;
{
}
/*
* delete_db_key - delete a key for looking up our database entry.
*/
static void
const char *str;
{
}
/*
* cleanup_db - delete all the entries we put in the database.
*/
static void
{
int i;
char *p;
for (i = 0; (p = script_env[i]) != NULL; ++i)
if (p[-1] != '\0')
delete_db_key(p);
}
#endif /* HAVE_MULTILINK */
/*
* open_socket - establish a stream socket connection to the nominated
* host and port.
* XXX: Need IPv6 support for those systems that support it (use getaddrinfo),
* but requires portability changes.
*/
static int
char *dest;
{
int sock;
int port = -1;
struct sockaddr_in sad;
/* parse host:port and resolve host to an IP address */
} else {
error("Can't parse host:port for socket destination");
return (-1);
}
}
}
error("Can't parse host:port for socket destination");
return (-1);
}
*sep = '\0';
*sep = ':';
return (-1);
}
hent->h_addr_list++;
}
*sep = ':';
for (;;) {
/* get a socket and connect it to the other end */
if (sock < 0) {
error("Can't create socket: %m");
return (-1);
}
break; /* return sock file descriptor */
}
hent->h_addr_list++;
continue;
}
return (-1);
}
return (sock);
}
/*
* print_ncpstate - prints out current NCP state.
*
* We're normally called from SIGUSR1 here, but this is safe because
* these signals are blocked unless we're idle waiting for events.
* There's no need to otherwise lock the data structures referenced.
*/
void
int unit;
{
int i;
}
}
/*
* start_charshunt - create a child process to run the character shunt.
*/
static int
{
error("Can't fork process for character shunt: %m");
return (0);
}
/* child */
pty_slave = -1;
fatal("setuid failed");
if (!nodetach)
log_to_fd = -1;
exit(0);
}
(void) close(pty_master);
pty_master = -1;
return (1);
}
/*ARGSUSED*/
static void
void *arg;
int status;
{
charshunt_pid = (pid_t)0;
}
static void
{
exit(1);
}
/*
* charshunt - the character shunt, which passes characters between
* This runs as the user (not as root).
* (We assume ofd >= ifd which is true the way this gets called. :-).
*/
static void
char *record_file;
{
int n, nfds;
int flags;
/*
* Reset signal handlers.
*/
#ifndef DEBUG
#endif
#ifdef SIGBUS
#endif
#ifdef SIGEMT
#endif
#ifdef SIGPOLL
#endif
#ifdef SIGPROF
#endif
#ifdef SIGSYS
#endif
#ifdef SIGTRAP
#endif
#ifdef SIGVTALRM
#endif
#ifdef SIGXCPU
#endif
#ifdef SIGXFSZ
#endif
/*
* Open the record file if required.
*/
if (record_file != NULL) {
}
/* set all the fds to non-blocking mode */
if (flags == -1
warn("couldn't set pty master to nonblock: %m");
if (flags == -1
if (flags == -1
warn("couldn't set stdout to nonblock: %m");
}
if (max_data_rate) {
if (max_level < MAXLEVELMINSIZE)
} else
}
top = 0;
if (nibuf != 0) {
else if (pty_master >= 0)
} else if (ifd >= 0)
if (nobuf != 0) {
else if (ofd >= 0)
} else {
/* Don't read from pty if it's gone or it has closed. */
if (pty_master >= 0 && ofd >= 0)
}
fatal("select");
continue;
}
if (max_data_rate) {
double dt;
int nbt;
} else
nibuf = 0;
error("Error reading standard input: %m");
break;
}
nibuf = 0;
} else if (nibuf == 0) {
/* end of file from stdin */
(void) close(pty_master);
pty_master = -1;
ifd = -1;
if (recordf)
&lasttime))
} else {
if (recordf)
&lasttime))
}
}
nobuf = 0;
error("Error reading pseudo-tty master: %m");
break;
}
nobuf = 0;
} else if (nobuf == 0) {
/* end of file from the pty - slave side has closed */
nibuf = 0;
ofd = -1;
if (recordf)
&lasttime))
} else {
if (recordf)
&lasttime))
}
}
if (ofd == -1)
nobuf = 0;
n = nobuf;
if (n < 0) {
ofd = -1;
nobuf = 0;
error("Error writing standard output: %m");
break;
}
} else {
obufp += n;
nobuf -= n;
olevel += n;
}
}
if (pty_master == -1)
nibuf = 0;
n = nibuf;
if (n < 0) {
continue;
error("Error writing pseudo-tty master: %m");
break;
}
(void) close(pty_master);
pty_master = -1;
nibuf = 0;
} else {
ibufp += n;
nibuf -= n;
ilevel += n;
}
}
}
exit(0);
}
static int
FILE *f;
int code;
int nb;
{
int diff;
if (diff > 0) {
if (diff > 255) {
(void) putc(RECMARK_TIMEDELTA32, f);
} else {
(void) putc(RECMARK_TIMEDELTA8, f);
}
}
}
(void) fflush(f);
if (ferror(f)) {
error("Error writing record file: %m");
return (0);
}
return (1);
}