/* 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 . */
#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 "json.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 action
{
size_t nstat;
unsigned *status;
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,
/*
** 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 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 */
size_t argc; /* Number of command line arguments */
char **argv; /* Program command line */
char **env; /* Program environment */
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 */
/* 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: */
int facility; /* Syslog facility. */
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) ((cp)->mode >= pies_mark_sysvinit || (cp)->runlevels)
enum pies_action {
ACTION_CONT,
ACTION_STOP,
ACTION_RESTART,
ACTION_RELOAD,
ACTION_CTRLALTDEL,
ACTION_KBREQUEST,
ACTION_POWER,
ACTION_COMMIT
};
extern char *instance;
extern char *log_tag;
extern int log_facility;
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 free_action (struct action *act);
#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);
int progman_waiting_p (void);
void progman_start (void);
void progman_gc (void);
void progman_wake_sleeping (int);
void progman_stop (void);
void progman_cleanup (int expect_term);
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 (RETSIGTYPE (*sf)(int));
void setsigvhan (RETSIGTYPE (*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);
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_clear_all (pies_depmap_t dmap, enum pies_depmap_direction dir,
size_t coord);
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);
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);
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 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);
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 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);
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);