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