+ for (;;) {
+
+ need_holdoff = 1;
+
+ if (demand) {
+ /*
+ * Don't do anything until we see some activity.
+ */
+ kill_link = 0;
+ phase = PHASE_DORMANT;
+ demand_unblock();
+ add_fd(fd_loop);
+ for (;;) {
+ 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) {
+ if (!persist)
+ break;
+ kill_link = 0;
+ }
+ if (get_loop_output())
+ break;
+ reap_kids();
+ }
+ remove_fd(fd_loop);
+ if (kill_link && !persist)
+ break;
+
+ /*
+ * Now we want to bring up the link.
+ */
+ demand_block();
+ info("Starting link");
+ }
+
+ /*
+ * Lock the device if we've been asked to.
+ */
+ if (lockflag && !default_device) {
+ if (lock(devnam) < 0)
+ goto fail;
+ locked = 1;
+ }
+
+ /*
+ * Open the serial device and set it up to be the ppp interface.
+ * First we open it in non-blocking mode so we can set the
+ * various termios flags appropriately. If we aren't dialling
+ * out and we want to use the modem lines, we reopen it later
+ * in order to wait for the carrier detect signal from the modem.
+ */
+ hungup = 0;
+ kill_link = 0;
+ for (;;) {
+ /* If the user specified the device name, become the
+ user before opening it. */
+ if (!devnam_info.priv)
+ seteuid(uid);
+ ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+ if (!devnam_info.priv)
+ seteuid(0);
+ if (ttyfd >= 0)
+ break;
+ if (errno != EINTR)
+ error("Failed to open %s: %m", devnam);
+ if (!persist || errno != EINTR)
+ goto fail;
+ }
+ if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+ || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ warn("Couldn't reset non-blocking mode on device: %m");
+
+ /*
+ * Do the equivalent of `mesg n' to stop broadcast messages.
+ */
+ if (fstat(ttyfd, &statbuf) < 0
+ || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+ warn("Couldn't restrict write permissions to %s: %m", devnam);
+ } else
+ tty_mode = statbuf.st_mode;
+
+ /* run connection script */
+ if (connector && connector[0]) {
+ MAINDEBUG(("Connecting with <%s>", connector));
+
+ if (!default_device && modem) {
+ hangup_modem(ttyfd); /* in case modem is off hook */
+ sleep(1);
+ }
+
+ /*
+ * Set line speed, flow control, etc.
+ * On most systems we set CLOCAL for now so that we can talk
+ * to the modem before carrier comes up. But this has the
+ * side effect that we might miss it if CD drops before we
+ * get to clear CLOCAL below. On systems where we can talk
+ * successfully to the modem with CLOCAL clear and CD down,
+ * we could clear CLOCAL at this point.
+ */
+ set_up_tty(ttyfd, 1);
+
+ if (device_script(connector, ttyfd, ttyfd) < 0) {
+ error("Connect script failed");
+ goto fail;
+ }
+
+ info("Serial connection established.");
+ sleep(1); /* give it time to set up its terminal */
+ }
+
+ /* set line speed, flow control, etc.; clear CLOCAL if modem option */
+ set_up_tty(ttyfd, 0);
+
+ /* reopen tty if necessary to wait for carrier */
+ if (connector == NULL && modem) {
+ 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)
+ warn("Welcome script failed");
+ }
+
+ /* set up the serial device as a ppp interface */
+ fd_ppp = establish_ppp(ttyfd);
+
+ 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, devnam);
+ lcp_lowerup(0);
+ 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();
+ if (kill_link) {
+ lcp_close(0, "User request");
+ kill_link = 0;
+ }
+ get_input();
+ 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(); /* Don't leave dead kids lying around */
+ }
+
+ /*
+ * 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);
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ if (disconnector && !hungup) {
+ set_up_tty(ttyfd, 1);
+ if (device_script(disconnector, ttyfd, ttyfd) < 0) {
+ warn("disconnect script failed");
+ } else {
+ info("Serial link disconnected.");
+ }
+ }