*/
#ifndef lint
-static char rcsid[] = "$Id: main.c,v 1.59 1999/03/16 02:50:15 paulus Exp $";
+static char rcsid[] = "$Id: main.c,v 1.70 1999/03/31 05:39:42 paulus Exp $";
#endif
#include <stdio.h>
char *progname; /* Name of this program */
char hostname[MAXNAMELEN]; /* Our hostname */
static char pidfilename[MAXPATHLEN]; /* name of pid file */
-static char default_devnam[MAXPATHLEN]; /* name of default device */
+static char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */
static pid_t pid; /* Our pid */
static uid_t uid; /* Our real user-id */
static int conn_running; /* we have a [dis]connector running */
-int ttyfd = -1; /* Serial port file descriptor */
+int ttyfd; /* Serial port file descriptor */
mode_t tty_mode = -1; /* Original access permissions to tty */
int baud_rate; /* Actual bits/second for serial device */
int hungup; /* terminal has been hung up */
int privileged; /* we're running as real uid root */
int need_holdoff; /* need holdoff period before restarting */
int detached; /* have detached from terminal */
-int log_to_stderr; /* send log messages to stderr too */
+int log_to_fd; /* send log messages to this fd too */
-static int fd_ppp; /* fd for talking PPP */
+static int fd_ppp = -1; /* fd for talking PPP */
static int fd_loop; /* fd for getting demand-dial packets */
+static int pty_master; /* fd for master side of pty */
+static int pty_slave; /* fd for slave side of pty */
+static int real_ttyfd; /* fd for actual serial port (not pty) */
int phase; /* where the link is at */
int kill_link;
u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
static int n_children; /* # child processes still running */
+static int got_sigchld; /* set if we have received a SIGCHLD */
static int locked; /* lock() has succeeded */
GIDSET_TYPE groups[NGROUPS_MAX];/* groups the user is in */
int ngroups; /* How many groups valid in groups */
+static struct timeval start_time; /* Time when link was started. */
+
+struct pppd_stats link_stats;
+int link_stats_valid;
+
+static int charshunt_pid; /* Process ID for charshunt */
+
/* Prototypes for procedures local to this file. */
static void create_pidfile __P((void));
static void open_ccp __P((int));
static void bad_signal __P((int));
static void holdoff_end __P((void *));
-static int device_script __P((char *, int, int));
-static void reap_kids __P((void));
+static int device_script __P((char *, int, int, int));
+static void reap_kids __P((int waitfor));
static void pr_log __P((void *, char *, ...));
static void logit __P((int, char *, va_list));
static void vslp_printer __P((void *, char *, ...));
+static void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
+ void *));
+static void record_child __P((int, char *, void (*) (void *), void *));
+static int start_charshunt __P((int, int));
+static void charshunt_done __P((void *));
+static void charshunt __P((int, int, char *));
+static int record_write(FILE *, int code, u_char *buf, int nb,
+ struct timeval *);
extern char *ttyname __P((int));
extern char *getlogin __P((void));
struct protent *protp;
struct stat statbuf;
char numbuf[16];
+ struct timeval now;
phase = PHASE_INITIALIZE;
- p = ttyname(0);
- if (p)
- strlcpy(devnam, sizeof(devnam), p);
- strlcpy(default_devnam, sizeof(default_devnam), devnam);
+ log_to_fd = -1;
script_env = NULL;
/* Initialize syslog facilities */
-#ifdef ULTRIX
- openlog("pppd", LOG_PID);
-#else
- openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
- setlogmask(LOG_UPTO(LOG_INFO));
-#endif
+ reopen_log();
if (gethostname(hostname, MAXNAMELEN) < 0 ) {
option_error("Couldn't get hostname: %m");
if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
|| !options_from_user())
exit(1);
+ using_pty = notty || ptycommand != NULL;
scan_args(argc-1, argv+1); /* look for tty name on command line */
+
+ /*
+ * Work out the device name, if it hasn't already been specified.
+ */
+ if (!using_pty) {
+ p = isatty(0)? ttyname(0): NULL;
+ if (p != NULL) {
+ if (default_device)
+ strlcpy(devnam, p, sizeof(devnam));
+ else if (strcmp(devnam, p) == 0)
+ default_device = 1;
+ }
+ }
+
+ /*
+ * Parse the tty options file and the command line.
+ */
if (!options_for_tty()
|| !parse_args(argc-1, argv+1))
exit(1);
exit(1);
}
- script_setenv("DEVICE", devnam);
- slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
- script_setenv("SPEED", numbuf);
-
- /*
- * If the user has specified the default device name explicitly,
- * pretend they hadn't.
- */
- if (!default_device && strcmp(devnam, default_devnam) == 0)
- default_device = 1;
+ if (using_pty) {
+ if (!default_device) {
+ option_error("%s option precludes specifying device name",
+ notty? "notty": "pty");
+ exit(1);
+ }
+ if (ptycommand != NULL && notty) {
+ option_error("pty option is incompatible with notty option");
+ exit(1);
+ }
+ default_device = notty;
+ lockflag = 0;
+ modem = 0;
+ } else {
+ if (devnam[0] == 0) {
+ option_error("no device specified and stdin is not a tty");
+ exit(1);
+ }
+ }
if (default_device)
nodetach = 1;
- log_to_stderr = !default_device;
+ else
+ log_to_fd = 1; /* default to stdout */
+
+ script_setenv("DEVICE", devnam);
/*
* Initialize system-dependent stuff and magic number package.
for (;;) {
need_holdoff = 1;
+ ttyfd = -1;
+ real_ttyfd = -1;
if (demand) {
/*
}
if (get_loop_output())
break;
- reap_kids();
+ if (got_sigchld)
+ reap_kids(0);
}
remove_fd(fd_loop);
if (kill_link && !persist)
info("Starting link");
}
+ /*
+ * Get a pty master/slave pair if the pty, notty, or record
+ * options were specified.
+ */
+ strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+ pty_master = -1;
+ pty_slave = -1;
+ if (ptycommand != NULL || notty || record_file != NULL) {
+ if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
+ error("Couldn't allocate pseudo-tty");
+ goto fail;
+ }
+ set_up_tty(pty_slave, 1);
+ }
+
/*
* Lock the device if we've been asked to.
*/
*/
hungup = 0;
kill_link = 0;
- for (;;) {
- /* If the user specified the device name, become the
- user before opening it. */
- if (!devnam_info.priv)
- seteuid(uid);
- ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
- if (!devnam_info.priv)
- seteuid(0);
- if (ttyfd >= 0)
- break;
- if (errno != EINTR)
- error("Failed to open %s: %m", devnam);
- if (!persist || errno != EINTR)
- goto fail;
- }
- if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
- || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
- warn("Couldn't reset non-blocking mode on device: %m");
-
- /*
- * Do the equivalent of `mesg n' to stop broadcast messages.
- */
- if (fstat(ttyfd, &statbuf) < 0
- || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
- warn("Couldn't restrict write permissions to %s: %m", devnam);
- } else
- tty_mode = statbuf.st_mode;
-
- /* run connection script */
- if (connector && connector[0]) {
- MAINDEBUG(("Connecting with <%s>", connector));
-
- if (!default_device && modem) {
- hangup_modem(ttyfd); /* in case modem is off hook */
- sleep(1);
+ if (devnam[0] != 0) {
+ for (;;) {
+ /* If the user specified the device name, become the
+ user before opening it. */
+ if (!devnam_info.priv && !default_device)
+ seteuid(uid);
+ ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+ if (!devnam_info.priv && !default_device)
+ seteuid(0);
+ if (ttyfd >= 0)
+ break;
+ if (errno != EINTR)
+ error("Failed to open %s: %m", devnam);
+ if (!persist || errno != EINTR)
+ goto fail;
}
+ if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+ || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ warn("Couldn't reset non-blocking mode on device: %m");
+
+ /*
+ * Do the equivalent of `mesg n' to stop broadcast messages.
+ */
+ if (fstat(ttyfd, &statbuf) < 0
+ || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+ warn("Couldn't restrict write permissions to %s: %m", devnam);
+ } else
+ tty_mode = statbuf.st_mode;
/*
* Set line speed, flow control, etc.
- * On most systems we set CLOCAL for now so that we can talk
+ * If we have a non-null connection 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.
*/
- set_up_tty(ttyfd, 1);
+ set_up_tty(ttyfd, (connector != NULL && connector[0] != 0));
+ real_ttyfd = ttyfd;
+ }
+
+ /*
+ * If the notty and/or record option was specified,
+ * start up the character shunt now.
+ */
+ if (ptycommand != NULL) {
+ if (record_file != NULL) {
+ int ipipe[2], opipe[2], ok;
+
+ if (pipe(ipipe) < 0 || pipe(opipe) < 0)
+ fatal("Couldn't create pipes for record option: %m");
+ ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
+ && start_charshunt(ipipe[0], opipe[1]);
+ close(ipipe[0]);
+ close(ipipe[1]);
+ close(opipe[0]);
+ close(opipe[1]);
+ if (!ok)
+ goto fail;
+ } else {
+ if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
+ goto fail;
+ ttyfd = pty_slave;
+ close(pty_master);
+ pty_master = -1;
+ }
+ } else if (notty) {
+ if (!start_charshunt(0, 1))
+ goto fail;
+ } else if (record_file != NULL) {
+ if (!start_charshunt(ttyfd, ttyfd))
+ goto fail;
+ }
- if (device_script(connector, ttyfd, ttyfd) < 0) {
+ /* run connection script */
+ if (connector && connector[0]) {
+ MAINDEBUG(("Connecting with <%s>", connector));
+
+ if (real_ttyfd != -1) {
+ if (!default_device && modem) {
+ setdtr(real_ttyfd, 0); /* in case modem is off hook */
+ sleep(1);
+ setdtr(real_ttyfd, 1);
+ }
+ }
+
+ if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
error("Connect script failed");
goto fail;
}
info("Serial connection established.");
- sleep(1); /* give it time to set up its terminal */
- }
- /* set line speed, flow control, etc.; clear CLOCAL if modem option */
- set_up_tty(ttyfd, 0);
+ /* set line speed, flow control, etc.;
+ clear CLOCAL if modem option */
+ if (real_ttyfd != -1)
+ set_up_tty(real_ttyfd, 0);
+ }
/* reopen tty if necessary to wait for carrier */
- if (connector == NULL && modem) {
+ if (connector == NULL && modem && devnam[0] != 0) {
for (;;) {
if ((i = open(devnam, O_RDWR)) >= 0)
break;
close(i);
}
+ slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
+ script_setenv("SPEED", numbuf);
+
/* run welcome script, if any */
if (welcomer && welcomer[0]) {
- if (device_script(welcomer, ttyfd, ttyfd) < 0)
+ if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
warn("Welcome script failed");
}
/* set up the serial device as a ppp interface */
fd_ppp = establish_ppp(ttyfd);
+ if (fd_ppp < 0)
+ goto fail;
if (!demand) {
* Start opening the connection and wait for
* incoming events (reply, timeout, etc.).
*/
- notice("Connect: %s <--> %s", ifname, devnam);
+ notice("Connect: %s <--> %s", ifname, ppp_devnam);
+ gettimeofday(&start_time, NULL);
lcp_lowerup(0);
+
+ /*
+ * 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.
+ */
+ if (connector != NULL || ptycommand != NULL) {
+ struct timeval t;
+ t.tv_sec = 1;
+ t.tv_usec = 0;
+ wait_input(&t);
+ }
+
lcp_open(0); /* Start protocol */
open_ccp_flag = 0;
add_fd(fd_ppp);
}
waiting = 0;
calltimeout();
+ get_input();
if (kill_link) {
lcp_close(0, "User request");
kill_link = 0;
}
- get_input();
if (open_ccp_flag) {
if (phase == PHASE_NETWORK) {
ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
}
open_ccp_flag = 0;
}
- reap_kids(); /* Don't leave dead kids lying around */
+ if (got_sigchld)
+ reap_kids(0); /* Don't leave dead kids lying around */
+ }
+
+ /*
+ * Print connect time and statistics.
+ */
+ if (gettimeofday(&now, NULL) >= 0) {
+ int t = now.tv_sec - start_time.tv_sec;
+ t = (t + 5) / 6; /* now in 1/10ths of minutes */
+ info("Connect time %d.%d minutes.", t/10, t%10);
+ }
+ if (link_stats_valid) {
+ info("Sent %d bytes, received %d bytes.",
+ link_stats.bytes_out, link_stats.bytes_in);
}
/*
if (demand)
restore_loop();
disestablish_ppp(ttyfd);
+ fd_ppp = -1;
/*
* Run disconnector script, if requested.
* XXX we may not be able to do this if the line has hung up!
*/
if (disconnector && !hungup) {
- set_up_tty(ttyfd, 1);
- if (device_script(disconnector, ttyfd, ttyfd) < 0) {
+ if (real_ttyfd >= 0)
+ set_up_tty(real_ttyfd, 1);
+ if (device_script(disconnector, ttyfd, ttyfd, 0) < 0) {
warn("disconnect script failed");
} else {
info("Serial link disconnected.");
}
}
+ if (!hungup)
+ lcp_lowerdown(0);
fail:
- if (ttyfd >= 0)
+ if (pty_master >= 0)
+ close(pty_master);
+ if (pty_slave >= 0)
+ close(pty_slave);
+ if (real_ttyfd >= 0)
close_tty();
if (locked) {
unlock();
kill_link = 0;
phase = PHASE_DORMANT; /* allow signal to end holdoff */
}
- reap_kids();
+ if (got_sigchld)
+ reap_kids(0);
} while (phase == PHASE_HOLDOFF);
if (!persist)
break;
/* Wait for scripts to finish */
while (n_children > 0)
- reap_kids();
+ reap_kids(1);
die(0);
return 0;
die(1);
}
detached = 1;
- log_to_stderr = 0;
+ log_to_fd = -1;
pid = getpid();
/* update pid file if it has been written already */
if (pidfilename[0])
create_pidfile();
}
+/*
+ * reopen_log - (re)open our connection to syslog.
+ */
+void
+reopen_log()
+{
+#ifdef ULTRIX
+ openlog("pppd", LOG_PID);
+#else
+ openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+}
+
/*
* Create a file containing our process ID.
*/
/*
- * quit - Clean up state and exit (with an error indication).
- */
-void
-quit()
-{
- die(1);
-}
-
-/*
- * die - like quit, except we can specify an exit status.
+ * die - clean up state and exit with the specified status.
*/
void
die(status)
{
sys_cleanup();
- if (ttyfd >= 0)
+ if (fd_ppp >= 0)
+ disestablish_ppp(ttyfd);
+ if (real_ttyfd >= 0)
close_tty();
if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
static void
close_tty()
{
- disestablish_ppp(ttyfd);
-
/* drop dtr to hang up */
if (!default_device && modem) {
- hangup_modem(ttyfd);
+ setdtr(real_ttyfd, 0);
/*
* This sleep is in case the serial port has CLOCAL set by default,
* and consequently will reassert DTR when we close the device.
sleep(1);
}
- restore_tty(ttyfd);
+ restore_tty(real_ttyfd);
if (tty_mode != (mode_t) -1) {
- if (fchmod(ttyfd, tty_mode) != 0) {
+ if (fchmod(real_ttyfd, tty_mode) != 0) {
/* XXX if devnam is a symlink, this will change the link */
chmod(devnam, tty_mode);
}
}
- close(ttyfd);
- ttyfd = -1;
-}
-
-/*
- * hangup_modem - hang up the modem by clearing DTR.
- */
-void hangup_modem(ttyfd)
- int ttyfd;
-{
- setdtr(ttyfd, 0);
+ close(real_ttyfd);
+ real_ttyfd = -1;
}
if (conn_running)
/* Send the signal to the [dis]connector process(es) also */
kill_my_pg(sig);
+ if (charshunt_pid)
+ kill(charshunt_pid, sig);
if (waiting)
siglongjmp(sigjmp, 1);
}
if (conn_running)
/* Send the signal to the [dis]connector process(es) also */
kill_my_pg(sig);
+ if (charshunt_pid)
+ kill(charshunt_pid, sig);
if (waiting)
siglongjmp(sigjmp, 1);
}
/*
* chld - Catch SIGCHLD signal.
- * Calls reap_kids to get status for any dead kids.
+ * Sets a flag so we will call reap_kids in the mainline.
*/
static void
chld(sig)
int sig;
{
+ got_sigchld = 1;
if (waiting)
siglongjmp(sigjmp, 1);
}
error("Fatal signal %d", sig);
if (conn_running)
kill_my_pg(SIGTERM);
+ if (charshunt_pid)
+ kill(charshunt_pid, SIGTERM);
die(1);
}
/*
- * device_script - run a program to connect or disconnect the
- * serial device.
+ * device_script - run a program to talk to the serial device
+ * (e.g. to run the connector or disconnector script).
*/
static int
-device_script(program, in, out)
+device_script(program, in, out, dont_wait)
char *program;
int in, out;
+ int dont_wait;
{
int pid;
- int status;
+ int status = 0;
int errfd;
- conn_running = 1;
+ ++conn_running;
pid = fork();
if (pid < 0) {
- conn_running = 0;
- fatal("Failed to create child process: %m");
+ --conn_running;
+ error("Failed to create child process: %m");
+ return -1;
}
if (pid == 0) {
sys_close();
closelog();
- if (in == out) {
- if (in != 0) {
- dup2(in, 0);
- close(in);
- }
- dup2(0, 1);
- } else {
- if (out == 0)
- out = dup(out);
- if (in != 0) {
- dup2(in, 0);
- close(in);
- }
- if (out != 1) {
- dup2(out, 1);
- close(out);
- }
+ if (in == 2) {
+ /* aargh!!! */
+ int newin = dup(in);
+ if (in == out)
+ out = newin;
+ in = newin;
+ } else if (out == 2) {
+ out = dup(out);
}
- if (!nodetach && !updetach) {
+ if (log_to_fd >= 0) {
+ if (log_to_fd != 2)
+ dup2(log_to_fd, 2);
+ } else {
close(2);
errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
if (errfd >= 0 && errfd != 2) {
close(errfd);
}
}
+ if (in != 0) {
+ if (out == 0)
+ out = dup(out);
+ dup2(in, 0);
+ if (in > 2)
+ close(in);
+ }
+ if (out != 1) {
+ dup2(out, 1);
+ if (out > 2)
+ close(out);
+ }
+ if (real_ttyfd > 2)
+ close(real_ttyfd);
+ if (pty_master > 2)
+ close(pty_master);
+ if (pty_slave > 2)
+ close(pty_slave);
setuid(uid);
if (getuid() != uid) {
error("setuid failed");
/* NOTREACHED */
}
- while (waitpid(pid, &status, 0) < 0) {
- if (errno == EINTR)
- continue;
- fatal("error waiting for (dis)connection process: %m");
+ if (dont_wait) {
+ record_child(pid, program, NULL, NULL);
+ } else {
+ while (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ fatal("error waiting for (dis)connection process: %m");
+ }
+ --conn_running;
}
- conn_running = 0;
return (status == 0 ? 0 : -1);
}
void *arg;
{
int pid;
- struct subprocess *chp;
struct stat sbuf;
/*
}
pid = fork();
- if (pid == -1)
- fatal("Failed to create child process for %s: %m", prog);
+ if (pid == -1) {
+ error("Failed to create child process for %s: %m", prog);
+ return -1;
+ }
if (pid == 0) {
int new_fd;
close (1);
close (2);
close (ttyfd); /* tty interface to the ppp device */
+ if (real_ttyfd >= 0)
+ close(real_ttyfd);
/* Don't pass handles to the PPP device, even by accident. */
new_fd = open (_PATH_DEVNULL, O_RDWR);
/* run the program */
execve(prog, args, script_env);
- if (must_exist || errno != ENOENT)
- warn("Can't execute %s: %m", prog);
+ if (must_exist || errno != ENOENT) {
+ /* have to reopen the log, there's nowhere else
+ for the message to go. */
+ reopen_log();
+ syslog(LOG_ERR, "Can't execute %s: %m", prog);
+ closelog();
+ }
_exit(-1);
}
if (debug)
dbglog("Script %s started; pid = %d", prog, pid);
+ record_child(pid, prog, done, arg);
+
+ return pid;
+}
+
+
+/*
+ * record_child - add a child process to the list for reap_kids
+ * to use.
+ */
+static void
+record_child(pid, prog, done, arg)
+ int pid;
+ char *prog;
+ void (*done) __P((void *));
+ void *arg;
+{
+ struct subprocess *chp;
+
++n_children;
chp = (struct subprocess *) malloc(sizeof(struct subprocess));
chp->next = children;
children = chp;
}
-
- return pid;
}
* and log a message for abnormal terminations.
*/
static void
-reap_kids()
+reap_kids(waitfor)
+ int waitfor;
{
int pid, status;
struct subprocess *chp, **prevp;
+ got_sigchld = 0;
if (n_children == 0)
return;
- while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
+ while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1
+ && pid != 0) {
--n_children;
for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next)
if (chp->pid == pid)
break;
- if (debug)
- dbglog("process %d (%s) finished, status = 0x%x",
- pid, (chp? chp->prog: "??"), status);
if (WIFSIGNALED(status)) {
warn("Child process %s (pid %d) terminated with signal %d",
(chp? chp->prog: "??"), pid, WTERMSIG(status));
- }
+ } else if (debug)
+ dbglog("process %d (%s) finished, status = 0x%x",
+ pid, (chp? chp->prog: "??"), status);
if (chp && chp->done)
(*chp->done)(chp->arg);
}
- if (pid == -1 && errno != ECHILD)
+ if (pid == -1 && errno != ECHILD && errno != EINTR)
error("Error waiting for child process: %m");
}
char *prefix;
int level;
{
- strlcpy(line, sizeof(line), prefix);
+ strlcpy(line, prefix, sizeof(line));
linep = line + strlen(line);
format_packet(p, len, pr_log, NULL);
if (linep != line)
* format_packet - make a readable representation of a packet,
* calling `printer(arg, format, ...)' to output it.
*/
-void
+static void
format_packet(p, len, printer, arg)
u_char *p;
int len;
{
int i, n;
u_short proto;
- u_char x;
struct protent *protp;
if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
p += n;
len -= n;
} else {
- printer(arg, "[proto=0x%x]", proto);
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == (protp->protocol & ~0x8000))
+ break;
+ if (protp != 0 && protp->data_name != 0) {
+ printer(arg, "[%s data]", protp->data_name);
+ if (len > 8)
+ printer(arg, "%.8B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
+ len = 0;
+ } else
+ printer(arg, "[proto=0x%x]", proto);
}
}
- for (i = 0; i < len && i < 32; ++i) {
- GETCHAR(x, p);
- printer(arg, " %.2x", x);
- }
- if (i < len)
- printer(arg, " ...");
+ if (len > 32)
+ printer(arg, "%.32B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
}
static void
syslog(LOG_DEBUG, "%s", line);
linep = line;
}
- strlcpy(linep, line + sizeof(line) - linep, buf);
+ strlcpy(linep, buf, line + sizeof(line) - linep);
linep += n;
}
* strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
* always leaves destination null-terminated (for len > 0).
*/
-void
-strlcpy(char *dest, size_t len, const char *src)
+size_t
+strlcpy(dest, src, len)
+ char *dest;
+ const char *src;
+ size_t len;
{
- if (len == 0)
- return;
- if (strlen(src) < len)
- strcpy(dest, src);
- else {
- strncpy(dest, src, len - 1);
- dest[len-1] = 0;
+ size_t ret = strlen(src);
+
+ if (len != 0) {
+ if (ret < len)
+ strcpy(dest, src);
+ else {
+ strncpy(dest, src, len - 1);
+ dest[len-1] = 0;
+ }
}
+ return ret;
}
/*
* strlcat - like strcat/strncat, doesn't overflow destination buffer,
* always leaves destination null-terminated (for len > 0).
*/
-void
-strlcat(char *dest, size_t len, const char *src)
+size_t
+strlcat(dest, src, len)
+ char *dest;
+ const char *src;
+ size_t len;
{
- size_t dlen;
+ size_t dlen = strlen(dest);
- if (len == 0)
- return;
- dlen = strlen(dest);
- if (dlen < len - 1)
- strlcpy(dest + dlen, len - dlen, src);
+ return dlen + strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0));
}
/*
{
int n;
char buf[256];
- static char nl = '\n';
n = vslprintf(buf, sizeof(buf), fmt, args);
syslog(level, "%s", buf);
- if (log_to_stderr) {
- if (write(2, buf, n) != n
- || (buf[n-1] != '\n' && write(2, &nl, 1) != 1))
- log_to_stderr = 0;
+ if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
+ if (buf[n-1] != '\n')
+ buf[n++] = '\n';
+ if (write(log_to_fd, buf, n) != n)
+ log_to_fd = -1;
}
}
logit(LOG_DEBUG, fmt, pvar);
va_end(pvar);
}
+
+/*
+ * start_charshunt - create a child process to run the character shunt.
+ */
+static int
+start_charshunt(ifd, ofd)
+ int ifd, ofd;
+{
+ int cpid;
+
+ cpid = fork();
+ if (cpid == -1) {
+ error("Can't fork process for character shunt: %m");
+ return 0;
+ }
+ if (cpid == 0) {
+ /* child */
+ close(pty_slave);
+ setuid(uid);
+ if (getuid() != uid)
+ fatal("setuid failed");
+ setgid(getgid());
+ if (!nodetach)
+ log_to_fd = -1;
+ charshunt(ifd, ofd, record_file);
+ exit(0);
+ }
+ charshunt_pid = cpid;
+ close(pty_master);
+ pty_master = -1;
+ ttyfd = pty_slave;
+ record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
+ return 1;
+}
+
+static void
+charshunt_done(arg)
+ void *arg;
+{
+ charshunt_pid = 0;
+}
+
+/*
+ * charshunt - the character shunt, which passes characters between
+ * the pty master side and the serial port (or stdin/stdout).
+ * This runs as the user (not as root).
+ * (We assume ofd >= ifd which is true the way this gets called. :-).
+ */
+static void
+charshunt(ifd, ofd, record_file)
+ int ifd, ofd;
+ char *record_file;
+{
+ int n, nfds;
+ fd_set ready, writey;
+ u_char *ibufp, *obufp;
+ int nibuf, nobuf;
+ int flags;
+ int pty_readable, stdin_readable;
+ struct timeval lasttime;
+ FILE *recordf = NULL;
+
+ /*
+ * Reset signal handlers.
+ */
+ signal(SIGHUP, SIG_IGN); /* Hangup */
+ signal(SIGINT, SIG_DFL); /* Interrupt */
+ signal(SIGTERM, SIG_DFL); /* Terminate */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+#ifdef SIGBUS
+ signal(SIGBUS, SIG_DFL);
+#endif
+#ifdef SIGEMT
+ signal(SIGEMT, SIG_DFL);
+#endif
+#ifdef SIGPOLL
+ signal(SIGPOLL, SIG_DFL);
+#endif
+#ifdef SIGPROF
+ signal(SIGPROF, SIG_DFL);
+#endif
+#ifdef SIGSYS
+ signal(SIGSYS, SIG_DFL);
+#endif
+#ifdef SIGTRAP
+ signal(SIGTRAP, SIG_DFL);
+#endif
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, SIG_DFL);
+#endif
+#ifdef SIGXCPU
+ signal(SIGXCPU, SIG_DFL);
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_DFL);
+#endif
+
+ /*
+ * Open the record file if required.
+ */
+ if (record_file != NULL) {
+ recordf = fopen(record_file, "a");
+ if (recordf == NULL)
+ error("Couldn't create record file %s: %m", record_file);
+ }
+
+ /* set all the fds to non-blocking mode */
+ flags = fcntl(pty_master, F_GETFL);
+ if (flags == -1
+ || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set pty master to nonblock: %m");
+ flags = fcntl(ifd, F_GETFL);
+ if (flags == -1
+ || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
+ if (ofd != ifd) {
+ flags = fcntl(ofd, F_GETFL);
+ if (flags == -1
+ || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set stdout to nonblock: %m");
+ }
+
+ nibuf = nobuf = 0;
+ ibufp = obufp = NULL;
+ pty_readable = stdin_readable = 1;
+ nfds = (ofd > pty_master? ofd: pty_master) + 1;
+ if (recordf != NULL) {
+ gettimeofday(&lasttime, NULL);
+ putc(7, recordf); /* put start marker */
+ putc(lasttime.tv_sec >> 24, recordf);
+ putc(lasttime.tv_sec >> 16, recordf);
+ putc(lasttime.tv_sec >> 8, recordf);
+ putc(lasttime.tv_sec, recordf);
+ lasttime.tv_usec = 0;
+ }
+
+ while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
+ FD_ZERO(&ready);
+ FD_ZERO(&writey);
+ if (nibuf != 0)
+ FD_SET(pty_master, &writey);
+ else if (stdin_readable)
+ FD_SET(ifd, &ready);
+ if (nobuf != 0)
+ FD_SET(ofd, &writey);
+ else if (pty_readable)
+ FD_SET(pty_master, &ready);
+ if (select(nfds, &ready, &writey, NULL, NULL) < 0) {
+ if (errno != EINTR)
+ fatal("select");
+ continue;
+ }
+ if (FD_ISSET(ifd, &ready)) {
+ ibufp = inpacket_buf;
+ nibuf = read(ifd, ibufp, sizeof(inpacket_buf));
+ if (nibuf < 0 && errno == EIO)
+ nibuf = 0;
+ if (nibuf < 0) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading standard input: %m");
+ break;
+ }
+ nibuf = 0;
+ } else if (nibuf == 0) {
+ /* end of file from stdin */
+ stdin_readable = 0;
+ /* do a 0-length write, hopefully this will generate
+ an EOF (hangup) on the slave side. */
+ write(pty_master, inpacket_buf, 0);
+ if (recordf)
+ if (!record_write(recordf, 4, NULL, 0, &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(pty_master, &writey);
+ if (recordf)
+ if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (FD_ISSET(pty_master, &ready)) {
+ obufp = outpacket_buf;
+ nobuf = read(pty_master, obufp, sizeof(outpacket_buf));
+ if (nobuf < 0 && errno == EIO)
+ nobuf = 0;
+ if (nobuf < 0) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading pseudo-tty master: %m");
+ break;
+ }
+ nobuf = 0;
+ } else if (nobuf == 0) {
+ /* end of file from the pty - slave side has closed */
+ pty_readable = 0;
+ stdin_readable = 0; /* pty is not writable now */
+ nibuf = 0;
+ close(ofd);
+ if (recordf)
+ if (!record_write(recordf, 3, NULL, 0, &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(ofd, &writey);
+ if (recordf)
+ if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (FD_ISSET(ofd, &writey)) {
+ n = write(ofd, obufp, nobuf);
+ if (n < 0) {
+ if (errno != EIO) {
+ error("Error writing standard output: %m");
+ break;
+ }
+ pty_readable = 0;
+ nobuf = 0;
+ } else {
+ obufp += n;
+ nobuf -= n;
+ }
+ }
+ if (FD_ISSET(pty_master, &writey)) {
+ n = write(pty_master, ibufp, nibuf);
+ if (n < 0) {
+ if (errno != EIO) {
+ error("Error writing pseudo-tty master: %m");
+ break;
+ }
+ stdin_readable = 0;
+ nibuf = 0;
+ } else {
+ ibufp += n;
+ nibuf -= n;
+ }
+ }
+ }
+ exit(0);
+}
+
+static int
+record_write(f, code, buf, nb, tp)
+ FILE *f;
+ int code;
+ u_char *buf;
+ int nb;
+ struct timeval *tp;
+{
+ struct timeval now;
+ int diff;
+
+ gettimeofday(&now, NULL);
+ now.tv_usec /= 100000; /* actually 1/10 s, not usec now */
+ diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
+ if (diff > 0) {
+ if (diff > 255) {
+ putc(5, f);
+ putc(diff >> 24, f);
+ putc(diff >> 16, f);
+ putc(diff >> 8, f);
+ putc(diff, f);
+ } else {
+ putc(6, f);
+ putc(diff, f);
+ }
+ *tp = now;
+ }
+ putc(code, f);
+ if (buf != NULL) {
+ putc(nb >> 8, f);
+ putc(nb, f);
+ fwrite(buf, nb, 1, f);
+ }
+ fflush(f);
+ if (ferror(f)) {
+ error("Error writing record file: %m");
+ return 0;
+ }
+ return 1;
+}