+ /*
+ * 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;
+ }
+
+ /* 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.");
+
+ /* 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 && devnam[0] != 0) {
+ for (;;) {
+ if ((i = open(devnam, O_RDWR)) >= 0)
+ break;
+ if (errno != EINTR)
+ error("Failed to reopen %s: %m", devnam);
+ if (!persist || errno != EINTR || hungup || kill_link)
+ goto fail;
+ }
+ close(i);
+ }
+
+ /* run welcome script, if any */
+ if (welcomer && welcomer[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) {
+
+ info("Using interface ppp%d", ifunit);
+ slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
+ script_setenv("IFNAME", ifname);
+
+ create_pidfile(); /* write pid to file */
+ }
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ 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);
+ for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) {
+ if (sigsetjmp(sigjmp, 1) == 0) {
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ if (kill_link || open_ccp_flag) {
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ } else {
+ waiting = 1;
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ wait_input(timeleft(&timo));
+ }
+ }
+ waiting = 0;
+ calltimeout();
+ get_input();
+ if (kill_link) {
+ lcp_close(0, "User request");
+ kill_link = 0;
+ }
+ if (open_ccp_flag) {
+ if (phase == PHASE_NETWORK) {
+ ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
+ (*ccp_protent.open)(0);
+ }
+ open_ccp_flag = 0;
+ }
+ 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 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(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) {
+ 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 (pty_master >= 0)
+ close(pty_master);
+ if (pty_slave >= 0)
+ close(pty_slave);
+ if (real_ttyfd >= 0)
+ close_tty();
+ if (locked) {
+ unlock();
+ locked = 0;
+ }
+
+ if (!demand) {
+ if (pidfilename[0] != 0
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file: %m");
+ pidfilename[0] = 0;
+ }
+
+ if (!persist)
+ break;
+
+ kill_link = 0;
+ if (demand)
+ demand_discard();
+ if (holdoff > 0 && need_holdoff) {
+ phase = PHASE_HOLDOFF;
+ TIMEOUT(holdoff_end, NULL, holdoff);
+ do {
+ if (sigsetjmp(sigjmp, 1) == 0) {
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ if (kill_link) {
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ } else {
+ waiting = 1;
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ wait_input(timeleft(&timo));
+ }
+ }
+ waiting = 0;
+ calltimeout();
+ if (kill_link) {
+ kill_link = 0;
+ phase = PHASE_DORMANT; /* allow signal to end holdoff */
+ }
+ reap_kids(0);
+ } while (phase == PHASE_HOLDOFF);
+ if (!persist)
+ break;
+ }
+ }
+
+ /* Wait for scripts to finish */
+ while (n_children > 0)
+ reap_kids(1);
+
+ die(0);
+ return 0;
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+detach()
+{
+ if (detached)
+ return;
+ if (daemon(0, 0) < 0) {
+ perror("Couldn't detach from controlling terminal");
+ die(1);
+ }
+ detached = 1;
+ log_to_fd = -1;
+ pid = getpid();
+ /* update pid file if it has been written already */
+ if (pidfilename[0])
+ create_pidfile();
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+static void
+create_pidfile()
+{
+ FILE *pidfile;
+ char numbuf[16];
+
+ slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
+ _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ (void) fclose(pidfile);
+ } else {
+ error("Failed to create pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+ slprintf(numbuf, sizeof(numbuf), "%d", pid);
+ script_setenv("PPPD_PID", numbuf);
+}
+
+/*
+ * holdoff_end - called via a timeout when the holdoff period ends.
+ */
+static void
+holdoff_end(arg)
+ void *arg;
+{
+ phase = PHASE_DORMANT;
+}
+
+/*
+ * get_input - called when incoming data is available.
+ */
+static void
+get_input()
+{
+ int len, i;
+ u_char *p;
+ u_short protocol;
+ struct protent *protp;
+
+ p = inpacket_buf; /* point to beginning of packet buffer */
+
+ len = read_packet(inpacket_buf);
+ if (len < 0)
+ return;
+
+ if (len == 0) {
+ notice("Modem hangup");
+ hungup = 1;
+ lcp_lowerdown(0); /* serial link is no longer available */
+ link_terminated(0);
+ return;
+ }
+
+ if (debug /*&& (debugflags & DBG_INPACKET)*/)
+ dbglog("rcvd %P", p, len);
+
+ if (len < PPP_HDRLEN) {
+ MAINDEBUG(("io(): Received short packet."));
+ return;
+ }
+
+ p += 2; /* Skip address and control */
+ GETSHORT(protocol, p);
+ len -= PPP_HDRLEN;
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ */
+ if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
+ MAINDEBUG(("get_input: Received non-LCP packet when LCP not open."));
+ return;
+ }
+
+ /*
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if (phase <= PHASE_AUTHENTICATE
+ && !(protocol == PPP_LCP || protocol == PPP_LQR
+ || protocol == PPP_PAP || protocol == PPP_CHAP)) {
+ MAINDEBUG(("get_input: discarding proto 0x%x in phase %d",
+ protocol, phase));
+ return;
+ }
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ (*protp->input)(0, p, len);
+ return;
+ }
+ if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
+ && protp->datainput != NULL) {
+ (*protp->datainput)(0, p, len);
+ return;
+ }
+ }
+
+ if (debug)
+ warn("Unsupported protocol (0x%x) received", protocol);
+ lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
+}
+
+
+/*
+ * die - clean up state and exit with the specified status.
+ */
+void
+die(status)
+ int status;
+{
+ cleanup();
+ syslog(LOG_INFO, "Exit.");
+ exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup()
+{
+ sys_cleanup();
+
+ if (fd_ppp >= 0)
+ disestablish_ppp(ttyfd);
+ if (real_ttyfd >= 0)
+ close_tty();
+
+ if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file: %m");
+ pidfilename[0] = 0;
+
+ if (locked)
+ unlock();
+}
+
+/*
+ * close_tty - restore the terminal device and close it.
+ */
+static void
+close_tty()
+{
+ /* drop dtr to hang up */
+ if (!default_device && modem) {
+ 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(real_ttyfd);
+
+ if (tty_mode != (mode_t) -1) {
+ if (fchmod(real_ttyfd, tty_mode) != 0) {
+ /* XXX if devnam is a symlink, this will change the link */
+ chmod(devnam, tty_mode);
+ }
+ }
+
+ close(real_ttyfd);
+ real_ttyfd = -1;
+}
+
+
+struct callout {
+ struct timeval c_time; /* time at which to call routine */
+ void *c_arg; /* argument to routine */
+ void (*c_func) __P((void *)); /* routine */
+ struct callout *c_next;
+};
+
+static struct callout *callout = NULL; /* Callout list */
+static struct timeval timenow; /* Current time */
+
+/*