/* 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; 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_unset, value->v.string, p)) 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 */