tmp.c

来源:互联网 发布:python while循环终止 编辑:程序博客网 时间:2024/06/01 09:56

/*
 * main.c - Point-to-Point Protocol main module
 *
 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The name "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any legal
 *    details, please contact
 *      Office of Technology Transfer
 *      Carnegie Mellon University
 *      5000 Forbes Avenue
 *      Pittsburgh, PA  15213-3890
 *      (412) 268-4387, fax: (412) 268-7395
 *      tech-transfer@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Copyright (c) 1999-2004 Paul Mackerras. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. The name(s) of the authors of this software must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 3. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Paul Mackerras
 *     <paulus@samba.org>".
 *
 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define RCSID "$Id: main.c,v 1.156 2008/06/23 11:47:18 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-new.h"
#include "eap.h"
#include "ccp.h"
#include "ecp.h"
#include "pathnames.h"

#ifdef USE_TDB
#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

static 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;
struct notifier *fork_notifier = 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 */
int ppp_session_number;  /* Session number, for channels with such a
       concept (eg PPPoE) */
int childwait_done;  /* have timed out waiting for children */

#ifdef USE_TDB
TDB_CONTEXT *pppdb;  /* database for storing status etc. */
#endif

char db_key[32];

int ( *holdoff_hook ) __P ( ( void ) ) = NULL;
int ( *new_phase_hook ) __P ( ( int ) ) = NULL;
void ( *snoop_recv_hook ) __P ( ( unsigned char *p, int len ) ) = NULL;
void ( *snoop_send_hook ) __P ( ( unsigned char *p, int len ) ) = NULL;

static int conn_running; /* we have a [dis]connector running */
static int fd_loop;  /* fd for getting demand-dial packets */

int fd_devnull;   /* fd for /dev/null */
int devfd = -1;   /* fd of underlying device */
int fd_ppp = -1;  /* fd for talking PPP */
int phase;   /* where the link is at */
int kill_link;
int asked_to_quit;
int open_ccp_flag;
int listen_time;
int got_sigusr2;
int got_sigterm;
int got_sighup;

static sigset_t signals_handled;
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. */

static struct pppd_stats old_link_stats;
struct pppd_stats link_stats;
unsigned link_connect_time;
int link_stats_valid;

int error_count;

bool bundle_eof;
bool bundle_terminating;

/*
 * 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;
    int  killable;
    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 ( ( int pid ) );
static void create_linkpidfile __P ( ( int pid ) );
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 void forget_child __P ( ( int pid, int status ) );
static int reap_kids __P ( ( void ) );
static void childwait_end __P ( ( void * ) );

#ifdef USE_TDB
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

static void handle_events __P ( ( void ) );
void print_link_stats __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,
    &ecp_protent,
#ifdef IPX_CHANGE
    &ipxcp_protent,
#endif
#ifdef AT_CHANGE
    &atcp_protent,
#endif
    &eap_protent,
    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) */

int main ( int argc, char *argv[] )
{
    int i, t;
    char *p;
    struct passwd *pw;
    struct protent *protp;
    char numbuf[16];


    link_stats_valid = 0;
    new_phase ( PHASE_INITIALIZE );

    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 );
    }

    /* add by wangchenxi */
    getchar();

    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 );
    }

    /* Make sure fds 0, 1, 2 are open to somewhere. */
    fd_devnull = open ( _PATH_DEVNULL, O_RDWR );

    if ( fd_devnull < 0 )
    {
        fatal ( "Couldn't open %s: %m", _PATH_DEVNULL );
    }

    while ( fd_devnull <= 2 )
    {
        i = dup ( fd_devnull );

        if ( i < 0 )
            fatal ( "Critical shortage of file descriptors: dup failed: %m" );

        fd_devnull = i;
    }

    /*
    * Initialize system-dependent stuff.
    */
    sys_init();

#ifdef USE_TDB
    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;
        }
    }

#endif

    /*
    * 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();

    create_linkpidfile ( getpid() );

    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.
        */
        fd_loop = open_ppp_loopback();
        set_ifunit ( 1 );
        /*
        * Configure the interface and mark it up, etc.
        */
        demand_conf();
    }

    do_callback = 0;

    for ( ;; )
    {
        bundle_eof = 0;
        bundle_terminating = 0;
        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 ( asked_to_quit )
                    break;

                if ( get_loop_output() )
                    break;
            }

            remove_fd ( fd_loop );

            if ( asked_to_quit )
                break;

            /*
            * Now we want to bring up the link.
            */
            demand_block();
            info ( "Starting link" );
        }

        gettimeofday ( &start_time, NULL );
        script_unsetenv ( "CONNECT_TIME" );
        script_unsetenv ( "BYTES_SENT" );
        script_unsetenv ( "BYTES_RCVD" );

        lcp_open ( 0 );  /* Start protocol */
        start_link ( 0 );

        while ( phase != PHASE_DEAD )
        {
            handle_events();
            get_input();

            if ( kill_link )
                lcp_close ( 0, "User request" );

            if ( asked_to_quit )
            {
                bundle_terminating = 1;

                if ( phase == PHASE_MASTER )
                    mp_bundle_terminated();
            }

            if ( open_ccp_flag )
            {
                if ( phase == PHASE_NETWORK || phase == PHASE_RUNNING )
                {
                    ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
                    ( *ccp_protent.open ) ( 0 );
                }
            }
        }

        /* restore FSMs to original state */
        lcp_close ( 0, "" );

        if ( !persist || asked_to_quit || ( 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 */
    reap_kids();

    if ( n_children > 0 )
    {
        if ( child_wait > 0 )
            TIMEOUT ( childwait_end, NULL, child_wait );

        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 );
        }

        while ( n_children > 0 && !childwait_done )
        {
            handle_events();