main.c revision 67912
16059Samurai/*
26059Samurai *			User Process PPP
36059Samurai *
46059Samurai *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
56059Samurai *
66059Samurai *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
76059Samurai *
86059Samurai * Redistribution and use in source and binary forms are permitted
96059Samurai * provided that the above copyright notice and this paragraph are
106059Samurai * duplicated in all such forms and that any documentation,
116059Samurai * advertising materials, and other materials related to such
126059Samurai * distribution and use acknowledge that the software was developed
136059Samurai * by the Internet Initiative Japan, Inc.  The name of the
146059Samurai * IIJ may not be used to endorse or promote products derived
156059Samurai * from this software without specific prior written permission.
166059Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
176059Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
186059Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
196059Samurai *
2050479Speter * $FreeBSD: head/usr.sbin/ppp/main.c 67912 2000-10-30 00:15:29Z brian $
218857Srgrimes *
226059Samurai *	TODO:
236059Samurai */
2436285Sbrian
2543313Sbrian#include <sys/param.h>
2630715Sbrian#include <netinet/in.h>
2730715Sbrian#include <netinet/in_systm.h>
2830715Sbrian#include <netinet/ip.h>
2936285Sbrian#include <sys/un.h>
3058032Sbrian#include <sys/socket.h>
3158032Sbrian#include <net/route.h>
3230715Sbrian
3330715Sbrian#include <errno.h>
346059Samurai#include <fcntl.h>
3511336Samurai#include <paths.h>
3630715Sbrian#include <signal.h>
3730715Sbrian#include <stdio.h>
3852396Sbrian#include <stdlib.h>
3930715Sbrian#include <string.h>
406059Samurai#include <sys/time.h>
416059Samurai#include <termios.h>
4218786Sjkh#include <unistd.h>
4349581Sbrian#include <sys/stat.h>
4430715Sbrian
4550059Sbrian#ifndef NONAT
4658037Sbrian#ifdef LOCALNAT
4758037Sbrian#include "alias.h"
4858037Sbrian#else
4946086Sbrian#include <alias.h>
5039395Sbrian#endif
5139395Sbrian#endif
5258037Sbrian
5346686Sbrian#include "layer.h"
5437141Sbrian#include "probe.h"
5530715Sbrian#include "mbuf.h"
5630715Sbrian#include "log.h"
5730715Sbrian#include "defs.h"
5831061Sbrian#include "id.h"
5930715Sbrian#include "timer.h"
6030715Sbrian#include "fsm.h"
6136285Sbrian#include "lqr.h"
626059Samurai#include "hdlc.h"
6331514Sbrian#include "lcp.h"
6413389Sphk#include "ccp.h"
6536285Sbrian#include "iplist.h"
6636285Sbrian#include "throughput.h"
6736285Sbrian#include "slcompress.h"
686059Samurai#include "ipcp.h"
6936285Sbrian#include "filter.h"
7036285Sbrian#include "descriptor.h"
7136285Sbrian#include "link.h"
7236285Sbrian#include "mp.h"
7343313Sbrian#ifndef NORADIUS
7443313Sbrian#include "radius.h"
7543313Sbrian#endif
7636285Sbrian#include "bundle.h"
776735Samurai#include "auth.h"
7813389Sphk#include "systems.h"
7923840Sbrian#include "sig.h"
8030715Sbrian#include "main.h"
8136285Sbrian#include "server.h"
8236285Sbrian#include "prompt.h"
8336285Sbrian#include "chat.h"
8436285Sbrian#include "chap.h"
8538174Sbrian#include "cbcp.h"
8636285Sbrian#include "datalink.h"
8740561Sbrian#include "iface.h"
886059Samurai
896735Samurai#ifndef O_NONBLOCK
906735Samurai#ifdef O_NDELAY
916735Samurai#define	O_NONBLOCK O_NDELAY
926735Samurai#endif
936735Samurai#endif
946735Samurai
9536431Sbrianstatic void DoLoop(struct bundle *);
9630715Sbrianstatic void TerminalStop(int);
9730715Sbrian
9836285Sbrianstatic struct bundle *SignalBundle;
9936285Sbrianstatic struct prompt *SignalPrompt;
1006059Samurai
10110528Samuraivoid
10236285SbrianCleanup(int excode)
1036059Samurai{
10436285Sbrian  SignalBundle->CleaningUp = 1;
10538008Sbrian  bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
1066059Samurai}
1076059Samurai
1086059Samuraivoid
10936285SbrianAbortProgram(int excode)
1106059Samurai{
11136285Sbrian  server_Close(SignalBundle);
11236285Sbrian  log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
11337007Sbrian  bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
11436285Sbrian  bundle_Destroy(SignalBundle);
11536285Sbrian  log_Close();
1166059Samurai  exit(excode);
1176059Samurai}
1186059Samurai
1196059Samuraistatic void
12028679SbrianCloseConnection(int signo)
1216059Samurai{
12226858Sbrian  /* NOTE, these are manual, we've done a setsid() */
12336285Sbrian  sig_signal(SIGINT, SIG_IGN);
12436285Sbrian  log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo);
12537018Sbrian  bundle_Down(SignalBundle, CLOSE_STAYDOWN);
12636285Sbrian  sig_signal(SIGINT, CloseConnection);
1276059Samurai}
1286059Samurai
1296059Samuraistatic void
13028679SbrianCloseSession(int signo)
1316059Samurai{
13236285Sbrian  log_Printf(LogPHASE, "Signal %d, terminate.\n", signo);
13328679Sbrian  Cleanup(EX_TERM);
1346059Samurai}
1356059Samurai
13636285Sbrianstatic pid_t BGPid = 0;
13736285Sbrian
13810528Samuraistatic void
13936285SbrianKillChild(int signo)
14010528Samurai{
14147119Sbrian  signal(signo, SIG_IGN);
14236285Sbrian  log_Printf(LogPHASE, "Parent: Signal %d\n", signo);
14336285Sbrian  kill(BGPid, SIGINT);
14410528Samurai}
14510528Samurai
14610528Samuraistatic void
14736285SbrianTerminalCont(int signo)
14810528Samurai{
14936285Sbrian  signal(SIGCONT, SIG_DFL);
15036285Sbrian  prompt_Continue(SignalPrompt);
15110528Samurai}
15210528Samurai
15326940Sbrianstatic void
15436285SbrianTerminalStop(int signo)
15526940Sbrian{
15636285Sbrian  prompt_Suspend(SignalPrompt);
15736285Sbrian  signal(SIGCONT, TerminalCont);
15836285Sbrian  raise(SIGSTOP);
15926940Sbrian}
16026940Sbrian
16131081Sbrianstatic void
16231081SbrianBringDownServer(int signo)
16331081Sbrian{
16436285Sbrian  /* Drops all child prompts too ! */
16536285Sbrian  server_Close(SignalBundle);
16631081Sbrian}
16731081Sbrian
16830715Sbrianstatic void
16931343SbrianUsage(void)
1706059Samurai{
17167912Sbrian  fprintf(stderr, "Usage: ppp [-auto | -foreground | -background | -direct |"
17267912Sbrian          " -dedicated | -ddial | -interactive]"
17331343Sbrian#ifndef NOALIAS
17450059Sbrian          " [-nat]"
17531343Sbrian#endif
17652396Sbrian          " [-quiet] [-unit N] [system ...]\n");
1776059Samurai  exit(EX_START);
1786059Samurai}
1796059Samurai
18052396Sbrianstruct switches {
18152396Sbrian  unsigned nat : 1;
18252396Sbrian  unsigned fg : 1;
18352396Sbrian  unsigned quiet : 1;
18452396Sbrian  int mode;
18552396Sbrian  int unit;
18652396Sbrian};
18752396Sbrian
18840797Sbrianstatic int
18952396SbrianProcessArgs(int argc, char **argv, struct switches *sw)
1906059Samurai{
19140797Sbrian  int optc, newmode, arg;
1926059Samurai  char *cp;
1936059Samurai
19440797Sbrian  optc = 0;
19552396Sbrian  memset(sw, '\0', sizeof *sw);
19652396Sbrian  sw->mode = PHYS_INTERACTIVE;
19752396Sbrian  sw->unit = -1;
19852396Sbrian
19940797Sbrian  for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) {
20040797Sbrian    cp = argv[arg] + 1;
20136465Sbrian    newmode = Nam2mode(cp);
20236465Sbrian    switch (newmode) {
20336465Sbrian      case PHYS_NONE:
20452396Sbrian        if (strcmp(cp, "nat") == 0) {
20550059Sbrian#ifdef NONAT
20652396Sbrian          log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
20737191Sbrian#else
20852396Sbrian          sw->nat = 1;
20936285Sbrian#endif
21036465Sbrian          optc--;			/* this option isn't exclusive */
21152396Sbrian        } else if (strcmp(cp, "alias") == 0) {
21252396Sbrian#ifdef NONAT
21352396Sbrian          log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
21452396Sbrian          fprintf(stderr, "%s ignored: NAT is compiled out\n", argv[arg]);
21552396Sbrian#else
21653889Sbrian          log_Printf(LogWARN, "%s is deprecated\n", argv[arg]);
21753889Sbrian          fprintf(stderr, "%s is deprecated\n", argv[arg]);
21852396Sbrian          sw->nat = 1;
21952396Sbrian#endif
22052396Sbrian          optc--;			/* this option isn't exclusive */
22152396Sbrian        } else if (strncmp(cp, "unit", 4) == 0) {
22253067Sbrian          optc--;			/* this option isn't exclusive */
22352396Sbrian          if (cp[4] == '\0') {
22453067Sbrian            optc--;			/* nor is the argument */
22552396Sbrian            if (++arg == argc) {
22652396Sbrian              fprintf(stderr, "-unit: Expected unit number\n");
22752396Sbrian              Usage();
22852396Sbrian            } else
22952396Sbrian              sw->unit = atoi(argv[arg]);
23052396Sbrian          } else
23152396Sbrian            sw->unit = atoi(cp + 4);
23250059Sbrian        } else if (strcmp(cp, "quiet") == 0) {
23352396Sbrian          sw->quiet = 1;
23450059Sbrian          optc--;			/* this option isn't exclusive */
23536465Sbrian        } else
23636465Sbrian          Usage();
23736465Sbrian        break;
23836465Sbrian
23936465Sbrian      case PHYS_ALL:
24036465Sbrian        Usage();
24136465Sbrian        break;
24236465Sbrian
24336465Sbrian      default:
24452396Sbrian        sw->mode = newmode;
24553830Sbrian        if (newmode == PHYS_FOREGROUND)
24653830Sbrian          sw->fg = 1;
24736465Sbrian    }
2486059Samurai  }
24936465Sbrian
2506059Samurai  if (optc > 1) {
25136285Sbrian    fprintf(stderr, "You may specify only one mode.\n");
2526059Samurai    exit(EX_START);
2536059Samurai  }
25431197Sbrian
25552396Sbrian  if (sw->mode == PHYS_AUTO && arg == argc) {
25640797Sbrian    fprintf(stderr, "A system must be specified in auto mode.\n");
25736285Sbrian    exit(EX_START);
25836285Sbrian  }
25936285Sbrian
26040797Sbrian  return arg;		/* Don't SetLabel yet ! */
2616059Samurai}
2626059Samurai
26340797Sbrianstatic void
26440797SbrianCheckLabel(const char *label, struct prompt *prompt, int mode)
26540797Sbrian{
26640797Sbrian  const char *err;
26740797Sbrian
26840797Sbrian  if ((err = system_IsValid(label, prompt, mode)) != NULL) {
26940797Sbrian    fprintf(stderr, "%s: %s\n", label, err);
27040797Sbrian    if (mode == PHYS_DIRECT)
27140797Sbrian      log_Printf(LogWARN, "Label %s rejected -direct connection: %s\n",
27240797Sbrian                 label, err);
27340797Sbrian    log_Close();
27440797Sbrian    exit(1);
27540797Sbrian  }
27640797Sbrian}
27740797Sbrian
27840797Sbrian
27926940Sbrianint
28028679Sbrianmain(int argc, char **argv)
2816059Samurai{
28240797Sbrian  char *name;
28343187Sbrian  const char *lastlabel;
28466898Sbrian  int label, arg;
28536285Sbrian  struct bundle *bundle;
28636285Sbrian  struct prompt *prompt;
28752396Sbrian  struct switches sw;
28826516Sbrian
28930715Sbrian  name = strrchr(argv[0], '/');
29036285Sbrian  log_Open(name ? name + 1 : argv[0]);
29126516Sbrian
29250059Sbrian#ifndef NONAT
29338198Sbrian  PacketAliasInit();
29438198Sbrian#endif
29552396Sbrian  label = ProcessArgs(argc, argv, &sw);
29631121Sbrian
29736285Sbrian  /*
29844539Sbrian   * A FreeBSD & OpenBSD hack to dodge a bug in the tty driver that drops
29944539Sbrian   * output occasionally.... I must find the real reason some time.  To
30044539Sbrian   * display the dodgy behaviour, comment out this bit, make yourself a large
30136285Sbrian   * routing table and then run ppp in interactive mode.  The `show route'
30236285Sbrian   * command will drop chunks of data !!!
30336285Sbrian   */
30452396Sbrian  if (sw.mode == PHYS_INTERACTIVE) {
30536285Sbrian    close(STDIN_FILENO);
30636285Sbrian    if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) {
30736285Sbrian      fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY);
30836285Sbrian      return 2;
30936285Sbrian    }
31036285Sbrian  }
31136285Sbrian
31236285Sbrian  /* Allow output for the moment (except in direct mode) */
31352396Sbrian  if (sw.mode == PHYS_DIRECT)
31436285Sbrian    prompt = NULL;
31543526Sbrian  else
31636285Sbrian    SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD);
31736285Sbrian
31831121Sbrian  ID0init();
31931158Sbrian  if (ID0realuid() != 0) {
32031158Sbrian    char conf[200], *ptr;
32131158Sbrian
32231158Sbrian    snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE);
32331158Sbrian    do {
32449581Sbrian      struct stat sb;
32549581Sbrian
32649581Sbrian      if (stat(conf, &sb) == 0 && sb.st_mode & S_IWOTH) {
32737019Sbrian        log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n",
32837019Sbrian                   conf);
32931158Sbrian        return -1;
33031158Sbrian      }
33131158Sbrian      ptr = conf + strlen(conf)-2;
33231158Sbrian      while (ptr > conf && *ptr != '/')
33331158Sbrian        *ptr-- = '\0';
33431158Sbrian    } while (ptr >= conf);
33531158Sbrian  }
33631158Sbrian
33740797Sbrian  if (label < argc)
33840797Sbrian    for (arg = label; arg < argc; arg++)
33952396Sbrian      CheckLabel(argv[arg], prompt, sw.mode);
34040797Sbrian  else
34152396Sbrian    CheckLabel("default", prompt, sw.mode);
34231121Sbrian
34352396Sbrian  if (!sw.quiet)
34452396Sbrian    prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(sw.mode));
34543526Sbrian
34653298Sbrian  if ((bundle = bundle_Create(TUN_PREFIX, sw.mode, sw.unit)) == NULL)
34726940Sbrian    return EX_START;
34843187Sbrian
34943187Sbrian  /* NOTE:  We may now have changed argv[1] via a ``set proctitle'' */
35043187Sbrian
35136314Sbrian  if (prompt) {
35236314Sbrian    prompt->bundle = bundle;	/* couldn't do it earlier */
35352396Sbrian    if (!sw.quiet)
35450059Sbrian      prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name);
35536314Sbrian  }
35636285Sbrian  SignalBundle = bundle;
35752396Sbrian  bundle->NatEnabled = sw.nat;
35852396Sbrian  if (sw.nat)
35940561Sbrian    bundle->cfg.opt |= OPT_IFACEALIAS;
36031121Sbrian
36137008Sbrian  if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0)
36236285Sbrian    prompt_Printf(prompt, "Warning: No default entry found in config file.\n");
36336285Sbrian
36436285Sbrian  sig_signal(SIGHUP, CloseSession);
36536285Sbrian  sig_signal(SIGTERM, CloseSession);
36636285Sbrian  sig_signal(SIGINT, CloseConnection);
36736285Sbrian  sig_signal(SIGQUIT, CloseSession);
36836285Sbrian  sig_signal(SIGALRM, SIG_IGN);
36924753Sache  signal(SIGPIPE, SIG_IGN);
3706059Samurai
37152396Sbrian  if (sw.mode == PHYS_INTERACTIVE)
37236285Sbrian    sig_signal(SIGTSTP, TerminalStop);
37336285Sbrian
37436285Sbrian  sig_signal(SIGUSR2, BringDownServer);
37536285Sbrian
37653298Sbrian  lastlabel = argv[argc - 1];
37740797Sbrian  for (arg = label; arg < argc; arg++) {
37840797Sbrian    /* In case we use LABEL or ``set enddisc label'' */
37943187Sbrian    bundle_SetLabel(bundle, lastlabel);
38053298Sbrian    system_Select(bundle, argv[arg], CONFFILE, prompt, NULL);
3816059Samurai  }
38226940Sbrian
38340797Sbrian  if (label < argc)
38440797Sbrian    /* In case the last label did a ``load'' */
38543187Sbrian    bundle_SetLabel(bundle, lastlabel);
38640797Sbrian
38752396Sbrian  if (sw.mode == PHYS_AUTO &&
38840797Sbrian      bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
38940797Sbrian    prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address "
39040797Sbrian                  "in auto mode.\n");
39140797Sbrian    AbortProgram(EX_START);
39240797Sbrian  }
39340797Sbrian
39452396Sbrian  if (sw.mode != PHYS_INTERACTIVE) {
39552396Sbrian    if (sw.mode != PHYS_DIRECT) {
39652396Sbrian      if (!sw.fg) {
39750059Sbrian        int bgpipe[2];
39850059Sbrian        pid_t bgpid;
39936285Sbrian
40052396Sbrian        if (sw.mode == PHYS_BACKGROUND && pipe(bgpipe)) {
40150059Sbrian          log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
40250059Sbrian	  AbortProgram(EX_SOCK);
40350059Sbrian        }
4046059Samurai
40550059Sbrian        bgpid = fork();
40650059Sbrian        if (bgpid == -1) {
40750059Sbrian	  log_Printf(LogERROR, "fork: %s\n", strerror(errno));
40850059Sbrian	  AbortProgram(EX_SOCK);
40950059Sbrian        }
41036285Sbrian
41150059Sbrian        if (bgpid) {
41250059Sbrian	  char c = EX_NORMAL;
41359084Sbrian          int ret;
41411336Samurai
41552396Sbrian	  if (sw.mode == PHYS_BACKGROUND) {
41650059Sbrian	    close(bgpipe[1]);
41750059Sbrian	    BGPid = bgpid;
41850059Sbrian            /* If we get a signal, kill the child */
41950059Sbrian            signal(SIGHUP, KillChild);
42050059Sbrian            signal(SIGTERM, KillChild);
42150059Sbrian            signal(SIGINT, KillChild);
42250059Sbrian            signal(SIGQUIT, KillChild);
42336285Sbrian
42450059Sbrian	    /* Wait for our child to close its pipe before we exit */
42559084Sbrian            while ((ret = read(bgpipe[0], &c, 1)) == 1) {
42659084Sbrian              switch (c) {
42759084Sbrian                case EX_NORMAL:
42859084Sbrian	          prompt_Printf(prompt, "PPP enabled\n");
42959084Sbrian	          log_Printf(LogPHASE, "Parent: PPP enabled\n");
43059104Sbrian	          break;
43159084Sbrian                case EX_REDIAL:
43259084Sbrian                  if (!sw.quiet)
43359084Sbrian	            prompt_Printf(prompt, "Attempting redial\n");
43459084Sbrian                  continue;
43559084Sbrian                case EX_RECONNECT:
43659084Sbrian                  if (!sw.quiet)
43759084Sbrian	            prompt_Printf(prompt, "Attempting reconnect\n");
43859084Sbrian                  continue;
43959084Sbrian	        default:
44059084Sbrian	          prompt_Printf(prompt, "Child failed (%s)\n",
44159084Sbrian                                ex_desc((int)c));
44259084Sbrian	          log_Printf(LogPHASE, "Parent: Child failed (%s)\n",
44359084Sbrian		             ex_desc((int) c));
44459084Sbrian	      }
44559084Sbrian	      break;
44659084Sbrian            }
44759084Sbrian            if (ret != 1) {
44850059Sbrian	      prompt_Printf(prompt, "Child exit, no status.\n");
44950059Sbrian	      log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
45050059Sbrian	    }
45150059Sbrian	    close(bgpipe[0]);
45228679Sbrian	  }
45350059Sbrian	  return c;
45452396Sbrian        } else if (sw.mode == PHYS_BACKGROUND) {
45536285Sbrian	  close(bgpipe[0]);
45650059Sbrian          bundle->notify.fd = bgpipe[1];
45750059Sbrian        }
45850059Sbrian
45956350Sbrian        bundle_ChangedPID(bundle);
46050059Sbrian        bundle_LockTun(bundle);	/* we have a new pid */
46136285Sbrian      }
46220813Sjkh
46350059Sbrian      /* -auto, -dedicated, -ddial, -foreground & -background */
46436285Sbrian      prompt_Destroy(prompt, 0);
46536285Sbrian      close(STDOUT_FILENO);
46636285Sbrian      close(STDERR_FILENO);
46736285Sbrian      close(STDIN_FILENO);
46852396Sbrian      if (!sw.fg)
46950059Sbrian        setsid();
47036285Sbrian    } else {
47150059Sbrian      /* -direct - STDIN_FILENO gets used by physical_Open */
47236285Sbrian      prompt_TtyInit(NULL);
47336285Sbrian      close(STDOUT_FILENO);
47436285Sbrian      close(STDERR_FILENO);
47526686Sbrian    }
4766059Samurai  } else {
47750059Sbrian    /* -interactive */
47832129Sbrian    close(STDERR_FILENO);
47936285Sbrian    prompt_TtyInit(prompt);
48036285Sbrian    prompt_TtyCommandMode(prompt);
48136285Sbrian    prompt_Required(prompt);
4826059Samurai  }
48329696Sbrian
48452396Sbrian  log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode));
48536431Sbrian  DoLoop(bundle);
48636285Sbrian  AbortProgram(EX_NORMAL);
4876059Samurai
48836285Sbrian  return EX_NORMAL;
4896059Samurai}
4906059Samurai
4916059Samuraistatic void
49236431SbrianDoLoop(struct bundle *bundle)
4936059Samurai{
49466898Sbrian  fd_set *rfds, *wfds, *efds;
49537141Sbrian  int i, nfds, nothing_done;
49637141Sbrian  struct probe probe;
4976059Samurai
49837141Sbrian  probe_Init(&probe);
49937141Sbrian
50066898Sbrian  if ((rfds = mkfdset()) == NULL) {
50166898Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
50266898Sbrian    return;
50366898Sbrian  }
50466898Sbrian
50566898Sbrian  if ((wfds = mkfdset()) == NULL) {
50666898Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
50766898Sbrian    free(rfds);
50866898Sbrian    return;
50966898Sbrian  }
51066898Sbrian
51166898Sbrian  if ((efds = mkfdset()) == NULL) {
51266898Sbrian    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
51366898Sbrian    free(rfds);
51466898Sbrian    free(wfds);
51566898Sbrian    return;
51666898Sbrian  }
51766898Sbrian
51853070Sbrian  for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) {
51923598Sache    nfds = 0;
52066898Sbrian    zerofdset(rfds);
52166898Sbrian    zerofdset(wfds);
52266898Sbrian    zerofdset(efds);
5237001Samurai
52436314Sbrian    /* All our datalinks, the tun device and the MP socket */
52566898Sbrian    descriptor_UpdateSet(&bundle->desc, rfds, wfds, efds, &nfds);
52625067Sbrian
52736314Sbrian    /* All our prompts and the diagnostic socket */
52866898Sbrian    descriptor_UpdateSet(&server.desc, rfds, NULL, NULL, &nfds);
52936314Sbrian
53058453Sbrian    bundle_CleanDatalinks(bundle);
53136285Sbrian    if (bundle_IsDead(bundle))
53236285Sbrian      /* Don't select - we'll be here forever */
53336285Sbrian      break;
53426551Sbrian
53545126Sbrian    /*
53645126Sbrian     * It's possible that we've had a signal since we last checked.  If
53745126Sbrian     * we don't check again before calling select(), we may end up stuck
53845126Sbrian     * after having missed the event.... sig_Handle() tries to be as
53945126Sbrian     * quick as possible if nothing is likely to have happened.
54045126Sbrian     * This is only really likely if we block in open(... O_NONBLOCK)
54145126Sbrian     * which will happen with a misconfigured device.
54245126Sbrian     */
54345126Sbrian    if (sig_Handle())
54445126Sbrian      continue;
54545126Sbrian
54666898Sbrian    i = select(nfds, rfds, wfds, efds, NULL);
54726696Sbrian
54836345Sbrian    if (i < 0 && errno != EINTR) {
54936285Sbrian      log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
55036285Sbrian      if (log_IsKept(LogTIMER)) {
55136285Sbrian        struct timeval t;
55236285Sbrian
55336285Sbrian        for (i = 0; i <= nfds; i++) {
55466898Sbrian          if (FD_ISSET(i, rfds)) {
55536285Sbrian            log_Printf(LogTIMER, "Read set contains %d\n", i);
55666898Sbrian            FD_CLR(i, rfds);
55736285Sbrian            t.tv_sec = t.tv_usec = 0;
55866898Sbrian            if (select(nfds, rfds, wfds, efds, &t) != -1) {
55936285Sbrian              log_Printf(LogTIMER, "The culprit !\n");
56036285Sbrian              break;
56136285Sbrian            }
56236285Sbrian          }
56366898Sbrian          if (FD_ISSET(i, wfds)) {
56436285Sbrian            log_Printf(LogTIMER, "Write set contains %d\n", i);
56566898Sbrian            FD_CLR(i, wfds);
56636285Sbrian            t.tv_sec = t.tv_usec = 0;
56766898Sbrian            if (select(nfds, rfds, wfds, efds, &t) != -1) {
56836285Sbrian              log_Printf(LogTIMER, "The culprit !\n");
56936285Sbrian              break;
57036285Sbrian            }
57136285Sbrian          }
57266898Sbrian          if (FD_ISSET(i, efds)) {
57336285Sbrian            log_Printf(LogTIMER, "Error set contains %d\n", i);
57466898Sbrian            FD_CLR(i, efds);
57536285Sbrian            t.tv_sec = t.tv_usec = 0;
57666898Sbrian            if (select(nfds, rfds, wfds, efds, &t) != -1) {
57736285Sbrian              log_Printf(LogTIMER, "The culprit !\n");
57836285Sbrian              break;
57936285Sbrian            }
58036285Sbrian          }
58136285Sbrian        }
58228679Sbrian      }
58328679Sbrian      break;
5848857Srgrimes    }
5856059Samurai
58643693Sbrian    log_Printf(LogTIMER, "Select returns %d\n", i);
58743693Sbrian
58836345Sbrian    sig_Handle();
58936345Sbrian
59036345Sbrian    if (i <= 0)
59136345Sbrian      continue;
59236345Sbrian
59336285Sbrian    for (i = 0; i <= nfds; i++)
59466898Sbrian      if (FD_ISSET(i, efds)) {
59558038Sbrian        log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i);
59658038Sbrian        /* We deal gracefully with link descriptor exceptions */
59741654Sbrian        if (!bundle_Exception(bundle, i)) {
59841654Sbrian          log_Printf(LogERROR, "Exception cannot be handled !\n");
59941654Sbrian          break;
60041654Sbrian        }
6016059Samurai      }
60228679Sbrian
60336285Sbrian    if (i <= nfds)
60436285Sbrian      break;
60528536Sbrian
60637141Sbrian    nothing_done = 1;
60737141Sbrian
60866898Sbrian    if (descriptor_IsSet(&server.desc, rfds)) {
60966898Sbrian      descriptor_Read(&server.desc, bundle, rfds);
61037141Sbrian      nothing_done = 0;
61137141Sbrian    }
61232039Sbrian
61366898Sbrian    if (descriptor_IsSet(&bundle->desc, rfds)) {
61466898Sbrian      descriptor_Read(&bundle->desc, bundle, rfds);
61537141Sbrian      nothing_done = 0;
61637141Sbrian    }
61737141Sbrian
61866898Sbrian    if (descriptor_IsSet(&bundle->desc, wfds))
61966898Sbrian      if (!descriptor_Write(&bundle->desc, bundle, wfds) && nothing_done) {
62037141Sbrian        /*
62137141Sbrian         * This is disasterous.  The OS has told us that something is
62237141Sbrian         * writable, and all our write()s have failed.  Rather than
62337141Sbrian         * going back immediately to do our UpdateSet()s and select(),
62437141Sbrian         * we sleep for a bit to avoid gobbling up all cpu time.
62537141Sbrian         */
62637141Sbrian        struct timeval t;
62736285Sbrian
62837141Sbrian        t.tv_sec = 0;
62937141Sbrian        t.tv_usec = 100000;
63037141Sbrian        select(0, NULL, NULL, NULL, &t);
63137141Sbrian      }
63253070Sbrian  }
63336285Sbrian
63436285Sbrian  log_Printf(LogDEBUG, "DoLoop done.\n");
6356059Samurai}
636