/* This file is part of GNU Pies. Copyright (C) 2008-2023 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 . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "progname.h" #include "inttostr.h" #include "c-ctype.h" #include "quotearg.h" #include "fprintftime.h" #include "identity.h" #include "acl.h" #include "libpies.h" #include "envop.h" #include "grecs/json.h" #include "pies_syslog.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define TESTTIME 2*60 #define SLEEPTIME 5*60 #define MAXSPAWN 10 #define DEFAULT_PASS_FD_TIMEOUT 5 #define RETR_OUT 0 #define RETR_ERR 1 enum redir_type { redir_null, redir_syslog, redir_file }; struct redirector { enum redir_type type; union { int prio; char *file; } v; }; typedef struct limits_rec *limits_record_t; enum return_action { action_restart, action_disable, }; #define STATUS_SIG_BIT 0x80000000 #define STATUS_CODE(c) ((c) & ~STATUS_SIG_BIT) struct status_cond { unsigned status; int neg; }; struct action { struct grecs_list *cond_list; /* List of status conditions. */ enum return_action act; /* Action to take when the component terminates */ char *addr; /* Addresses to notify about it. */ char *message; /* Notification mail. */ char *command; /* Execute this command */ }; /* user privs */ struct pies_privs { char *user; int allgroups; struct grecs_list *groups; }; enum pies_comp_mode { /* ** Pies native component types. */ /* Execute the component, no sockets are opened. This is the default Pies mode. */ pies_comp_exec, /* Open a socket and start a component with stdin/stdout bound to that socket. Corresponds to MeTA1 notion of `start_action = accept'. */ pies_comp_accept, /* Inetd mode: like above, but start the component only when an incoming connection is requested. Corresponds to `start_action = nostartaccept' in MeTA1. */ pies_comp_inetd, /* Open a socket, start a component, and pass the socket fd to the component via the UNIX domain socket. Corresponds to `start_action = pass' in MeTA1. */ pies_comp_pass_fd, /* Components of this type runs once on program startup. Running other components is delayed until the last startup component finishes. */ pies_comp_startup, /* Components of this type are run right before program termination. They have shutdown_timeout seconds to finish their job and terminate gracefully, othervise they will be terminated forcefully via SIGTERM (and SIGKILL, for persisting ones). */ pies_comp_shutdown, /* ** Init-style components */ pies_mark_sysvinit, /* Start the process when the specified runlevel is entered and wait for its termination */ pies_comp_wait = pies_mark_sysvinit, /* Execute the component once, when the specified runlevel is entered */ pies_comp_once, /* Execute the component during system boot. Ignore runlevel settings. */ pies_comp_boot, /* Execute the component during system boot and wait for it to terminate. Ignore runlevel settings. */ pies_comp_bootwait, /* Execute the component when the power goes down. */ pies_comp_powerfail, /* Execute the component when the power goes down. Wait for it to terminate. */ pies_comp_powerwait, /* Execute the component when the power is restored. Wait for it to terminate. */ pies_comp_powerokwait, /* Execute the process when SIGINT is delivered, i.e. someone has pressed the Ctrl+Alt+Del combination. */ pies_comp_ctrlaltdel, /* Execute the component when a specified ondemand runlevel is called */ pies_comp_ondemand, /* Execute the component on the system boot. */ pies_comp_sysinit, /* Execute the component when running on the UPS and pies is informed that the UPS battery is almost empty. */ pies_comp_powerfailnow, /* Execute the component a signal from the keyboard handler arrives, indicating that a special key combination was pressed on the console keyboard. */ pies_comp_kbrequest, /* Restart the component wherever it terminates */ pies_comp_respawn = pies_comp_exec, }; #define PIES_COMP_DEFAULT 0 #define PIES_COMP_MASK(m) (1 << ((m))) #define CF_DISABLED 0x001 /* The componenet is disabled */ #define CF_PRECIOUS 0x002 /* The component is precious (should not be disabled) */ #define CF_WAIT 0x004 /* Wait for the component instance to terminate. */ #define CF_TCPMUX 0x008 /* A plain TCPMUX service */ #define CF_TCPMUXPLUS 0x010 /* A TCPMUX-plus service, i.e. pies must emit a '+' response before starting it */ #define CF_INTERNAL 0x020 /* An internal inetd service */ #define CF_SOCKENV 0x040 /* Component wants socket information in the environment */ #define CF_RESOLVE 0x080 /* Resolve IP addresses */ #define CF_SIGGROUP 0x100 /* Send signals to the process group */ #define CF_NULLINPUT 0x200 /* Provide null input stream */ #define CF_SHELL 0x400 /* Invoke via sh -c */ #define CF_EXPANDENV 0x800 /* Expand environment variables in the command line */ #define CF_REMOVE 0xf000 /* Marked for removal */ #define ISCF_TCPMUX(f) ((f) & (CF_TCPMUX | CF_TCPMUXPLUS)) struct prog; struct component { struct component *prev, *next; /* Components form doubly-linked list. */ int listidx; /* Index of the list. */ size_t arridx; /* Index of this component. */ size_t ref_count; /* Reference count. */ struct prog *prog; /* Prog associated with this component. */ enum pies_comp_mode mode; char *tag; /* Entry tag (for diagnostics purposes) */ char *program; /* Program name */ char *command; /* Full command line */ size_t argc; /* Number of command line arguments */ char **argv; /* Program command line */ envop_t *envop; /* Environment modification program */ char *dir; /* Working directory */ struct grecs_list *prereq; /* Prerequisites */ struct grecs_list *depend; /* Dependency targets */ int flags; /* CF_ bitmask */ size_t max_instances; /* Maximum number of simultaneously running instances */ char *rmfile; /* Try to remove this file before starting */ struct pies_privs privs; /* UID/GIDS+groups to run as */ mode_t umask; /* Umask to install before starting */ limits_record_t limits; /* System limits */ int sigterm; /* Signal to terminate the process */ unsigned long shutdown_seqno; /* Shutdown sequence number */ /* For exec (init) components */ char *runlevels; /* For inetd components */ size_t max_rate; /* Maximum number of invocations per minute */ size_t max_ip_connections; /* Max. number of connections per IP address */ int socket_type; /* Socket type */ struct inetd_builtin *builtin; /* Builtin function */ char *service; struct pies_url *socket_url; /* Socket to listen on (if mode != pies_comp_exec) */ char *pass_fd_socket; /* Socket to pass fd on (if mode == pies_comp_pass_fd) */ unsigned pass_fd_timeout; /* Maximum time to wait for pass_fd socket to become available. */ pies_acl_t acl; /* Connection ACL */ char *tcpmux; /* Master service for TCPMUX */ /* Optional error messages to be sent back on the socket: */ char *access_denied_message; char *max_instances_message; char *max_ip_connections_message; /* Redirectors: */ struct redirector redir[2]; /* Repeaters for stdout and stderr */ /* Actions to execute on various exit codes: */ struct grecs_list *act_list; /* ACLs for control interface */ pies_acl_t list_acl; /* List access control list */ pies_acl_t adm_acl; /* Administrative ACL (stop, start, etc.) */ }; #define is_sysvinit(cp) \ (PIES_SYSVINIT_ENABLED \ && ((cp)->mode >= pies_mark_sysvinit || (cp)->runlevels)) #define SYSVINIT_ACTIVE (PIES_SYSVINIT_ENABLED && init_process) enum pies_action { ACTION_CONT, ACTION_STOP, ACTION_RESTART, ACTION_RELOAD, ACTION_CTRLALTDEL, ACTION_KBREQUEST, ACTION_POWER, ACTION_COMMIT }; extern char *instance; extern unsigned long shutdown_timeout; extern struct component default_component; extern pies_acl_t pies_acl; extern limits_record_t pies_limits; extern char *mailer_program; extern char *mailer_command_line; extern int mailer_argc; extern char **mailer_argv; extern size_t default_max_rate; extern char *qotdfile; extern int init_process; extern char *console_device; extern int initdefault; extern size_t pies_master_argc; extern char **pies_master_argv; extern char *default_control_url[2]; enum config_syntax_type { CONF_PIES, CONF_META1, CONF_INETD, CONF_INITTAB }; struct config_syntax; struct config_syntax *str_to_config_syntax (const char *str); void config_file_add (struct config_syntax *syntax, const char *name); void config_file_add_type (enum config_syntax_type syntax, const char *name); void config_file_list_serialize (struct json_value *ar); int config_file_remove (const char *name); void config_file_remove_all (void); void free_redirector (struct redirector *rp); void pies_schedule_action (int act); void listel_dispose (void *el); void argv_free (char **argv); #define PIES_CHLD_NONE 0 #define PIES_CHLD_CLEANUP 0x01 #define PIES_CHLD_WAKEUP 0x02 #define PIES_CHLD_GC 0x04 #define PIES_CHLD_RESCHEDULE_ALARM 0x08 void pies_schedule_children (int op); int pies_read_config (void); int pies_reread_config (void); void register_prog (struct component *comp); void program_init_startup (void); int progman_waiting_p (void); void progman_start (void); void progman_gc (void); void progman_wake_sleeping (int); void progman_stop (void); #define CLEANUP_DEFAULT 0 #define CLEANUP_EXPECT_TERM 1 void progman_cleanup (int flags); void progman_filter (int (*filter) (struct component *, void *data), void *data); int progman_accept (int socket, void *data); void progman_create_sockets (void); struct component *progman_lookup_component (const char *tag); struct component *progman_lookup_tcpmux (const char *service, const char *master); void progman_run_comp (struct component *comp, int fd, union pies_sockaddr_storage *sa, socklen_t salen); void progman_recompute_alarm (void); void fd_report (int fd, const char *msg); int check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen, pies_identity_t identity); void log_setup (int want_stderr); void signal_setup (void (*sf)(int)); void setsigvhan (void (*handler) (int signo), int *sigv, int sigc); void add_extra_sigv (int *sigv, int sigc); typedef struct pies_depmap *pies_depmap_t; typedef struct pies_depmap_pos *pies_depmap_pos_t; enum pies_depmap_direction { depmap_row = 0, depmap_col = !depmap_row }; pies_depmap_t depmap_alloc (size_t count); pies_depmap_t depmap_copy (pies_depmap_t dpm); size_t depmap_dim (struct pies_depmap *dmap); void depmap_free (pies_depmap_t dmap); void depmap_set (pies_depmap_t dmap, size_t row, size_t col); int depmap_isset (pies_depmap_t dmap, size_t row, size_t col); void depmap_clear (pies_depmap_t dmap, size_t row, size_t col); void depmap_remove (pies_depmap_t dmap, size_t n); void depmap_tc (pies_depmap_t dmap); size_t depmap_first (pies_depmap_t dmap, enum pies_depmap_direction dir, size_t coord, pies_depmap_pos_t *ppos); size_t depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos); void depmap_end (pies_depmap_pos_t pos); struct depmap_path_elem { int idx; struct depmap_path_elem *next; }; struct depmap_path { size_t len; struct depmap_path_elem *head, *tail; struct depmap_path *next; }; void depmap_path_free (struct depmap_path *path); struct depmap_path *depmap_cycle_detect (pies_depmap_t dmap); int assert_grecs_value_type (grecs_locus_t *locus, const grecs_value_t *value, int type); int str_to_socket_type (const char *str, int *pret); int socket_type_to_str (int socket_type, const char **pres); struct component *component_create (const char *name); void component_free (struct component *comp); void component_ref_incr (struct component *comp); void component_ref_decr (struct component *comp); int component_list_is_empty (void); void component_config_begin (void); void component_config_rollback (void); void component_config_commit (void); int component_is_active (struct component *comp); void component_finish (struct component *comp, grecs_locus_t *locus); struct grecs_keyword *find_component_keyword (const char *ident); int component_foreach (int (*filter) (struct component *, void *), void *data); void components_dump_depmap (void); void components_trace (char **argv, enum pies_depmap_direction dir); void components_list_shutdown_sequence (void); size_t components_shutdown_sequence_numbers (unsigned long **ret); struct component *component_depmap_first (enum pies_depmap_direction dir, size_t idx, pies_depmap_pos_t *ppos); struct component *component_depmap_next (pies_depmap_pos_t pos); void pies_set_hook (int (*f) (void)); void pies_pause (void); enum { PIES_EVT_RD, PIES_EVT_WR, PIES_EVT_EX }; typedef int (*socket_handler_t) (int, void *); void *register_socket (int fd, socket_handler_t rd, socket_handler_t wr, socket_handler_t ex, void *data, void (*free_data)(void*)); void deregister_socket (int fd); void update_socket (int fd, int evt, socket_handler_t f); int register_program_socket (int socktype, int fd, void *data, void (*free_data)(void*)); int pass_fd (const char *socket, int fd, unsigned time_out); int create_socket (struct pies_url *url, int socket_type, const char *user, mode_t umask); void disable_socket (int fd); void enable_socket (int fd); int parse_limits (limits_record_t *plrec, char *str, char **endp); int set_limits (const char *name, limits_record_t lrec); void free_limits (limits_record_t rec); int limits_cmp (limits_record_t a, limits_record_t b); void meta1_parser_set_debug (void); int meta1lex (void); int meta1error (char const *s); int meta1parse (void); /* diag.c */ #define DIAG_TO_SYSLOG 0x01 #define DIAG_TO_STDERR 0x02 #define DIAG_TO_MASK 0x0f #define DIAG_REOPEN_LOG 0x10 #define DIAG_ALL (DIAG_REOPEN_LOG|DIAG_TO_STDERR|DIAG_TO_SYSLOG) extern int diag_output; #define DIAG_OUTPUT(x) (diag_output & (x)) void diag_setup (int flags); #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) # define __attribute__(x) #endif #ifndef PIES_PRINTFLIKE # define PIES_PRINTFLIKE(fmt,narg) __attribute__ ((__format__ (__printf__, fmt, narg))) #endif void diagmsg (int logf, int prio, const char *fmt, ...) PIES_PRINTFLIKE(3,4); void vlogmsg (int prio, const char *fmt, va_list ap); void logmsg (int prio, const char *fmt, ...) PIES_PRINTFLIKE(2,3); void logmsg_printf (int prio, const char *fmt, ...) PIES_PRINTFLIKE(2,3); void logmsg_vprintf (int prio, const char *fmt, va_list ap); void logfuncall (const char *fun, const char *arg, int err); void pies_diag_printer (grecs_locus_t const *locus, int err, int errcode, const char *msg); extern unsigned debug_level; extern int source_info_option; void debug_msg (const char *fmt, ...) PIES_PRINTFLIKE(1,2); #define debug(lev, args) \ do \ if (debug_level >= lev) \ { \ if (source_info_option) \ logmsg_printf (LOG_DEBUG, "%s:%d:%s: ", \ __FILE__, __LINE__, __FUNCTION__); \ debug_msg args; \ } \ while (0) /* userprivs.c */ int switch_to_privs (uid_t uid, gid_t gid, struct grecs_list *retain_groups); void pies_priv_setup (struct pies_privs *); void pies_epriv_setup (struct pies_privs *); int pies_privs_cmp (struct pies_privs const *a, struct pies_privs const *b); void pies_privs_free (struct pies_privs *p); /* inetd.c */ int inetd_config_parse (const char *file); /* inetd-bi.c */ struct inetd_builtin { const char *service; int socktype; int single_process; int flags; void (*fun) (int, struct component const *); }; struct inetd_builtin *inetd_builtin_lookup (const char *service, int socktype); /* sysvinit.c */ void sysvinit_begin (void); int is_comp_wait (struct component *comp); int is_valid_runlevel (int c); int console_open (int mode); int telinit (int argc, char **argv); int inittab_parse (const char *file); int sysvinit_sigtrans (int sig, int *pact); void sysvinit_runlevel_setup (int mask); void sysvinit_sysdep_begin (void); void sysvinit_power (void); void sysvinit_report (struct json_value *obj); int sysvinit_set_runlevel (int newlevel); void sysvinit_parse_argv (int argc, char **argv); int sysvinit_envlocate (char const *name, char **value); int sysvinit_envdelete (char const *name); int sysvinit_envupdate (char const *var); int cb_initdefault (enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data); int cb_runlevels (enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data); extern char *sysvinit_environ_hint[]; extern char *init_fifo; #ifndef INIT_FIFO # define INIT_FIFO "/dev/initctl" #endif #ifndef POWER_STAT_FILE # define POWER_STAT_FILE "/var/run/powerstatus" #endif /* Power status values */ #define POWER_STAT_FAIL 'F' #define POWER_STAT_LOW 'L' #define POWER_STAT_OK 'O' /* Request codes */ #define INIT_MAGIC 0x03091969 #define INIT_CMD_START 0 #define INIT_CMD_RUNLVL 1 #define INIT_CMD_POWERFAIL 2 #define INIT_CMD_POWERFAILNOW 3 #define INIT_CMD_POWEROK 4 #define INIT_CMD_BSD 5 #define INIT_CMD_SETENV 6 #define INIT_CMD_UNSETENV 7 #define INIT_CMD_CHANGECONS 12345 struct sysvinit_request { int magic; /* Magic number */ int cmd; /* What kind of request */ int runlevel; /* Runlevel to change to */ int sleeptime; /* Time between TERM and KILL */ char data[368]; }; /* utmp.c */ #define SYSV_ACCT_BOOT 0 #define SYSV_ACCT_RUNLEVEL 1 #define SYSV_ACCT_PROC_START 2 #define SYSV_ACCT_PROC_STOP 3 void sysvinit_acct (int what, const char *user, const char *id, pid_t pid, const char *line); /* ctl.c */ struct control { struct pies_url *url; /* Control socket URL */ pies_acl_t conn_acl; /* Connection ACL */ pies_acl_t adm_acl; /* Administrative ACL */ pies_acl_t usr_acl; /* User ACL */ unsigned int idle_timeout; /* Session idle timeout */ char *realm; /* Authentication realm */ }; extern struct control control; int ctl_open(void); void json_object_set_string (struct json_value *obj, char const *name, char const *fmt, ...); void json_object_set_number (struct json_value *obj, char const *name, double val); void json_object_set_bool (struct json_value *obj, char const *name, int val);