📄 main.c.zhh
字号:
/* * 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.105 2001/03/12 22:58:59 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 <arpa/inet.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 "tdb.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 */struct channel *the_channel;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 */char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */uid_t uid; /* Our real user-id */struct notifier *pidchange = NULL;struct notifier *phasechange = NULL;struct notifier *exitnotify = NULL;struct notifier *sigreceived = NULL;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 */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 */TDB_CONTEXT *pppdb; /* database for storing status etc. */char db_key[32];int (*holdoff_hook) __P((void)) = NULL;int (*new_phase_hook) __P((int)) = NULL;static int conn_running; /* we have a [dis]connector running */static int devfd; /* fd of underlying device */static int fd_ppp = -1; /* fd for talking PPP */static int fd_loop; /* fd for getting demand-dial packets */int phase; /* where the link is at */int kill_link;int open_ccp_flag;int listen_time;int got_sigusr2;int got_sigterm;int got_sighup;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 */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;/* * 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 setup_signals __P((void));static void create_pidfile __P((void));static void create_linkpidfile __P((void));static void cleanup __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 reap_kids __P((int waitfor));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));static void handle_events __P((void));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};/* * If PPP_DRV_NAME is not defined, use the default "ppp" as the device name. */#if !defined(PPP_DRV_NAME)#define PPP_DRV_NAME "ppp"#endif /* !defined(PPP_DRV_NAME) */intmain(argc, argv) int argc; char *argv[];{ int i, t; char *p; struct passwd *pw; struct protent *protp; 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, 0); ngroups = getgroups(NGROUPS_MAX, groups); /* * Initialize magic number generator now so that protocols may * use magic numbers in initialization. */ magic_init(); /* * Initialize each protocol. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) (*protp->init)(0); /* * Initialize the default channel. */ tty_init(); progname = *argv; /* * Parse, in order, the system options file, the user's options file, * and the command line arguments. */ if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1) || !options_from_user() || !parse_args(argc-1, argv+1)) exit(EXIT_OPTION_ERROR); devnam_fixed = 1; /* can no longer change device name */ /* * Work out the device name, if it hasn't already been specified, * and parse the tty's options file. */ if (the_channel->process_extra_options) (*the_channel->process_extra_options)(); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); /* * 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("%s", no_ppp_msg); exit(EXIT_NO_KERNEL_SUPPORT); } /* * Check that the options given are valid and consistent. */ check_options(); if (!sys_check_options()) exit(EXIT_OPTION_ERROR); auth_check_options();#ifdef HAVE_MULTILINK mp_check_options();#endif for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->check_options != NULL) (*protp->check_options)(); if (the_channel->check_options) (*the_channel->check_options)(); if (dump_options || dryrun) { init_pr_log(NULL, LOG_INFO); print_options(pr_log, NULL); end_pr_log(); if (dryrun) die(0); } /* * Initialize system-dependent stuff. */ sys_init(); pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644); if (pppdb != NULL) { slprintf(db_key, sizeof(db_key), "pppd%d", getpid()); update_db_entry(); } else { warn("Warning: couldn't open ppp database %s", _PATH_PPPDB); if (multilink) { warn("Warning: disabling multilink"); multilink = 0; } } /* * 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 started by %s, uid %d", VERSION, p, uid); script_setenv("PPPLOGNAME", p, 0); if (devnam[0]) script_setenv("DEVICE", devnam, 1); slprintf(numbuf, sizeof(numbuf), "%d", getpid()); script_setenv("PPPD_PID", numbuf, 1); setup_signals(); 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. */ tdb_writelock(pppdb); fd_loop = open_ppp_loopback(); set_ifunit(1); tdb_writeunlock(pppdb); /* * Configure the interface and mark it up, etc. */ demand_conf(); } do_callback = 0; for (;;) { listen_time = 0; need_holdoff = 1; devfd = -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. */ new_phase(PHASE_DORMANT); demand_unblock(); add_fd(fd_loop); for (;;) { handle_events(); if (kill_link && !persist) break; if (get_loop_output()) break; } remove_fd(fd_loop); if (kill_link && !persist) break; /* * Now we want to bring up the link. */ demand_block(); info("Starting link"); } new_phase(PHASE_SERIALCONN); devfd = the_channel->connect(); if (devfd < 0) goto fail; /* set up the serial device as a ppp interface */ tdb_writelock(pppdb); fd_ppp = the_channel->establish_ppp(devfd); if (fd_ppp < 0) { tdb_writeunlock(pppdb); status = EXIT_FATAL_ERROR; goto disconnect; } if (!demand && ifunit >= 0) set_ifunit(1); tdb_writeunlock(pppdb); /* * Start opening the connection and wait for * incoming events (reply, timeout, etc.). */ notice("Connect: %s <--> %s", ifname, ppp_devnam); gettimeofday(&start_time, NULL); link_stats_valid = 0; script_unsetenv("CONNECT_TIME"); script_unsetenv("BYTES_SENT"); script_unsetenv("BYTES_RCVD"); lcp_lowerup(0); add_fd(fd_ppp); lcp_open(0); /* Start protocol */ status = EXIT_NEGOTIATION_FAILED; new_phase(PHASE_ESTABLISH); while (phase != PHASE_DEAD) { handle_events(); get_input(); if (kill_link) lcp_close(0, "User request"); if (open_ccp_flag) { if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) { ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */ (*ccp_protent.open)(0); } } } /* * Print connect time and statistics. */ if (link_stats_valid) { int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */ info("Connect time %d.%d minutes.", t/10, t%10); info("Sent %u bytes, received %u bytes.", link_stats.bytes_out, link_stats.bytes_in); } /* * 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 && unlink(pidfilename) < 0 && errno != ENOENT) warn("unable to delete pid file %s: %m", pidfilename); 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. */ remove_fd(fd_ppp); clean_check(); the_channel->disestablish_ppp(devfd); 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! */ disconnect: new_phase(PHASE_DISCONNECT); the_channel->disconnect(); fail: if (the_channel->cleanup) (*the_channel->cleanup)(); if (!demand) { if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) warn("unable to delete pid file %s: %m", pidfilename); pidfilename[0] = 0; } if (!persist || (maxfail > 0 && unsuccess >= maxfail)) break; if (demand) demand_discard(); t = need_holdoff? holdoff: 0; if (holdoff_hook) t = (*holdoff_hook)(); if (t > 0) { new_phase(PHASE_HOLDOFF); TIMEOUT(holdoff_end, NULL, t); do { handle_events(); if (kill_link) new_phase(PHASE_DORMANT); /* allow signal to end holdoff */ } while (phase == PHASE_HOLDOFF); if (!persist) break; } } /* Wait for scripts to finish */ /* XXX should have a timeout here */ while (n_children > 0) { if (debug) { struct subprocess *chp; dbglog("Waiting for %d child processes...", n_children); for (chp = children; chp != NULL; chp = chp->next) dbglog(" script %s, pid %d", chp->prog, chp->pid); } if (reap_kids(1) < 0) break; } die(status); return 0;}/* * handle_events - wait for something to happen and respond to it. */static voidhandle_events(){ struct timeval timo; sigset_t mask; kill_link = open_ccp_flag = 0; if (sigsetjmp(sigjmp, 1) == 0) { sigprocmask(SIG_BLOCK, &mask, NULL); if (got_sighup || got_sigterm || got_sigusr2 || got_sigchld) { sigprocmask(SIG_UNBLOCK, &mask, NULL); } else { waiting = 1; sigprocmask(SIG_UNBLOCK, &mask, NULL); wait_input(timeleft(&timo)); } } waiting = 0; calltimeout(); if (got_sighup) { kill_link = 1; got_sighup = 0; if (status != EXIT_HANGUP) status = EXIT_USER_REQUEST;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -