#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <netdb.h>
#include <stropts.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/sppptun.h>
#include "common.h"
#include "logging.h"
#define MAX_KEYWORD 4096
#define MAX_NEST 32
#define MAXARGS 256
struct filter_entry {
struct filter_entry *fe_prev;
struct ether_addr fe_mac;
struct ether_addr fe_mask;
uchar_t fe_isexcept;
uchar_t fe_prevcopy;
uchar_t fe_unused[2];
};
struct service_entry {
const char *se_name;
struct filter_entry *se_flist;
uint_t se_flags;
int se_debug;
char *se_server;
char *se_pppd;
char *se_path;
char *se_extra;
char *se_log;
uid_t se_uid;
gid_t se_gid;
};
#define SEF_WILD 0x00000001
#define SEF_NOWILD 0x00000002
#define SEF_CFLIST 0x00000004
#define SEF_CSERVER 0x00000008
#define SEF_CPPPD 0x00000010
#define SEF_CPATH 0x00000020
#define SEF_CEXTRA 0x00000040
#define SEF_CLOG 0x00000080
#define SEF_UIDSET 0x00000100
#define SEF_GIDSET 0x00000200
#define SEF_DEBUGCLR 0x00000400
#define SEF_CDEV 0x00000800
struct device_entry {
const char *de_name;
const struct service_entry **de_services;
int de_nservices;
};
struct option_state {
const struct device_entry *os_devices;
int os_ndevices;
struct per_file *os_pfjunk;
char **os_evjunk;
};
static struct option_state *cur_options;
static struct service_entry glob_svc;
struct device_list {
struct device_list *dl_next;
const char *dl_name;
};
struct service_list {
struct service_entry sl_entry;
struct service_list *sl_next;
struct parse_state *sl_parse;
struct device_list *sl_dev;
int sl_serial;
};
#define SESERIAL(x) ((struct service_list *)&(x))->sl_serial
#define ISGLOBAL(x) ((x) == &(x)->sl_parse->ps_cfile->pf_global)
struct per_file {
struct per_file *pf_prev;
struct service_list pf_global;
struct service_list *pf_svc;
struct service_list *pf_svc_last;
FILE *pf_input;
const char *pf_name;
int pf_nsvc;
};
enum key_state {
ksDefault, ksService, ksDevice, ksClient, ksClientE, ksServer,
ksPppd, ksFile, ksPath, ksExtra, ksLog, ksUser, ksGroup
};
struct parse_state {
enum key_state ps_state;
int ps_serial;
struct per_file *ps_files;
struct per_file *ps_cfile;
struct service_list *ps_csvc;
struct device_list *ps_star;
int ps_flags;
char **ps_evlist;
int ps_evsize;
};
#define PSF_PERDEV 0x0001
#define PSF_SETLEVEL 0x0002
static char *
strsave(const char *str)
{
char *newstr;
if (str == NULL)
return (NULL);
newstr = (char *)malloc(strlen(str) + 1);
if (newstr != NULL)
(void) strcpy(newstr, str);
return (newstr);
}
static void
close_service(struct service_list *slp)
{
struct parse_state *psp;
struct per_file *cfile;
struct service_entry *sep;
struct service_entry *sedefp;
struct filter_entry *fep;
assert(slp != NULL);
psp = slp->sl_parse;
cfile = psp->ps_cfile;
if (cfile == NULL)
return;
sep = &slp->sl_entry;
if (sep->se_flags & SEF_CFLIST) {
sep->se_flist = fep = NULL;
} else {
for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev)
if (fep->fe_prev == NULL || fep->fe_prevcopy) {
fep->fe_prev = NULL;
break;
}
}
if (slp == &cfile->pf_global) {
cfile = cfile->pf_prev;
while (cfile != NULL) {
if (fep == NULL) {
sep->se_flist = fep =
cfile->pf_global.sl_entry.se_flist;
sep->se_flags |= SEF_CFLIST;
} else if (fep->fe_prev == NULL) {
fep->fe_prev =
cfile->pf_global.sl_entry.se_flist;
fep->fe_prevcopy = 1;
}
cfile = cfile->pf_prev;
}
} else {
logdbg("service %s ends", slp->sl_entry.se_name);
while (cfile != NULL) {
if (slp->sl_dev == NULL) {
slp->sl_dev = cfile->pf_global.sl_dev;
sep->se_flags |= SEF_CDEV;
}
sedefp = &cfile->pf_global.sl_entry;
if (fep == NULL) {
sep->se_flist = fep = sedefp->se_flist;
sep->se_flags |= SEF_CFLIST;
} else if (fep->fe_prev == NULL) {
fep->fe_prev = sedefp->se_flist;
fep->fe_prevcopy = 1;
}
if (sep->se_server == NULL) {
sep->se_server = sedefp->se_server;
sep->se_flags |= SEF_CSERVER;
}
if (sep->se_pppd == NULL) {
sep->se_pppd = sedefp->se_pppd;
sep->se_flags |= SEF_CPPPD;
}
if (sep->se_path == NULL) {
sep->se_path = sedefp->se_path;
sep->se_flags |= SEF_CPATH;
}
if (sep->se_extra == NULL) {
sep->se_extra = sedefp->se_extra;
sep->se_flags |= SEF_CEXTRA;
}
if (sep->se_log == NULL) {
sep->se_log = sedefp->se_log;
sep->se_flags |= SEF_CLOG;
}
if (!(sep->se_flags & SEF_UIDSET) &&
(sedefp->se_flags & SEF_UIDSET)) {
sep->se_uid = sedefp->se_uid;
sep->se_flags |= SEF_UIDSET;
}
if (!(sep->se_flags & SEF_GIDSET) &&
(sedefp->se_flags & SEF_GIDSET)) {
sep->se_gid = sedefp->se_gid;
sep->se_flags |= SEF_GIDSET;
}
if (!(sep->se_flags & (SEF_WILD|SEF_NOWILD)))
sep->se_flags |= sedefp->se_flags &
(SEF_WILD|SEF_NOWILD);
if (!(sep->se_flags & SEF_DEBUGCLR)) {
sep->se_debug += sedefp->se_debug;
sep->se_flags |= sedefp->se_flags &
SEF_DEBUGCLR;
}
cfile = cfile->pf_prev;
}
}
psp->ps_csvc = &psp->ps_cfile->pf_global;
}
static void
free_device_list(struct device_list *dlp)
{
struct device_list *dln;
while (dlp != NULL) {
dln = dlp->dl_next;
free(dlp);
dlp = dln;
}
}
static int
set_service(struct service_list *slp, const char *str)
{
struct parse_state *psp;
struct per_file *cfile;
close_service(slp);
psp = slp->sl_parse;
slp = (struct service_list *)calloc(sizeof (*slp) + strlen(str) + 1,
1);
if (slp == NULL) {
logerr("no memory for service \"%s\"", str);
return (-1);
}
cfile = psp->ps_cfile;
if (cfile->pf_svc_last == NULL)
cfile->pf_svc = slp;
else
cfile->pf_svc_last->sl_next = slp;
cfile->pf_svc_last = slp;
cfile->pf_nsvc++;
slp->sl_entry.se_name = (const char *)(slp+1);
(void) strcpy((char *)(slp+1), str);
logdbg("service %s begins", slp->sl_entry.se_name);
slp->sl_serial = psp->ps_serial++;
slp->sl_parse = psp;
psp->ps_csvc = slp;
return (0);
}
static int
set_wildcard(struct service_list *slp, const char *str)
{
if (!ISGLOBAL(slp) &&
(slp->sl_entry.se_flags & (SEF_WILD|SEF_NOWILD))) {
logdbg("%s: extra \"%s\" ignored",
slp->sl_parse->ps_cfile->pf_name, str);
return (0);
}
slp->sl_entry.se_flags =
(slp->sl_entry.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
(*str == 'n' ? SEF_NOWILD : SEF_WILD);
return (0);
}
static int
set_debug(struct service_list *slp, const char *str)
{
slp->sl_entry.se_debug++;
if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
log_level = slp->sl_entry.se_debug;
}
return (0);
}
static int
set_nodebug(struct service_list *slp, const char *str)
{
slp->sl_entry.se_flags |= SEF_DEBUGCLR;
slp->sl_entry.se_debug = 0;
if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
log_level = slp->sl_entry.se_debug;
}
return (0);
}
static int
set_string(struct service_list *slp, const char *str)
{
char **cpp;
assert(!(slp->sl_entry.se_flags &
(SEF_CSERVER|SEF_CPPPD|SEF_CPATH|SEF_CEXTRA|SEF_CLOG)));
switch (slp->sl_parse->ps_state) {
case ksServer:
cpp = &slp->sl_entry.se_server;
break;
case ksPppd:
cpp = &slp->sl_entry.se_pppd;
break;
case ksPath:
cpp = &slp->sl_entry.se_path;
break;
case ksExtra:
cpp = &slp->sl_entry.se_extra;
break;
case ksLog:
cpp = &slp->sl_entry.se_log;
break;
default:
assert(0);
return (-1);
}
if (*cpp != NULL)
free(*cpp);
*cpp = strsave(str);
return (0);
}
static int
set_file(struct service_list *slp, const char *str)
{
FILE *fp;
struct per_file *pfp;
struct parse_state *psp;
close_service(slp);
if ((fp = fopen(str, "r")) == NULL) {
logwarn("%s: %s: %s", slp->sl_parse->ps_cfile->pf_name, str,
mystrerror(errno));
return (-1);
}
pfp = (struct per_file *)calloc(sizeof (*pfp) + strlen(str) + 1, 1);
if (pfp == NULL) {
logerr("no memory for parsing file %s", str);
(void) fclose(fp);
return (-1);
}
logdbg("config file %s open", str);
pfp->pf_name = (const char *)(pfp+1);
(void) strcpy((char *)(pfp+1), str);
pfp->pf_input = fp;
psp = slp->sl_parse;
pfp->pf_prev = psp->ps_cfile;
psp->ps_cfile = pfp;
psp->ps_csvc = &pfp->pf_global;
pfp->pf_global.sl_parse = psp;
pfp->pf_global.sl_entry.se_name = "<global>";
return (0);
}
static int
set_device(struct service_list *slp, const char *str)
{
struct parse_state *psp = slp->sl_parse;
struct device_list *dlp;
struct device_list *dln;
struct device_list **dlpp;
const char *cp;
int len;
if (psp->ps_flags & PSF_PERDEV) {
logerr("\"device %s\" ignored in %s", str,
psp->ps_cfile->pf_name);
return (0);
}
if (strcmp(str, "*") == 0 || strcmp(str, "all") == 0) {
if (!(slp->sl_entry.se_flags & SEF_CDEV))
free_device_list(slp->sl_dev);
slp->sl_dev = psp->ps_star;
slp->sl_entry.se_flags |= SEF_CDEV;
} else {
dlpp = &dlp;
for (;;) {
while (isspace(*str) || *str == ',')
str++;
if (*str == '\0')
break;
cp = str;
while (*str != '\0' && !isspace(*str) && *str != ',')
str++;
len = str - cp;
if ((len == 1 && *cp == '*') ||
(len == 3 && strncmp(cp, "all", 3) == 0)) {
logerr("%s: cannot use %.*s in device list",
psp->ps_cfile->pf_name, len, cp);
continue;
}
dln = (struct device_list *)malloc(sizeof (*dln) +
len + 1);
if (dln == NULL) {
logerr("no memory for device name");
break;
}
dln->dl_name = (const char *)(dln + 1);
(void) memcpy(dln + 1, cp, len);
((char *)(dln + 1))[len] = '\0';
logdbg("%s: device %s", psp->ps_cfile->pf_name,
dln->dl_name);
*dlpp = dln;
dlpp = &dln->dl_next;
}
*dlpp = NULL;
dlpp = &slp->sl_dev;
if (!(slp->sl_entry.se_flags & SEF_CDEV))
while (*dlpp != NULL)
dlpp = &(*dlpp)->dl_next;
*dlpp = dlp;
slp->sl_entry.se_flags &= ~SEF_CDEV;
}
return (0);
}
static int
set_client(struct service_list *slp, const char *str)
{
struct parse_state *psp = slp->sl_parse;
struct filter_entry *fep;
struct filter_entry *fen;
const char *cp;
int len;
char hbuf[MAXHOSTNAMELEN];
struct ether_addr ea;
struct ether_addr mask;
uchar_t *ucp;
uchar_t *mcp;
fep = slp->sl_entry.se_flist;
for (;;) {
while (isspace(*str) || *str == ',')
str++;
if (*str == '\0')
break;
cp = str;
while (*str != '\0' && !isspace(*str) && *str != ',')
str++;
len = str - cp;
(void) memcpy(hbuf, cp, len);
hbuf[len] = '\0';
mcp = mask.ether_addr_octet;
mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
if (ether_hostton(hbuf, &ea) != 0) {
ucp = ea.ether_addr_octet;
while (cp < str) {
if (ucp >= ea.ether_addr_octet + sizeof (ea))
break;
if (*cp == '*') {
*mcp++ = *ucp++ = 0;
cp++;
} else {
if (!isxdigit(*cp))
break;
*ucp = hexdecode(*cp++);
if (cp < str && isxdigit(*cp)) {
*ucp = (*ucp << 4) |
hexdecode(*cp++);
}
ucp++;
*mcp++ = 0xFF;
}
if (cp < str) {
if (*cp != ':' || cp + 1 == str)
break;
cp++;
}
}
if (cp < str) {
logerr("%s: illegal Ethernet address %.*s",
psp->ps_cfile->pf_name, len, cp);
continue;
}
}
fen = (struct filter_entry *)malloc(sizeof (*fen));
if (fen == NULL) {
logerr("unable to allocate memory for filter");
break;
}
fen->fe_isexcept = psp->ps_state == ksClientE;
fen->fe_prevcopy = 0;
(void) memcpy(&fen->fe_mac, &ea, sizeof (fen->fe_mac));
(void) memcpy(&fen->fe_mask, &mask, sizeof (fen->fe_mask));
fen->fe_prev = fep;
fep = fen;
}
slp->sl_entry.se_flist = fep;
return (0);
}
static int
set_user(struct service_list *slp, const char *str)
{
struct passwd *pw;
char *cp;
uid_t myuid, uid;
if ((pw = getpwnam(str)) == NULL) {
uid = (uid_t)strtol(str, &cp, 0);
if (str == cp || *cp != '\0') {
logerr("%s: bad user name \"%s\"",
slp->sl_parse->ps_cfile->pf_name, str);
return (0);
}
} else {
uid = pw->pw_uid;
}
slp->sl_entry.se_uid = uid;
myuid = getuid();
if (myuid != 0) {
if (myuid == uid)
return (0);
logdbg("%s: not root; ignoring attempt to set UID %d (%s)",
slp->sl_parse->ps_cfile->pf_name, uid, str);
return (0);
}
slp->sl_entry.se_flags |= SEF_UIDSET;
return (0);
}
static int
set_group(struct service_list *slp, const char *str)
{
struct group *gr;
char *cp;
gid_t gid;
if ((gr = getgrnam(str)) == NULL) {
gid = (gid_t)strtol(str, &cp, 0);
if (str == cp || *cp != '\0') {
logerr("%s: bad group name \"%s\"",
slp->sl_parse->ps_cfile->pf_name, str);
return (0);
}
} else {
gid = gr->gr_gid;
}
slp->sl_entry.se_gid = gid;
if (getuid() != 0) {
logdbg("%s: not root; ignoring attempt to set GID %d (%s)",
slp->sl_parse->ps_cfile->pf_name, gid, str);
return (0);
}
slp->sl_entry.se_flags |= SEF_GIDSET;
return (0);
}
struct kw_entry {
const char *kwe_word;
enum key_state kwe_in;
enum key_state kwe_out;
int (*kwe_func)(struct service_list *slp, const char *str);
};
static const struct kw_entry key_list[] = {
{ "service", ksDefault, ksService, NULL },
{ "device", ksDefault, ksDevice, NULL },
{ "client", ksDefault, ksClient, NULL },
{ "except", ksClient, ksClientE, NULL },
{ "wildcard", ksDefault, ksDefault, set_wildcard },
{ "nowildcard", ksDefault, ksDefault, set_wildcard },
{ "server", ksDefault, ksServer, NULL },
{ "pppd", ksDefault, ksPppd, NULL },
{ "debug", ksDefault, ksDefault, set_debug },
{ "nodebug", ksDefault, ksDefault, set_nodebug },
{ "file", ksDefault, ksFile, NULL },
{ "path", ksDefault, ksPath, NULL },
{ "extra", ksDefault, ksExtra, NULL },
{ "log", ksDefault, ksLog, NULL },
{ "user", ksDefault, ksUser, NULL },
{ "group", ksDefault, ksGroup, NULL },
{ "", ksService, ksDefault, set_service },
{ "", ksDevice, ksDefault, set_device },
{ "", ksClient, ksDefault, set_client },
{ "", ksClientE, ksDefault, set_client },
{ "", ksServer, ksDefault, set_string },
{ "", ksPppd, ksDefault, set_string },
{ "", ksFile, ksDefault, set_file },
{ "", ksPath, ksDefault, set_string },
{ "", ksExtra, ksDefault, set_string },
{ "", ksLog, ksDefault, set_string },
{ "", ksUser, ksDefault, set_user },
{ "", ksGroup, ksDefault, set_group },
{ NULL, ksDefault, ksDefault, NULL }
};
static const char *
after_key(enum key_state kstate)
{
const struct kw_entry *kep;
for (kep = key_list; kep->kwe_word != NULL; kep++)
if (kep->kwe_out == kstate)
return (kep->kwe_word);
return ("nothing");
}
static void
file_end(struct parse_state *psp)
{
struct per_file *pfp;
if (psp->ps_state != ksDefault) {
logerr("%s ends with \"%s\"", psp->ps_cfile->pf_name,
after_key(psp->ps_state));
psp->ps_state = ksDefault;
}
close_service(psp->ps_csvc);
if ((pfp = psp->ps_cfile) != NULL) {
psp->ps_cfile = pfp->pf_prev;
pfp->pf_prev = psp->ps_files;
psp->ps_files = pfp;
if (pfp->pf_input != NULL) {
logdbg("file %s closed", pfp->pf_name);
(void) fclose(pfp->pf_input);
pfp->pf_input = NULL;
}
if ((pfp = psp->ps_cfile) != NULL)
psp->ps_csvc = &pfp->pf_global;
}
}
static int
dispatch_keyword(struct parse_state *psp, const char *keybuf)
{
const struct kw_entry *kep;
int retv;
char *cp;
char *env;
char **evlist;
int len;
retv = 0;
for (kep = key_list; kep->kwe_word != NULL; kep++) {
if (kep->kwe_in == psp->ps_state &&
(*kep->kwe_word == '\0' ||
strcasecmp(kep->kwe_word, keybuf) == 0)) {
if (kep->kwe_func != NULL)
retv = (*kep->kwe_func)(psp->ps_csvc, keybuf);
psp->ps_state = kep->kwe_out;
return (retv);
}
}
if (strchr(keybuf, '=') != NULL) {
if ((cp = strsave(keybuf)) == NULL) {
logerr("no memory to save %s", keybuf);
return (0);
}
len = (strchr(cp, '=') - cp) + 1;
if ((evlist = psp->ps_evlist) == NULL) {
psp->ps_evlist = evlist =
(char **)malloc(8 * sizeof (*evlist));
if (evlist == NULL) {
logerr("no memory for evlist");
free(cp);
return (0);
}
psp->ps_evsize = 8;
evlist[0] = evlist[1] = NULL;
} else {
while ((env = *evlist) != NULL) {
if (strncmp(cp, env, len) == 0)
break;
evlist++;
}
if (env == NULL &&
evlist-psp->ps_evlist >= psp->ps_evsize-1) {
evlist = (char **)realloc(psp->ps_evlist,
(psp->ps_evsize + 8) * sizeof (*evlist));
if (evlist == NULL) {
logerr("cannot realloc evlist to %d",
psp->ps_evsize + 8);
free(cp);
return (0);
}
psp->ps_evlist = evlist;
evlist += psp->ps_evsize - 1;
psp->ps_evsize += 8;
evlist[1] = NULL;
}
}
logdbg("setenv \"%s\"", cp);
if (*evlist != NULL)
free(*evlist);
*evlist = cp;
return (0);
}
logerr("%s: unknown keyword '%s'", psp->ps_cfile->pf_name, keybuf);
return (-1);
}
static char *
my_getenv(struct parse_state *psp, char *estr)
{
char **evlist, *ent;
int elen;
if (psp != NULL && (evlist = psp->ps_evlist) != NULL) {
elen = strlen(estr);
while ((ent = *evlist++) != NULL) {
if (strncmp(ent, estr, elen) == 0 &&
ent[elen] == '=')
return (ent + elen + 1);
}
}
return (getenv(estr));
}
static char *
env_replace(struct parse_state *psp, char *keybuf, char kwstate)
{
char *cpe;
char *cp;
if ((cp = strrchr(keybuf, kwstate)) != NULL) {
if ((cpe = my_getenv(psp, cp + 1)) != NULL) {
*cp = '\0';
(void) strncat(cp, cpe,
MAX_KEYWORD - (cp - keybuf) - 1);
keybuf[MAX_KEYWORD - 1] = '\0';
cp += strlen(cp);
} else {
logerr("unknown variable \"%s\"", cp + 1);
}
} else {
cp = keybuf + strlen(keybuf);
}
return (cp);
}
static int
getkeyword(struct parse_state *psp, char *keybuf, int keymax,
int (*nextchr)(void *), void *arg, int flag)
{
char varnest[MAX_NEST];
char *kbp;
char *vnp;
char chr;
int ichr;
char kwstate;
static const char escstr[] = "a\ab\bf\fn\nr\r";
const char *cp;
keymax--;
kwstate = '\0';
kbp = keybuf;
vnp = varnest;
for (;;) {
ichr = (*nextchr)(arg);
chr = (char)ichr;
tryagain:
switch (kwstate) {
case '\\':
case '|':
case '~':
if (chr == '\n')
chr = '\0';
else if (isalpha(chr) &&
(cp = strchr(escstr, chr)) != NULL)
chr = cp[1];
switch (kwstate) {
case '\\':
kwstate = 'A';
break;
case '|':
kwstate = '"';
break;
case '~':
kwstate = '\'';
break;
}
break;
case '"':
if (!flag && chr == '$') {
kwstate = '%';
chr = '\0';
break;
}
case '\'':
if (chr == '\\') {
kwstate = kwstate == '"' ? '|' : '~';
chr = '\0';
break;
}
if (chr == kwstate) {
kwstate = 'A';
chr = '\0';
}
break;
case '$':
case '%':
if (chr == '{') {
kwstate = chr =
kwstate == '$' ? '{' : '[';
break;
}
*kbp++ = kwstate = kwstate == '$' ? '+' : '*';
case '+':
case '*':
if (chr == '$' &&
vnp < varnest + sizeof (varnest)) {
*vnp++ = kwstate;
kwstate = '$';
chr = '\0';
break;
}
if (!isalnum(chr) && chr != '_' &&
chr != '.' && chr != '-') {
*kbp = '\0';
kbp = env_replace(psp, keybuf, kwstate);
if (vnp > varnest)
kwstate = *--vnp;
else
kwstate = kwstate == '+' ?
'A' : '"';
goto tryagain;
}
break;
case '{':
case '[':
if (chr == '}') {
*kbp = '\0';
kbp = env_replace(psp, keybuf, kwstate);
kwstate = kwstate == '{' ? 'A' : '"';
chr = '\0';
}
break;
case '#':
case '@':
if (chr == '\n' || chr == '\r' || ichr == EOF) {
kwstate = kwstate == '#' ? '\0' : ' ';
chr = '\0';
break;
}
chr = '\0';
break;
case '\0':
if (ichr == EOF || isspace(chr)) {
chr = '\0';
break;
}
if (chr == '#') {
kwstate = '#';
chr = '\0';
break;
}
kwstate = 'A';
default:
if (ichr == EOF)
break;
if (isspace(chr)) {
kwstate = ' ';
break;
}
if (chr == '"' || chr == '\'' || chr == '\\') {
kwstate = chr;
chr = '\0';
break;
}
if (flag)
break;
if (chr == '#') {
kwstate = '@';
chr = '\0';
break;
}
if (chr == '$') {
kwstate = '$';
chr = '\0';
}
break;
}
if (ichr == EOF || kwstate == ' ')
break;
if (chr != '\0' && kbp < keybuf + keymax)
*kbp++ = (char)chr;
}
*kbp = '\0';
if (ichr == EOF) {
return (kwstate == '\0' ? 1 : 2);
}
return (0);
}
static void
parse_from_file(struct parse_state *psp)
{
char keybuf[MAX_KEYWORD];
int retv;
while (psp->ps_cfile != NULL && psp->ps_cfile->pf_input != NULL) {
retv = getkeyword(psp, keybuf, sizeof (keybuf),
(int (*)(void *))fgetc, (void *)psp->ps_cfile->pf_input,
0);
if (retv != 1)
(void) dispatch_keyword(psp, keybuf);
if (retv != 0)
file_end(psp);
}
}
static void
parse_file(struct parse_state *psp, const char *fname)
{
struct stat sb;
if (stat(fname, &sb) == -1 && errno == ENOENT)
return;
if (set_file(psp->ps_csvc, fname) == 0)
parse_from_file(psp);
}
static void
parse_arg_list(struct parse_state *psp, int argc, char **argv)
{
if (--argc <= 0)
return;
while (--argc >= 0) {
(void) dispatch_keyword(psp, *++argv);
if (psp->ps_cfile->pf_input != NULL)
parse_from_file(psp);
}
}
static int
count_devs(struct device_list *dlp)
{
int ndevs;
ndevs = 0;
for (; dlp != NULL; dlp = dlp->dl_next)
ndevs++;
return (ndevs);
}
static int
count_per_file(struct per_file *pfp)
{
struct service_list *slp;
int ndevs = 0;
for (; pfp != NULL; pfp = pfp->pf_prev) {
ndevs += count_devs(pfp->pf_global.sl_dev);
for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
if (!(slp->sl_entry.se_flags & SEF_CDEV))
ndevs += count_devs(slp->sl_dev);
}
return (ndevs);
}
static const char **
devs_to_list(struct device_list *dlp, const char **dnames)
{
for (; dlp != NULL; dlp = dlp->dl_next)
*dnames++ = dlp->dl_name;
return (dnames);
}
static const char **
per_file_to_list(struct per_file *pfp, const char **dnames)
{
struct service_list *slp;
for (; pfp != NULL; pfp = pfp->pf_prev) {
dnames = devs_to_list(pfp->pf_global.sl_dev, dnames);
for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
if (!(slp->sl_entry.se_flags & SEF_CDEV))
dnames = devs_to_list(slp->sl_dev, dnames);
}
return (dnames);
}
static int
devcmp(const void *d1, const void *d2)
{
return (strcmp(*(const char **)d1, *(const char **)d2));
}
static const char **
get_unique_devs(struct parse_state *psp)
{
int ndevs;
const char **dnames;
const char **dnp;
const char **dnf;
ndevs = count_per_file(psp->ps_files);
ndevs += count_per_file(psp->ps_cfile);
if (ndevs <= 0) {
return (NULL);
}
dnames = (const char **)malloc((ndevs+1) * sizeof (const char *));
if (dnames == NULL) {
logerr("unable to allocate space for %d devices", ndevs + 1);
return (NULL);
}
dnp = per_file_to_list(psp->ps_files, dnames);
(void) per_file_to_list(psp->ps_cfile, dnp);
qsort(dnames, ndevs, sizeof (const char *), devcmp);
for (dnf = (dnp = dnames) + 1; dnf < dnames+ndevs; dnf++)
if (strcmp(*dnf, *dnp) != 0)
*++dnp = *dnf;
*++dnp = NULL;
return (dnames);
}
static struct option_state *
organize_state(struct parse_state *psp)
{
struct per_file *pfp;
struct per_file *pftopp;
struct service_list *slp;
struct device_list *dlp;
int ndevs;
int nsvcs;
const char **dnames;
const char **dnp;
struct device_entry *dep;
struct option_state *osp;
struct service_entry **sepp;
struct service_entry **sebpp;
struct service_entry **se2pp;
close_service(psp->ps_csvc);
psp->ps_csvc = NULL;
if ((pfp = psp->ps_cfile) != NULL) {
pfp->pf_prev = psp->ps_files;
psp->ps_files = pfp;
psp->ps_cfile = NULL;
}
pftopp = psp->ps_files;
for (pfp = pftopp->pf_prev; pfp != NULL; pfp = pfp->pf_prev)
if (pfp->pf_svc != NULL) {
if (pftopp->pf_svc_last == NULL)
pftopp->pf_svc = pfp->pf_svc;
else
pftopp->pf_svc_last->sl_next = pfp->pf_svc;
pftopp->pf_svc_last = pfp->pf_svc_last;
pfp->pf_svc = pfp->pf_svc_last = NULL;
}
nsvcs = 0;
for (slp = psp->ps_files->pf_svc; slp != NULL; slp = slp->sl_next)
for (dlp = slp->sl_dev; dlp != NULL; dlp = dlp->dl_next)
nsvcs++;
dnames = get_unique_devs(psp);
if (dnames == NULL) {
logdbg("no devices referenced by any service");
return (NULL);
}
ndevs = 0;
for (dnp = dnames; *dnp != NULL; dnp++)
ndevs++;
osp = (struct option_state *)malloc(sizeof (*osp) +
ndevs * sizeof (*dep) + nsvcs * ndevs * sizeof (*sepp));
if (osp == NULL) {
logerr("unable to allocate option state structure");
free(dnames);
return (NULL);
}
osp->os_devices = dep = (struct device_entry *)(osp+1);
osp->os_pfjunk = psp->ps_files;
psp->ps_files = NULL;
osp->os_evjunk = psp->ps_evlist;
psp->ps_evlist = NULL;
sepp = (struct service_entry **)(dep + ndevs);
for (dnp = dnames; *dnp != NULL; dnp++) {
dep->de_name = *dnp;
dep->de_services = (const struct service_entry **)sepp;
sebpp = sepp;
for (slp = osp->os_pfjunk->pf_svc; slp != NULL;
slp = slp->sl_next)
for (dlp = slp->sl_dev; dlp != NULL;
dlp = dlp->dl_next) {
if (dlp->dl_name == *dnp ||
strcmp(dlp->dl_name, *dnp) == 0) {
for (se2pp = sebpp; se2pp < sepp;
se2pp++)
if ((*se2pp)->se_name ==
slp->sl_entry.se_name ||
strcmp((*se2pp)->
se_name, slp->sl_entry.
se_name) == 0)
break;
if (se2pp >= sepp)
*sepp++ = &slp->sl_entry;
else if (SESERIAL(**se2pp) <
SESERIAL(slp->sl_entry))
*se2pp = &slp->sl_entry;
}
}
dep->de_nservices = (const struct service_entry **)sepp -
dep->de_services;
if (dep->de_nservices > 0)
dep++;
}
osp->os_ndevices = dep - osp->os_devices;
free(dnames);
return (osp);
}
static void
free_service(struct service_list *slp)
{
struct filter_entry *fep;
struct filter_entry *fen;
if (!(slp->sl_entry.se_flags & SEF_CDEV))
free_device_list(slp->sl_dev);
if (!(slp->sl_entry.se_flags & SEF_CFLIST)) {
fep = slp->sl_entry.se_flist;
while (fep != NULL) {
fen = fep->fe_prevcopy ? NULL : fep->fe_prev;
free(fep);
fep = fen;
}
}
if (!(slp->sl_entry.se_flags & SEF_CPPPD) &&
slp->sl_entry.se_pppd != NULL)
free(slp->sl_entry.se_pppd);
if (!(slp->sl_entry.se_flags & SEF_CSERVER) &&
slp->sl_entry.se_server != NULL)
free(slp->sl_entry.se_server);
if (!(slp->sl_entry.se_flags & SEF_CPATH) &&
slp->sl_entry.se_path != NULL)
free(slp->sl_entry.se_path);
if (!(slp->sl_entry.se_flags & SEF_CEXTRA) &&
slp->sl_entry.se_extra != NULL)
free(slp->sl_entry.se_extra);
if (!(slp->sl_entry.se_flags & SEF_CLOG) &&
slp->sl_entry.se_log != NULL)
free(slp->sl_entry.se_log);
}
static void
free_service_list(struct service_list *slp)
{
struct service_list *sln;
while (slp != NULL) {
free_service(slp);
sln = slp->sl_next;
free(slp);
slp = sln;
}
}
static void
free_file_list(struct per_file *pfp)
{
struct per_file *pfn;
while (pfp != NULL) {
free_service(&pfp->pf_global);
free_service_list(pfp->pf_svc);
pfn = pfp->pf_prev;
free(pfp);
pfp = pfn;
}
}
static void
free_env_list(char **evlist)
{
char **evp;
char *env;
if ((evp = evlist) != NULL) {
while ((env = *evp++) != NULL)
free(env);
free(evlist);
}
}
static void
add_new_dev(int tunfd, const char *dname)
{
union ppptun_name ptn;
(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
dname);
if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
} else {
logdbg("added %s", ptn.ptn_name);
}
}
static void
rem_old_dev(int tunfd, const char *dname)
{
union ppptun_name ptn;
(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
dname);
if (strioctl(tunfd, PPPTUN_DCTL, &ptn, sizeof (ptn), 0) < 0) {
logerr("PPPTUN_DCTL %s: %s", ptn.ptn_name, mystrerror(errno));
} else {
logdbg("removed %s", ptn.ptn_name);
}
}
static void
get_device_list(struct parse_state *psp, int tunfd)
{
struct device_list *dlp;
struct device_list **dlpp;
struct device_list *dlalt;
struct device_list **dl2pp;
struct device_list *dla;
int i;
union ppptun_name ptn;
char *cp;
dlpp = &psp->ps_star;
dl2pp = &dlalt;
for (i = 0; ; i++) {
ptn.ptn_index = i;
if (strioctl(tunfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
sizeof (ptn)) < 0) {
logerr("PPPTUN_GNNAME %d: %s", i, mystrerror(errno));
break;
}
if (ptn.ptn_name[0] == '\0')
break;
if ((cp = strchr(ptn.ptn_name, ':')) == NULL ||
strncmp(cp, ":pppoe", 6) != 0 ||
(cp[6] != '\0' && strcmp(cp+6, "d") != 0))
continue;
*cp = '\0';
dlp = (struct device_list *)malloc(sizeof (*dlp) +
strlen(ptn.ptn_name) + 1);
if (dlp == NULL)
break;
dlp->dl_name = (const char *)(dlp + 1);
(void) strcpy((char *)(dlp + 1), ptn.ptn_name);
if (cp[6] == '\0') {
*dlpp = dlp;
dlpp = &dlp->dl_next;
} else {
*dl2pp = dlp;
dl2pp = &dlp->dl_next;
}
}
*dlpp = NULL;
*dl2pp = NULL;
for (dlpp = &psp->ps_star; (dlp = *dlpp) != NULL; ) {
for (dla = dlalt; dla != NULL; dla = dla->dl_next)
if (strcmp(dla->dl_name, dlp->dl_name) == 0)
break;
if (dla == NULL) {
*dlpp = dlp->dl_next;
free(dlp);
} else {
dlpp = &dlp->dl_next;
}
}
free_device_list(dlalt);
dlp = (struct device_list *)malloc(sizeof (*dlp) + 2);
if (dlp != NULL) {
dlp->dl_name = (const char *)(dlp + 1);
(void) strcpy((char *)(dlp + 1), "*");
dlp->dl_next = psp->ps_star;
psp->ps_star = dlp;
}
}
void
global_logging(void)
{
log_for_service(glob_svc.se_log, glob_svc.se_debug);
}
void
parse_options(int tunfd, int argc, char **argv)
{
struct parse_state pstate;
struct per_file *argpf;
struct option_state *newopt;
const char **dnames;
const char **dnp;
const struct device_entry *newdep, *newmax;
const struct device_entry *olddep, *oldmax;
int cmpval;
struct service_entry newglobsvc, *mainsvc;
argpf = (struct per_file *)calloc(sizeof (*argpf), 1);
if (argpf == NULL) {
return;
}
(void) memset(&pstate, '\0', sizeof (pstate));
pstate.ps_state = ksDefault;
pstate.ps_cfile = argpf;
pstate.ps_csvc = &argpf->pf_global;
argpf->pf_global.sl_parse = &pstate;
argpf->pf_name = "command line";
argpf->pf_global.sl_entry.se_debug++;
argpf->pf_global.sl_entry.se_name = "<global>";
get_device_list(&pstate, tunfd);
pstate.ps_flags |= PSF_SETLEVEL;
parse_arg_list(&pstate, argc, argv);
parse_file(&pstate, "/etc/ppp/pppoe");
pstate.ps_flags &= ~PSF_SETLEVEL;
newglobsvc = argpf->pf_global.sl_entry;
if (pstate.ps_files != NULL) {
mainsvc = &pstate.ps_files->pf_global.sl_entry;
if (mainsvc->se_log != NULL)
newglobsvc.se_log = mainsvc->se_log;
if (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD))
newglobsvc.se_flags =
(newglobsvc.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
(mainsvc->se_flags & (SEF_WILD|SEF_NOWILD));
if (mainsvc->se_flags & SEF_DEBUGCLR)
newglobsvc.se_debug = 0;
newglobsvc.se_debug += mainsvc->se_debug;
}
glob_svc = newglobsvc;
global_logging();
dnames = get_unique_devs(&pstate);
if (dnames != NULL) {
pstate.ps_flags |= PSF_PERDEV;
for (dnp = dnames; *dnp != NULL; dnp++)
parse_file(&pstate, *dnp);
pstate.ps_flags &= ~PSF_PERDEV;
free(dnames);
}
file_end(&pstate);
newopt = organize_state(&pstate);
if (newopt == NULL && glob_svc.se_log != NULL) {
glob_svc.se_log = NULL;
global_logging();
}
free_file_list(pstate.ps_files);
free_file_list(pstate.ps_cfile);
free_device_list(pstate.ps_star);
free_env_list(pstate.ps_evlist);
if (newopt != NULL) {
newdep = newopt->os_devices;
newmax = newdep + newopt->os_ndevices;
} else {
newdep = newmax = NULL;
}
if (cur_options != NULL) {
olddep = cur_options->os_devices;
oldmax = olddep + cur_options->os_ndevices;
} else {
olddep = oldmax = NULL;
}
while ((newdep != NULL && newdep < newmax) ||
(olddep != NULL && olddep < oldmax)) {
if (newdep < newmax) {
if (olddep >= oldmax) {
add_new_dev(tunfd, newdep->de_name);
newdep++;
} else {
cmpval = strcmp(newdep->de_name,
olddep->de_name);
if (cmpval < 0) {
add_new_dev(tunfd, newdep->de_name);
newdep++;
} else if (cmpval == 0) {
newdep++;
olddep++;
}
}
}
if (olddep < oldmax) {
if (newdep >= newmax) {
rem_old_dev(tunfd, olddep->de_name);
olddep++;
} else {
cmpval = strcmp(newdep->de_name,
olddep->de_name);
if (cmpval > 0) {
rem_old_dev(tunfd, olddep->de_name);
olddep++;
} else if (cmpval == 0) {
newdep++;
olddep++;
}
}
}
}
if (cur_options != NULL) {
free_file_list(cur_options->os_pfjunk);
free_env_list(cur_options->os_evjunk);
free(cur_options);
}
cur_options = newopt;
}
static boolean_t
allow_service(const struct service_entry *sep, const ppptun_atype *pap)
{
const struct filter_entry *fep;
const struct filter_entry *lmatch;
boolean_t anynonexcept = B_FALSE;
const uchar_t *upt;
const uchar_t *macp;
const uchar_t *maskp;
int i;
lmatch = NULL;
for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev) {
anynonexcept |= !fep->fe_isexcept;
upt = pap->pta_pppoe.ptma_mac;
macp = fep->fe_mac.ether_addr_octet;
maskp = fep->fe_mask.ether_addr_octet;
for (i = sizeof (pap->pta_pppoe.ptma_mac); i > 0; i--)
if (((*macp++ ^ *upt++) & *maskp++) != 0)
break;
if (i <= 0)
lmatch = fep;
}
if (lmatch == NULL) {
return (!anynonexcept);
}
return (!lmatch->fe_isexcept);
}
int
locate_service(poep_t *poep, int plen, const char *iname, ppptun_atype *pap,
uint32_t *outp, void **srvp)
{
poep_t *opoe;
const uint8_t *tagp;
const char *cp;
int ttyp;
int tlen;
int nsvcs;
const struct device_entry *dep, *depe;
const struct device_entry *wdep;
const struct service_entry **sepp, **seppe;
const struct service_entry *sep;
char *str;
boolean_t ispadi;
ispadi = poep->poep_code == POECODE_PADI;
opoe = poe_mkheader(outp, ispadi ? POECODE_PADO : POECODE_PADS, 0);
*srvp = NULL;
if (cur_options == NULL)
return (0);
dep = cur_options->os_devices;
depe = dep + cur_options->os_ndevices;
wdep = NULL;
if ((cp = strchr(iname, ':')) != NULL)
tlen = cp - iname;
else
tlen = strlen(iname);
for (; dep < depe; dep++)
if (strncmp(iname, dep->de_name, tlen) == 0 &&
dep->de_name[tlen] == '\0')
break;
else if (dep->de_name[0] == '*' && dep->de_name[1] == '\0')
wdep = dep;
if (dep >= depe)
dep = wdep;
if (dep == NULL || dep->de_services == NULL || dep->de_nservices <= 0)
return (0);
nsvcs = 0;
sepp = dep->de_services;
tagp = (const uint8_t *)(poep + 1);
while (poe_tagcheck(poep, plen, tagp)) {
ttyp = POET_GET_TYPE(tagp);
if (ttyp == POETT_END)
break;
tlen = POET_GET_LENG(tagp);
switch (ttyp) {
case POETT_SERVICE:
if (sepp != dep->de_services) {
if (nsvcs != -1)
(void) poe_add_str(opoe, POETT_NAMERR,
"Too many Service-Name tags");
nsvcs = -1;
break;
}
seppe = sepp + dep->de_nservices;
if (tlen == 0) {
if (!ispadi && (glob_svc.se_flags & SEF_NOWILD))
sepp = seppe;
while (sepp < seppe) {
sep = *sepp++;
if (sep->se_name[0] == '\0' ||
(sep->se_flags & SEF_NOWILD) ||
!allow_service(sep, pap))
continue;
*srvp = (void *)sep;
if (ispadi && nsvcs == 0 &&
!(glob_svc.se_flags & SEF_NOWILD))
(void) poe_tag_copy(opoe, tagp);
nsvcs++;
(void) poe_add_str(opoe, POETT_SERVICE,
sep->se_name);
if (!ispadi)
break;
}
if (nsvcs == 0)
(void) poe_tag_copy(opoe, tagp);
} else {
(void) poe_tag_copy(opoe, tagp);
cp = (char *)POET_DATA(tagp);
while (sepp < seppe) {
sep = *sepp++;
if (strlen(sep->se_name) == tlen &&
strncasecmp(sep->se_name, cp,
tlen) == 0) {
if (allow_service(sep, pap)) {
nsvcs++;
*srvp = (void *)sep;
}
break;
}
}
}
if (*srvp != NULL) {
sep = (const struct service_entry *)*srvp;
log_for_service(sep->se_log, sep->se_debug);
str = "Solaris PPPoE";
if (sep->se_server != NULL)
str = sep->se_server;
(void) poe_add_str(opoe, POETT_ACCESS, str);
}
break;
case POETT_ACCESS:
case POETT_COOKIE:
case POETT_NAMERR:
case POETT_SYSERR:
case POETT_GENERR:
case POETT_HURL:
case POETT_MOTM:
case POETT_RTEADD:
case POETT_VENDOR:
case POETT_MULTI:
default:
break;
case POETT_UNIQ:
case POETT_RELAY:
(void) poe_tag_copy(opoe, tagp);
break;
}
tagp = POET_NEXT(tagp);
}
return (nsvcs);
}
static int
sgetc(void *arg)
{
char **cpp = (char **)arg;
if (**cpp == '\0')
return (EOF);
return (*(*cpp)++);
}
int
launch_service(int tunfd, poep_t *poep, void *srvp, struct ppptun_control *ptc)
{
const struct service_entry *sep = (const struct service_entry *)srvp;
const char *path;
const char *extra;
const char *pppd;
const char *cp;
pid_t pidv;
int newtun;
struct ppptun_peer ptp;
union ppptun_name ptn;
const char *args[MAXARGS];
struct strbuf ctrl;
struct strbuf data;
const char **cpp;
char *sptr;
char *spv;
int slen;
int retv;
char keybuf[MAX_KEYWORD];
assert(sep != NULL);
newtun = open(tunnam, O_RDWR);
if (newtun == -1)
goto syserr;
(void) memset(&ptp, '\0', sizeof (ptp));
ptp.ptp_style = PTS_PPPOE;
ptp.ptp_address = ptc->ptc_address;
if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
0)
goto syserr;
ptp.ptp_rsessid = ptp.ptp_lsessid;
if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
0)
goto syserr;
cp = strchr(ptc->ptc_name, ':');
if (cp == NULL)
cp = ptc->ptc_name + strlen(ptc->ptc_name);
(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
cp-ptc->ptc_name, ptc->ptc_name);
if (strioctl(newtun, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0)
goto syserr;
(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoed",
cp-ptc->ptc_name, ptc->ptc_name);
if (strioctl(newtun, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0)
goto syserr;
pidv = fork();
if (pidv == (pid_t)-1)
goto syserr;
if (pidv == (pid_t)0) {
close_log_files();
if ((path = sep->se_path) == NULL)
path = "/usr/bin/pppd";
if ((extra = sep->se_extra) == NULL)
extra = "plugin pppoe.so directtty";
if ((pppd = sep->se_pppd) == NULL)
pppd = "";
slen = strlen(path) + strlen(extra) + strlen(pppd) + 3;
if ((sptr = (char *)malloc(slen)) == NULL)
goto bail_out;
(void) strcpy(sptr, path);
(void) strcat(sptr, " ");
(void) strcat(sptr, extra);
(void) strcat(sptr, " ");
(void) strcat(sptr, pppd);
cpp = args;
spv = sptr;
while (cpp < args + MAXARGS - 1) {
retv = getkeyword(NULL, keybuf, sizeof (keybuf), sgetc,
(void *)&spv, 1);
if (retv != 1)
*cpp++ = strsave(keybuf);
if (retv != 0)
break;
}
*cpp = NULL;
if (cpp == args)
goto bail_out;
if (newtun != 0 && dup2(newtun, 0) < 0)
goto bail_out;
if (newtun != 1 && dup2(newtun, 1) < 0)
goto bail_out;
if (newtun > 1)
(void) close(newtun);
if (tunfd > 1)
(void) close(tunfd);
(void) close(2);
(void) open("/etc/ppp/pppoe-errors", O_WRONLY | O_APPEND |
O_CREAT, 0600);
if ((sep->se_flags & SEF_GIDSET) &&
setgid(sep->se_gid) == -1) {
cp = mystrerror(errno);
reopen_log();
logerr("setgid(%d): %s", sep->se_gid, cp);
goto logged;
}
if ((sep->se_flags & SEF_UIDSET) &&
setuid(sep->se_uid) == -1) {
cp = mystrerror(errno);
reopen_log();
logerr("setuid(%d): %s", sep->se_uid, cp);
goto logged;
}
path = args[0];
cp = strrchr(args[0], '/');
if (cp != NULL && cp[1] != '\0')
args[0] = cp+1;
errno = 0;
(void) execv(path, (char * const *)args);
newtun = 0;
bail_out:
cp = mystrerror(errno);
reopen_log();
logerr("\"%s\": %s", (sptr == NULL ? path : sptr), cp);
logged:
poep = poe_mkheader(pkt_output, POECODE_PADT, ptp.ptp_lsessid);
poep->poep_session_id = htons(ptp.ptp_lsessid);
(void) poe_add_str(poep, POETT_SYSERR, cp);
(void) sleep(1);
ctrl.len = sizeof (*ptc);
ctrl.buf = (caddr_t)ptc;
data.len = poe_length(poep) + sizeof (*poep);
data.buf = (caddr_t)poep;
if (putmsg(newtun, &ctrl, &data, 0) < 0) {
logerr("putmsg %s: %s", ptc->ptc_name,
mystrerror(errno));
}
exit(1);
}
(void) close(newtun);
poep->poep_session_id = htons(ptp.ptp_lsessid);
return (1);
syserr:
retv = errno;
if (newtun >= 0)
(void) close(newtun);
(void) poe_add_str(poep, POETT_SYSERR, mystrerror(retv));
return (0);
}
static void
print_filter_list(FILE *fp, struct filter_entry *fep)
{
if (fep->fe_prev != NULL)
print_filter_list(fp, fep->fe_prev);
(void) fprintf(fp, "\t\t MAC %s", ehost2(&fep->fe_mac));
(void) fprintf(fp, ", mask %s%s\n", ehost2(&fep->fe_mask),
(fep->fe_isexcept ? ", except" : ""));
}
void
dump_configuration(FILE *fp)
{
const struct device_entry *dep;
const struct service_entry *sep, **sepp;
struct per_file *pfp;
int i, j;
(void) fprintf(fp, "Will%s respond to wildcard queries.\n",
(glob_svc.se_flags & SEF_NOWILD) ? " not" : "");
(void) fprintf(fp,
"Global debug level %d, log to %s; current level %d\n",
glob_svc.se_debug,
((glob_svc.se_log == NULL || *glob_svc.se_log == '\0') ?
"syslog" : glob_svc.se_log),
log_level);
if (cur_options == NULL) {
(void) fprintf(fp, "No current configuration.\n");
return;
}
(void) fprintf(fp, "Current configuration:\n");
(void) fprintf(fp, " %d device(s):\n", cur_options->os_ndevices);
dep = cur_options->os_devices;
for (i = 0; i < cur_options->os_ndevices; i++, dep++) {
(void) fprintf(fp, "\t%s: %d service(s):\n",
dep->de_name, dep->de_nservices);
sepp = dep->de_services;
for (j = 0; j < dep->de_nservices; j++, sepp++) {
sep = *sepp;
(void) fprintf(fp, "\t %s: debug level %d",
sep->se_name, sep->se_debug);
if (sep->se_flags & SEF_UIDSET)
(void) fprintf(fp, ", UID %u", sep->se_uid);
if (sep->se_flags & SEF_GIDSET)
(void) fprintf(fp, ", GID %u", sep->se_gid);
if (sep->se_flags & SEF_WILD)
(void) fprintf(fp, ", wildcard");
else if (sep->se_flags & SEF_NOWILD)
(void) fprintf(fp, ", nowildcard");
else
(void) fprintf(fp, ", wildcard (default)");
(void) putc('\n', fp);
if (sep->se_server != NULL)
(void) fprintf(fp, "\t\tserver \"%s\"\n",
sep->se_server);
if (sep->se_pppd != NULL)
(void) fprintf(fp, "\t\tpppd \"%s\"\n",
sep->se_pppd);
if (sep->se_path != NULL)
(void) fprintf(fp, "\t\tpath \"%s\"\n",
sep->se_path);
if (sep->se_extra != NULL)
(void) fprintf(fp, "\t\textra \"%s\"\n",
sep->se_extra);
if (sep->se_log != NULL)
(void) fprintf(fp, "\t\tlog \"%s\"\n",
sep->se_log);
if (sep->se_flist != NULL) {
(void) fprintf(fp, "\t\tfilter list:\n");
print_filter_list(fp, sep->se_flist);
}
}
}
(void) fprintf(fp, "\nConfiguration read from:\n");
for (pfp = cur_options->os_pfjunk; pfp != NULL; pfp = pfp->pf_prev) {
(void) fprintf(fp, " %s: %d service(s)\n", pfp->pf_name,
pfp->pf_nsvc);
}
}