/* This file is part of GNU Pies.
Copyright (C) 2008-2011, 2013-2016 Sergey Poznyakoff
GNU Pies is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Pies is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Pies. If not, see . */
#include "pies.h"
#include
#include
#include "meta1lex.h"
#include "identity.h"
#include "grecsasrt.h"
int preprocess_only; /* Preprocess config, do nothing more */
int lint_mode; /* Test configuration syntax and exit */
int log_to_stderr_only; /* Use only stderr for logging */
int log_facility = LOG_USER;
char *log_tag;
struct pies_privs pies_privs;
int foreground;
int init_process;
enum pies_command
{
COM_START,
COM_RESTART,
COM_RELOAD,
COM_STATUS,
COM_STOP,
COM_DUMP_PREREQ,
COM_DUMP_DEPMAP
};
enum pies_command command;
char *statedir = DEFAULT_STATE_DIR;
char *instance;
char *pidfile;
char *qotdfile;
int inetd_mode;
mode_t pies_umask = 0;
unsigned long shutdown_timeout = 5;
size_t default_max_rate;
pies_acl_t pies_acl;
limits_record_t pies_limits;
int force_option;
char *mailer_program = "/usr/sbin/sendmail";
char *mailer_command_line = "/usr/sbin/sendmail -oi -t";
int mailer_argc;
char **mailer_argv;
struct config_file
{
enum config_syntax syntax;
char *name;
};
struct grecs_list *config_list;
struct config_syntax_descr
{
const char *name;
enum config_syntax type;
};
static struct config_syntax_descr config_syntax_tab[] = {
{ "pies" , CONF_PIES },
{ "meta1", CONF_META1 },
{ "inetd", CONF_INETD },
{ "inittab", CONF_INITTAB },
{ NULL }
};
int
str_to_config_syntax (const char *str, enum config_syntax *psynt)
{
struct config_syntax_descr *p;
for (p = config_syntax_tab; p->name; p++)
if (strcmp (p->name, str) == 0)
{
*psynt = p->type;
return 0;
}
return 1;
}
void
add_config (enum config_syntax syntax, const char *name)
{
struct config_file *file = xmalloc (sizeof (file[0]));
file->syntax = syntax;
file->name = xstrdup (name);
if (!config_list)
config_list = grecs_list_create ();
grecs_list_append (config_list, file);
}
/* Logging */
static int
stderr_closed_p ()
{
int fd = dup (0);
if (fd < 0)
return 1;
close (fd);
return fd <= 2;
}
static int
_cb_action (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
enum return_action *pact = varptr;
static struct tokendef actab[] = {
{"disable", action_disable},
{"restart", action_restart},
{NULL}
};
int res;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (strtotok (actab, value->v.string, &res))
{
grecs_error (locus, 0, _("unknown action code: %s"), value->v.string);
return 1;
}
*pact = res;
return 0;
}
struct grecs_keyword return_code_keywords[] = {
{"action",
/* TRANSLATORS: disable and restart are keywords, do not translate them. */
N_("arg: {disable | restart}"),
N_("Specifies action to take when a component finishes with this "
"return code."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, act_temp.act),
_cb_action,
},
{"notify",
N_("arg: emails"),
N_("Notify this address when a component terminates."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, act_temp.addr)
},
{"message",
NULL,
N_("Notification message text (with headers)."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, act_temp.message),
NULL},
{"exec",
NULL,
N_("Execute this command."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, act_temp.command),
NULL,
},
{NULL}
};
#define S(s) { #s, s }
static struct tokendef ex_tokendef[] = {
S (EX_OK),
S (EX_USAGE),
S (EX_DATAERR),
S (EX_NOINPUT),
S (EX_NOUSER),
S (EX_NOHOST),
S (EX_UNAVAILABLE),
S (EX_SOFTWARE),
S (EX_OSERR),
S (EX_OSFILE),
S (EX_CANTCREAT),
S (EX_IOERR),
S (EX_TEMPFAIL),
S (EX_PROTOCOL),
S (EX_NOPERM),
S (EX_CONFIG),
{NULL}
};
static struct tokendef sig_tokendef[] = {
S (SIGHUP),
S (SIGINT),
S (SIGQUIT),
S (SIGILL),
S (SIGTRAP),
S (SIGABRT),
S (SIGIOT),
S (SIGBUS),
S (SIGFPE),
S (SIGKILL),
S (SIGUSR1),
S (SIGSEGV),
S (SIGUSR2),
S (SIGPIPE),
S (SIGALRM),
S (SIGTERM),
#ifdef SIGSTKFLT
S (SIGSTKFLT),
#endif
S (SIGCHLD),
S (SIGCONT),
S (SIGSTOP),
S (SIGTSTP),
S (SIGTTIN),
S (SIGTTOU),
#ifdef SIGURG
S (SIGURG),
#endif
#ifdef SIGXCPU
S (SIGXCPU),
#endif
#ifdef SIGXFSZ
S (SIGXFSZ),
#endif
#ifdef SIGVTALRM
S (SIGVTALRM),
#endif
#ifdef SIGPROF
S (SIGPROF),
#endif
#ifdef SIGWINCH
S (SIGWINCH),
#endif
#ifdef SIGPOLL
S (SIGPOLL),
#endif
#ifdef SIGIO
S (SIGIO),
#endif
#ifdef SIGPWR
S (SIGPWR),
#endif
#ifdef SIGSYS
S (SIGSYS),
#endif
{NULL}
};
#undef S
void
free_action (struct action *act)
{
if (!act)
return;
if (act->nstat > 0)
free (act->status);
free (act->addr);
free (act->message);
free (act->command);
free (act);
}
static struct action *
create_action (struct component *comp,
grecs_locus_t *locus,
grecs_value_t *val, int argc,
const char *(*getarg) (grecs_value_t *, int, grecs_locus_t *))
{
int i;
unsigned *retv;
int retc = 0;
int allflag = 0;
struct action *act;
retv = xcalloc (argc, sizeof *retv);
if (argc == 0 || (argc == 1 && strcmp (getarg (val, 0, locus), "*") == 0))
allflag = 1;
else
{
for (i = 0; i < argc; i++)
{
unsigned n;
const char *arg = getarg (val, i, locus);
size_t len = strlen (arg);
if (isdigit (arg[0]))
{
char *p;
n = strtoul (arg, &p, 0);
if (*p)
{
grecs_error (locus, 0, _("%s: not a number"), p);
continue;
}
}
else if (len > 3 && memcmp (arg, "SIG", 3) == 0)
{
if (arg[4] == '+')
{
char *p;
n = strtoul (arg + 4, &p, 0);
if (*p)
{
grecs_error (locus, 0, _("%s: not a number"), p);
continue;
}
}
else if (strtotok_ci (sig_tokendef, arg, (int*) &n))
{
grecs_error (locus, 0, _("%s: not a signal code"), arg);
continue;
}
n |= STATUS_SIG_BIT;
}
else if (strtotok_ci (ex_tokendef, arg, (int *) &n))
{
grecs_error (locus, 0, _("%s: not a return code"), arg);
continue;
}
/* Alles in ordnung */
retv[retc++] = n;
}
}
if (retc == 0 && !allflag)
{
free (retv);
return NULL;
}
act = xzalloc (sizeof *act);
if (!allflag)
{
act->nstat = retc;
act->status = retv;
}
if (comp->act_tail)
comp->act_tail->next = act;
else
comp->act_head = act;
comp->act_tail = act;
return act;
}
const char *
_get_string_arg (grecs_value_t *val, int num, grecs_locus_t *locus)
{
if (num != 0)
return NULL;
return val->v.string;
}
const char *
_get_array_arg (grecs_value_t *val, int num, grecs_locus_t *locus)
{
if (num < val->v.arg.c)
{
if (assert_grecs_value_type (locus, val->v.arg.v[num],
GRECS_TYPE_STRING) == 0)
return val->v.arg.v[num]->v.string;
}
return NULL;
}
const char *
_get_list_arg (grecs_value_t *val, int num, grecs_locus_t *locus)
{
grecs_value_t *elt = (grecs_value_t *) grecs_list_index (val->v.list, num);
if (!elt)
{
grecs_error (locus, 0, _("cannot get list item"));
}
else if (assert_grecs_value_type (locus, elt, GRECS_TYPE_STRING) == 0)
return elt->v.string;
return NULL;
}
static int
return_code_section_parser (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
size_t count;
struct action *act;
switch (cmd)
{
case grecs_callback_section_begin:
if (GRECS_VALUE_EMPTY_P (value))
{
grecs_error (locus, 0, _("missing tag"));
return 1;
}
switch (value->type)
{
case GRECS_TYPE_STRING:
act = create_action (comp, locus, value, 1, _get_string_arg);
break;
case GRECS_TYPE_ARRAY:
act = create_action (comp, locus, value,
value->v.arg.c, _get_array_arg);
break;
case GRECS_TYPE_LIST:
count = grecs_list_size (value->v.list);
act = create_action (comp, locus, value, count, _get_list_arg);
}
*(struct component **) cb_data = comp;
if (!act)
return 1;
memset (&comp->act_temp, 0, sizeof (comp->act_temp));
break;
case grecs_callback_section_end:
act = comp->act_tail;
act->act = comp->act_temp.act;
act->addr = comp->act_temp.addr;
act->message = comp->act_temp.message;
act->command = comp->act_temp.command;
break;
case grecs_callback_set_value:
grecs_error (locus, 0, _("invalid use of block statement"));
}
return 0;
}
static char **
config_array_to_argv (grecs_value_t *val, grecs_locus_t *locus, size_t *pargc)
{
int i, j;
int argc;
char **argv;
argc = val->v.arg.c;
argv = xcalloc (argc + 1, sizeof (argv[0]));
for (i = j = 0; i < argc; i++)
{
if (assert_grecs_value_type (locus, val->v.arg.v[i], GRECS_TYPE_STRING)
== 0)
argv[j++] = xstrdup (val->v.arg.v[i]->v.string);
}
argv[j] = NULL;
if (pargc)
*pargc = argc;
return argv;
}
static int
_cb_command (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
struct wordsplit ws;
switch (value->type)
{
case GRECS_TYPE_STRING:
if (wordsplit (value->v.string, &ws, WRDSF_DEFFLAGS))
{
grecs_error (locus, 0, "wordsplit: %s", strerror (errno));
return 1;
}
comp->argc = ws.ws_wordc;
comp->argv = ws.ws_wordv;
break;
case GRECS_TYPE_ARRAY:
comp->argv = config_array_to_argv (value, locus, &comp->argc);
break;
case GRECS_TYPE_LIST:
grecs_error (locus, 0, _("unexpected list"));
return 1;
}
return 0;
}
static int
_cb_umask (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
mode_t *pmode = varptr;
char *p;
unsigned long n;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
n = strtoul (value->v.string, &p, 8);
if (*p)
{
grecs_error (locus, 0, _("invalid octal number"));
return 1;
}
*pmode = n;
return 0;
}
static int
_cb_env (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
char **argv;
char ***penv = varptr;
struct wordsplit ws;
switch (value->type)
{
case GRECS_TYPE_STRING:
if (wordsplit (value->v.string, &ws, WRDSF_DEFFLAGS))
{
grecs_error (locus, 0, "wordsplit: %s", strerror (errno));
return 1;
}
argv = ws.ws_wordv;
ws.ws_wordc = 0;
ws.ws_wordv = NULL;
break;
case GRECS_TYPE_ARRAY:
argv = config_array_to_argv (value, locus, NULL);
break;
case GRECS_TYPE_LIST:
grecs_error (locus, 0, _("unexpected list"));
return 1;
}
*penv = argv;
return 0;
}
int
string_to_syslog_priority (const char *key, int *pres)
{
static struct tokendef tokdef_prio[] = {
{"EMERG", LOG_EMERG},
{"ALERT", LOG_ALERT},
{"CRIT", LOG_CRIT},
{"ERR", LOG_ERR},
{"WARNING", LOG_WARNING},
{"NOTICE", LOG_NOTICE},
{"INFO", LOG_INFO},
{"DEBUG", LOG_DEBUG},
{NULL}
};
return strtotok_ci (tokdef_prio, key, pres);
}
int
string_to_syslog_facility (const char *key, int *pres)
{
static struct tokendef tokdef_fac[] = {
{"auth", LOG_AUTH},
{"authpriv", LOG_AUTHPRIV},
{"cron", LOG_CRON},
{"daemon", LOG_DAEMON},
{"ftp", LOG_FTP},
{"kern", LOG_KERN},
{"lpr", LOG_LPR},
{"mail", LOG_MAIL},
{"news", LOG_NEWS},
{"syslog", LOG_SYSLOG},
{"user", LOG_USER},
{"uucp", LOG_UUCP},
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
{"local3", LOG_LOCAL3},
{"local4", LOG_LOCAL4},
{"local5", LOG_LOCAL5},
{"local6", LOG_LOCAL6},
{"local7", LOG_LOCAL7},
{NULL}
};
return strtotok_ci (tokdef_fac, key, pres);
}
static int
cb_syslog_facility (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
const char *str;
if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
str = value->v.string;
if (c_isdigit (str[0]))
{
char *p;
int n = strtoul (str, &p, 10);
if (*p)
grecs_error (locus, 0,
_("expected facility number or symbolic name"));
else
*(int *) varptr = n;
}
else if (string_to_syslog_facility (str, varptr))
grecs_error (locus, 0, _("unknown syslog facility `%s'"), str);
return 0;
}
static int
_cb_redir (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct redirector *rp = varptr;
static struct tokendef redirtab[] = {
{"null", redir_null},
{"syslog", redir_syslog},
{"file", redir_file},
{NULL}
};
int res;
switch (value->type)
{
case GRECS_TYPE_STRING:
if (strcmp (value->v.string, "null") == 0)
{
rp->type = redir_null;
break;
}
rp->type = redir_syslog;
if (string_to_syslog_priority (value->v.string, &rp->v.prio))
{
grecs_error (locus, 0, _("unknown syslog priority `%s'"),
value->v.string);
return 0;
}
break;
case GRECS_TYPE_ARRAY:
if (assert_grecs_value_type (locus, value->v.arg.v[0],
GRECS_TYPE_STRING))
return 0;
if (strtotok (redirtab, value->v.arg.v[0]->v.string, &res))
grecs_error (locus, 0, _("%s: unrecognised redirector type"),
value->v.arg.v[0]->v.string);
else
{
if (res != redir_null)
{
if (value->v.arg.c != 2)
{
grecs_error (locus, 0, _("wrong number of arguments"));
return 0;
}
if (assert_grecs_value_type (locus, value->v.arg.v[1],
GRECS_TYPE_STRING))
return 0;
switch (res)
{
case redir_null:
break;
case redir_syslog:
if (string_to_syslog_priority (value->v.arg.v[1]->v.string,
&rp->v.prio))
{
grecs_error (locus, 0,
_("unknown syslog priority `%s'"),
value->v.arg.v[1]->v.string);
return 0;
}
break;
case redir_file:
rp->v.file = xstrdup (value->v.arg.v[1]->v.string);
break;
}
}
rp->type = res;
}
break;
default:
grecs_error (locus, 0, _("unexpected list"));
}
return 0;
}
static struct tokendef socktype_xtab[] = {
{ "stream", SOCK_STREAM },
{ "dgram", SOCK_DGRAM },
{ "seqpacket", SOCK_SEQPACKET },
{ "raw", SOCK_RAW },
{ "rdm", SOCK_RDM },
#ifdef SOCK_PACKET
{ "packet", SOCK_PACKET },
#endif
{ NULL }
};
int
str_to_socket_type (const char *str, int *pres)
{
return strtotok (socktype_xtab, str, pres);
}
int
socket_type_to_str (int socket_type, const char **pres)
{
return toktostr (socktype_xtab, socket_type, pres);
}
static int
_cb_socket_type (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (str_to_socket_type (value->v.string, varptr))
grecs_error (locus, 0, _("bad socket type"));
return 0;
}
static struct tokendef modetab[] = {
{"exec", pies_comp_exec},
{"respawn", pies_comp_exec},
{"wait", pies_comp_wait},
{"once", pies_comp_once},
{"accept", pies_comp_accept},
{"inetd", pies_comp_inetd},
{"nostartaccept", pies_comp_inetd},
{"pass-fd", pies_comp_pass_fd},
{"pass", pies_comp_pass_fd},
{"boot", pies_comp_boot},
{"bootwait", pies_comp_boot},
{"powerfail", pies_comp_powerfail},
{"powerwait", pies_comp_powerwait},
{"powerokwait", pies_comp_powerokwait},
{"ctrlaltdel", pies_comp_ctrlaltdel},
{"ondemand", pies_comp_ondemand},
{"sysinit", pies_comp_sysinit},
{"powerfailnow", pies_comp_powerfailnow},
{"kbrequest", pies_comp_kbrequest},
{NULL}
};
static int
_cb_mode (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
int res;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (strtotok (modetab, value->v.string, &res))
grecs_error (locus, 0, _("%s: unrecognised mode"), value->v.string);
else
*(enum pies_comp_mode *) varptr = res;
return 0;
}
static int
_cb_limits (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
limits_record_t *plrec = varptr;
char *p;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (parse_limits (plrec, (char *) value->v.string, &p))
grecs_error (locus, 0, _("invalid limit string (near %s)"), p);
return 0;
}
int
str_to_cf (const char *string, int *flags)
{
size_t len = strlen (string);
int neg = 0;
int mask;
static struct tokendef cf_tab[] = {
{ "disable", CF_DISABLED },
{ "precious", CF_PRECIOUS },
{ "wait", CF_WAIT },
{ "tcpmux", CF_TCPMUX },
{ "tcpmuxplus", CF_TCPMUXPLUS },
{ "internal", CF_INTERNAL },
{ "sockenv", CF_SOCKENV },
{ "resolve", CF_RESOLVE },
{ "siggroup", CF_SIGGROUP },
{ "nullinput", CF_NULLINPUT },
{ NULL }
};
if (len > 2 && memcmp (string, "no", 2) == 0)
{
neg++;
string += 2;
}
if (strtotok (cf_tab, string, &mask))
return 1;
if (neg)
*flags &= ~mask;
else
*flags |= mask;
return 0;
}
static int
_cb_flags (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
int *flags = varptr;
switch (value->type)
{
case GRECS_TYPE_STRING:
if (str_to_cf (value->v.string, flags))
{
grecs_error (locus, 0, _("%s: unrecognised flag"), value->v.string);
return 1;
}
break;
case GRECS_TYPE_LIST:
{
struct grecs_list_entry *ep;
for (ep = value->v.list->head; ep; ep = ep->next)
{
const grecs_value_t *vp = ep->data;
if (assert_grecs_value_type (locus, vp, GRECS_TYPE_STRING))
return 1;
if (str_to_cf (vp->v.string, flags))
{
grecs_error (locus, 0, _("%s: unrecognised flag"),
vp->v.string);
return 1;
}
}
}
break;
case GRECS_TYPE_ARRAY:
grecs_error (locus, 0, _("too many arguments"));
return 1;
}
return 0;
}
static int
_cb_initdefault (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
int *val = varptr;
if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (strlen (value->v.string) != 1)
{
grecs_error (locus, 0, _("argument must be a single character"));
return 1;
}
if (!is_valid_runlevel (value->v.string[0]))
{
grecs_error (locus, 0, _("not a valid runlevel"));
return 1;
}
*val = toupper (value->v.string[0]);
return 0;
}
static int
_cb_runlevels (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
char **sptr = varptr, *p;
if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
for (p = value->v.string; *p; p++)
{
if (!is_valid_runlevel (*p))
{
grecs_error (locus, 0, _("not a valid runlevel: %c"), *p);
return 1;
}
}
*sptr = grecs_strdup(value->v.string);
for (p = *sptr; *p; p++)
*p = toupper (*p);
return 0;
}
struct grecs_keyword component_keywords[] = {
{"mode",
/* TRANSLATORS: The words between '{' and '}' are keywords, do not
translate them. */
N_
("mode: {exec | wait | accept | inetd | nostartaccept | pass-fd | pass}"),
N_("Component execution mode."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, mode),
_cb_mode,
},
{"program",
NULL,
N_("Full name of the program."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, program),
NULL,
},
{"command",
NULL,
N_("Command line."),
grecs_type_string, GRECS_DFLT,
NULL, 0,
_cb_command,
},
{"prerequisites",
N_("list"),
N_("List of prerequisites."),
grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, prereq),
NULL,
},
{"dependents",
N_("list"),
N_("List of components for which this one is a prerequisite."),
grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, depend),
NULL,
},
{"flags",
N_("list"),
N_("List of flags."),
grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, flags),
_cb_flags },
{"runlevels",
N_("chars"),
N_("Runlevels to start that component in."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, runlevels),
_cb_runlevels },
{"pass-fd-timeout",
NULL,
N_("Time to wait for pass-fd socket to become available."),
grecs_type_uint, GRECS_DFLT,
NULL, offsetof (struct component, pass_fd_timeout),
NULL,
},
{"max-instances",
NULL,
N_("Maximum number of running instances."),
grecs_type_size, GRECS_DFLT,
NULL, offsetof (struct component, max_instances),
NULL },
{"max-instances-message",
NULL,
N_("Text to send back if max-instances is reached (inetd-components only)."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, max_instances_message),
NULL },
{"max-ip-connections",
NULL,
N_("Maximum number of simultaneous connections per IP address (inetd only)."),
grecs_type_size, GRECS_DFLT,
NULL, offsetof (struct component, max_ip_connections),
NULL },
{"max-ip-connections-message",
NULL,
N_("Text to send back if max-ip-connections-message is reached (inetd only)."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, max_ip_connections_message),
NULL },
{"max-rate",
NULL,
N_("Maximum number of times an inetd component can be invoked in one minute."),
grecs_type_size, GRECS_DFLT,
NULL, offsetof (struct component, max_rate),
NULL },
{"socket",
N_("url: string"),
N_("Listen on the given url."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, socket_url),
conf_callback_url,
},
{"socket-type",
/* TRANSLATORS: words after `type:' are keywords. */
N_("type: {stream | dgram | raw | rdm | seqpacket}"),
N_("Set socket type."),
grecs_type_int, GRECS_DFLT,
NULL, offsetof (struct component, socket_type),
_cb_socket_type },
{"pass-fd-socket",
N_("name"),
N_("Pass fd through this socket."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, pass_fd_socket),
NULL,
},
{"acl",
NULL,
N_("Define connection ACL."),
grecs_type_section, GRECS_DFLT,
NULL, offsetof (struct component, acl),
acl_section_parser, NULL, acl_keywords},
{"list-acl",
NULL,
N_("Define who can list this component."),
grecs_type_section, GRECS_DFLT,
NULL, offsetof (struct component, list_acl),
acl_section_parser, NULL, acl_keywords},
{"admin-acl",
NULL,
N_("Define who can change this component."),
grecs_type_section, GRECS_DFLT,
NULL, offsetof (struct component, adm_acl),
acl_section_parser, NULL, acl_keywords},
{"access-denied-message",
NULL,
N_("Text to send back if access is denied (inetd-components only)."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, access_denied_message),
NULL },
{"remove-file",
N_("file"),
N_("Remove file before starting the component."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, rmfile),
NULL,
},
{"facility",
N_("arg"),
N_("Override default syslog facility for this component."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct component, facility),
cb_syslog_facility,
},
{"stdout",
/* TRANSLATORS: file and syslog are keywords. Do not translate them. */
N_("type: {file | syslog}> ident; kwp++)
if (strcmp (kwp->ident, ident) == 0)
return kwp;
return NULL;
}
static char *
make_full_name (const char *dir, const char *file)
{
char *p;
size_t len = strlen (dir);
while (len > 0 && dir[len - 1] == '/')
len--;
p = xmalloc (len + 1 + strlen (file) + 1);
memcpy (p, dir, len);
p[len++] = '/';
strcpy (p + len, file);
return p;
}
static int
component_verify (struct component *comp, grecs_locus_t *locus)
{
int header = 0;
int i;
#define COMPERR(func, fmt, arg) \
do \
{ \
if (!header) \
{ \
grecs_warning (locus, 0, _("in component %s:"), comp->tag); \
header = 1; \
} \
func (locus, 0, fmt, arg); \
} \
while (0)
if (comp->flags & CF_INTERNAL)
{
comp->mode = pies_comp_inetd;
if (!comp->service)
/* TRANSLATORS: do not translate quoted words, they are keywords. */
COMPERR (grecs_error,
"%s", _("`internal' used without `service'"));
else
{
comp->builtin = inetd_builtin_lookup (comp->service,
comp->socket_type);
if (!comp->builtin)
COMPERR (grecs_error,
"%s", _("unknown internal service"));
if (comp->argv)
/* TRANSLATORS: do not translate quoted words, they are
keywords. */
COMPERR (grecs_error,
"%s", _("`internal' used with `command'"));
}
}
else if (!comp->argv)
COMPERR (grecs_error,
"%s", _("missing command line"));
if (ISCF_TCPMUX (comp->flags))
{
comp->mode = pies_comp_inetd;
if ((comp->flags & (CF_TCPMUX | CF_TCPMUXPLUS))
== (CF_TCPMUX | CF_TCPMUXPLUS))
COMPERR (grecs_error,
"%s", _("both `tcpmux' and `tcpmuxplus' used"));
else if (!comp->service)
/* TRANSLATORS: do not translate quoted words, they are keywords. */
COMPERR (grecs_error,
"%s", _("`internal' used without `service'"));
}
if (comp->pass_fd_socket && comp->mode != pies_comp_pass_fd)
COMPERR (grecs_error,
"%s", _("pass-fd-socket ignored: wrong mode"));
switch (comp->mode)
{
case pies_comp_exec:
if (comp->socket_url)
COMPERR (grecs_error,
"%s", _("socket ignored: wrong mode"));
break;
case pies_comp_pass_fd:
if (!comp->pass_fd_socket)
COMPERR (grecs_error,
"%s", _("must supply pass-fd-socket in this mode"));
else if (comp->pass_fd_socket[0] != '/')
{
if (comp->dir)
{
char *p = make_full_name (comp->dir, comp->pass_fd_socket);
/*free (comp->pass_fd_socket);*/
comp->pass_fd_socket = p;
}
else
COMPERR (grecs_error,
"%s", _("pass-fd-socket must be an absolute "
"file name or chdir must be specified"));
}
/* Fall through */
case pies_comp_accept:
if (!comp->socket_url)
{
COMPERR (grecs_error,
"%s", _("socket must be specified in this mode"));
return 1;
}
break;
case pies_comp_inetd:
if (ISCF_TCPMUX (comp->flags))
{
pies_url_destroy (&comp->socket_url);
if (!comp->tcpmux)
{
COMPERR (grecs_warning,
"%s",
_("TCPMUX master not specified, assuming \"tcpmux\""));
comp->tcpmux = xstrdup ("tcpmux");
}
}
else if (comp->tcpmux)
{
comp->flags |= CF_TCPMUX;
pies_url_destroy (&comp->socket_url);
}
else if (!comp->socket_url)
{
COMPERR (grecs_error,
"%s", _("socket must be specified in this mode"));
return 1;
}
default:
/* FIXME: more checks perhaps */
break;
}
if (comp->mode == pies_comp_inetd)
{
if ((comp->flags & CF_WAIT) && comp->socket_type == SOCK_STREAM)
{
if (comp->max_instances)
COMPERR (grecs_error, "%s", _("max-instances ignored"));
else
comp->max_instances = 1;
}
}
else if (comp->flags & CF_WAIT)
{
/* TRANSLATORS: `wait' is a keywords, do not translate. */
COMPERR (grecs_error, "%s", _("wait is useless in this mode"));
comp->flags &= ~CF_WAIT;
}
if (comp->mode != pies_comp_exec
&& comp->redir[RETR_OUT].type != redir_null)
{
COMPERR (grecs_error,
"%s", _("stdout translation invalid in this mode"));
comp->redir[RETR_OUT].type = redir_null;
}
for (i = RETR_OUT; i <= RETR_ERR; i++)
{
if (comp->redir[i].type == redir_file
&& comp->redir[i].v.file[0] != '/')
{
if (comp->dir)
{
char *p = make_full_name (comp->dir, comp->redir[i].v.file);
free (comp->redir[i].v.file);
comp->redir[i].v.file = p;
}
else
COMPERR (grecs_error,
_("%s: must be an absolute "
"file name or chdir must be specified"),
comp->redir[i].v.file);
}
}
return header;
#undef COMPERR
}
struct component *
component_create (const char *name)
{
struct component *comp = progman_lookup_component (name);
if (!comp)
{
comp = xzalloc (sizeof (*comp));
comp->facility = log_facility;
comp->redir[RETR_OUT].type = comp->redir[RETR_ERR].type = redir_null;
comp->tag = xstrdup (name);
comp->socket_type = SOCK_STREAM;
}
return comp;
}
void
component_free (struct component *comp)
{
size_t i;
free (comp->tag);
free (comp->program);
if (comp->argv)
{
for (i = 0; i < comp->argc; i++)
free (comp->argv[i]);
free (comp->argv);
}
if (comp->env)
{
for (i = 0; comp->env[i]; i++)
free (comp->env[i]);
free (comp->env);
}
free (comp->dir);
grecs_list_free (comp->prereq);
grecs_list_free (comp->depend);
free (comp->rmfile);
free_limits (comp->limits);
free (comp->runlevels);
free (comp->service);
pies_url_destroy (&comp->socket_url);
free (comp->pass_fd_socket);
free (comp->tcpmux);
free (comp->access_denied_message);
free (comp->max_instances_message);
free (comp->max_ip_connections_message);
free_redirector (&comp->redir[0]);
free_redirector (&comp->redir[1]);
if (comp->act_head)
{
struct action *act;
for (act = comp->act_head; act; act = act->next)
free_action (act);
}
free (comp);
}
void
component_finish (struct component *comp, grecs_locus_t *locus)
{
if (component_verify (comp, locus) == 0)
{
/* FIXME: The prog list is traversed twice for each component
statement, this is suboptimal. */
if (progman_lookup_component (comp->tag) == NULL)
register_prog (comp);
}
else
component_free (comp);
}
static int
component_section_parser (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct component *comp;
void **section_data = cb_data;
switch (cmd)
{
case grecs_callback_section_begin:
if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
comp = component_create (value->v.string);
*section_data = comp;
break;
case grecs_callback_section_end:
comp = *(struct component **) section_data;
component_finish (comp, locus);
break;
case grecs_callback_set_value:
grecs_error (locus, 0, _("expected block statement"));
}
return 0;
}
struct grecs_keyword control_keywords[] = {
{"socket",
N_("url: string"),
N_("Listen on the given url."),
grecs_type_string, GRECS_DFLT,
&control.url, 0, conf_callback_url},
{"acl",
NULL,
N_("Set connection ACL."),
grecs_type_section, GRECS_DFLT,
&control.conn_acl, 0,
acl_section_parser, NULL, acl_keywords},
{"admin-acl",
NULL,
N_("Administrative access"),
grecs_type_section, GRECS_DFLT,
&control.adm_acl, 0,
acl_section_parser, NULL, acl_keywords},
{"user-acl",
NULL,
N_("User access"),
grecs_type_section, GRECS_DFLT,
&control.usr_acl, 0,
acl_section_parser, NULL, acl_keywords},
{"idle-timeout",
"n",
N_("Disconnect after seconds of inaction (not implemented)."),
grecs_type_uint, GRECS_DFLT,
&control.idle_timeout, 0,
NULL,
},
{ NULL }
};
/* syslog */
static struct grecs_keyword syslog_kw[] = {
{"facility",
N_("name"),
N_("Set syslog facility. Arg is one of the following: user, daemon, "
"auth, authpriv, mail, cron, local0 through local7 (case-insensitive), "
"or a facility number."),
grecs_type_string, GRECS_DFLT,
&log_facility, 0, cb_syslog_facility},
{"tag", N_("string"), N_("Tag syslog messages with this string"),
grecs_type_string, GRECS_DFLT,
&log_tag},
#if 0
/* This is reserved for future use */
{
"print-priority", N_("arg"), N_("Prefix each message with its priority"),
grecs_type_bool, GRECS_DFLT,
&syslog_include_prio},
#endif
{NULL},
};
struct component default_component;
static int
_cb_include_meta1 (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
meta1_config_parse (value->v.string);
return 0;
}
static int
_cb_include_inetd (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
if (assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
return inetd_parse_conf (value->v.string);
}
struct grecs_keyword pies_keywords[] = {
{"component",
N_("tag: string"),
N_("Define a component"),
grecs_type_section, GRECS_DFLT,
NULL, 0,
component_section_parser, NULL, component_keywords},
{"control",
NULL,
N_("Define control socket"),
grecs_type_section, GRECS_DFLT,
NULL, 0,
NULL, NULL, control_keywords },
{"syslog",
NULL,
N_("Configure syslog logging"),
grecs_type_section, GRECS_DFLT,
NULL, 0, NULL, NULL, syslog_kw},
{"debug",
NULL,
N_("Set debug verbosity level."),
grecs_type_uint, GRECS_DFLT,
&debug_level, 0, NULL},
{"source-info",
NULL,
N_("Show source info with debugging messages."),
grecs_type_bool, GRECS_DFLT,
&source_info_option, 0, NULL},
{"state-directory",
NULL,
N_("Full file name of the program state directory."),
grecs_type_string, GRECS_DFLT,
&statedir, 0, NULL},
{"pidfile",
NULL,
N_("Write PID to this file."),
grecs_type_string, GRECS_DFLT,
&pidfile, 0,
NULL,
},
{"control-file",
NULL,
N_("Ignored for compatibility with version 1.2."),
grecs_type_string, GRECS_DFLT|GRECS_INAC,
NULL, 0,
NULL,
},
{"stat-file",
NULL,
N_("Ignored for compatibility with version 1.2."),
grecs_type_string, GRECS_DFLT|GRECS_INAC,
NULL, 0,
NULL,
},
{"qotd-file",
NULL,
N_("Set location of the QOTD file."),
grecs_type_string, GRECS_DFLT,
&qotdfile, 0,
NULL },
{"user",
NULL,
N_("Run with this user privileges."),
grecs_type_string, GRECS_DFLT,
&pies_privs.user, 0,
NULL,
},
{"group",
NULL,
N_("Retain supplementary group."),
grecs_type_string, GRECS_LIST,
&pies_privs.groups, 0,
NULL,
},
{"allgroups",
NULL,
N_("Retain all supplementary groups of which user is a member."),
grecs_type_bool, GRECS_DFLT,
&pies_privs.allgroups, 0,
NULL,
},
{"umask",
N_("arg: number"),
N_("Force this umask."),
grecs_type_string, GRECS_DFLT,
&pies_umask, 0,
_cb_umask,
},
{"limits",
NULL,
N_("Set global system limits."),
grecs_type_string, GRECS_DFLT,
&pies_limits, 0, _cb_limits,
},
{"initdefault",
N_("arg: char"),
N_("Default runlevel"),
grecs_type_string, GRECS_DFLT,
&initdefault, 0, _cb_initdefault,
},
{"shutdown-timeout",
"n",
N_("Wait seconds for all components to shut down."),
grecs_type_uint, GRECS_DFLT,
&shutdown_timeout, 0,
NULL,
},
{"return-code",
N_("tag: exit-code-list"),
N_("Define what to do when the component finishes."),
grecs_type_section, GRECS_DFLT,
&default_component, 0,
return_code_section_parser, NULL, return_code_keywords},
{"acl",
NULL,
N_("Set global ACL."),
grecs_type_section, GRECS_DFLT,
&pies_acl, 0,
acl_section_parser, NULL, acl_keywords},
{"defacl",
N_("name: string"),
N_("Define an ACL."),
grecs_type_section, GRECS_DFLT,
NULL, 0,
defacl_section_parser, NULL, acl_keywords},
{"include-inetd",
N_("file-or-dir: string"),
N_("Include inetd configuration file or directory"),
grecs_type_string, GRECS_DFLT,
NULL, 0,
_cb_include_inetd },
{"include-meta1",
N_("file: string"),
N_("Include components from the specified MeTA1 configuration file."),
grecs_type_string, GRECS_DFLT,
NULL, 0,
_cb_include_meta1,
},
{"meta1-queue-dir",
NULL,
N_("Set the name of MeTA1 queue directory (default /var/spool/meta1)."),
grecs_type_string, GRECS_DFLT,
&meta1_queue_dir, 0,
NULL,
},
{"mailer-program",
NULL,
N_("Full path to the mailer binary."),
grecs_type_string, GRECS_DFLT,
&mailer_program, 0,
NULL
},
{"mailer-command-line",
NULL,
N_("Mailer command line (without recipient addresses)."),
grecs_type_string, GRECS_DFLT,
&mailer_command_line, 0,
NULL
},
{ "identity-provider", "name: string", "Configure identity provider",
grecs_type_section, GRECS_INAC | GRECS_HIDDEN },
{NULL}
};
void
config_init ()
{
grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR,
DEFAULT_INCLUDE_DIR, NULL);
grecs_log_to_stderr = log_to_stderr_only;
pies_identity_mechanism_register (&system_identity_mechanism);
#ifdef WITH_PAM
pies_identity_mechanism_register (&pam_identity_mechanism);
#endif
}
static void
config_error ()
{
if (!init_process)
exit (EX_CONFIG);
}
void
config_parse (char const *name)
{
struct grecs_node *node;
struct grecs_node *tree = grecs_parse (name);
if (!tree)
{
config_error ();
return;
}
for (node = tree; node; node = node->next)
{
node = grecs_find_node (node, "identity-provider");
if (!node)
break;
pies_config_provider (node);
}
if (grecs_tree_process (tree, pies_keywords))
config_error ();
grecs_tree_free (tree);
}
void
config_help ()
{
static char docstring[] =
/* TRANSLATORS: do not translate words between ` and ' */
N_("Configuration file structure for pies.\n"
"For more information, use `info pies configuration'.");
grecs_print_docstring (docstring, 0, stdout);
grecs_print_statement_array (pies_keywords, 1, 0, stdout);
pies_config_identity_mechanisms_help ();
}
static enum config_syntax current_syntax = CONF_PIES;
#include "cmdline.h"
int action = ACTION_CONT;
int children_cleanup = 0;
int got_alarm = 0;
void
pies_schedule_action (int act)
{
action = act;
/*FIXME: got_alarm?*/
}
RETSIGTYPE
sig_handler (int sig)
{
if (init_process && sysvinit_sigtrans (sig, &action))
return;
switch (sig)
{
case SIGCHLD:
children_cleanup = 1;
break;
case SIGINT:
case SIGTERM:
case SIGQUIT:
action = ACTION_STOP;
logmsg (LOG_NOTICE, "received signal %d", sig);
break;
case SIGHUP:
logmsg (LOG_NOTICE, "received signal %d", sig);
action = ACTION_RESTART;
break;
case SIGALRM:
got_alarm = 1;
break;
}
}
void
setsigvhan (RETSIGTYPE (*handler) (int signo), int *sigv, int sigc)
{
int i;
struct sigaction act;
act.sa_flags = 0;
sigemptyset (&act.sa_mask);
for (i = 0; i < sigc; i++)
sigaddset (&act.sa_mask, i);
for (i = 0; i < sigc; i++)
{
act.sa_handler = handler;
sigaction (sigv[i], &act, NULL);
}
}
#define PIES_MAXSIG 16
static int default_sigv[] = {
SIGCHLD,
SIGTERM,
SIGQUIT,
SIGINT,
SIGHUP,
SIGALRM,
SIGPIPE
};
static int extra_sigv[PIES_MAXSIG];
static int extra_sigc;
void
add_extra_sigv (int *sigv, int sigc)
{
while (sigc--)
{
if (extra_sigc == ARRAY_SIZE(extra_sigv))
abort ();
extra_sigv[extra_sigc++] = *sigv++;
}
}
void
signal_setup (RETSIGTYPE (*sf) (int))
{
setsigvhan (sf, default_sigv, ARRAY_SIZE (default_sigv));
if (extra_sigc)
setsigvhan (sf, extra_sigv, extra_sigc);
}
pid_t
pidfile_read (int must_exist)
{
int c;
pid_t n = 0;
FILE *fp = fopen (pidfile, "r");
if (!fp)
{
if (must_exist && errno != ENOENT)
logmsg (LOG_ERR,
_("cannot open pid file `%s': %s"),
pidfile, strerror (errno));
return -1;
}
while ((c = fgetc (fp)) != EOF)
{
if (isdigit (c))
n = n * 10 + c - '0';
else if (c == '\n')
break;
else
{
logmsg (LOG_ERR,
_("unexpected character %#03o in pidfile `%s'"),
c, pidfile);
return -1;
}
}
fclose (fp);
if (n && kill (n, 0))
{
if (errno != ESRCH)
logmsg (LOG_ERR,
_("cannot signal master process %lu: %s"),
(unsigned long) n, strerror (errno));
if (errno == EPERM)
return n; /* be on the safe side */
return -1;
}
return n;
}
enum pies_status
{
pies_status_ctr, /* clear to run */
pies_status_stale,
pies_status_noresp,
pies_status_running
};
//FIXME: If telinit?
enum pies_status
pies_check_status (pid_t *ppid)
{
pid_t pid = pidfile_read (0);
if (pid <= 0)
return pies_status_ctr;
*ppid = pid;
if (kill (pid, 0))
return pies_status_stale;
return pies_status_running;
}
void
request_restart_components (size_t cc, char **cv)
{
char **argv;
size_t i;
argv = xcalloc (cc + 4, sizeof (*argv));
argv[0] = "piesctl";
argv[1] = "--url";
argv[2] = (char*) pies_control_url ();
for (i = 0; i < cc; i++)
argv[3 + i] = cv[i];
argv[3 + i] = NULL;
execvp (argv[0], argv);
logmsg (LOG_ERR, "can't run piesctl: %s", strerror (errno));
exit (EX_OSFILE);
}
void
list_components (void)
{
char *argv[5];
argv[0] = "piesctl";
argv[1] = "--url";
argv[2] = (char*) pies_control_url ();
argv[3] = "list";
argv[4] = NULL;
execvp (argv[0], argv);
logmsg (LOG_ERR, "can't run piesctl: %s", strerror (errno));
exit (EX_OSFILE);
}
int
pies_reload ()
{
pid_t pid = pidfile_read (1);
if (pid == -1)
{
logmsg (LOG_CRIT, _("pies is not running"));
return 1;
}
logmsg (LOG_INFO, _("reloading pies at PID %lu"), (unsigned long) pid);
return kill (pid, SIGHUP) ? EX_SOFTWARE : 0;
}
int
pies_status ()
{
pid_t pid;
switch (pies_check_status (&pid))
{
case pies_status_ctr:
logmsg (LOG_INFO, _("pies is not running"));
break;
case pies_status_stale:
logmsg (LOG_INFO,
_("pies is not running, but a pidfile "
"is found (pid %lu)"), (unsigned long) pid);
return 1;
case pies_status_noresp:
logmsg (LOG_INFO,
_("pies seems to run with pid %lu, but is not responding"),
(unsigned long) pid);
return 2;
case pies_status_running:
list_components ();
break;
}
return 0;
}
int
pies_stop ()
{
pid_t pid = pidfile_read (1);
if (pid == -1)
{
logmsg (LOG_CRIT, _("pies is not running"));
return EX_USAGE;
}
logmsg (LOG_INFO, _("stopping pies at PID %lu"), (unsigned long) pid);
return kill (pid, SIGTERM) ? EX_SOFTWARE : 0;
}
/* Pidfile */
/* Check whether pidfile NAME exists and if so, whether its PID is still
active. Exit if it is. */
void
check_pidfile (char *name)
{
unsigned long pid;
FILE *fp = fopen (name, "r");
if (!fp)
{
if (errno == ENOENT)
return;
logmsg (LOG_ERR, _("cannot open pidfile `%s': %s"),
name, strerror (errno));
exit (EX_TEMPFAIL);
}
if (fscanf (fp, "%lu", &pid) != 1)
{
logmsg (LOG_ERR, ("cannot get pid from pidfile `%s'"), name);
}
else
{
if (kill (pid, 0) == 0)
{
logmsg (LOG_ERR,
_
("%s appears to run with pid %lu. If it does not, remove `%s' and retry."),
program_name, pid, name);
exit (EX_USAGE);
}
}
fclose (fp);
if (unlink (pidfile))
{
logmsg (LOG_ERR, _("cannot unlink pidfile `%s': %s"),
name, strerror (errno));
exit (EX_USAGE);
}
}
void
create_pidfile (char *name)
{
FILE *fp = fopen (name, "w");
if (!fp)
{
logmsg (LOG_ERR, _("cannot create pidfile `%s': %s"),
name, strerror (errno));
exit (EX_TEMPFAIL);
}
fprintf (fp, "%lu", (unsigned long) getpid ());
fclose (fp);
}
void
remove_pidfile (char *name)
{
if (unlink (name))
logmsg (LOG_ERR, _("cannot unlink pidfile `%s': %s"),
name, strerror (errno));
}
static void
set_mailer_argcv ()
{
int i;
struct wordsplit ws;
if (wordsplit (mailer_command_line, &ws, WRDSF_DEFFLAGS))
{
logmsg (LOG_CRIT, _("cannot parse mailer command line: %s"),
strerror (errno));
exit (EX_CONFIG);
}
mailer_argc = ws.ws_wordc;
mailer_argv = xcalloc (mailer_argc + 1, sizeof (mailer_argv[0]));
for (i = 0; i < mailer_argc; i++)
mailer_argv[i] = xstrdup (ws.ws_wordv[i]);
mailer_argv[i] = NULL;
wordsplit_free (&ws);
}
static void
set_conf_file_names (const char *base)
{
if (!config_list)
{
char *name = mkfilename (SYSCONFDIR, base, ".conf");
add_config (current_syntax, name);
free (name);
}
}
static void
set_state_file_names (const char *base)
{
if (!pidfile)
pidfile = mkfilename (statedir, base, ".pid");
if (!qotdfile)
qotdfile = mkfilename (statedir, base, ".qotd");
}
int
main (int argc, char **argv)
{
int index;
pid_t pid;
extern char **environ;
struct grecs_list_entry *ep;
int diag_flags;
set_program_name (argv[0]);
#ifdef ENABLE_NLS
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
#endif
mf_proctitle_init (argc, argv, environ);
set_quoting_style (NULL, shell_quoting_style);
init_process = getpid () == 1;
#ifdef INIT_EMU
# warning "pies compiled with init emulation code"
if (!init_process)
{
init_process = getenv ("INIT_EMU") != NULL;
if (init_process)
fprintf (stderr, "%s: running in init emulation mode\n", program_name);
else
{
fprintf (stderr,
"%s: to enable init emulation code,\n", program_name);
fprintf (stderr,
"%s: define environment variable INIT_EMU=[:]\n", program_name);
fprintf (stderr,
"%s: define variable INIT_FIFO= to override the default FIFO name\n",
program_name);
}
}
#endif
/* Set default logging */
if (init_process)
diag_flags = DIAG_TO_STDERR;
else
diag_flags = DIAG_TO_SYSLOG | (stderr_closed_p () ? 0 : DIAG_TO_STDERR);
diag_setup (diag_flags);
config_init ();
if (init_process)
{
#ifdef INIT_EMU
char *emu = getenv ("INIT_EMU");
if (emu)
{
char *inittab = strtok (emu, ":");
char *piesinit = strtok (NULL, ":");
add_config (CONF_INITTAB, inittab);
add_config (CONF_PIES, piesinit ? piesinit : "/etc/pies.init");
init_fifo = getenv ("INIT_FIFO");
if (!init_fifo)
init_fifo = "/tmp/initctl";
}
else
{
add_config (CONF_INITTAB, "/etc/inittab");
add_config (CONF_PIES, "/etc/pies.init");
}
#else
add_config (CONF_INITTAB, "/etc/inittab");
add_config (CONF_PIES, "/etc/pies.init");
#endif
for (index = 1; index < argc; index++)
{
if (!strcmp (argv[index], "single") || !strcmp (argv[index], "-s"))
dfl_level = 'S';
else if (strchr("0123456789sS", argv[index][0]) && !argv[index][1])
{
dfl_level = toupper (argv[index][0]);
}
}
}
else
parse_options (argc, argv, &index);
if (!instance)
{
instance = strrchr (program_name, '/');
if (!instance)
instance = (char*) program_name;
else
instance++;
}
log_tag = instance;
if (!init_process)
set_conf_file_names (instance);
if (init_process || !DEFAULT_PREPROCESSOR)
grecs_preprocessor = NULL;
else
grecs_preprocessor = pp_command_line ();
if (preprocess_only)
{
for (ep = config_list->head; ep; ep = ep->next)
{
struct config_file *file = ep->data;
if (file->syntax == CONF_PIES
&& grecs_preproc_run (file->name, grecs_preprocessor))
exit (EX_CONFIG);
}
exit (0);
}
else
for (ep = config_list->head; ep; ep = ep->next)
{
struct config_file *file = ep->data;
switch (file->syntax)
{
case CONF_PIES:
config_parse (file->name);
break;
case CONF_INETD:
if (inetd_parse_conf (file->name))
config_error ();
break;
case CONF_META1:
if (meta1_config_parse (file->name))
config_error ();
break;
case CONF_INITTAB:
if (inittab_parse (file->name))
config_error ();
break;
}
}
set_state_file_names (instance);
set_mailer_argcv ();
if (lint_mode)
{
progman_build_depmap ();
exit (0);
}
/* Re-setup logging: it might have been reset in the config file */
diag_setup (log_to_stderr_only ? DIAG_TO_STDERR : 0);
if (argc != index && command != COM_RESTART)
{
logmsg (LOG_ERR, "extra command line arguments");
exit (EX_CONFIG);
}
progman_build_depmap ();
switch (command)
{
case COM_RESTART:
pies_priv_setup (&pies_privs);
if (pies_umask)
umask (pies_umask);
request_restart_components (argc - index, argv + index);
case COM_RELOAD:
exit (pies_reload ());
case COM_STATUS:
exit (pies_status ());
case COM_STOP:
exit (pies_stop ());
case COM_DUMP_PREREQ:
progman_dump_prereq ();
exit (0);
case COM_DUMP_DEPMAP:
progman_dump_depmap ();
exit (0);
default:
pies_priv_setup (&pies_privs);
if (pies_umask)
umask (pies_umask);
}
if (init_process)
{
foreground = 1;
sysvinit_begin ();
}
else
switch (pies_check_status (&pid))
{
case pies_status_ctr:
break;
case pies_status_stale:
case pies_status_noresp:
if (!force_option)
{
logmsg (LOG_ERR,
_("another pies instance may be running (pid %lu), "
"use --force to override"), (unsigned long) pid);
exit (EX_USAGE);
}
break;
case pies_status_running:
logmsg (LOG_ERR, _("another pies instance already running (pid %lu)"),
(unsigned long) pid);
exit (EX_USAGE);
}
logmsg (LOG_INFO, _("%s %s starting"), proginfo.package, proginfo.version);
if (!foreground)
{
check_pidfile (pidfile);
if (daemon (0, 0) == -1)
{
logmsg (LOG_ERR, _("cannot become a daemon: %s"), strerror (errno));
exit (EX_SOFTWARE);
}
diag_setup (DIAG_TO_SYSLOG);
}
if (!init_process)
{
ctl_open ();
create_pidfile (pidfile);
}
if (argv[0][0] != '/')
logmsg (LOG_NOTICE,
_("not started as an absolute pathname; "
"SIGHUP will not work"));
signal_setup (sig_handler);
progman_create_sockets ();
progman_start ();
do
{
if (init_process && inittrans ())
{
got_alarm = 1;
action = ACTION_CONT;
}
else if (!children_cleanup)
pies_pause ();
switch (action)
{
case ACTION_STOP:
case ACTION_RESTART:
if (init_process)
{
debug (1, ("ignoring stop/restart"));
action = ACTION_CONT;
}
break;
case ACTION_CTRLALTDEL:
debug (1, ("ctrl-alt-del"));
sysvinit_runlevel_setup (PIES_COMP_MASK (pies_comp_ctrlaltdel),
NULL);
got_alarm = 1;
action = ACTION_CONT;
break;
case ACTION_KBREQUEST:
debug (1, ("kbrequest"));
sysvinit_runlevel_setup (PIES_COMP_MASK (pies_comp_kbrequest),
NULL);
got_alarm = 1;
action = ACTION_CONT;
break;
}
if (action == ACTION_CONT)
{
if (children_cleanup)
{
children_cleanup = 0;
progman_cleanup (0);
}
if (got_alarm)
{
progman_wake_sleeping (1);
got_alarm = 0;
}
}
}
while (init_process || action == ACTION_CONT);
progman_stop ();
remove_pidfile (pidfile);
if (action == ACTION_RESTART && argv[0][0] == '/')
{
int minfd = DIAG_OUTPUT (DIAG_TO_STDERR) ? 2 : 0;
int i;
for (i = getmaxfd (); i > minfd; i--)
close (i);
signal_setup (SIG_DFL);
execv (argv[0], argv);
}
logmsg (LOG_INFO, _("%s %s terminated"), proginfo.package, proginfo.version);
exit (EX_OK);
}
void
xalloc_die ()
{
logmsg (LOG_CRIT, _("not enough memory"));
abort ();
}
/* EOF */