+ 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 = 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 %d bytes, received %d 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();
+ if (demand)
+ restore_loop();
+ disestablish_ppp(devfd); /* XXX */
+ 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 void
+handle_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;
+ }
+ if (got_sigterm) {
+ kill_link = 1;
+ persist = 0;
+ status = EXIT_USER_REQUEST;
+ got_sigterm = 0;
+ }
+ if (got_sigchld) {
+ reap_kids(0); /* Don't leave dead kids lying around */
+ got_sigchld = 0;
+ }
+ if (got_sigusr2) {
+ open_ccp_flag = 1;
+ got_sigusr2 = 0;
+ }
+}
+
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+ struct sigaction sa;
+ sigset_t mask;
+