Merge pull request #445 from troglobit/space-in-argval

Space in argval

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-07-02 14:50:21 +02:00
committed by GitHub
14 changed files with 290 additions and 83 deletions
+12
View File
@@ -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;
+7
View File
@@ -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;
+5
View File
@@ -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;
+38 -16
View File
@@ -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;
+1
View File
@@ -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);
+10 -1
View File
@@ -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 ?: "<NIL>");
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");
+2
View File
@@ -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
}
+70
View File
@@ -32,6 +32,9 @@
# include <lite/lite.h>
#endif
#define SYSLOG_NAMES
#include <syslog.h>
#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 <syslog.h>. 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.
+1
View File
@@ -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)));
+2
View File
@@ -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++;
+95 -66
View File
@@ -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);
}
+23
View File
@@ -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 */
}
+3
View File
@@ -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;
+21
View File
@@ -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_ */
/**