diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/comp.c | 16 | ||||
-rw-r--r-- | src/pies.c | 278 | ||||
-rw-r--r-- | src/pies.h | 5 | ||||
-rw-r--r-- | src/prog.h | 1 | ||||
-rw-r--r-- | src/progman.c | 310 |
5 files changed, 323 insertions, 287 deletions
@@ -166,24 +166,12 @@ component_create (const char *name) void component_free (struct component *comp) { - size_t i; - component_unlink (comp); free (comp->tag); free (comp->program); free (comp->command); - 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); - } + argv_free (comp->argv); + envop_free (comp->envop); free (comp->dir); grecs_list_free (comp->prereq); grecs_list_free (comp->depend); @@ -544,16 +544,127 @@ _cb_umask (enum grecs_callback_command cmd, *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 -_cb_env (enum grecs_callback_command cmd, - grecs_locus_t *locus, - void *varptr, grecs_value_t *value, void *cb_data) +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) { - size_t argc; char **argv; - char ***penv = varptr; - struct wordsplit ws; + int rc; switch (value->type) { @@ -572,11 +683,147 @@ _cb_env (enum grecs_callback_command cmd, return 1; } - *penv = argv; + rc = parse_legacy_env (argv, envop); + argv_free (argv); + if (rc) + { + grecs_error (locus, errno, _("can't parse legacy env statement")); + return 1; + } + return 0; +} + +static int +cb_env_section_parser (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, void *cb_data) +{ + struct component *comp = varptr; + + switch (cmd) + { + case grecs_callback_section_begin: + //FIXME + *(struct component **) cb_data = comp; + break; + + case grecs_callback_section_end: + //FIXME + break; + + case grecs_callback_set_value: + return _cb_env (&comp->envop, value, locus); + } + return 0; +} + +static int +_cb_env_clear (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + struct component *comp = varptr; + int clear; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + if (grecs_string_convert(&clear, grecs_type_bool, value->v.string, locus)) + return 1; + if (clear) + { + if (envop_entry_add (&comp->envop, envop_clear, NULL, NULL)) + grecs_error (locus, errno, "envop_entry_add"); + } return 0; } - +static int +_cb_env_keep (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + struct component *comp = varptr; + char *p; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + p = strchr (value->v.string, '='); + if (p) + *p++ = 0; + if (envop_entry_add (&comp->envop, envop_clear, NULL, NULL)) + grecs_error (locus, errno, "envop_entry_add"); + if (envop_entry_add (&comp->envop, envop_keep, value->v.string, p)) + grecs_error (locus, errno, "envop_entry_add"); + return 0; +} + +static int +_cb_env_set (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + struct component *comp = varptr; + char *p; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + p = strchr (value->v.string, '='); + if (p) + *p++ = 0; + if (envop_entry_add (&comp->envop, envop_set, value->v.string, p)) + grecs_error (locus, errno, "envop_entry_add"); + return 0; +} + +static int +_cb_env_unset (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + struct component *comp = varptr; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + if (envop_entry_add (&comp->envop, envop_unset, value->v.string, NULL)) + grecs_error (locus, errno, "envop_entry_add"); + return 0; +} + +struct grecs_keyword cb_env_keywords[] = { + { "clear", + N_("bool"), + N_("Clear environment."), + grecs_type_bool, GRECS_DFLT, + NULL, 0, + _cb_env_clear }, + { "keep", + N_("name[=value]"), + N_("Keep this variable. Unless value is supplied, name can contain wildcards.\n" + "Implies \"clear yes\"."), + grecs_type_string, GRECS_DFLT, + NULL, 0, + _cb_env_keep }, + { "set", + N_("name=value"), + N_("Set environment variable. Note, that argument must be quoted."), + grecs_type_string, GRECS_DFLT, + NULL, 0, + _cb_env_set }, + { "unset", + N_("name"), + N_("Unset environment variable. Name can contain wildcards."), + grecs_type_string, GRECS_DFLT, + NULL, 0, + _cb_env_unset }, + { NULL } +}; + + int string_to_syslog_priority (const char *key, int *pres) { @@ -1157,12 +1404,19 @@ struct grecs_keyword component_keywords[] = { _cb_limits, }, {"env", + NULL, + N_("Modify program environment."), + grecs_type_section, GRECS_DFLT, + NULL, 0, + cb_env_section_parser, NULL, cb_env_keywords + }, + {"env", N_("arg: list"), - N_("Set program environment. Argument is a list of assignments " - "separated by white space."), + N_("Modify program environment (legacy syntax).\n" + "Argument is a list of quoted assignments separated by white space."), grecs_type_string, GRECS_DFLT, - NULL, offsetof (struct component, env), - _cb_env, + NULL, 0, + cb_env_section_parser, NULL, NULL }, {"chdir", N_("dir"), @@ -56,6 +56,7 @@ #include "identity.h" #include "acl.h" #include "libpies.h" +#include "envop.h" #include "grecs/json.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) @@ -229,7 +230,7 @@ struct component char *command; /* Full command line */ size_t argc; /* Number of command line arguments */ char **argv; /* Program command line */ - char **env; /* Program environment */ + envop_t *envop; /* Environment modification program */ char *dir; /* Working directory */ struct grecs_list *prereq; /* Prerequisites */ struct grecs_list *depend; /* Dependency targets */ @@ -333,6 +334,8 @@ void free_redirector (struct redirector *rp); void pies_schedule_action (int act); void free_action (struct action *act); +void argv_free (char **argv); + #define PIES_CHLD_NONE 0 #define PIES_CHLD_CLEANUP 0x01 #define PIES_CHLD_WAKEUP 0x02 @@ -58,6 +58,7 @@ struct prog time_t timestamp; /* Time of last startup */ size_t failcount; /* Number of failed starts since timestamp */ enum prog_status status; /* Current component status */ + environ_t *env; /* Execution environment (only in child) */ /* If status == status_listener: */ size_t num_instances; /* Number of running instances */ /* If comp->type == pies_comp_inetd && status == status_running */ diff --git a/src/progman.c b/src/progman.c index 85670f2..b75a2db 100644 --- a/src/progman.c +++ b/src/progman.c @@ -390,6 +390,14 @@ redirect_to_file (struct prog *master, int stream) master->v.p.comp->redir[stream].v.file, strerror (errno)); } + /* Position to the end of file */ + if (lseek (fd, 0, SEEK_END) == -1) + { + if (errno != ESPIPE) + logmsg (LOG_ERR, "lseek(%s): %s", + master->v.p.comp->redir[stream].v.file, + strerror (errno)); + } return fd; } @@ -604,10 +612,6 @@ conn_class_remove (struct conn_class *pcclass) } -extern char **environ; /* Environment */ -static size_t envsize; /* Size of environ. If it is not 0, then we - have allocated space for the environ. */ - #define ENV_PROTO "PROTO" #define ENV_SOCKTYPE "SOCKTYPE" #define ENV_LOCALIP "LOCALIP" @@ -616,254 +620,30 @@ static size_t envsize; /* Size of environ. If it is not 0, then we #define ENV_REMOTEIP "REMOTEIP" #define ENV_REMOTEPORT "REMOTEPORT" #define ENV_REMOTEHOST "REMOTEHOST" -static char *sockenv_hint[] = { - "-" ENV_PROTO, - "-" ENV_SOCKTYPE, - "-" ENV_LOCALIP, - "-" ENV_LOCALPORT, - "-" ENV_LOCALHOST, - "-" ENV_REMOTEIP, - "-" ENV_REMOTEPORT, - "-" ENV_REMOTEHOST, +static char *sockenv_var[] = { + ENV_PROTO, + ENV_SOCKTYPE, + ENV_LOCALIP, + ENV_LOCALPORT, + ENV_LOCALHOST, + ENV_REMOTEIP, + ENV_REMOTEPORT, + ENV_REMOTEHOST, NULL }; -#define DEBUG_ENVIRON(l) \ - do \ - if (debug_level >= (l)) \ - { \ - int i; \ - logmsg_printf (LOG_DEBUG, "environment: "); \ - for (i = 0; environ[i]; i++) \ - logmsg_printf (LOG_DEBUG, "%s ", environ[i]); \ - logmsg_printf (LOG_DEBUG, "\n"); \ - } \ - while (0) - -static void -add_env (const char *name, const char *value) -{ - size_t i; - size_t namelen = strlen (name); - char *p; - - for (i = 0; environ[i]; i++) - { - if (!strncmp (environ[i], name, namelen) && environ[i][namelen] == '=') - break; - } - - if (environ[i] == NULL) - { - if (envsize == 0) - { - char **new_env; - - envsize = i + 4; - new_env = grecs_calloc (envsize, sizeof new_env[0]); - for (i = 0; (new_env[i] = environ[i]); i++) - ; - environ = new_env; - } - else if (i == envsize) - { - envsize += 4; - environ = grecs_realloc (environ, - envsize * sizeof environ[0]); - } - environ[i+1] = NULL; - } - - if (!value) - value = ""; - p = grecs_malloc (namelen + 1 + strlen (value) + 1); - strcpy (p, name); - p[namelen] = '='; - strcpy (p + namelen + 1, value); - environ[i] = p; -} - -/* Find variable NAME in environment ENV. - On success, store the index of the ENV slot in *IDX, - the offset of the value (position right past '=') in *VALOFF, and - return 0 (IDX and/or VALOFF can be NULL, if that info is not needed). - Return -1 if NAME was not found. */ -static int -find_env_pos (char **env, char *name, size_t *idx, size_t *valoff) +static inline void +debug_environ (struct prog *prog, int l) { - size_t nlen = strcspn (name, "+="); - size_t i; - - for (i = 0; env[i]; i++) + if (debug_level >= l) { - size_t elen = strcspn (env[i], "="); - if (elen == nlen && memcmp (name, env[i], nlen) == 0) - { - if (idx) - *idx = i; - if (valoff) - *valoff = elen + 1; - return 0; - } - } - return -1; -} - -/* Find variable NAME in environment ENV. - On success, return pointer to the variable assignment (if VAL is 0), - or to the value (if VAL is 1). - Return NULL if NAME is not present in ENV. */ -static char * -find_env_ptr (char **env, char *name, int val) -{ - size_t i, j; - if (find_env_pos (env, name, &i, &j)) - return NULL; - return val ? env[i] + j : env[i]; -} - -/* Return 1 if ENV contains a matching unset statement for variable NAME. */ -static int -var_is_unset (char **env, const char *name) -{ - int i; - int nlen = strcspn (name, "="); - - for (i = 0; env[i]; i++) - { - if (env[i][0] == '-') - { - size_t elen = strcspn (env[i] + 1, "="); - if (elen == nlen && memcmp (name, env[i] + 1, nlen) == 0) - { - if (env[i][nlen + 1]) - return strcmp (name + nlen, env[i] + 1 + nlen) == 0; - else - return 1; - } - } - } - return 0; -} - -static char * -env_concat (const char *name, size_t namelen, const char *a, const char *b) -{ - char *res; - size_t len; - - if (a && b) - { - res = grecs_malloc (namelen + 1 + strlen (a) + strlen (b) + 1); - strcpy (res + namelen + 1, a); - strcat (res + namelen + 1, b); - } - else if (a) - { - len = strlen (a); - if (c_ispunct (a[len-1])) - len--; - res = grecs_malloc (namelen + 1 + len + 1); - memcpy (res + namelen + 1, a, len); - res[namelen + 1 + len] = 0; - } - else /* if (a == NULL) */ - { - if (c_ispunct (b[0])) - b++; - len = strlen (b); - res = grecs_malloc (namelen + 1 + len + 1); - strcpy (res + namelen + 1, b); - } - memcpy (res, name, namelen); - res[namelen] = '='; - return res; -} - -static void -environ_setup (char **hint) -{ - char **old_env = environ; - char **new_env; - size_t count, i, j, n; - - if (!hint) - return; - - if (strcmp (hint[0], "-") == 0) - { - old_env = NULL; - hint++; - } - - /* Count new environment size */ - count = 0; - if (old_env) - for (i = 0; old_env[i]; i++) - count++; - - for (i = 0; hint[i]; i++) - count++; - - /* Allocate new environment. */ - new_env = grecs_calloc (count + 1, sizeof new_env[0]); - - /* Populate the environment. */ - n = 0; - - if (old_env) - for (i = 0; old_env[i]; i++) - { - if (!var_is_unset (hint, old_env[i])) - new_env[n++] = old_env[i]; - } - - for (i = 0; hint[i]; i++) - { - char *p; - - if (hint[i][0] == '-') - { - /* Skip unset directives. */ - continue; - } - - /* Find the slot for the variable. Use next available - slot if there's no such variable in new_env */ - if (find_env_pos (new_env, hint[i], &j, NULL)) - j = n; - - if ((p = strchr (hint[i], '='))) - { - if (p == hint[i]) - continue; /* Ignore erroneous entry */ - if (p[-1] == '+') - new_env[j] = env_concat (hint[i], p - hint[i] - 1, - find_env_ptr (environ, hint[i], 1), - p + 1); - else if (p[1] == '+') - new_env[j] = env_concat (hint[i], p - hint[i], - p + 2, - find_env_ptr (environ, hint[i], 1)); - else - new_env[j] = hint[i]; - } - else if ((p = find_env_ptr (environ, hint[i], 0))) - new_env[j] = p; - else - continue; - - /* Adjust environment size */ - if (j == n) - ++n; + int i; + char **env = environ_ptr (prog->v.p.env); + logmsg_printf (LOG_DEBUG, "environment: "); + for (i = 0; env[i]; i++) + logmsg_printf (LOG_DEBUG, "%s ", env[i]); + logmsg_printf (LOG_DEBUG, "\n"); } - new_env[n] = NULL; - - if (envsize) - free (environ); - - environ = new_env; - envsize = count + 1; } /* Pass socket information in environment variables. */ @@ -884,7 +664,7 @@ prog_sockenv (struct prog *prog) if (socket_type_to_str (prog->v.p.comp->socket_type, &proto)) proto = umaxtostr (prog->v.p.comp->socket_type, buf); - add_env (ENV_SOCKTYPE, proto); + environ_set (prog->v.p.env, ENV_SOCKTYPE, proto); if (prog->v.p.comp->socket_url) proto = prog->v.p.comp->socket_url->proto_s; @@ -893,7 +673,7 @@ prog_sockenv (struct prog *prog) else if (ISCF_TCPMUX (prog->v.p.comp->flags)) proto = "TCP"; - add_env (ENV_PROTO, proto); + environ_set (prog->v.p.env, ENV_PROTO, proto); if (getsockname (prog->v.p.socket, (struct sockaddr *) &sa_server, &len) < 0) @@ -902,10 +682,10 @@ prog_sockenv (struct prog *prog) { p = inet_ntoa (sa_server.s_in.sin_addr); if (p) - add_env (ENV_LOCALIP, p); + environ_set (prog->v.p.env, ENV_LOCALIP, p); p = umaxtostr (ntohs (sa_server.s_in.sin_port), buf); - add_env (ENV_LOCALPORT, p); + environ_set (prog->v.p.env, ENV_LOCALPORT, p); if (prog->v.p.comp->flags & CF_RESOLVE) { @@ -914,7 +694,7 @@ prog_sockenv (struct prog *prog) AF_INET)) == NULL) logmsg (LOG_WARNING, "gethostbyaddr: %s", strerror (errno)); else - add_env (ENV_LOCALHOST, host->h_name); + environ_set (prog->v.p.env, ENV_LOCALHOST, host->h_name); } } @@ -922,10 +702,10 @@ prog_sockenv (struct prog *prog) { p = inet_ntoa (sa_client->s_in.sin_addr); if (p) - add_env (ENV_REMOTEIP, p); + environ_set (prog->v.p.env, ENV_REMOTEIP, p); p = umaxtostr (ntohs (sa_client->s_in.sin_port), buf); - add_env (ENV_REMOTEPORT, p); + environ_set (prog->v.p.env, ENV_REMOTEPORT, p); if (prog->v.p.comp->flags & CF_RESOLVE) { @@ -935,11 +715,11 @@ prog_sockenv (struct prog *prog) logmsg (LOG_WARNING, "gethostbyaddr: %s", strerror (errno)); else - add_env (ENV_REMOTEHOST, host->h_name); + environ_set (prog->v.p.env, ENV_REMOTEHOST, host->h_name); } } /* FIXME: $REMOTEINFO ? */ - DEBUG_ENVIRON (4); + debug_environ (prog, 4); } static int @@ -1036,12 +816,21 @@ prog_start_prologue (struct prog *prog) prog_tag (prog), prog->v.p.comp->dir, strerror (errno)); } - environ_setup (prog->v.p.comp->env ? - prog->v.p.comp->env : - ((prog->v.p.comp->flags & CF_SOCKENV) ? sockenv_hint : NULL)); + prog->v.p.env = environ_create (environ); + if (prog->v.p.comp->flags & CF_SOCKENV) + { + size_t i; + for (i = 0; sockenv_var[i]; i++) + environ_unset (prog->v.p.env, sockenv_var[i]); + } + envop_exec (prog->v.p.comp->envop, prog->v.p.env); if (init_process) - environ_setup (sysvinit_environ_hint); - DEBUG_ENVIRON (4); + { + size_t i; + for (i = 0; sysvinit_environ_hint[i]; i++) + environ_add (prog->v.p.env, sysvinit_environ_hint[i]); + } + debug_environ (prog, 4); pies_priv_setup (&prog->v.p.comp->privs); if (prog->v.p.comp->umask) @@ -1066,6 +855,7 @@ prog_start_prologue (struct prog *prog) static void prog_execute (struct prog *prog) { + environ = environ_ptr (prog->v.p.env); if (prog->v.p.comp->builtin) { mf_proctitle_format ("inetd %s", |