/*
* main.c
*
* Written by Toshiharu OHNO <tony-o@iij.ad.jp>
* Copyright (c) 1993, Internet Initiative Japan, Inc. All rights reserved.
* See ``COPYRIGHT.iij''
*
* Rewritten by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "mp.h"
#include "iface.h"
#include "command.h"
#include "console.h"
#ifndef NOWEB
#include "web.h"
#endif
#include "radsrv.h"
#include "ngfunc.h"
#include "util.h"
#include "ippool.h"
#ifdef CCP_MPPC
#include "ccp_mppc.h"
#endif
#ifdef USE_BACKTRACE
#include <execinfo.h>
#endif
#include <netgraph.h>
/*
* DEFINITIONS
*/
/* Implied system name when none specified on the command line */
#define DEFAULT_CONF "default"
#define STARTUP_CONF "startup"
#define MAX_ARGS 50
struct option {
short n_args;
char sflag;
const char *lflag;
const char *usage;
const char *desc;
};
typedef struct option *Option;
static const char *UsageStr = "[options] [configuration]";
static struct option OptList[] = {
{ 0, 'b', "background", "",
"Run as a background daemon" },
{ 1, 'd', "directory", "config-dir",
"Set config file directory" },
{ 0, 'k', "kill", "",
"Kill running mpd process before start" },
{ 1, 'f', "file", "config-file",
"Set configuration file" },
{ 0, 'o', "one-shot", "",
"Terminate daemon after last link shutdown" },
{ 1, 'p', "pidfile", "filename",
"Set PID filename" },
#ifdef SYSLOG_FACILITY
{ 1, 's', "syslog-ident", "ident",
"Identifier to use for syslog" },
#endif
#ifdef USE_PAM
{ 1, 'm', "pam-service", "service",
"PAM service name" },
#endif
{ 0, 'v', "version", "",
"Show version information" },
{ 0, 'h', "help", "",
"Show usage information" },
};
#define OPTLIST_SIZE (sizeof(OptList) / sizeof(*OptList))
/* How long to wait for graceful shutdown when we recieve a SIGTERM */
#define TERMINATE_DEATH_WAIT (2 * SECONDS)
/*
* GLOBAL VARIABLES
*/
Rep *gReps;
Link *gLinks;
Bund *gBundles;
int gNumReps;
int gNumLinks;
int gNumBundles;
struct console gConsole;
#ifndef NOWEB
struct web gWeb;
#endif
#ifdef USE_RADIUS
struct radsrv gRadsrv;
#endif
int gBackground = FALSE;
int gShutdownInProgress = FALSE;
int gOverload = 0;
pid_t gPid;
int gRouteSeq = 0;
#ifdef PHYSTYPE_PPTP
int gPPTPto = 10;
unsigned gPPTPtunlimit = 100;
#endif
#ifdef PHYSTYPE_L2TP
int gL2TPto = 10;
#if ((__FreeBSD_version > 603100 && __FreeBSD_version < 700000) || __FreeBSD_version >= 700055)
unsigned gL2TPtunlimit = 100;
#else
unsigned gL2TPtunlimit = 10;
#endif
#endif
int gChildren = 0; /* Current number of children links */
int gMaxChildren = 10000; /* Maximal number of children links */
#ifdef USE_NG_BPF
struct acl *acl_filters[ACL_FILTERS]; /* mpd's global internal bpf filters */
#endif
#ifdef USE_PAM
char gPamService[32];
#endif
struct globalconf gGlobalConf;
pthread_mutex_t gGiantMutex;
const char *gConfigFile = CONF_FILE;
const char *gConfDirectory = PATH_CONF_DIR;
const char *gVersion = MPD_VERSION;
/*
* INTERNAL FUNCTIONS
*/
static void Usage(int ex) __dead2;
static void OptParse(int ac, char *av[]);
static int OptApply(Option opt, int ac, char *av[]);
static Option OptDecode(char *arg, int longform);
static void ConfigRead(int type, void *arg);
static void OpenCloseSignal(int sig);
static void FatalSignal(int sig);
static void SignalHandler(int type, void *arg);
static void CloseIfaces(void);
/*
* INTERNAL VARIABLES
*/
static int gKillProc = FALSE;
static const char *gPidFile = PID_FILE;
static const char *gPeerSystem = NULL;
static EventRef gSignalEvent;
static EventRef gConfigReadEvent;
static int gSignalPipe[2];
static struct context gCtx;
/*
* main()
*/
int
main(int ac, char *av[])
{
int ret, k;
char *args[MAX_ARGS];
Context c;
const struct phystype *pt;
gPid = getpid();
/* enable libpdel typed_mem */
typed_mem_enable();
/* init global-config */
memset(&gGlobalConf, 0, sizeof(gGlobalConf));
/* Read and parse command line */
if (ac > MAX_ARGS)
ac = MAX_ARGS;
memcpy(args, av, ac * sizeof(*av)); /* Copy to preserve "ps" output */
OptParse(ac - 1, args + 1);
/* init console-stuff */
ConsoleInit(&gConsole);
memset(&gCtx, 0, sizeof(gCtx));
gCtx.priv = 2;
if (gBackground) {
c = &gCtx;
} else {
c = StdConsoleConnect(&gConsole);
if (c == NULL)
c = &gCtx;
}
#ifndef NOWEB
/* init web-stuff */
WebInit(&gWeb);
#endif
#ifdef RAD_COA_REQUEST
/* init RADIUS server */
RadsrvInit(&gRadsrv);
#endif
#ifdef USE_PAM
if (!*gPamService)
strcpy(gPamService, "mpd");
#endif
/* Set up libnetgraph logging */
NgSetErrLog(NgFuncErr, NgFuncErrx);
/* Background mode? */
if (gBackground) {
if (daemon(TRUE, FALSE) < 0)
err(1, "daemon");
gPid=getpid();
(void) chdir(gConfDirectory);
}
/* Open log file */
if (LogOpen())
exit(EX_ERRDEAD);
/* Randomize */
srandomdev();
/* Welcome */
Greetings();
/* Check PID file */
if (PIDCheck(gPidFile, gKillProc) < 0)
exit(EX_UNAVAILABLE);
/* Do some initialization */
MpSetDiscrim();
IPPoolInit();
#ifdef CCP_MPPC
MppcTestCap();
#endif
if ((LinksInit() != 0) || (CcpsInit() != 0) || (EcpsInit() != 0))
exit(EX_UNAVAILABLE);
/* Init device types. */
for (k = 0; (pt = gPhysTypes[k]); k++) {
if (pt->tinit && (pt->tinit)()) {
Log(LG_ERR, ("Device type '%s' initialization error.\n", pt->name));
exit(EX_UNAVAILABLE);
}
}
ret = pthread_mutex_init (&gGiantMutex, NULL);
if (ret != 0) {
Log(LG_ERR, ("Could not create giant mutex %d", ret));
exit(EX_UNAVAILABLE);
}
/* Create signaling pipe */
if (pipe(gSignalPipe) < 0) {
Perror("Could not create signal pipe");
exit(EX_UNAVAILABLE);
}
if (EventRegister(&gSignalEvent, EVENT_READ, gSignalPipe[0],
EVENT_RECURRING, SignalHandler, NULL) != 0)
exit(EX_UNAVAILABLE);
/* Register for some common fatal signals so we can exit cleanly */
signal(SIGINT, SendSignal);
signal(SIGTERM, SendSignal);
signal(SIGHUP, SendSignal);
/* Catastrophic signals */
signal(SIGSEGV, SendSignal);
signal(SIGBUS, SendSignal);
signal(SIGABRT, SendSignal);
/* Other signals make us do things */
signal(SIGUSR1, SendSignal);
signal(SIGUSR2, SendSignal);
/* Signals we ignore */
signal(SIGPIPE, SIG_IGN);
EventRegister(&gConfigReadEvent, EVENT_TIMEOUT,
0, 0, ConfigRead, c);
pthread_exit(NULL);
assert(0);
return(1); /* Never reached, but needed to silence compiler warning */
}
/*
* Greetings()
*/
void
Greetings(void)
{
Log(LG_ALWAYS, ("Multi-link PPP daemon for FreeBSD"));
Log(LG_ALWAYS, (" "));
Log(LG_ALWAYS, ("process %lu started, version %s", (u_long) gPid, gVersion));
}
/*
* ConfigRead()
*
* handler of initial configuration reading event
*/
static void
ConfigRead(int type, void *arg)
{
Context c = (Context)arg;
int err;
(void)type;
/* Read startup configuration section */
err = ReadFile(gConfigFile, STARTUP_CONF, DoCommand, c);
/* Read configuration as specified on the command line, or default */
if (!gPeerSystem) {
if (err != -2)
ReadFile(gConfigFile, DEFAULT_CONF, DoCommand, c);
} else {
if (err == -2 || ReadFile(gConfigFile, gPeerSystem, DoCommand, c) < 0) {
Log(LG_ERR, ("can't read configuration for \"%s\"", gPeerSystem));
DoExit(EX_CONFIG);
}
}
CheckOneShot();
if (c->cs)
c->cs->prompt(c->cs);
}
/*
* CloseIfaces()
*/
static void
CloseIfaces(void)
{
Bund b;
int k;
/* Shut down all interfaces we grabbed */
for (k = 0; k < gNumBundles; k++) {
if (((b = gBundles[k]) != NULL) && (!b->tmpl)) {
IfaceClose(b);
BundNcpsClose(b);
}
}
}
/*
* DoExit()
*
* Cleanup and exit
*/
void
DoExit(int code)
{
Bund b;
Rep r;
Link l;
const struct phystype *pt;
int k;
gShutdownInProgress=1;
/* Weak attempt to record what happened */
if (code == EX_ERRDEAD)
Log(LG_ERR, ("fatal error, exiting"));
/* Shutdown stuff */
if (code != EX_TERMINATE) /* kludge to avoid double shutdown */
CloseIfaces();
NgFuncShutdownGlobal();
/* Blow away all netgraph nodes */
for (k = 0; k < gNumBundles; k++) {
if ((b = gBundles[k]) != NULL)
BundShutdown(b);
}
for (k = 0; k < gNumReps; k++) {
if ((r = gReps[k]) != NULL)
RepShutdown(r);
}
for (k = 0; k < gNumLinks; k++) {
if ((l = gLinks[k]) != NULL)
LinkShutdown(l);
}
/* Shutdown device types. */
for (k = 0; (pt = gPhysTypes[k]); k++) {
if (pt->tshutdown)
(pt->tshutdown)();
}
EcpsShutdown();
CcpsShutdown();
LinksShutdown();
/* Remove our PID file and exit */
ConsoleShutdown(&gConsole);
Log(LG_ALWAYS, ("process %d terminated", gPid));
LogClose();
(void) unlink(gPidFile);
exit(code == EX_TERMINATE ? EX_NORMAL : code);
}
/*
* SendSignal()
*
* Send a signal through the signaling pipe
* don't add printf's here or any function
* wich isn't re-entrant
*/
void
SendSignal(int sig)
{
if (sig == SIGSEGV || sig == SIGBUS || sig == SIGABRT)
FatalSignal(sig);
write(gSignalPipe[1], &sig, 1);
}
/*
* SignalHandler()
*
* dispatch signal
*/
static void
SignalHandler(int type, void *arg)
{
u_char sig;
(void)type;
(void)arg;
read(gSignalPipe[0], &sig, sizeof(sig));
switch(sig) {
case SIGUSR1:
case SIGUSR2:
OpenCloseSignal(sig);
break;
default:
FatalSignal(sig);
}
}
/*
* FatalSignal()
*
* Gracefully exit on receipt of a fatal signal
*/
static void
FatalSignal(int sig)
{
Bund b;
static struct pppTimer gDeathTimer;
int k;
int upLinkCount;
#ifdef USE_BACKTRACE
void *buffer[100];
char **strings;
int n;
#endif
/* If a SIGTERM or SIGINT, gracefully shutdown; otherwise shutdown now */
Log(LG_ERR, ("caught fatal signal %s", sys_signame[sig]));
gShutdownInProgress=1;
#ifdef USE_BACKTRACE
if (sig != SIGTERM && sig != SIGINT) {
n = backtrace(buffer, 100);
strings = backtrace_symbols(buffer, n);
if (strings == NULL) {
Log(LG_ERR, ("No backtrace symbols found"));
} else {
for (k = 0; k < n; k++) {
Log(LG_ERR, ("%s", strings[k]));
}
free(strings);
}
}
#endif
for (k = 0; k < gNumBundles; k++) {
if ((b = gBundles[k])) {
if (sig != SIGTERM && sig != SIGINT)
RecordLinkUpDownReason(b, NULL, 0, STR_FATAL_SHUTDOWN, NULL);
else
RecordLinkUpDownReason(b, NULL, 0, STR_ADMIN_SHUTDOWN, NULL);
}
}
upLinkCount = 0;
for (k = 0; k < gNumLinks; k++) {
if (gLinks[k] && (gLinks[k]->state!=PHYS_STATE_DOWN))
upLinkCount++;
}
/* We are going down. No more signals. */
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGUSR1, SIG_IGN);
signal(SIGUSR2, SIG_IGN);
if (sig != SIGTERM && sig != SIGINT)
DoExit(EX_ERRDEAD);
CloseIfaces();
TimerInit(&gDeathTimer, "DeathTimer",
TERMINATE_DEATH_WAIT * (upLinkCount/100+1), (void (*)(void *)) DoExit, (void *) EX_TERMINATE);
TimerStart(&gDeathTimer);
}
/*
* OpenCloseSignal()
*/
static void
OpenCloseSignal(int sig)
{
int k;
Link l;
for (k = 0;
k < gNumLinks && (gLinks[k] == NULL || gLinks[k]->tmpl);
k++);
if (k == gNumLinks) {
Log(LG_ALWAYS, ("rec'd signal %s, no link defined, ignored", sys_signame[sig]));
return;
}
l = gLinks[k];
/* Open/Close Link */
if (l && l->type) {
if (sig == SIGUSR1) {
Log(LG_ALWAYS, ("[%s] rec'd signal %s, opening",
l->name, sys_signame[sig]));
LinkOpenAdm(l);
} else {
Log(LG_ALWAYS, ("[%s] rec'd signal %s, closing",
l->name, sys_signame[sig]));
LinkCloseAdm(l);
}
} else
Log(LG_ALWAYS, ("rec'd signal %s, ignored", sys_signame[sig]));
}
void
CheckOneShot(void)
{
int i;
if (!Enabled(&gGlobalConf.options, GLOBAL_CONF_ONESHOT))
return;
for (i = 0; i < gNumLinks; i++) {
if (gLinks[i] && !gLinks[i]->tmpl)
return;
}
Log(LG_ALWAYS, ("One-shot mode enabled and no links found. Terminating daemon."));
SendSignal(SIGTERM);
}
/*
* OptParse()
*/
static void
OptParse(int ac, char *av[])
{
int used, consumed;
/* Get option flags */
for ( ; ac > 0 && **av == '-'; ac--, av++) {
if (*++(*av) == '-') { /* Long form */
if (*++(*av) == 0) { /* "--" forces end of options */
ac--; av++;
break;
} else {
used = OptApply(OptDecode(*av, TRUE), ac - 1, av + 1);
ac -= used; av += used;
}
} else { /* Short form */
for (used = 0; **av; (*av)++, used += consumed) {
consumed = OptApply(OptDecode(*av, FALSE), ac - 1, av + 1);
if (used && consumed)
Usage(EX_USAGE);
}
ac -= used; av += used;
}
}
/* Get system names */
switch (ac) {
case 0:
break;
case 1:
gPeerSystem = *av;
break;
default:
Usage(EX_USAGE);
}
}
/*
* OptApply()
*/
static int
OptApply(Option opt, int ac, char *av[])
{
#ifdef SYSLOG_FACILITY
memset(gSysLogIdent, 0, sizeof(gSysLogIdent));
#endif
#ifdef USE_PAM
memset(gPamService, 0, sizeof(gPamService));
#endif
if (opt == NULL)
Usage(EX_USAGE);
if (ac < opt->n_args)
Usage(EX_USAGE);
switch (opt->sflag) {
case 'b':
gBackground = TRUE;
return(0);
case 'd':
gConfDirectory = *av;
return(1);
case 'f':
gConfigFile = *av;
return(1);
case 'o':
Enable(&gGlobalConf.options, GLOBAL_CONF_ONESHOT);
return(0);
case 'p':
gPidFile = *av;
return(1);
case 'k':
gKillProc = TRUE;
return(0);
#ifdef SYSLOG_FACILITY
case 's':
strlcpy(gSysLogIdent, *av, sizeof(gSysLogIdent));
return(1);
#endif
#ifdef USE_PAM
case 'm':
strlcpy(gPamService, *av, sizeof(gPamService));
return(1);
#endif
case 'v':
fprintf(stderr, "Version %s\n", gVersion);
exit(EX_NORMAL);
case 'h':
Usage(EX_NORMAL);
default:
assert(0);
}
return(0);
}
/*
* OptDecode()
*/
static Option
OptDecode(char *arg, int longform)
{
Option opt;
size_t k;
for (k = 0; k < OPTLIST_SIZE; k++) {
opt = OptList + k;
if (longform ?
!strcmp(arg, opt->lflag) : (*arg == opt->sflag))
return(opt);
}
return(NULL);
}
/*
* Usage()
*/
static void
Usage(int ex)
{
Option opt;
char buf[100];
size_t k;
fprintf(stderr, "Usage: mpd5 %s\n", UsageStr);
fprintf(stderr, "Options:\n");
for (k = 0; k < OPTLIST_SIZE; k++) {
opt = OptList + k;
snprintf(buf, sizeof(buf), " -%c, --%-s %s",
opt->sflag, opt->lflag, opt->usage);
fprintf(stderr, "%-35s%s\n", buf, opt->desc);
}
exit(ex);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>