/* This file is part of GNU Pies.
Copyright (C) 2008-2019 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 "meta1parse.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_COMPONENT,
COM_RELOAD,
COM_STATUS,
COM_STOP,
COM_DUMP_DEPMAP,
COM_TRACE_PREREQ,
COM_TRACE_DEPEND
};
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;
char *default_control_url[2] = {
DEFAULT_PIES_CONTROL_URL,
DEFAULT_INIT_CONTROL_URL
};
struct config_syntax
{
const char *name;
int (*parser) (char const *);
};
static int pies_config_parse (char const *);
static struct config_syntax config_syntax_tab[] = {
[CONF_PIES] = { "pies" , pies_config_parse },
[CONF_META1] = { "meta1", meta1_config_parse },
[CONF_INETD] = { "inetd", inetd_config_parse },
[CONF_INITTAB] = { "inittab", inittab_parse },
};
struct config_file
{
struct config_syntax *syntax;
char *name;
};
struct grecs_list *config_list;
struct config_syntax *
str_to_config_syntax (const char *str)
{
int i;
for (i = 0; i < ARRAY_SIZE (config_syntax_tab); i++)
if (strcmp (config_syntax_tab[i].name, str) == 0)
return &config_syntax_tab[i];
return NULL;
}
static void
config_file_free (void *ptr)
{
if (ptr)
{
struct config_file *file = ptr;
grecs_free (file->name);
grecs_free (file);
}
}
void
config_file_add (struct config_syntax *syntax, const char *name)
{
struct config_file *file = grecs_malloc (sizeof (file[0]));
file->syntax = syntax;
file->name = grecs_strdup (name);
if (!config_list)
{
config_list = grecs_list_create ();
config_list->free_entry = config_file_free;
}
grecs_list_append (config_list, file);
}
void
config_file_add_type (enum config_syntax_type syntax, const char *name)
{
config_file_add (&config_syntax_tab[syntax], name);
}
int
config_file_remove (const char *name)
{
struct grecs_list_entry *ep;
for (ep = config_list->head; ep; ep = ep->next)
{
struct config_file *file = ep->data;
if (strcmp (file->name, name) == 0)
{
grecs_list_remove_entry (config_list, ep);
config_file_free (file);
return 0;
}
}
return 1;
}
void
config_file_remove_all (void)
{
grecs_list_clear (config_list);
}
void
config_file_list_serialize (struct json_value *ar)
{
struct grecs_list_entry *ep;
for (ep = config_list->head; ep; ep = ep->next)
{
struct config_file *file = ep->data;
struct json_value *obj = json_new_object ();
json_object_set (obj, "syntax", json_new_string (file->syntax->name));
json_object_set (obj, "file", json_new_string (file->name));
json_array_append (ar, obj);
}
}
/* Logging */
static int
stderr_closed_p (void)
{
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 action, act),
_cb_action,
},
{"notify",
N_("arg: emails"),
N_("Notify this address when a component terminates."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct action, addr)
},
{"message",
NULL,
N_("Notification message text (with headers)."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct action, message),
NULL},
{"exec",
NULL,
N_("Execute this command."),
grecs_type_string, GRECS_DFLT,
NULL, offsetof (struct action, 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
action_free (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 void
free_entry_action (void *act)
{
action_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 = grecs_calloc (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 = grecs_zalloc (sizeof *act);
if (!allflag)
{
act->nstat = retc;
act->status = retv;
}
if (!comp->act_list)
{
comp->act_list = grecs_list_create ();
comp->act_list->free_entry = free_entry_action;
}
grecs_list_append (comp->act_list, 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);
}
if (!act)
return 1;
*(struct action **) cb_data = act;
break;
case grecs_callback_section_end:
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 = grecs_calloc (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++] = grecs_strdup (val->v.arg.v[i]->v.string);
}
argv[j] = NULL;
if (pargc)
*pargc = argc;
return argv;
}
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;
}
void
argv_free (char **argv)
{
if (argv)
{
size_t i;
for (i = 0; argv[i]; i++)
free (argv[i]);
free (argv);
}
}
static int
parse_legacy_env (char **argv, envop_t **envop)
{
size_t i = 0;
int rc;
char *name;
if (strcmp (argv[0], "-") == 0)
{
rc = envop_entry_add (envop, envop_clear, NULL, NULL);
if (rc)
return rc;
i++;
}
for (; (name = argv[i]) != NULL; i++)
{
char *name = argv[i];
size_t len = strcspn (name, "=");
char *value;
char *mem = NULL;
size_t msize = 0;
enum envop_code code;
if (name[0] == '-')
{
/* Unset directive */
name++;
len--;
if (name[len])
{
name[len] = 0;
value = name + len + 1;
}
else
value = NULL;
code = envop_unset;
}
else if (name[len])
{
size_t vlen;
if (len == 0)
/* Skip erroneous entry */
continue;
value = name + len + 1;
vlen = strlen (value);
name[len] = 0;
if (name[len-1] == '+')
{
name[--len] = 0;
if (c_ispunct (value[0]))
{
msize = 2*len + 9 + vlen + 1;
mem = grecs_malloc (msize);
snprintf (mem, msize, "${%s:-}${%s+%c}%s",
name, name, value[0], value + 1);
}
else
{
msize = len + vlen + 6;
snprintf (mem, msize, "${%s:-}%s", name, value);
}
value = mem;
}
else if (value[0] == '+')
{
value++;
vlen--;
if (vlen > 0 && c_ispunct (value[vlen-1]))
{
int c = value[vlen-1];
value[--vlen] = 0;
msize = 2*len + 10 + vlen + 1;
mem = grecs_malloc (msize);
snprintf (mem, msize, "%s${%s+%c}${%s:-}",
value, name, c, name);
}
else
{
msize = len + vlen + 6;
snprintf (mem, msize, "%s${%s:-}", value, name);
}
value = mem;
}
code = envop_set;
}
else
{
value = NULL;
code = envop_keep;
}
rc = envop_entry_add (envop, code, name, value);
free (mem);
if (rc)
return rc;
}
return 0;
}
static int
_cb_env (envop_t **envop, grecs_value_t *value, grecs_locus_t *locus)
{
char **argv;
int rc;
switch (value->type)
{
case GRECS_TYPE_STRING:
argv = grecs_calloc (2, sizeof (argv[0]));
argv[0] = grecs_strdup (value->v.string);
argv[1] = 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;
}
rc = parse_legacy_env (argv, envop);
argv_free (argv);
if (rc)
{
grecs_error (locus, errno, _("can't parse legacy env statement"));
return 1;
}
return 0;
}
static int
cb_env_section_parser (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
switch (cmd)
{
case grecs_callback_section_begin:
//FIXME
*(struct component **) cb_data = comp;
break;
case grecs_callback_section_end:
//FIXME
break;
case grecs_callback_set_value:
return _cb_env (&comp->envop, value, locus);
}
return 0;
}
static int
_cb_env_clear (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
int clear;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (grecs_string_convert(&clear, grecs_type_bool, value->v.string, locus))
return 1;
if (clear)
{
if (envop_entry_add (&comp->envop, envop_clear, NULL, NULL))
grecs_error (locus, errno, "envop_entry_add");
}
return 0;
}
static int
_cb_env_keep (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
char *p;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
p = strchr (value->v.string, '=');
if (p)
*p++ = 0;
if (envop_entry_add (&comp->envop, envop_clear, NULL, NULL))
grecs_error (locus, errno, "envop_entry_add");
if (envop_entry_add (&comp->envop, envop_keep, value->v.string, p))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
static int
_cb_env_set (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
char *p;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
p = strchr (value->v.string, '=');
if (p)
*p++ = 0;
if (envop_entry_add (&comp->envop, envop_set, value->v.string, p))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
static int
_cb_env_unset (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr, grecs_value_t *value, void *cb_data)
{
struct component *comp = varptr;
if (assert_scalar_stmt (locus, cmd)
|| assert_grecs_value_type (locus, value, GRECS_TYPE_STRING))
return 1;
if (envop_entry_add (&comp->envop, envop_unset, value->v.string, NULL))
grecs_error (locus, errno, "envop_entry_add");
return 0;
}
struct grecs_keyword cb_env_keywords[] = {
{ "clear",
N_("bool"),
N_("Clear environment."),
grecs_type_bool, GRECS_DFLT,
NULL, 0,
_cb_env_clear },
{ "keep",
N_("name[=value]"),
N_("Keep this variable. Unless value is supplied, name can contain wildcards.\n"
"Implies \"clear yes\"."),
grecs_type_string, GRECS_DFLT,
NULL, 0,
_cb_env_keep },
{ "set",
N_("name=value"),
N_("Set environment variable. Note, that argument must be quoted."),
grecs_type_string, GRECS_DFLT,
NULL, 0,
_cb_env_set },
{ "unset",
N_("name"),
N_("Unset environment variable. Name can contain wildcards."),
grecs_type_string, GRECS_DFLT,
NULL, 0,
_cb_env_unset },
{ NULL }
};
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},
#ifdef LOG_AUTHPRIV
{"authpriv", LOG_AUTHPRIV},
#endif
{"cron", LOG_CRON},
{"daemon", LOG_DAEMON},
#ifdef LOG_FTP
{"ftp", LOG_FTP},
#endif
{"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 1;
}
break;
case GRECS_TYPE_ARRAY:
if (assert_grecs_value_type (locus, value->v.arg.v[0],
GRECS_TYPE_STRING))
return 1;
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 1;
}
if (assert_grecs_value_type (locus, value->v.arg.v[1],
GRECS_TYPE_STRING))
return 1;
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 1;
}
break;
case redir_file:
rp->v.file = grecs_strdup (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},
{"startup", pies_comp_startup},
{"shutdown", pies_comp_shutdown},
{"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 },
{ "shell", CF_SHELL },
{ 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",
N_("mode"),
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, offsetof (struct component, command),
NULL,
},
{"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 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 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,
},
{"realm",
N_("name"),
N_("Authentication realm name"),
grecs_type_string, GRECS_CONST,
&control.realm, 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_config_parse (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_CONST,
&statedir, 0, NULL},
{"pidfile",
NULL,
N_("Write PID to this file."),
grecs_type_string, GRECS_CONST,
&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_CONST,
&qotdfile, 0,
NULL },
{"user",
NULL,
N_("Run with this user privileges."),
grecs_type_string, GRECS_CONST,
&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_CONST,
&meta1_queue_dir, 0,
NULL,
},
{"mailer-program",
NULL,
N_("Full path to the mailer binary."),
grecs_type_string, GRECS_CONST,
&mailer_program, 0,
NULL
},
{"mailer-command-line",
NULL,
N_("Mailer command line (without recipient addresses)."),
grecs_type_string, GRECS_CONST,
&mailer_command_line, 0,
NULL
},
{ "identity-provider", "name: string", "Configure identity provider",
grecs_type_section, GRECS_INAC | GRECS_HIDDEN },
{NULL}
};
void
config_init (void)
{
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
}
int
pies_config_parse (char const *name)
{
struct grecs_node *node;
struct grecs_node *tree = grecs_parse (name);
if (!tree)
return 1;
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))
return 1;
grecs_tree_free (tree);
if (grecs_error_count)
return 1;
return 0;
}
void
config_help (void)
{
static char docstring[] =
/* TRANSLATORS: do not translate words in quotes */
N_("Configuration file structure for pies.\n"
"For more information, use command \"info pies configuration\".");
grecs_print_docstring (docstring, 0, stdout);
grecs_print_statement_array (pies_keywords, 1, 0, stdout);
pies_config_identity_mechanisms_help ();
}
int
pies_read_config (void)
{
struct grecs_list_entry *ep;
int err = 0;
component_config_begin ();
for (ep = config_list->head; ep; ep = ep->next)
{
struct config_file *file = ep->data;
if (file->syntax->parser (file->name))
++err;
}
if (init_process)
err = 0;
if (err)
component_config_rollback ();
return err;
}
int
pies_reread_config (void)
{
logmsg (LOG_INFO, _("reading configuration"));
return pies_read_config ();
}
static struct config_syntax *current_syntax = &config_syntax_tab[CONF_PIES];
#include "cmdline.h"
static int action = ACTION_CONT;
static int children_op = PIES_CHLD_NONE;
void
pies_schedule_action (int act)
{
action = act;
}
void
pies_schedule_children (int op)
{
children_op |= op;
}
RETSIGTYPE
sig_handler (int sig)
{
if (init_process && sysvinit_sigtrans (sig, &action))
return;
switch (sig)
{
case SIGCHLD:
pies_schedule_children (PIES_CHLD_CLEANUP);
break;
case SIGINT:
case SIGTERM:
case SIGQUIT:
logmsg (LOG_NOTICE, "received signal %d", sig);
pies_schedule_action (ACTION_STOP);
break;
case SIGHUP:
logmsg (LOG_NOTICE, "received signal %d", sig);
pies_schedule_action (ACTION_RELOAD);
break;
case SIGUSR1:
logmsg (LOG_NOTICE, "received signal %d", sig);
pies_schedule_action (ACTION_RESTART);
break;
case SIGALRM:
pies_schedule_children (PIES_CHLD_WAKEUP);
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, sigv[i]);
act.sa_handler = handler;
for (i = 0; i < sigc; i++)
{
sigaction (sigv[i], &act, NULL);
}
}
#define PIES_MAXSIG 16
static int default_sigv[] = {
SIGCHLD,
SIGTERM,
SIGQUIT,
SIGINT,
SIGHUP,
SIGUSR1,
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 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;
}
#define pies_control_url() control.url->string
int
request_restart_components (size_t cc, char **cv)
{
char **argv;
size_t i, j;
argv = grecs_calloc (5 + 3 * cc - 1, sizeof (*argv));
argv[0] = "piesctl";
argv[1] = "--url";
argv[2] = (char*) pies_control_url ();
argv[3] = "restart";
j = 4;
for (i = 0; i < cc; i++)
{
if (i > 0)
argv[j++] = "or";
argv[j++] = "component";
argv[j++] = cv[i];
}
argv[j] = NULL;
execvp (argv[0], argv);
logmsg (LOG_ERR, "can't run piesctl: %s", strerror (errno));
return 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
request_reload (void)
{
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
request_status (void)
{
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
request_stop (void)
{
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 file %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))
{
logfuncall ("unlink", name, 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))
logfuncall ("unlink", name, errno);
}
static void
set_mailer_argcv (void)
{
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 = grecs_calloc (mailer_argc + 1, sizeof (mailer_argv[0]));
for (i = 0; i < mailer_argc; i++)
mailer_argv[i] = grecs_strdup (ws.ws_wordv[i]);
mailer_argv[i] = NULL;
wordsplit_free (&ws);
}
static inline int
init_emu (void)
{
#ifdef INIT_EMU
# warning "pies compiled with init emulation code"
char *emu = getenv ("INIT_EMU");
if (emu)
{
char *inittab = strtok (emu, ":");
char *piesinit = strtok (NULL, ":");
config_file_add_type (CONF_INITTAB, inittab);
config_file_add_type (CONF_PIES,
piesinit ? piesinit : "/etc/pies.init");
init_fifo = getenv ("INIT_FIFO");
if (!init_fifo)
init_fifo = "/tmp/initctl";
init_process = 1;
fprintf (stderr, "%s: running in init emulation mode\n", program_name);
return 1;
}
else
{
fprintf (stderr, "%s: Notice:\n", program_name);
fprintf (stderr,
" To enable init emulation code, define environment variable\n"
" INIT_EMU=[:]\n"
" where and are names of the inittab and\n"
" Pies configuration files, correspondingly.\n"
"\n"
" To override the default FIFO name, define:\n"
" INIT_FIFO=\n");
fprintf (stderr, "%s: End of notice\n", program_name);
}
#endif
return 0;
}
static void
set_conf_file_names (const char *base)
{
if (init_process)
{
config_file_add_type (CONF_INITTAB, "/etc/inittab");
config_file_add_type (CONF_PIES, "/etc/pies.init");
}
else if (!config_list && !init_emu ())
{
char *name = mkfilename (SYSCONFDIR, base, ".conf");
config_file_add (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");
}
size_t pies_master_argc;
char **pies_master_argv;
int
main (int argc, char **argv)
{
pid_t pid;
extern char **environ;
struct grecs_list_entry *ep;
int diag_flags;
int i;
set_program_name (argv[0]);
#ifdef ENABLE_NLS
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
#endif
mf_proctitle_init (argc, argv, environ);
grecs_print_diag_fun = pies_diag_printer;
pies_master_argc = argc;
pies_master_argv = argv;
set_quoting_style (NULL, shell_quoting_style);
init_process = getpid () == 1;
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "--no-init") == 0)
{
init_process = 0;
break;
}
}
/* Set default logging */
if (init_process)
{
log_facility = LOG_DAEMON;
diag_flags = DIAG_TO_STDERR | DIAG_REOPEN_LOG;
}
else
diag_flags = DIAG_TO_SYSLOG | (stderr_closed_p () ? 0 : DIAG_TO_STDERR);
diag_setup (diag_flags);
config_init ();
parse_options (&argc, &argv);
if (argc && !(command == COM_RESTART_COMPONENT
|| command == COM_TRACE_DEPEND
|| command == COM_TRACE_PREREQ))
{
logmsg (LOG_ERR, "extra command line arguments");
exit (EX_USAGE);
}
if (!instance)
{
instance = strrchr (program_name, '/');
if (!instance)
instance = (char*) program_name;
else
instance++;
}
setenv ("PIES_INSTANCE", instance, 1);
log_tag = grecs_strdup (instance);
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 == &config_syntax_tab[CONF_PIES]
&& grecs_preproc_run (file->name, grecs_preprocessor))
exit (EX_CONFIG);
}
exit (0);
}
else if (pies_read_config ())
exit (EX_CONFIG);
component_config_commit ();
set_state_file_names (instance);
set_mailer_argcv ();
if (lint_mode)
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 (!control.url)
{
char const *str = default_control_url[init_process];
if (pies_url_create (&control.url, str))
{
logmsg (LOG_CRIT, _("%s: cannot create control URL: %s"),
str, strerror (errno));
if (!init_process)
exit (EX_OSERR);
}
}
switch (command)
{
case COM_RESTART_COMPONENT:
pies_priv_setup (&pies_privs);
if (pies_umask)
umask (pies_umask);
exit (request_restart_components (argc, argv));
case COM_RELOAD:
exit (request_reload ());
case COM_STATUS:
exit (request_status ());
case COM_STOP:
exit (request_stop ());
case COM_DUMP_DEPMAP:
components_dump_depmap ();
exit (0);
case COM_TRACE_DEPEND:
components_trace (argv, depmap_row);
exit (0);
case COM_TRACE_PREREQ:
components_trace (argv, depmap_col);
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);
}
if (!foreground)
{
check_pidfile (pidfile);
if (daemon (0, 0) == -1)
{
logfuncall ("daemon", NULL, errno);
exit (EX_SOFTWARE);
}
diag_setup (DIAG_TO_SYSLOG);
}
logmsg (LOG_INFO, _("%s %s starting"), proginfo.package, proginfo.version);
if (!init_process)
{
if (ctl_open ())
exit (EX_UNAVAILABLE);
create_pidfile (pidfile);
}
if (pies_master_argv[0][0] != '/')
logmsg (LOG_NOTICE,
_("not started as an absolute pathname; "
"restart will not work"));
signal_setup (sig_handler);
progman_create_sockets ();
program_init_startup ();
progman_start ();
do
{
if (children_op == PIES_CHLD_NONE)
pies_pause ();
switch (action)
{
case ACTION_RESTART:
if (pies_master_argv[0][0] != '/' || init_process)
{
logmsg (LOG_INFO, _("restart command ignored"));
action = ACTION_CONT;
}
break;
case ACTION_RELOAD:
if (pies_reread_config ())
{
action = ACTION_CONT;
break;
}
/* fall through */
case ACTION_COMMIT:
component_config_commit ();
if (init_process)
sysvinit_runlevel_setup (PIES_COMP_DEFAULT);
progman_create_sockets ();
progman_start ();
pies_schedule_children (PIES_CHLD_WAKEUP);
action = ACTION_CONT;
break;
case ACTION_STOP:
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));
pies_schedule_children (PIES_CHLD_WAKEUP);
action = ACTION_CONT;
break;
case ACTION_KBREQUEST:
debug (1, ("kbrequest"));
sysvinit_runlevel_setup (PIES_COMP_MASK (pies_comp_kbrequest));
pies_schedule_children (PIES_CHLD_WAKEUP);
action = ACTION_CONT;
break;
case ACTION_POWER:
debug (1, ("SIGPWR"));
sysvinit_power ();
pies_schedule_children (PIES_CHLD_WAKEUP);
action = ACTION_CONT;
}
if (action == ACTION_CONT)
{
if (children_op & PIES_CHLD_RESCHEDULE_ALARM)
progman_recompute_alarm ();
if (children_op & PIES_CHLD_GC)
progman_gc ();
if (children_op & PIES_CHLD_CLEANUP)
progman_cleanup (0);
if (children_op & PIES_CHLD_WAKEUP)
progman_wake_sleeping (1);
children_op = PIES_CHLD_NONE;
}
}
while (init_process || action == ACTION_CONT);
progman_stop ();
remove_pidfile (pidfile);
if (action == ACTION_RESTART)
{
int minfd = DIAG_OUTPUT (DIAG_TO_STDERR) ? 2 : 0;
int i;
for (i = getmaxfd (); i > minfd; i--)
close (i);
signal_setup (SIG_DFL);
execv (pies_master_argv[0], pies_master_argv);
}
logmsg (LOG_INFO, _("%s %s terminated"), proginfo.package, proginfo.version);
exit (EX_OK);
}
void
xalloc_die (void)
{
logmsg (LOG_CRIT, _("not enough memory"));
abort ();
}
/* EOF */