diff --git a/src/api.c b/src/api.c index d896d8df..69b2f228 100644 --- a/src/api.c +++ b/src/api.c @@ -59,6 +59,8 @@ static int call(int (*action)(svc_t *, void *), char *buf, size_t len) static int stop(svc_t *svc, void *user_data) { + (void)user_data; + if (!svc) return 1; @@ -72,6 +74,8 @@ static int stop(svc_t *svc, void *user_data) static int start(svc_t *svc, void *user_data) { + (void)user_data; + if (!svc) return 1; @@ -105,6 +109,8 @@ static int restart(svc_t *svc, void *user_data) static int reload(svc_t *svc, void *user_data) { + (void)user_data; + if (!svc) return 1; @@ -149,6 +155,8 @@ static int missing(char *job, char *id, void *user_data) { char buf[20]; + (void)user_data; + if (!job) job = ""; if (!id) @@ -228,6 +236,8 @@ struct wq emergency = { .cb = bypass_shutdown }; static void bypass_shutdown(void *unused) { + (void)unused; + cprintf("TIMEOUT TIMEOUT SHUTTING DOWN NOW!!\n"); do_shutdown(halt); } @@ -332,6 +342,8 @@ static void api_cb(uev_t *w, void *arg, int events) int sd, lvl; svc_t *svc; + (void)arg; + if (UEV_ERROR == events) { dbg("%s(): api socket %d invalid.", __func__, w->fd); goto error; diff --git a/src/cgroup.c b/src/cgroup.c index 9e99848a..0c7e3155 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -88,6 +88,12 @@ static void cgset(const char *path, char *ctrl, char *prop) } *val++ = 0; + /* unquote value, if quoted */ + if (unquote(&val, NULL)) { + errx(1, "Syntax error, unterminated quote in %s/%s.%s=%s", path, ctrl, prop, val); + return; + } + /* disallow sneaky relative paths */ if (strstr(ctrl, "..") || strstr(prop, "..")) { errx(1, "Possible security violation; '..' not allowed in cgroup config!"); @@ -261,6 +267,7 @@ static void cgroup_events_cb(uev_t *w, void *arg, int events) ssize_t sz; size_t off; + (void)arg; if (UEV_ERROR == events) { dbg("%s(): inotify socket %d invalid.", __func__, w->fd); return; diff --git a/src/cond-w.c b/src/cond-w.c index facc57c9..faf72e2f 100644 --- a/src/cond-w.c +++ b/src/cond-w.c @@ -202,6 +202,9 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct { const char *cond, *ptr; + (void)sb; + (void)tflag; + if (ftw->level == 0) return 1; @@ -390,6 +393,8 @@ static int do_assert(const char *fpath, const struct stat *sb, int tflg, struct { char *nm; + (void)sb; + if (ftw->level == 0) return 1; diff --git a/src/conf.c b/src/conf.c index d90beb3b..6f277b2a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -490,14 +490,16 @@ void conf_save_service(int type, char *cfg, char *file) fclose(fp); } -/* - * Sets, and makes a note of, all KEY=VALUE lines in a given .conf line - * from finit.conf, or other .conf file. Note, PATH is always reset in - * the conf_reset_env() function. +/** + * conf_parse_env - Parse a key=value line + * @line: Line buffer without newline + * @value: Whitespace trimmed value + * + * Returns: + * %NULL on error, otherwise a whitespace trimmed key. */ -static void parse_env(char *line) +char *conf_parse_env(char *line, char **value) { - struct env_entry *node; char *key, *val, *end; /* skip any leading whitespace */ @@ -519,7 +521,7 @@ static void parse_env(char *line) val = strchr(key, '='); if (!val) - return; + return NULL; *val++ = 0; /* strip leading whitespace from value */ @@ -527,14 +529,8 @@ static void parse_env(char *line) val++; /* unquote value, if quoted */ - if (val[0] == '"' || val[0] == '\'') { - char q = val[0]; - - if (*end == q) { - val = &val[1]; - *end = 0; - } - } + unquote(&val, end); + *value = val; /* find end of key */ end = key; @@ -561,18 +557,42 @@ static void parse_env(char *line) end++; if (*end != 0) { warnx("'%s=%s': not a valid identifier", key, val); - return; /* invalid key */ + return NULL; /* invalid key */ } + return key; +} + +/* + * Sets, and makes a note of, all KEY=VALUE lines in a given .conf line + * from finit.conf, or other .conf file. Note, PATH is always reset in + * the conf_reset_env() function. + */ +static void parse_env(char *line) +{ + struct env_entry *node; + char *key, *val; + + key = conf_parse_env(line, &val); + if (!key) + return; + dbg("Global env '%s'='%s'", key, val); setenv(key, val, 1); node = malloc(sizeof(*node)); if (!node) { + nomem: err(1, "Out of memory cannot track env vars"); return; } + node->name = strdup(key); + if (!node->name) { + free(node); + goto nomem; + } + TAILQ_INSERT_HEAD(&env_list, node, link); } @@ -1542,6 +1562,8 @@ static int conf_iwatch_read(int fd) static void conf_cb(uev_t *w, void *arg, int events) { + (void)arg; + if (UEV_ERROR == events) { dbg("%s(): iwatch socket %d invalid.", __func__, w->fd); return; diff --git a/src/conf.h b/src/conf.h index 0ac82e75..972197b8 100644 --- a/src/conf.h +++ b/src/conf.h @@ -47,6 +47,7 @@ void conf_saverc (void); void conf_save_exec_order (svc_t *svc, char *cmdline, int result); void conf_save_service (int type, char *cfg, char *file); void conf_parse_cmdline (int argc, char *argv[]); +char *conf_parse_env (char *line, char **val); int conf_parse_runlevels (char *runlevels); void conf_parse_cond (svc_t *svc, char *cond); diff --git a/src/devmon.c b/src/devmon.c index fda09605..cea313c2 100644 --- a/src/devmon.c +++ b/src/devmon.c @@ -77,7 +77,7 @@ void devmon_add_cond(const char *cond) struct dev_node *node; if (!cond || strncmp(cond, "dev/", 4)) { - dbg("no match"); +// dbg("no match %s", cond ?: ""); return; } @@ -164,6 +164,8 @@ static void devmon_scandir(struct iwatch *iw, char *dir, int len) size_t i; int rc; + (void)iw; + snprintf(path, sizeof(path), "%s/*", dir); rc = glob(path, GLOB_NOSORT, NULL, &gl); if (rc && rc != GLOB_NOMATCH) @@ -207,6 +209,13 @@ static void devmon_cb(uev_t *w, void *arg, int events) ssize_t sz; size_t off; + (void)arg; + + if (UEV_ERROR == events) { + errx(1, "Unrecoverable error in devmon watcher"); + return; + } + sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1); if (sz <= 0) { err(1, "invalid inotify event"); diff --git a/src/helpers.c b/src/helpers.c index a15d66af..9129679c 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -293,6 +293,8 @@ static size_t print_timestamp(char *buf, size_t len) sscanf(buf, "%f %f", &stamp, &dummy); return snprintf(buf, len, "[ %.6f ]", stamp); #else + (void)buf; + (void)len; return 0; #endif } diff --git a/src/log.c b/src/log.c index 7eb80d62..7a50b6f4 100644 --- a/src/log.c +++ b/src/log.c @@ -32,6 +32,9 @@ # include #endif +#define SYSLOG_NAMES +#include + #include "finit.h" #include "helpers.h" #include "log.h" @@ -106,6 +109,73 @@ static const char *l2s(int prio) return "UNK"; } +static int s2f(const char *str) +{ + for (int i = 0; facilitynames[i].c_name; i++) { + if (string_match(facilitynames[i].c_name, str)) + return facilitynames[i].c_val; + } + + return -1; +} + +static int s2l(const char *str) +{ + for (int i = 0; prioritynames[i].c_name; i++) { + if (string_match(prioritynames[i].c_name, str)) + return prioritynames[i].c_val; + } + + return -1; +} + +/* + * Parse syslog facility.priority strings into their numerical values + * using arrays defined in . Note, the 'prio' argument may + * be one of: + * + * - facility.level + * - facility + * - level + * + * It is up to the callee to assign defaults to 'facility' and 'level' + * before calling this function. If There is any match the respective + * facility or level will be assigned and this function returns OK. + */ +int log_parse(char *prio, int *facility, int *level) +{ + char *fac = prio, *lvl; + int f = -1; + int l = -1; + + if (!prio || !facility || !level) + return -1; + + lvl = strchr(prio, '.'); + if (lvl) { + *lvl = 0; + lvl++; + } + + if (lvl && *lvl) + l = s2l(lvl); + + f = s2f(fac); + if (l < 0) + l = s2l(fac); + + if (l < 0 && f <= 0) + return -1; + + if (l <= LOG_DEBUG) + *level = l; + + if (f <= LOG_LOCAL7) + *facility = f; + + return 0; +} + /* * Log to /dev/kmsg until syslogd has started, then openlog() * and continue logging as a regular daemon. diff --git a/src/log.h b/src/log.h index 4a449484..ee6097f9 100644 --- a/src/log.h +++ b/src/log.h @@ -78,6 +78,7 @@ void log_init (void); void log_exit (void); void log_debug(void); +int log_parse(char *prio, int *facility, int *level); void logit (int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void flog (char *file, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); diff --git a/src/plugin.c b/src/plugin.c index 17d63afe..a155d5d9 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -320,6 +320,8 @@ static int init_plugins(uev_ctx_t *ctx) plugin_t *p, *tmp; int fail = 0; + (void)ctx; + PLUGIN_ITERATOR(p, tmp) { if (plugin_io_init(p)) fail++; diff --git a/src/service.c b/src/service.c index 0050c225..754fbe63 100644 --- a/src/service.c +++ b/src/service.c @@ -244,6 +244,9 @@ static void fallback_logger(char *ident, char *prio) int level = LOG_NOTICE; char buf[256]; + strlcpy(buf, prio, sizeof(buf)); + log_parse(buf, &facility, &level); + prctl(PR_SET_NAME, "finitlog", 0, 0, 0); openlog(ident, LOG_NOWAIT | LOG_PID, facility); while ((fgets(buf, sizeof(buf), stdin))) @@ -327,6 +330,11 @@ static int lredirect(svc_t *svc) /* Default syslog identity name[:id] */ tag = svc_ident(svc, buf, sizeof(buf)); + if (svc->log.ident[0]) + tag = svc->log.ident; + if (svc->log.prio[0]) + prio = svc->log.prio; + if (!have_sysklogd && !whichp(_PATH_LOGIT)) { logit(LOG_INFO, _PATH_LOGIT " missing, using syslog for %s instead", svc->name); fallback_logger(tag, prio); @@ -352,11 +360,6 @@ static int lredirect(svc_t *svc) _exit(1); } - if (svc->log.ident[0]) - tag = svc->log.ident; - if (svc->log.prio[0]) - prio = svc->log.prio; - if (have_sysklogd) { char pid[16]; @@ -425,11 +428,13 @@ static void source_env(svc_t *svc) line = buf; while (fgets(line, LINE_SIZE, fp)) { - char *key = chomp(line); wordexp_t we = { 0 }; - char *value, *end; + char *key, *value; size_t i; + /* Trim newline */ + key = chomp(line); + /* skip any leading whitespace */ while (isspace(*key)) key++; @@ -438,64 +443,9 @@ static void source_env(svc_t *svc) if (*key == '#' || *key == ';') continue; - /* find end of line */ - end = key; - while (*end) - end++; - - /* strip trailing whitespace */ - if (end > key) { - end--; - while (isspace(*end)) - *end-- = 0; - } - - value = strchr(key, '='); - if (!value) + key = conf_parse_env(key, &value); + if (!key) continue; - *value++ = 0; - - /* strip leading whitespace from value */ - while (isspace(*value)) - value++; - - /* unquote value, if quoted */ - if (value[0] == '"' || value[0] == '\'') { - char q = value[0]; - - if (*end == q) { - value = &value[1]; - *end = 0; - } - } - - /* find end of key */ - end = key; - while (*end) - end++; - - /* strip trailing whitespace */ - if (end > key) { - end--; - while (isspace(*end)) - *end-- = 0; - } - - /* strip any leading 'set ' */ - end = key; - if (!strncmp(key, "set", 3)) - end += 3; - - /* check key, no spaces allowed */ - while (*end && isspace(*end)) - end++; - key = end; - while (*end && !isspace(*end)) - end++; - if (*end != 0) { - warnx("'%s=%s': not a valid identifier", key, value); - continue; /* invalid key */ - } if (wordexp(value, &we, 0)) { setenv(key, value, 1); @@ -1190,6 +1140,83 @@ void service_runlevel(int newlevel) networking(0); } +/* + * Parse run/task/service arguments with support for quoted strings, both + * single and double quotes to allow arguments containing spaces. + * + * Returns next argument or NULL if end of line + * Updates *line to point past the parsed argument + */ +static char *parse_args(char **line) +{ + char *start, *end = NULL, *arg; + char in_quote = 0; + int has_colon = 0; + + if (!line || !*line) + return NULL; + + /* Skip leading whitespace */ + while (**line && (**line == ' ' || **line == '\t')) + (*line)++; + + if (!**line) + return NULL; + + start = *line; + + /* Parse the token */ + while (**line) { + if (in_quote) { + if (**line == in_quote) { + /* End quote found */ + in_quote = 0; + } + } else { + if (**line == '\'' || **line == '"') { + /* Start quote */ + in_quote = **line; + } else if (**line == ':') { + /* Found colon - this might be key:value format */ + has_colon = 1; + } else if (**line == ' ' || **line == '\t') { + /* Whitespace - check if we're in key:value mode */ + if (has_colon) { + /* Look ahead to see if there's a comma after whitespace */ + char *lookahead = *line; + while (*lookahead && (*lookahead == ' ' || *lookahead == '\t')) + lookahead++; + if (*lookahead == ',') { + /* Continue parsing - this space is part of the token */ + (*line)++; + continue; + } + } + /* End token at whitespace */ + end = *line; + break; + } + } + (*line)++; + } + + /* Set end if we reached end of string */ + if (!end) + end = *line; + + /* Null terminate the argument */ + if (end > start) { + arg = start; + if (*end) { + *end = '\0'; + *line = end + 1; + } + return arg; + } + + return NULL; +} + /* * log:/path/to/logfile,priority:facility.level,tag:ident */ @@ -1564,7 +1591,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) } } - cmd = strtok_r(line, " \t", &args); + args = line; + cmd = parse_args(&args); if (!cmd) { incomplete: errx(1, "Incomplete service '%s', cannot register", cfg); @@ -1644,7 +1672,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) break; /* Check if valid command follows... */ - cmd = strtok_r(NULL, " ", &args); + cmd = parse_args(&args); if (!cmd) goto incomplete; } @@ -2798,6 +2826,7 @@ void service_step_all(int types) void service_worker(void *unused) { + (void)unused; service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK); } diff --git a/src/sig.c b/src/sig.c index ffbe7faf..080ea305 100644 --- a/src/sig.c +++ b/src/sig.c @@ -260,6 +260,7 @@ static int status_cb(int pid, void *data) { int *has_proc = (int *)data; + (void)pid; *has_proc = 1; return 1; @@ -425,6 +426,8 @@ void do_shutdown(shutop_t op) */ static void sighup_cb(uev_t *w, void *arg, int events) { + (void)arg; + dbg("..."); if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); @@ -451,6 +454,9 @@ static void sighup_cb(uev_t *w, void *arg, int events) */ static void sigint_cb(uev_t *w, void *arg, int events) { + (void)w; + (void)arg; + dbg("..."); if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); @@ -467,6 +473,9 @@ static void sigint_cb(uev_t *w, void *arg, int events) */ static void sigpwr_cb(uev_t *w, void *arg, int events) { + (void)w; + (void)arg; + dbg("..."); if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); @@ -481,6 +490,8 @@ static void sigpwr_cb(uev_t *w, void *arg, int events) */ static void sigusr1_cb(uev_t *w, void *arg, int events) { + (void)arg; + dbg("..."); if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); @@ -502,6 +513,9 @@ static void sigusr1_cb(uev_t *w, void *arg, int events) */ static void sigusr2_cb(uev_t *w, void *arg, int events) { + (void)w; + (void)arg; + dbg("..."); if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); @@ -522,6 +536,9 @@ static void sigusr2_cb(uev_t *w, void *arg, int events) */ static void sigterm_cb(uev_t *w, void *arg, int events) { + (void)w; + (void)arg; + dbg("..."); if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); @@ -545,6 +562,9 @@ static void sigchld_cb(uev_t *w, void *arg, int events) int status; pid_t pid; + (void)w; + (void)arg; + if (UEV_ERROR == events) { errx(1, "Unrecoverable error in signal watcher"); return; @@ -601,6 +621,9 @@ const char *sig_name(int signo) */ static void chld_handler(int sig, siginfo_t *info, void *ctx) { + (void)sig; + (void)info; + (void)ctx; /* NOP */ } diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 8bd0d34c..ab68e233 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -114,6 +114,9 @@ static int parse_mm(char *arg, int *major, int *minor) static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw) { + (void)sb; + (void)tflag; + if (ftw->level == 0) return 1; diff --git a/src/util.h b/src/util.h index 5162385d..1e5fecfd 100644 --- a/src/util.h +++ b/src/util.h @@ -165,6 +165,27 @@ static inline void hexdump(const char *buf, size_t len) } } +/* drop quotes from a string at point, if any */ +static inline int unquote(char **str, char *end) +{ + char *ptr = *str; + + /* unquote value, if quoted */ + if (ptr[0] == '"' || ptr[0] == '\'') { + char q = ptr[0]; + + if (!end && !(end = strchr(&ptr[1], q))) + return -1; + + if (*end == q) { + *str = &ptr[1]; + *end = 0; + } + } + + return 0; +} + #endif /* FINIT_UTIL_H_ */ /**