📄 main.c
字号:
/* * main.c - Point-to-Point Protocol main module * * 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. */#define RCSID "$Id: main.c,v 1.88 1999/12/23 01:28:27 paulus Exp $"#include <stdio.h>#include <ctype.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 <utmp.h>#include <pwd.h>#include <setjmp.h>#include <sys/param.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/stat.h>#include <sys/socket.h>#include <netinet/in.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 CBCP_SUPPORT#include "cbcp.h"#endif#ifdef IPX_CHANGE#include "ipxcp.h"#endif /* IPX_CHANGE */#ifdef AT_CHANGE#include "atcp.h"#endifstatic const char rcsid[] = RCSID;/* interface vars */char ifname[32]; /* Interface name */int ifunit; /* Interface unit number */char *progname; /* Name of this program */char hostname[MAXNAMELEN]; /* Our hostname */static char pidfilename[MAXPATHLEN]; /* name of pid file */static char linkpidfile[MAXPATHLEN]; /* name of linkname pid file */static char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */static uid_t uid; /* Our real user-id */static int conn_running; /* we have a [dis]connector running */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 */struct stat devstat; /* result of stat() on devnam */int 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 */int (*holdoff_hook) __P((void)) = NULL;int (*new_phase_hook) __P((int)) = NULL;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;int open_ccp_flag;static int waiting;static sigjmp_buf sigjmp;char **script_env; /* Env. variable values for scripts */int s_env_nalloc; /* # words avail at script_env */u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */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 */static int privopen; /* don't lock, open device as root */char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";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_connect_time;int link_stats_valid;static int charshunt_pid; /* Process ID for charshunt *//* * We maintain a list of child process pids and * functions to call when they exit. */struct subprocess { pid_t pid; char *prog; void (*done) __P((void *)); void *arg; struct subprocess *next;};static struct subprocess *children;/* Prototypes for procedures local to this file. */static void create_pidfile __P((void));static void create_linkpidfile __P((void));static void cleanup __P((void));static void close_tty __P((void));static void get_input __P((void));static void calltimeout __P((void));static struct timeval *timeleft __P((struct timeval *));static void kill_my_pg __P((int));static void hup __P((int));static void term __P((int));static void chld __P((int));static void toggle_debug __P((int));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, int));static int reap_kids __P((int waitfor));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 __P((FILE *, int code, u_char *buf, int nb, struct timeval *));extern char *ttyname __P((int));extern char *getlogin __P((void));int main __P((int, char *[]));#ifdef ultrix#undef O_NONBLOCK#define O_NONBLOCK O_NDELAY#endif#ifdef ULTRIX#define setlogmask(x)#endif/* * PPP Data Link Layer "protocol" table. * One entry per supported protocol. * The last entry must be NULL. */struct protent *protocols[] = { &lcp_protent, &pap_protent, &chap_protent,#ifdef CBCP_SUPPORT &cbcp_protent,#endif &ipcp_protent,#ifdef INET6 &ipv6cp_protent,#endif &ccp_protent,#ifdef IPX_CHANGE &ipxcp_protent,#endif#ifdef AT_CHANGE &atcp_protent,#endif NULL};intmain(argc, argv) int argc; char *argv[];{ int i, fdflags, t; struct sigaction sa; char *p, *connector; struct passwd *pw; struct timeval timo; sigset_t mask; struct protent *protp; struct stat statbuf; char numbuf[16]; new_phase(PHASE_INITIALIZE); /* * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else. * This way we can close 0, 1, 2 in detach() without clobbering * a fd that we are using. */ if ((i = open("/dev/null", O_RDWR)) >= 0) { while (0 <= i && i <= 2) i = dup(i); if (i >= 0) close(i); } script_env = NULL; /* Initialize syslog facilities */ reopen_log(); if (gethostname(hostname, MAXNAMELEN) < 0 ) { option_error("Couldn't get hostname: %m"); exit(1); } hostname[MAXNAMELEN-1] = 0; /* make sure we don't create world or group writable files. */ umask(umask(0777) | 022); uid = getuid(); privileged = uid == 0; slprintf(numbuf, sizeof(numbuf), "%d", uid); script_setenv("ORIG_UID", numbuf); ngroups = getgroups(NGROUPS_MAX, groups); /* * Initialize magic number generator now so that protocols may * use magic numbers in initialization. */ magic_init(); /* * 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. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) (*protp->init)(0); progname = *argv; prepass = 0; if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1) || !options_from_user()) exit(EXIT_OPTION_ERROR); /* scan command line and options files to find device name */ prepass = 1; parse_args(argc-1, argv+1); prepass = 0; /* * Work out the device name, if it hasn't already been specified. */ using_pty = notty || ptycommand != NULL; if (!using_pty && default_device) { char *p; if (!isatty(0) || (p = ttyname(0)) == NULL) { option_error("no device specified and stdin is not a tty"); exit(EXIT_OPTION_ERROR); } strlcpy(devnam, p, sizeof(devnam)); if (stat(devnam, &devstat) < 0) fatal("Couldn't stat default device %s: %m", devnam); } /* * Parse the tty options file and the command line. * The per-tty options file should not change * ptycommand, notty or devnam. */ devnam_fixed = 1; if (!using_pty) { if (!options_for_tty()) exit(EXIT_OPTION_ERROR); } devnam_fixed = 0; if (!parse_args(argc-1, argv+1)) exit(EXIT_OPTION_ERROR); /* * 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]); exit(EXIT_NOT_ROOT); } if (!ppp_available()) { option_error(no_ppp_msg); exit(EXIT_NO_KERNEL_SUPPORT); } /* * Check that the options given are valid and consistent. */ if (!sys_check_options()) exit(EXIT_OPTION_ERROR); auth_check_options(); for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->check_options != NULL) (*protp->check_options)(); if (demand && connect_script == 0) { option_error("connect script is required for demand-dialling\n"); exit(EXIT_OPTION_ERROR); } /* default holdoff to 0 if no connect script has been given */ if (connect_script == 0 && !holdoff_specified) holdoff = 0; if (using_pty) { if (!default_device) { option_error("%s option precludes specifying device name", notty? "notty": "pty"); exit(EXIT_OPTION_ERROR); } if (ptycommand != NULL && notty) { option_error("pty option is incompatible with notty option"); exit(EXIT_OPTION_ERROR); } default_device = notty; lockflag = 0; modem = 0; if (notty && log_to_fd <= 1) 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. * If the device is already open read/write on stdin, * we assume we don't need to lock it, and we can open it as root. */ if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) { default_device = 1; fdflags = fcntl(0, F_GETFL); if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR) privopen = 1; } } if (default_device) nodetach = 1; /* * Don't send log messages to the serial port, it tends to * confuse the peer. :-) */ if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev) log_to_fd = -1; script_setenv("DEVICE", devnam); /* * Initialize system-dependent stuff. */ sys_init(); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); /* * Detach ourselves from the terminal, if required, * and identify who is running us. */ if (!nodetach && !updetach) detach(); p = getlogin(); if (p == NULL) { pw = getpwuid(uid); if (pw != NULL && pw->pw_name != NULL) p = pw->pw_name; else p = "(unknown)"; } syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d", VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid); script_setenv("PPPLOGNAME", p); /* * 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. */ sigemptyset(&mask); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGUSR2);#define SIGNAL(s, handler) do { \ sa.sa_handler = handler; \ if (sigaction(s, &sa, NULL) < 0) \ fatal("Couldn't establish signal handler (%d): %m", s); \ } while (0) sa.sa_mask = mask; sa.sa_flags = 0; SIGNAL(SIGHUP, hup); /* Hangup */ SIGNAL(SIGINT, term); /* Interrupt */ SIGNAL(SIGTERM, term); /* Terminate */ SIGNAL(SIGCHLD, chld); SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */ /* * Install a handler for other signals which would otherwise * cause pppd to exit without cleaning up. */ SIGNAL(SIGABRT, bad_signal); SIGNAL(SIGALRM, bad_signal); SIGNAL(SIGFPE, bad_signal); SIGNAL(SIGILL, bad_signal); SIGNAL(SIGPIPE, bad_signal); SIGNAL(SIGQUIT, bad_signal); SIGNAL(SIGSEGV, bad_signal);#ifdef SIGBUS SIGNAL(SIGBUS, bad_signal);#endif#ifdef SIGEMT SIGNAL(SIGEMT, bad_signal);#endif#ifdef SIGPOLL SIGNAL(SIGPOLL, bad_signal);#endif#ifdef SIGPROF SIGNAL(SIGPROF, bad_signal);#endif#ifdef SIGSYS SIGNAL(SIGSYS, bad_signal);#endif#ifdef SIGTRAP SIGNAL(SIGTRAP, bad_signal);#endif#ifdef SIGVTALRM SIGNAL(SIGVTALRM, bad_signal);#endif#ifdef SIGXCPU SIGNAL(SIGXCPU, bad_signal);#endif#ifdef SIGXFSZ SIGNAL(SIGXFSZ, bad_signal);#endif /* * Apparently we can get a SIGPIPE when we call syslog, if * syslogd has died and been restarted. Ignoring it seems * be sufficient. */ signal(SIGPIPE, SIG_IGN); waiting = 0; create_linkpidfile(); /* * 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. */ fd_loop = open_ppp_loopback(); syslog(LOG_INFO, "Using interface ppp%d", ifunit); slprintf(ifname, sizeof(ifname), "ppp%d", ifunit); script_setenv("IFNAME", ifname); create_pidfile(); /* write pid to file */ /* * Configure the interface and mark it up, etc. */ demand_conf(); } do_callback = 0; for (;;) { need_holdoff = 1; ttyfd = -1; real_ttyfd = -1; status = EXIT_OK; ++unsuccess; doing_callback = do_callback; do_callback = 0; if (demand && !doing_callback) { /* * Don't do anything until we see some activity. */ kill_link = 0; new_phase(PHASE_DORMANT); demand_unblock(); add_fd(fd_loop); for (;;) { if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &mask, NULL); if (kill_link || got_sigchld) { sigprocmask(SIG_UNBLOCK, &mask, NULL); } else { waiting = 1; sigprocmask(SIG_UNBLOCK, &mask, NULL); wait_input(timeleft(&timo)); } } waiting = 0; calltimeout(); if (kill_link) { if (!persist) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -