178189Sbrian/*-
278189Sbrian * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
378189Sbrian *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
478189Sbrian *                           Internet Initiative Japan, Inc (IIJ)
578189Sbrian * All rights reserved.
66059Samurai *
778189Sbrian * Redistribution and use in source and binary forms, with or without
878189Sbrian * modification, are permitted provided that the following conditions
978189Sbrian * are met:
1078189Sbrian * 1. Redistributions of source code must retain the above copyright
1178189Sbrian *    notice, this list of conditions and the following disclaimer.
1278189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1378189Sbrian *    notice, this list of conditions and the following disclaimer in the
1478189Sbrian *    documentation and/or other materials provided with the distribution.
156059Samurai *
1678189Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1778189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1878189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1978189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2078189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2178189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2278189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2378189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2478189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2578189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2678189Sbrian * SUCH DAMAGE.
276059Samurai *
2850479Speter * $FreeBSD$
296059Samurai */
3036285Sbrian
3143313Sbrian#include <sys/param.h>
3230715Sbrian#include <netinet/in.h>
3330715Sbrian#include <netinet/in_systm.h>
3430715Sbrian#include <netinet/ip.h>
3536285Sbrian#include <sys/un.h>
3658032Sbrian#include <sys/socket.h>
3758032Sbrian#include <net/route.h>
3830715Sbrian
3930715Sbrian#include <errno.h>
406059Samurai#include <fcntl.h>
4111336Samurai#include <paths.h>
4230715Sbrian#include <signal.h>
43102500Sbrian#include <stdarg.h>
4430715Sbrian#include <stdio.h>
4552396Sbrian#include <stdlib.h>
4630715Sbrian#include <string.h>
4797140Sbrian#include <sys/time.h>
486059Samurai#include <termios.h>
4918786Sjkh#include <unistd.h>
5049581Sbrian#include <sys/stat.h>
5130715Sbrian
5250059Sbrian#ifndef NONAT
5358037Sbrian#ifdef LOCALNAT
5458037Sbrian#include "alias.h"
5558037Sbrian#else
5646086Sbrian#include <alias.h>
5739395Sbrian#endif
5839395Sbrian#endif
5958037Sbrian
6046686Sbrian#include "layer.h"
6137141Sbrian#include "probe.h"
6230715Sbrian#include "mbuf.h"
6330715Sbrian#include "log.h"
6430715Sbrian#include "defs.h"
6531061Sbrian#include "id.h"
6630715Sbrian#include "timer.h"
6730715Sbrian#include "fsm.h"
6836285Sbrian#include "lqr.h"
696059Samurai#include "hdlc.h"
7031514Sbrian#include "lcp.h"
7113389Sphk#include "ccp.h"
7236285Sbrian#include "iplist.h"
7336285Sbrian#include "throughput.h"
7436285Sbrian#include "slcompress.h"
7581634Sbrian#include "ncpaddr.h"
766059Samurai#include "ipcp.h"
7736285Sbrian#include "filter.h"
7836285Sbrian#include "descriptor.h"
7936285Sbrian#include "link.h"
8036285Sbrian#include "mp.h"
8143313Sbrian#ifndef NORADIUS
8243313Sbrian#include "radius.h"
8343313Sbrian#endif
8481634Sbrian#include "ipv6cp.h"
8581634Sbrian#include "ncp.h"
8636285Sbrian#include "bundle.h"
876735Samurai#include "auth.h"
8813389Sphk#include "systems.h"
8923840Sbrian#include "sig.h"
9030715Sbrian#include "main.h"
9136285Sbrian#include "server.h"
9236285Sbrian#include "prompt.h"
9336285Sbrian#include "chat.h"
9436285Sbrian#include "chap.h"
9538174Sbrian#include "cbcp.h"
9636285Sbrian#include "datalink.h"
9740561Sbrian#include "iface.h"
986059Samurai
996735Samurai#ifndef O_NONBLOCK
1006735Samurai#ifdef O_NDELAY
1016735Samurai#define	O_NONBLOCK O_NDELAY
1026735Samurai#endif
1036735Samurai#endif
1046735Samurai
10536431Sbrianstatic void DoLoop(struct bundle *);
10630715Sbrianstatic void TerminalStop(int);
10730715Sbrian
10836285Sbrianstatic struct bundle *SignalBundle;
10936285Sbrianstatic struct prompt *SignalPrompt;
110177100Spisostruct libalias *la;
1116059Samurai
11210528Samuraivoid
113134789SbrianCleanup()
1146059Samurai{
11536285Sbrian  SignalBundle->CleaningUp = 1;
11638008Sbrian  bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
1176059Samurai}
1186059Samurai
1196059Samuraivoid
12036285SbrianAbortProgram(int excode)
1216059Samurai{
12298970Sbrian  if (SignalBundle)
12398970Sbrian    server_Close(SignalBundle);
12436285Sbrian  log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
12598970Sbrian  if (SignalBundle) {
12698970Sbrian    bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
12798970Sbrian    bundle_Destroy(SignalBundle);
12898970Sbrian  }
12936285Sbrian  log_Close();
1306059Samurai  exit(excode);
1316059Samurai}
1326059Samurai
1336059Samuraistatic void
13428679SbrianCloseConnection(int signo)
1356059Samurai{
13626858Sbrian  /* NOTE, these are manual, we've done a setsid() */
13736285Sbrian  sig_signal(SIGINT, SIG_IGN);
13836285Sbrian  log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo);
13937018Sbrian  bundle_Down(SignalBundle, CLOSE_STAYDOWN);
14036285Sbrian  sig_signal(SIGINT, CloseConnection);
1416059Samurai}
1426059Samurai
1436059Samuraistatic void
14428679SbrianCloseSession(int signo)
1456059Samurai{
14636285Sbrian  log_Printf(LogPHASE, "Signal %d, terminate.\n", signo);
147134789Sbrian  Cleanup();
1486059Samurai}
1496059Samurai
15036285Sbrianstatic pid_t BGPid = 0;
15136285Sbrian
15210528Samuraistatic void
15336285SbrianKillChild(int signo)
15410528Samurai{
15547119Sbrian  signal(signo, SIG_IGN);
15636285Sbrian  log_Printf(LogPHASE, "Parent: Signal %d\n", signo);
15736285Sbrian  kill(BGPid, SIGINT);
15810528Samurai}
15910528Samurai
16010528Samuraistatic void
161134789SbrianTerminalCont(int signo __unused)
16210528Samurai{
16336285Sbrian  signal(SIGCONT, SIG_DFL);
16436285Sbrian  prompt_Continue(SignalPrompt);
16510528Samurai}
16610528Samurai
16726940Sbrianstatic void
168134789SbrianTerminalStop(int signo __unused)
16926940Sbrian{
17036285Sbrian  prompt_Suspend(SignalPrompt);
17136285Sbrian  signal(SIGCONT, TerminalCont);
17236285Sbrian  raise(SIGSTOP);
17326940Sbrian}
17426940Sbrian
17531081Sbrianstatic void
176134789SbrianBringDownServer(int signo __unused)
17731081Sbrian{
17836285Sbrian  /* Drops all child prompts too ! */
17971657Sbrian  if (server_Close(SignalBundle))
18071657Sbrian    log_Printf(LogPHASE, "Closed server socket\n");
18131081Sbrian}
18231081Sbrian
18330715Sbrianstatic void
184134789SbrianRestartServer(int signo __unused)
18571657Sbrian{
18671657Sbrian  /* Drops all child prompts and re-opens the socket */
18771657Sbrian  server_Reopen(SignalBundle);
18871657Sbrian}
18971657Sbrian
19071657Sbrianstatic void
19131343SbrianUsage(void)
1926059Samurai{
19395258Sdes  fprintf(stderr, "usage: ppp [-auto | -foreground | -background | -direct |"
19467912Sbrian          " -dedicated | -ddial | -interactive]"
195139116Sru#ifndef NONAT
19650059Sbrian          " [-nat]"
19731343Sbrian#endif
19852396Sbrian          " [-quiet] [-unit N] [system ...]\n");
1996059Samurai  exit(EX_START);
2006059Samurai}
2016059Samurai
20252396Sbrianstruct switches {
20352396Sbrian  unsigned nat : 1;
20452396Sbrian  unsigned fg : 1;
20552396Sbrian  unsigned quiet : 1;
20652396Sbrian  int mode;
20752396Sbrian  int unit;
20852396Sbrian};
20952396Sbrian
21040797Sbrianstatic int
21152396SbrianProcessArgs(int argc, char **argv, struct switches *sw)
2126059Samurai{
21340797Sbrian  int optc, newmode, arg;
2146059Samurai  char *cp;
2156059Samurai
21640797Sbrian  optc = 0;
21752396Sbrian  memset(sw, '\0', sizeof *sw);
21852396Sbrian  sw->mode = PHYS_INTERACTIVE;
21952396Sbrian  sw->unit = -1;
22052396Sbrian
22140797Sbrian  for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) {
22240797Sbrian    cp = argv[arg] + 1;
22336465Sbrian    newmode = Nam2mode(cp);
22436465Sbrian    switch (newmode) {
22536465Sbrian      case PHYS_NONE:
22652396Sbrian        if (strcmp(cp, "nat") == 0) {
22750059Sbrian#ifdef NONAT
22852396Sbrian          log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
22937191Sbrian#else
23052396Sbrian          sw->nat = 1;
23136285Sbrian#endif
23236465Sbrian          optc--;			/* this option isn't exclusive */
23352396Sbrian        } else if (strcmp(cp, "alias") == 0) {
23452396Sbrian#ifdef NONAT
23552396Sbrian          log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
23652396Sbrian          fprintf(stderr, "%s ignored: NAT is compiled out\n", argv[arg]);
23752396Sbrian#else
23853889Sbrian          log_Printf(LogWARN, "%s is deprecated\n", argv[arg]);
23953889Sbrian          fprintf(stderr, "%s is deprecated\n", argv[arg]);
24052396Sbrian          sw->nat = 1;
24152396Sbrian#endif
24252396Sbrian          optc--;			/* this option isn't exclusive */
24352396Sbrian        } else if (strncmp(cp, "unit", 4) == 0) {
24453067Sbrian          optc--;			/* this option isn't exclusive */
24552396Sbrian          if (cp[4] == '\0') {
24653067Sbrian            optc--;			/* nor is the argument */
24752396Sbrian            if (++arg == argc) {
24852396Sbrian              fprintf(stderr, "-unit: Expected unit number\n");
24952396Sbrian              Usage();
25052396Sbrian            } else
25152396Sbrian              sw->unit = atoi(argv[arg]);
25252396Sbrian          } else
25352396Sbrian            sw->unit = atoi(cp + 4);
25450059Sbrian        } else if (strcmp(cp, "quiet") == 0) {
25552396Sbrian          sw->quiet = 1;
25650059Sbrian          optc--;			/* this option isn't exclusive */
25736465Sbrian        } else
25836465Sbrian          Usage();
25936465Sbrian        break;
26036465Sbrian
26136465Sbrian      case PHYS_ALL:
26236465Sbrian        Usage();
26336465Sbrian        break;
26436465Sbrian
26536465Sbrian      default:
26652396Sbrian        sw->mode = newmode;
26753830Sbrian        if (newmode == PHYS_FOREGROUND)
26853830Sbrian          sw->fg = 1;
26936465Sbrian    }
2706059Samurai  }
27136465Sbrian
2726059Samurai  if (optc > 1) {
27336285Sbrian    fprintf(stderr, "You may specify only one mode.\n");
2746059Samurai    exit(EX_START);
2756059Samurai  }
27631197Sbrian
27752396Sbrian  if (sw->mode == PHYS_AUTO && arg == argc) {
27840797Sbrian    fprintf(stderr, "A system must be specified in auto mode.\n");
27936285Sbrian    exit(EX_START);
28036285Sbrian  }
28136285Sbrian
28240797Sbrian  return arg;		/* Don't SetLabel yet ! */
2836059Samurai}
2846059Samurai
28540797Sbrianstatic void
28640797SbrianCheckLabel(const char *label, struct prompt *prompt, int mode)
28740797Sbrian{
28840797Sbrian  const char *err;
28940797Sbrian
29040797Sbrian  if ((err = system_IsValid(label, prompt, mode)) != NULL) {
29140797Sbrian    fprintf(stderr, "%s: %s\n", label, err);
29240797Sbrian    if (mode == PHYS_DIRECT)
29340797Sbrian      log_Printf(LogWARN, "Label %s rejected -direct connection: %s\n",
29440797Sbrian                 label, err);
29540797Sbrian    log_Close();
29640797Sbrian    exit(1);
29740797Sbrian  }
29840797Sbrian}
29940797Sbrian
30040797Sbrian
30126940Sbrianint
30228679Sbrianmain(int argc, char **argv)
3036059Samurai{
30440797Sbrian  char *name;
30543187Sbrian  const char *lastlabel;
306134789Sbrian  int arg, holdfd[3], label;
307134789Sbrian  unsigned f;
30836285Sbrian  struct bundle *bundle;
30936285Sbrian  struct prompt *prompt;
31052396Sbrian  struct switches sw;
31126516Sbrian
31281697Sbrian  probe_Init();
31381697Sbrian
31479173Sbrian  /*
31579173Sbrian   * We open 3 descriptors to ensure that STDIN_FILENO, STDOUT_FILENO and
31679173Sbrian   * STDERR_FILENO are always open.  These are closed before DoLoop(),
31779173Sbrian   * but *after* we've avoided the possibility of erroneously closing
31879173Sbrian   * an important descriptor with close(STD{IN,OUT,ERR}_FILENO).
31979173Sbrian   */
32079173Sbrian  if ((holdfd[0] = open(_PATH_DEVNULL, O_RDWR)) == -1) {
32179173Sbrian    fprintf(stderr, "Cannot open %s !\n", _PATH_DEVNULL);
32279173Sbrian    return 2;
32379173Sbrian  }
32479173Sbrian  for (f = 1; f < sizeof holdfd / sizeof *holdfd; f++)
32579186Sbrian    holdfd[f] = dup(holdfd[0]);
32679173Sbrian
32730715Sbrian  name = strrchr(argv[0], '/');
32836285Sbrian  log_Open(name ? name + 1 : argv[0]);
32926516Sbrian
33050059Sbrian#ifndef NONAT
331177100Spiso  la = LibAliasInit(NULL);
33238198Sbrian#endif
33352396Sbrian  label = ProcessArgs(argc, argv, &sw);
33431121Sbrian
33536285Sbrian  /*
33644539Sbrian   * A FreeBSD & OpenBSD hack to dodge a bug in the tty driver that drops
33744539Sbrian   * output occasionally.... I must find the real reason some time.  To
33844539Sbrian   * display the dodgy behaviour, comment out this bit, make yourself a large
33936285Sbrian   * routing table and then run ppp in interactive mode.  The `show route'
34036285Sbrian   * command will drop chunks of data !!!
34136285Sbrian   */
34252396Sbrian  if (sw.mode == PHYS_INTERACTIVE) {
34336285Sbrian    close(STDIN_FILENO);
34436285Sbrian    if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) {
34536285Sbrian      fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY);
34636285Sbrian      return 2;
34736285Sbrian    }
34836285Sbrian  }
34936285Sbrian
35036285Sbrian  /* Allow output for the moment (except in direct mode) */
35152396Sbrian  if (sw.mode == PHYS_DIRECT)
35236285Sbrian    prompt = NULL;
35343526Sbrian  else
35436285Sbrian    SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD);
35536285Sbrian
35631121Sbrian  ID0init();
35731158Sbrian  if (ID0realuid() != 0) {
35831158Sbrian    char conf[200], *ptr;
35931158Sbrian
36074687Sbrian    snprintf(conf, sizeof conf, "%s/%s", PPP_CONFDIR, CONFFILE);
36131158Sbrian    do {
36249581Sbrian      struct stat sb;
36349581Sbrian
36449581Sbrian      if (stat(conf, &sb) == 0 && sb.st_mode & S_IWOTH) {
36537019Sbrian        log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n",
36637019Sbrian                   conf);
36731158Sbrian        return -1;
36831158Sbrian      }
36931158Sbrian      ptr = conf + strlen(conf)-2;
37031158Sbrian      while (ptr > conf && *ptr != '/')
37131158Sbrian        *ptr-- = '\0';
37231158Sbrian    } while (ptr >= conf);
37331158Sbrian  }
37431158Sbrian
37540797Sbrian  if (label < argc)
37640797Sbrian    for (arg = label; arg < argc; arg++)
37752396Sbrian      CheckLabel(argv[arg], prompt, sw.mode);
37840797Sbrian  else
37952396Sbrian    CheckLabel("default", prompt, sw.mode);
38031121Sbrian
38152396Sbrian  if (!sw.quiet)
38252396Sbrian    prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(sw.mode));
38343526Sbrian
38453298Sbrian  if ((bundle = bundle_Create(TUN_PREFIX, sw.mode, sw.unit)) == NULL)
38526940Sbrian    return EX_START;
38643187Sbrian
38743187Sbrian  /* NOTE:  We may now have changed argv[1] via a ``set proctitle'' */
38843187Sbrian
38936285Sbrian  SignalBundle = bundle;
39052396Sbrian  bundle->NatEnabled = sw.nat;
39152396Sbrian  if (sw.nat)
392138198Sbrian    opt_enable(bundle, OPT_IFACEALIAS);
39331121Sbrian
39437008Sbrian  if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0)
39536285Sbrian    prompt_Printf(prompt, "Warning: No default entry found in config file.\n");
39636285Sbrian
39736285Sbrian  sig_signal(SIGHUP, CloseSession);
39836285Sbrian  sig_signal(SIGTERM, CloseSession);
39936285Sbrian  sig_signal(SIGINT, CloseConnection);
40036285Sbrian  sig_signal(SIGQUIT, CloseSession);
40136285Sbrian  sig_signal(SIGALRM, SIG_IGN);
40224753Sache  signal(SIGPIPE, SIG_IGN);
4036059Samurai
40452396Sbrian  if (sw.mode == PHYS_INTERACTIVE)
40536285Sbrian    sig_signal(SIGTSTP, TerminalStop);
40636285Sbrian
40771657Sbrian  sig_signal(SIGUSR1, RestartServer);
40836285Sbrian  sig_signal(SIGUSR2, BringDownServer);
40936285Sbrian
41053298Sbrian  lastlabel = argv[argc - 1];
41140797Sbrian  for (arg = label; arg < argc; arg++) {
41240797Sbrian    /* In case we use LABEL or ``set enddisc label'' */
41343187Sbrian    bundle_SetLabel(bundle, lastlabel);
41453298Sbrian    system_Select(bundle, argv[arg], CONFFILE, prompt, NULL);
4156059Samurai  }
41626940Sbrian
41740797Sbrian  if (label < argc)
41840797Sbrian    /* In case the last label did a ``load'' */
41943187Sbrian    bundle_SetLabel(bundle, lastlabel);
42040797Sbrian
42152396Sbrian  if (sw.mode == PHYS_AUTO &&
42281634Sbrian      ncprange_family(&bundle->ncp.ipcp.cfg.peer_range) == AF_UNSPEC) {
42340797Sbrian    prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address "
42440797Sbrian                  "in auto mode.\n");
42540797Sbrian    AbortProgram(EX_START);
42640797Sbrian  }
42740797Sbrian
428218397Sbrian  if (prompt) {
429218397Sbrian    prompt->bundle = bundle;	/* couldn't do it earlier */
430218397Sbrian    if (!sw.quiet)
431218397Sbrian      prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name);
432218397Sbrian  }
433218397Sbrian
43452396Sbrian  if (sw.mode != PHYS_INTERACTIVE) {
43552396Sbrian    if (sw.mode != PHYS_DIRECT) {
43652396Sbrian      if (!sw.fg) {
43750059Sbrian        int bgpipe[2];
43850059Sbrian        pid_t bgpid;
43936285Sbrian
44052396Sbrian        if (sw.mode == PHYS_BACKGROUND && pipe(bgpipe)) {
44150059Sbrian          log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
44250059Sbrian	  AbortProgram(EX_SOCK);
44350059Sbrian        }
4446059Samurai
44550059Sbrian        bgpid = fork();
44650059Sbrian        if (bgpid == -1) {
44750059Sbrian	  log_Printf(LogERROR, "fork: %s\n", strerror(errno));
44850059Sbrian	  AbortProgram(EX_SOCK);
44950059Sbrian        }
45036285Sbrian
45150059Sbrian        if (bgpid) {
45250059Sbrian	  char c = EX_NORMAL;
45359084Sbrian          int ret;
45411336Samurai
45552396Sbrian	  if (sw.mode == PHYS_BACKGROUND) {
45650059Sbrian	    close(bgpipe[1]);
45750059Sbrian	    BGPid = bgpid;
45850059Sbrian            /* If we get a signal, kill the child */
45950059Sbrian            signal(SIGHUP, KillChild);
46050059Sbrian            signal(SIGTERM, KillChild);
46150059Sbrian            signal(SIGINT, KillChild);
46250059Sbrian            signal(SIGQUIT, KillChild);
46336285Sbrian
46450059Sbrian	    /* Wait for our child to close its pipe before we exit */
46559084Sbrian            while ((ret = read(bgpipe[0], &c, 1)) == 1) {
46659084Sbrian              switch (c) {
46759084Sbrian                case EX_NORMAL:
46875120Sbrian                  if (!sw.quiet) {
46975120Sbrian	            prompt_Printf(prompt, "PPP enabled\n");
47075120Sbrian	            log_Printf(LogPHASE, "Parent: PPP enabled\n");
47175120Sbrian                  }
47259104Sbrian	          break;
47359084Sbrian                case EX_REDIAL:
47459084Sbrian                  if (!sw.quiet)
47559084Sbrian	            prompt_Printf(prompt, "Attempting redial\n");
47659084Sbrian                  continue;
47759084Sbrian                case EX_RECONNECT:
47859084Sbrian                  if (!sw.quiet)
47959084Sbrian	            prompt_Printf(prompt, "Attempting reconnect\n");
48059084Sbrian                  continue;
48159084Sbrian	        default:
48259084Sbrian	          prompt_Printf(prompt, "Child failed (%s)\n",
48359084Sbrian                                ex_desc((int)c));
48459084Sbrian	          log_Printf(LogPHASE, "Parent: Child failed (%s)\n",
48559084Sbrian		             ex_desc((int) c));
48659084Sbrian	      }
48759084Sbrian	      break;
48859084Sbrian            }
48959084Sbrian            if (ret != 1) {
49050059Sbrian	      prompt_Printf(prompt, "Child exit, no status.\n");
49150059Sbrian	      log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
49250059Sbrian	    }
49350059Sbrian	    close(bgpipe[0]);
49428679Sbrian	  }
49550059Sbrian	  return c;
49652396Sbrian        } else if (sw.mode == PHYS_BACKGROUND) {
49736285Sbrian	  close(bgpipe[0]);
49850059Sbrian          bundle->notify.fd = bgpipe[1];
49950059Sbrian        }
50050059Sbrian
50156350Sbrian        bundle_ChangedPID(bundle);
50250059Sbrian        bundle_LockTun(bundle);	/* we have a new pid */
50336285Sbrian      }
50420813Sjkh
50550059Sbrian      /* -auto, -dedicated, -ddial, -foreground & -background */
50636285Sbrian      prompt_Destroy(prompt, 0);
50736285Sbrian      close(STDOUT_FILENO);
50836285Sbrian      close(STDERR_FILENO);
50936285Sbrian      close(STDIN_FILENO);
51052396Sbrian      if (!sw.fg)
51150059Sbrian        setsid();
51236285Sbrian    } else {
513196514Sbrian      /*
514196514Sbrian       * -direct - STDIN_FILENO gets used by physical_Open.  STDOUT_FILENO
515196514Sbrian       * *may* get used in exec/pipe mode.
516196514Sbrian       */
51736285Sbrian      prompt_TtyInit(NULL);
51836285Sbrian      close(STDERR_FILENO);
51926686Sbrian    }
5206059Samurai  } else {
52150059Sbrian    /* -interactive */
52232129Sbrian    close(STDERR_FILENO);
52336285Sbrian    prompt_TtyInit(prompt);
52436285Sbrian    prompt_TtyCommandMode(prompt);
52536285Sbrian    prompt_Required(prompt);
5266059Samurai  }
52729696Sbrian
52879173Sbrian  /* We can get rid of these now */
52979173Sbrian  for (f = 0; f < sizeof holdfd / sizeof *holdfd; f++)
53079173Sbrian    close(holdfd[f]);
53179173Sbrian
53252396Sbrian  log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode));
53336431Sbrian  DoLoop(bundle);
53436285Sbrian  AbortProgram(EX_NORMAL);
5356059Samurai
53636285Sbrian  return EX_NORMAL;
5376059Samurai}
5386059Samurai
5396059Samuraistatic void
54036431SbrianDoLoop(struct bundle *bundle)
5416059Samurai{
54266898Sbrian  fd_set *rfds, *wfds, *efds;
54337141Sbrian  int i, nfds, nothing_done;
5446059Samurai
54566898Sbrian  if ((rfds = mkfdset()) == NULL) {
54666898Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
54766898Sbrian    return;
54866898Sbrian  }
54966898Sbrian
55066898Sbrian  if ((wfds = mkfdset()) == NULL) {
55166898Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
55266898Sbrian    free(rfds);
55366898Sbrian    return;
55466898Sbrian  }
55566898Sbrian
55666898Sbrian  if ((efds = mkfdset()) == NULL) {
55766898Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
55866898Sbrian    free(rfds);
55966898Sbrian    free(wfds);
56066898Sbrian    return;
56166898Sbrian  }
56266898Sbrian
56353070Sbrian  for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) {
56423598Sache    nfds = 0;
56566898Sbrian    zerofdset(rfds);
56666898Sbrian    zerofdset(wfds);
56766898Sbrian    zerofdset(efds);
5687001Samurai
56936314Sbrian    /* All our datalinks, the tun device and the MP socket */
57066898Sbrian    descriptor_UpdateSet(&bundle->desc, rfds, wfds, efds, &nfds);
57125067Sbrian
57236314Sbrian    /* All our prompts and the diagnostic socket */
57366898Sbrian    descriptor_UpdateSet(&server.desc, rfds, NULL, NULL, &nfds);
57436314Sbrian
57558453Sbrian    bundle_CleanDatalinks(bundle);
57636285Sbrian    if (bundle_IsDead(bundle))
57736285Sbrian      /* Don't select - we'll be here forever */
57836285Sbrian      break;
57926551Sbrian
58045126Sbrian    /*
58145126Sbrian     * It's possible that we've had a signal since we last checked.  If
58245126Sbrian     * we don't check again before calling select(), we may end up stuck
58345126Sbrian     * after having missed the event.... sig_Handle() tries to be as
58445126Sbrian     * quick as possible if nothing is likely to have happened.
58545126Sbrian     * This is only really likely if we block in open(... O_NONBLOCK)
58645126Sbrian     * which will happen with a misconfigured device.
58745126Sbrian     */
58845126Sbrian    if (sig_Handle())
58945126Sbrian      continue;
59045126Sbrian
59166898Sbrian    i = select(nfds, rfds, wfds, efds, NULL);
59226696Sbrian
59336345Sbrian    if (i < 0 && errno != EINTR) {
59436285Sbrian      log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
59536285Sbrian      if (log_IsKept(LogTIMER)) {
59636285Sbrian        struct timeval t;
59736285Sbrian
59836285Sbrian        for (i = 0; i <= nfds; i++) {
59966898Sbrian          if (FD_ISSET(i, rfds)) {
60036285Sbrian            log_Printf(LogTIMER, "Read set contains %d\n", i);
60166898Sbrian            FD_CLR(i, rfds);
60236285Sbrian            t.tv_sec = t.tv_usec = 0;
60366898Sbrian            if (select(nfds, rfds, wfds, efds, &t) != -1) {
60436285Sbrian              log_Printf(LogTIMER, "The culprit !\n");
60536285Sbrian              break;
60636285Sbrian            }
60736285Sbrian          }
60866898Sbrian          if (FD_ISSET(i, wfds)) {
60936285Sbrian            log_Printf(LogTIMER, "Write set contains %d\n", i);
61066898Sbrian            FD_CLR(i, wfds);
61136285Sbrian            t.tv_sec = t.tv_usec = 0;
61266898Sbrian            if (select(nfds, rfds, wfds, efds, &t) != -1) {
61336285Sbrian              log_Printf(LogTIMER, "The culprit !\n");
61436285Sbrian              break;
61536285Sbrian            }
61636285Sbrian          }
61766898Sbrian          if (FD_ISSET(i, efds)) {
61836285Sbrian            log_Printf(LogTIMER, "Error set contains %d\n", i);
61966898Sbrian            FD_CLR(i, efds);
62036285Sbrian            t.tv_sec = t.tv_usec = 0;
62166898Sbrian            if (select(nfds, rfds, wfds, efds, &t) != -1) {
62236285Sbrian              log_Printf(LogTIMER, "The culprit !\n");
62336285Sbrian              break;
62436285Sbrian            }
62536285Sbrian          }
62636285Sbrian        }
62728679Sbrian      }
62828679Sbrian      break;
6298857Srgrimes    }
6306059Samurai
63143693Sbrian    log_Printf(LogTIMER, "Select returns %d\n", i);
63243693Sbrian
63336345Sbrian    sig_Handle();
63436345Sbrian
63536345Sbrian    if (i <= 0)
63636345Sbrian      continue;
63736345Sbrian
63836285Sbrian    for (i = 0; i <= nfds; i++)
63966898Sbrian      if (FD_ISSET(i, efds)) {
64058038Sbrian        log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i);
64158038Sbrian        /* We deal gracefully with link descriptor exceptions */
64241654Sbrian        if (!bundle_Exception(bundle, i)) {
64341654Sbrian          log_Printf(LogERROR, "Exception cannot be handled !\n");
64441654Sbrian          break;
64541654Sbrian        }
6466059Samurai      }
64728679Sbrian
64836285Sbrian    if (i <= nfds)
64936285Sbrian      break;
65028536Sbrian
65137141Sbrian    nothing_done = 1;
65237141Sbrian
65366898Sbrian    if (descriptor_IsSet(&server.desc, rfds)) {
65466898Sbrian      descriptor_Read(&server.desc, bundle, rfds);
65537141Sbrian      nothing_done = 0;
65637141Sbrian    }
65732039Sbrian
65866898Sbrian    if (descriptor_IsSet(&bundle->desc, rfds)) {
65966898Sbrian      descriptor_Read(&bundle->desc, bundle, rfds);
66037141Sbrian      nothing_done = 0;
66137141Sbrian    }
66237141Sbrian
66366898Sbrian    if (descriptor_IsSet(&bundle->desc, wfds))
66493418Sbrian      if (descriptor_Write(&bundle->desc, bundle, wfds) <= 0 && nothing_done) {
66537141Sbrian        /*
66693418Sbrian         * This is disastrous.  The OS has told us that something is
66737141Sbrian         * writable, and all our write()s have failed.  Rather than
66837141Sbrian         * going back immediately to do our UpdateSet()s and select(),
66937141Sbrian         * we sleep for a bit to avoid gobbling up all cpu time.
67037141Sbrian         */
67137141Sbrian        struct timeval t;
67236285Sbrian
67337141Sbrian        t.tv_sec = 0;
67437141Sbrian        t.tv_usec = 100000;
67537141Sbrian        select(0, NULL, NULL, NULL, &t);
67637141Sbrian      }
67753070Sbrian  }
67836285Sbrian
67936285Sbrian  log_Printf(LogDEBUG, "DoLoop done.\n");
6806059Samurai}
681