mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
first attempt at proper service/socket logic
This commit is contained in:
32
automount.c
32
automount.c
@@ -8,13 +8,13 @@
|
||||
#include "load-fstab.h"
|
||||
#include "load-dropin.h"
|
||||
|
||||
static int automount_load(Name *n) {
|
||||
static int automount_init(Name *n) {
|
||||
int r;
|
||||
Automount *a = AUTOMOUNT(n);
|
||||
|
||||
assert(a);
|
||||
|
||||
exec_context_defaults(&a->exec_context);
|
||||
exec_context_init(&a->exec_context);
|
||||
|
||||
/* Load a .automount file */
|
||||
if ((r = name_load_fragment(n)) < 0 && errno != -ENOENT)
|
||||
@@ -31,6 +31,13 @@ static int automount_load(Name *n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void automount_done(Name *n) {
|
||||
Automount *d = AUTOMOUNT(n);
|
||||
|
||||
assert(d);
|
||||
free(d->path);
|
||||
}
|
||||
|
||||
static void automount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
@@ -67,7 +74,7 @@ static void automount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
for (c = 0; c < _AUTOMOUNT_EXEC_MAX; c++) {
|
||||
ExecCommand *i;
|
||||
|
||||
LIST_FOREACH(i, s->exec_command[c])
|
||||
LIST_FOREACH(command, i, s->exec_command[c])
|
||||
fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
|
||||
}
|
||||
}
|
||||
@@ -88,24 +95,13 @@ static NameActiveState automount_active_state(Name *n) {
|
||||
return table[AUTOMOUNT(n)->state];
|
||||
}
|
||||
|
||||
static void automount_free_hook(Name *n) {
|
||||
Automount *d = AUTOMOUNT(n);
|
||||
|
||||
assert(d);
|
||||
free(d->path);
|
||||
}
|
||||
|
||||
const NameVTable automount_vtable = {
|
||||
.suffix = ".mount",
|
||||
|
||||
.load = automount_load,
|
||||
.init = automount_init,
|
||||
.done = automount_done,
|
||||
|
||||
.dump = automount_dump,
|
||||
|
||||
.start = NULL,
|
||||
.stop = NULL,
|
||||
.reload = NULL,
|
||||
|
||||
.active_state = automount_active_state,
|
||||
|
||||
.free_hook = automount_free_hook
|
||||
.active_state = automount_active_state
|
||||
};
|
||||
|
||||
@@ -337,6 +337,36 @@ int config_parse_string(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_path(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char **s = data;
|
||||
char *n;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (*rvalue != '/') {
|
||||
log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(n = strdup(rvalue)))
|
||||
return -ENOMEM;
|
||||
|
||||
free(*s);
|
||||
*s = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_strv(
|
||||
const char *filename,
|
||||
@@ -360,7 +390,7 @@ int config_parse_strv(
|
||||
assert(data);
|
||||
|
||||
k = strv_length(*sv);
|
||||
FOREACH_WORD(w, &l, rvalue, state)
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
k++;
|
||||
|
||||
if (!(n = new(char*, k+1)))
|
||||
@@ -368,7 +398,7 @@ int config_parse_strv(
|
||||
|
||||
for (k = 0; (*sv)[k]; k++)
|
||||
n[k] = (*sv)[k];
|
||||
FOREACH_WORD(w, &l, rvalue, state)
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
if (!(n[k++] = strndup(w, l)))
|
||||
goto fail;
|
||||
|
||||
@@ -381,6 +411,7 @@ int config_parse_strv(
|
||||
fail:
|
||||
for (; k > 0; k--)
|
||||
free(n[k-1]);
|
||||
free(n);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ int config_parse_unsigned(const char *filename, unsigned line, const char *secti
|
||||
int config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
|
||||
#endif
|
||||
|
||||
25
device.c
25
device.c
@@ -4,6 +4,13 @@
|
||||
#include "device.h"
|
||||
#include "strv.h"
|
||||
|
||||
static void device_done(Name *n) {
|
||||
Device *d = DEVICE(n);
|
||||
|
||||
assert(d);
|
||||
strv_free(d->sysfs);
|
||||
}
|
||||
|
||||
static void device_dump(Name *n, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_DEVICE_STATE_MAX] = {
|
||||
@@ -24,24 +31,12 @@ static NameActiveState device_active_state(Name *n) {
|
||||
return DEVICE(n)->state == DEVICE_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
|
||||
}
|
||||
|
||||
static void device_free_hook(Name *n) {
|
||||
Device *d = DEVICE(n);
|
||||
|
||||
assert(d);
|
||||
strv_free(d->sysfs);
|
||||
}
|
||||
|
||||
const NameVTable device_vtable = {
|
||||
.suffix = ".device",
|
||||
|
||||
.load = name_load_fragment_and_dropin,
|
||||
.init = name_load_fragment_and_dropin,
|
||||
.done = device_done,
|
||||
.dump = device_dump,
|
||||
|
||||
.start = NULL,
|
||||
.stop = NULL,
|
||||
.reload = NULL,
|
||||
|
||||
.active_state = device_active_state,
|
||||
|
||||
.free_hook = device_free_hook
|
||||
.active_state = device_active_state
|
||||
};
|
||||
|
||||
246
execute.c
246
execute.c
@@ -1,41 +1,238 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "execute.h"
|
||||
#include "strv.h"
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
|
||||
int exec_spawn(const ExecCommand *command, const ExecContext *context, pid_t *ret) {
|
||||
assert(command);
|
||||
assert(context);
|
||||
assert(ret);
|
||||
static int close_fds(int except[], unsigned n_except) {
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
int r = 0;
|
||||
|
||||
/* Modifies the fds array! (sorts it) */
|
||||
|
||||
if (!(d = opendir("/proc/self/fd")))
|
||||
return -errno;
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
int fd;
|
||||
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if ((r = safe_atoi(de->d_name, &fd)) < 0)
|
||||
goto finish;
|
||||
|
||||
if (fd < 3)
|
||||
continue;
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
if (except) {
|
||||
bool found;
|
||||
unsigned i;
|
||||
|
||||
found = false;
|
||||
for (i = 0; i < n_except; i++)
|
||||
if (except[i] == fd) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((r = close_nointr(fd)) < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
finish:
|
||||
closedir(d);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int shift_fds(int fds[], unsigned n_fds) {
|
||||
int start, restart_from;
|
||||
|
||||
if (n_fds <= 0)
|
||||
return 0;
|
||||
|
||||
assert(fds);
|
||||
|
||||
start = 0;
|
||||
for (;;) {
|
||||
int i;
|
||||
|
||||
restart_from = -1;
|
||||
|
||||
for (i = start; i < (int) n_fds; i++) {
|
||||
int nfd;
|
||||
|
||||
/* Already at right index? */
|
||||
if (fds[i] == i+3)
|
||||
continue;
|
||||
|
||||
if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
|
||||
return -errno;
|
||||
|
||||
assert_se(close_nointr(fds[i]));
|
||||
fds[i] = nfd;
|
||||
|
||||
/* Hmm, the fd we wanted isn't free? Then
|
||||
* let's remember that and try again from here*/
|
||||
if (nfd != i+3 && restart_from < 0)
|
||||
restart_from = i;
|
||||
}
|
||||
|
||||
if (restart_from < 0)
|
||||
break;
|
||||
|
||||
start = restart_from;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exec_context_free(ExecContext *c) {
|
||||
int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret) {
|
||||
pid_t pid;
|
||||
|
||||
assert(command);
|
||||
assert(context);
|
||||
assert(ret);
|
||||
assert(fds || n_fds <= 0);
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
return -errno;
|
||||
|
||||
if (pid == 0) {
|
||||
char **e, **f = NULL;
|
||||
int i, r;
|
||||
char t[16];
|
||||
/* child */
|
||||
|
||||
umask(context->umask);
|
||||
|
||||
if (chdir(context->directory ? context->directory : "/") < 0) {
|
||||
r = EXIT_CHDIR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snprintf(t, sizeof(t), "%i", context->oom_adjust);
|
||||
char_array_0(t);
|
||||
|
||||
if (write_one_line_file("/proc/self/oom_adj", t) < 0) {
|
||||
r = EXIT_OOM_ADJUST;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
|
||||
r = EXIT_NICE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (close_fds(fds, n_fds) < 0 ||
|
||||
shift_fds(fds, n_fds) < 0) {
|
||||
r = EXIT_FDS;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (!context->rlimit[i])
|
||||
continue;
|
||||
|
||||
if (setrlimit(i, context->rlimit[i]) < 0) {
|
||||
r = EXIT_LIMITS;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_fds > 0) {
|
||||
char a[64], b[64];
|
||||
char *listen_env[3] = {
|
||||
a,
|
||||
b,
|
||||
NULL
|
||||
};
|
||||
|
||||
snprintf(a, sizeof(a), "LISTEN_PID=%llu", (unsigned long long) getpid());
|
||||
snprintf(b, sizeof(b), "LISTEN_FDS=%u", n_fds);
|
||||
|
||||
a[sizeof(a)-1] = 0;
|
||||
b[sizeof(b)-1] = 0;
|
||||
|
||||
if (context->environment) {
|
||||
if (!(f = strv_merge(listen_env, context->environment))) {
|
||||
r = EXIT_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
e = f;
|
||||
} else
|
||||
e = listen_env;
|
||||
|
||||
} else
|
||||
e = context->environment;
|
||||
|
||||
execve(command->path, command->argv, e);
|
||||
r = EXIT_EXEC;
|
||||
|
||||
fail:
|
||||
strv_free(f);
|
||||
_exit(r);
|
||||
}
|
||||
|
||||
*ret = pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exec_context_init(ExecContext *c) {
|
||||
assert(c);
|
||||
|
||||
c->umask = 0002;
|
||||
cap_clear(c->capabilities);
|
||||
c->oom_adjust = 0;
|
||||
c->nice = 0;
|
||||
}
|
||||
|
||||
void exec_context_done(ExecContext *c) {
|
||||
unsigned l;
|
||||
|
||||
assert(c);
|
||||
|
||||
strv_free(c->environment);
|
||||
c->environment = NULL;
|
||||
|
||||
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
|
||||
for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
|
||||
free(c->rlimit[l]);
|
||||
c->rlimit[l] = NULL;
|
||||
}
|
||||
|
||||
free(c->directory);
|
||||
c->directory = NULL;
|
||||
|
||||
free(c->chdir);
|
||||
free(c->user);
|
||||
c->user = NULL;
|
||||
|
||||
free(c->group);
|
||||
free(c->supplementary_groups);
|
||||
c->group = NULL;
|
||||
|
||||
strv_free(c->supplementary_groups);
|
||||
c->supplementary_groups = NULL;
|
||||
}
|
||||
|
||||
void exec_command_free_list(ExecCommand *c) {
|
||||
ExecCommand *i;
|
||||
|
||||
while ((i = c)) {
|
||||
LIST_REMOVE(ExecCommand, c, i);
|
||||
LIST_REMOVE(ExecCommand, command, c, i);
|
||||
|
||||
free(i->path);
|
||||
free(i->argv);
|
||||
@@ -43,6 +240,16 @@ void exec_command_free_list(ExecCommand *c) {
|
||||
}
|
||||
}
|
||||
|
||||
void exec_command_free_array(ExecCommand **c, unsigned n) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
exec_command_free_list(c[i]);
|
||||
c[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||
assert(c);
|
||||
assert(f);
|
||||
@@ -52,17 +259,20 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||
|
||||
fprintf(f,
|
||||
"%sUmask: %04o\n"
|
||||
"%sDumpable: %s\n"
|
||||
"%sDirectory: %s\n",
|
||||
"%sDirectory: %s\n"
|
||||
"%sNice: %i\n"
|
||||
"%sOOMAdjust: %i\n",
|
||||
prefix, c->umask,
|
||||
prefix, yes_no(c->dumpable),
|
||||
prefix, c->chdir ? c->chdir : "/");
|
||||
prefix, c->directory ? c->directory : "/",
|
||||
prefix, c->nice,
|
||||
prefix, c->oom_adjust);
|
||||
}
|
||||
|
||||
void exec_context_defaults(ExecContext *c) {
|
||||
assert(c);
|
||||
void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) {
|
||||
assert(s);
|
||||
|
||||
c->umask = 0002;
|
||||
cap_clear(c->capabilities);
|
||||
c->dumpable = true;
|
||||
s->pid = pid;
|
||||
s->code = code;
|
||||
s->status = status;
|
||||
s->timestamp = now(CLOCK_REALTIME);
|
||||
}
|
||||
|
||||
48
execute.h
48
execute.h
@@ -14,10 +14,11 @@ typedef struct ExecContext ExecContext;
|
||||
#include <stdio.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "util.h"
|
||||
|
||||
struct ExecStatus {
|
||||
pid_t pid;
|
||||
time_t timestamp;
|
||||
usec_t timestamp;
|
||||
int code; /* as in siginfo_t::si_code */
|
||||
int status; /* as in sigingo_t::si_status */
|
||||
};
|
||||
@@ -25,20 +26,20 @@ struct ExecStatus {
|
||||
struct ExecCommand {
|
||||
char *path;
|
||||
char **argv;
|
||||
ExecStatus last_exec_status;
|
||||
LIST_FIELDS(ExecCommand);
|
||||
ExecStatus exec_status;
|
||||
LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
|
||||
};
|
||||
|
||||
struct ExecContext {
|
||||
char **environment;
|
||||
mode_t umask;
|
||||
struct rlimit *rlimit[RLIMIT_NLIMITS];
|
||||
cap_t capabilities;
|
||||
bool capabilities_set:1;
|
||||
bool dumpable:1;
|
||||
int oom_adjust;
|
||||
int nice;
|
||||
char *chdir;
|
||||
char *directory;
|
||||
|
||||
cap_t capabilities;
|
||||
bool capabilities_set:1;
|
||||
|
||||
/* since resolving these names might might involve socket
|
||||
* connections and we don't want to deadlock ourselves these
|
||||
@@ -48,13 +49,40 @@ struct ExecContext {
|
||||
char **supplementary_groups;
|
||||
};
|
||||
|
||||
int exec_spawn(const ExecCommand *command, const ExecContext *context, pid_t *ret);
|
||||
typedef enum ExitStatus {
|
||||
/* EXIT_SUCCESS defined by libc */
|
||||
/* EXIT_FAILURE defined by libc */
|
||||
EXIT_INVALIDARGUMENT = 2,
|
||||
EXIT_NOTIMPLEMENTED = 3,
|
||||
EXIT_NOPERMISSION = 4,
|
||||
EXIT_NOTINSTALLED = 5,
|
||||
EXIT_NOTCONFIGURED = 6,
|
||||
EXIT_NOTRUNNING = 7,
|
||||
|
||||
/* The LSB suggests that error codes >= 200 are "reserved". We
|
||||
* use them here under the assumption that they hence are
|
||||
* unused by init scripts.
|
||||
*
|
||||
* http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
|
||||
|
||||
EXIT_CHDIR = 200,
|
||||
EXIT_NICE,
|
||||
EXIT_FDS,
|
||||
EXIT_EXEC,
|
||||
EXIT_MEMORY,
|
||||
EXIT_LIMITS,
|
||||
EXIT_OOM_ADJUST
|
||||
} ExitStatus;
|
||||
|
||||
int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, unsigned n_fds, pid_t *ret);
|
||||
|
||||
void exec_context_free(ExecContext *c);
|
||||
void exec_command_free_list(ExecCommand *c);
|
||||
void exec_command_free_array(ExecCommand **c, unsigned n);
|
||||
|
||||
void exec_context_init(ExecContext *c);
|
||||
void exec_context_done(ExecContext *c);
|
||||
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
|
||||
|
||||
void exec_context_defaults(ExecContext *c);
|
||||
void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status);
|
||||
|
||||
#endif
|
||||
|
||||
61
hashmap.c
61
hashmap.c
@@ -69,6 +69,18 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
|
||||
return h;
|
||||
}
|
||||
|
||||
int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
|
||||
assert(h);
|
||||
|
||||
if (*h)
|
||||
return 0;
|
||||
|
||||
if (!(*h = hashmap_new(hash_func, compare_func)))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
|
||||
assert(h);
|
||||
assert(e);
|
||||
@@ -248,26 +260,26 @@ void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
void *hashmap_iterate(Hashmap *h, void **state, const void **key) {
|
||||
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
|
||||
struct hashmap_entry *e;
|
||||
|
||||
assert(state);
|
||||
assert(i);
|
||||
|
||||
if (!h)
|
||||
goto at_end;
|
||||
|
||||
if (*state == (void*) -1)
|
||||
if (*i == ITERATOR_LAST)
|
||||
goto at_end;
|
||||
|
||||
if (!*state && !h->iterate_list_head)
|
||||
if (*i == ITERATOR_FIRST && !h->iterate_list_head)
|
||||
goto at_end;
|
||||
|
||||
e = *state ? *state : h->iterate_list_head;
|
||||
e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
|
||||
|
||||
if (e->iterate_next)
|
||||
*state = e->iterate_next;
|
||||
*i = (Iterator) e->iterate_next;
|
||||
else
|
||||
*state = (void*) -1;
|
||||
*i = ITERATOR_LAST;
|
||||
|
||||
if (key)
|
||||
*key = e->key;
|
||||
@@ -275,7 +287,7 @@ void *hashmap_iterate(Hashmap *h, void **state, const void **key) {
|
||||
return e->value;
|
||||
|
||||
at_end:
|
||||
*state = (void *) -1;
|
||||
*i = ITERATOR_LAST;
|
||||
|
||||
if (key)
|
||||
*key = NULL;
|
||||
@@ -283,26 +295,26 @@ at_end:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *hashmap_iterate_backwards(Hashmap *h, void **state, const void **key) {
|
||||
void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
|
||||
struct hashmap_entry *e;
|
||||
|
||||
assert(state);
|
||||
assert(i);
|
||||
|
||||
if (!h)
|
||||
goto at_beginning;
|
||||
|
||||
if (*state == (void*) -1)
|
||||
if (*i == ITERATOR_FIRST)
|
||||
goto at_beginning;
|
||||
|
||||
if (!*state && !h->iterate_list_tail)
|
||||
if (*i == ITERATOR_LAST && !h->iterate_list_tail)
|
||||
goto at_beginning;
|
||||
|
||||
e = *state ? *state : h->iterate_list_tail;
|
||||
e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
|
||||
|
||||
if (e->iterate_previous)
|
||||
*state = e->iterate_previous;
|
||||
*i = (Iterator) e->iterate_previous;
|
||||
else
|
||||
*state = (void*) -1;
|
||||
*i = ITERATOR_FIRST;
|
||||
|
||||
if (key)
|
||||
*key = e->key;
|
||||
@@ -310,7 +322,7 @@ void *hashmap_iterate_backwards(Hashmap *h, void **state, const void **key) {
|
||||
return e->value;
|
||||
|
||||
at_beginning:
|
||||
*state = (void *) -1;
|
||||
*i = ITERATOR_FIRST;
|
||||
|
||||
if (key)
|
||||
*key = NULL;
|
||||
@@ -318,6 +330,23 @@ at_beginning:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
|
||||
unsigned hash;
|
||||
struct hashmap_entry *e;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if (!(e = hash_scan(h, hash, key)))
|
||||
return NULL;
|
||||
|
||||
*i = (Iterator) e;
|
||||
|
||||
return e->value;
|
||||
}
|
||||
|
||||
void* hashmap_first(Hashmap *h) {
|
||||
|
||||
if (!h)
|
||||
|
||||
23
hashmap.h
23
hashmap.h
@@ -11,6 +11,11 @@
|
||||
* instantiate an object for each Hashmap use. */
|
||||
|
||||
typedef struct Hashmap Hashmap;
|
||||
typedef struct _IteratorStruct _IteratorStruct;
|
||||
typedef _IteratorStruct* Iterator;
|
||||
|
||||
#define ITERATOR_FIRST ((Iterator) 0)
|
||||
#define ITERATOR_LAST ((Iterator) -1)
|
||||
|
||||
typedef unsigned (*hash_func_t)(const void *p);
|
||||
typedef int (*compare_func_t)(const void *a, const void *b);
|
||||
@@ -24,6 +29,7 @@ int trivial_compare_func(const void *a, const void *b);
|
||||
Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
|
||||
void hashmap_free(Hashmap *h);
|
||||
Hashmap *hashmap_copy(Hashmap *h);
|
||||
int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
|
||||
|
||||
int hashmap_put(Hashmap *h, const void *key, void *value);
|
||||
int hashmap_replace(Hashmap *h, const void *key, void *value);
|
||||
@@ -36,21 +42,22 @@ int hashmap_merge(Hashmap *h, Hashmap *other);
|
||||
unsigned hashmap_size(Hashmap *h);
|
||||
bool hashmap_isempty(Hashmap *h);
|
||||
|
||||
void *hashmap_iterate(Hashmap *h, void **state, const void **key);
|
||||
void *hashmap_iterate_backwards(Hashmap *h, void **state, const void **key);
|
||||
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
|
||||
void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
|
||||
void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
|
||||
|
||||
void hashmap_clear(Hashmap *h);
|
||||
void *hashmap_steal_first(Hashmap *h);
|
||||
void* hashmap_first(Hashmap *h);
|
||||
void* hashmap_last(Hashmap *h);
|
||||
|
||||
#define HASHMAP_FOREACH(e, h, state) \
|
||||
for ((state) = NULL, (e) = hashmap_iterate((h), &(state), NULL); (e); (e) = hashmap_iterate((h), &(state), NULL))
|
||||
#define HASHMAP_FOREACH(e, h, i) \
|
||||
for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
|
||||
|
||||
#define HASHMAP_FOREACH_KEY(e, k, h, state) \
|
||||
for ((state) = NULL, (e) = hashmap_iterate((h), &(state), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(state), (const void**) &(k)))
|
||||
#define HASHMAP_FOREACH_KEY(e, k, h, i) \
|
||||
for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
|
||||
|
||||
#define HASHMAP_FOREACH_BACKWARDS(e, h, state) \
|
||||
for ((state) = NULL, (e) = hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = hashmap_iterate_backwards((h), &(state), NULL))
|
||||
#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \
|
||||
for ((i) = ITERATE_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL))
|
||||
|
||||
#endif
|
||||
|
||||
71
job.c
71
job.c
@@ -276,33 +276,8 @@ bool job_type_is_conflicting(JobType a, JobType b) {
|
||||
return (a == JOB_STOP) != (b == JOB_STOP);
|
||||
}
|
||||
|
||||
bool job_type_is_applicable(JobType j, NameType n) {
|
||||
assert(j >= 0 && j < _JOB_TYPE_MAX);
|
||||
assert(n >= 0 && n < _NAME_TYPE_MAX);
|
||||
|
||||
switch (j) {
|
||||
case JOB_VERIFY_ACTIVE:
|
||||
case JOB_START:
|
||||
return true;
|
||||
|
||||
case JOB_STOP:
|
||||
case JOB_RESTART:
|
||||
case JOB_TRY_RESTART:
|
||||
return name_type_can_start(n);
|
||||
|
||||
case JOB_RELOAD:
|
||||
return name_type_can_reload(n);
|
||||
|
||||
case JOB_RELOAD_OR_START:
|
||||
return name_type_can_reload(n) && name_type_can_start(n);
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid job type");
|
||||
}
|
||||
}
|
||||
|
||||
bool job_is_runnable(Job *j) {
|
||||
void *state;
|
||||
Iterator i;
|
||||
Name *other;
|
||||
|
||||
assert(j);
|
||||
@@ -323,7 +298,7 @@ bool job_is_runnable(Job *j) {
|
||||
* dependencies, regardless whether they are
|
||||
* starting or stopping something. */
|
||||
|
||||
SET_FOREACH(other, j->name->meta.dependencies[NAME_AFTER], state)
|
||||
SET_FOREACH(other, j->name->meta.dependencies[NAME_AFTER], i)
|
||||
if (other->meta.job)
|
||||
return false;
|
||||
}
|
||||
@@ -331,7 +306,7 @@ bool job_is_runnable(Job *j) {
|
||||
/* Also, if something else is being stopped and we should
|
||||
* change state after it, then lets wait. */
|
||||
|
||||
SET_FOREACH(other, j->name->meta.dependencies[NAME_BEFORE], state)
|
||||
SET_FOREACH(other, j->name->meta.dependencies[NAME_BEFORE], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.job->type == JOB_STOP ||
|
||||
other->meta.job->type == JOB_RESTART ||
|
||||
@@ -356,12 +331,17 @@ int job_run_and_invalidate(Job *j) {
|
||||
int r;
|
||||
assert(j);
|
||||
|
||||
if (!job_is_runnable(j))
|
||||
return -EAGAIN;
|
||||
if (j->in_run_queue) {
|
||||
LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
|
||||
j->in_run_queue = false;
|
||||
}
|
||||
|
||||
if (j->state != JOB_WAITING)
|
||||
return 0;
|
||||
|
||||
if (!job_is_runnable(j))
|
||||
return -EAGAIN;
|
||||
|
||||
j->state = JOB_RUNNING;
|
||||
|
||||
switch (j->type) {
|
||||
@@ -437,16 +417,18 @@ int job_run_and_invalidate(Job *j) {
|
||||
|
||||
int job_finish_and_invalidate(Job *j, bool success) {
|
||||
Name *n;
|
||||
void *state;
|
||||
Name *other;
|
||||
NameType t;
|
||||
Iterator i;
|
||||
|
||||
assert(j);
|
||||
|
||||
/* Patch restart jobs so that they become normal start jobs */
|
||||
if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) {
|
||||
j->state = JOB_RUNNING;
|
||||
j->type = JOB_START;
|
||||
return job_run_and_invalidate(j);
|
||||
job_schedule_run(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = j->name;
|
||||
@@ -460,14 +442,14 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
t == JOB_VERIFY_ACTIVE ||
|
||||
t == JOB_RELOAD_OR_START) {
|
||||
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], state)
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.type == JOB_START ||
|
||||
other->meta.type == JOB_VERIFY_ACTIVE ||
|
||||
other->meta.type == JOB_RELOAD_OR_START))
|
||||
job_finish_and_invalidate(other->meta.job, false);
|
||||
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRED_BY], state)
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRED_BY], i)
|
||||
if (other->meta.job &&
|
||||
!other->meta.job->forced &&
|
||||
(other->meta.type == JOB_START ||
|
||||
@@ -477,7 +459,7 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
|
||||
} else if (t == JOB_STOP) {
|
||||
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state)
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i)
|
||||
if (other->meta.job &&
|
||||
(t == JOB_START ||
|
||||
t == JOB_VERIFY_ACTIVE ||
|
||||
@@ -487,12 +469,23 @@ int job_finish_and_invalidate(Job *j, bool success) {
|
||||
}
|
||||
|
||||
/* Try to start the next jobs that can be started */
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state)
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], i)
|
||||
if (other->meta.job)
|
||||
job_run_and_invalidate(other->meta.job);
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state)
|
||||
job_schedule_run(other->meta.job);
|
||||
SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], i)
|
||||
if (other->meta.job)
|
||||
job_run_and_invalidate(other->meta.job);
|
||||
job_schedule_run(other->meta.job);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void job_schedule_run(Job *j) {
|
||||
assert(j);
|
||||
assert(j->linked);
|
||||
|
||||
if (j->in_run_queue)
|
||||
return;
|
||||
|
||||
LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
|
||||
j->in_run_queue = true;
|
||||
}
|
||||
|
||||
13
job.h
13
job.h
@@ -56,9 +56,8 @@ struct JobDependency {
|
||||
|
||||
bool matters;
|
||||
|
||||
/* Linked list for the subjects, resp objects */
|
||||
JobDependency *subject_prev, *subject_next;
|
||||
JobDependency *object_prev, *object_next;
|
||||
LIST_FIELDS(JobDependency, subject);
|
||||
LIST_FIELDS(JobDependency, object);
|
||||
};
|
||||
|
||||
struct Job {
|
||||
@@ -71,11 +70,12 @@ struct Job {
|
||||
JobState state;
|
||||
|
||||
bool linked:1;
|
||||
bool in_run_queue:1;
|
||||
bool matters_to_anchor:1;
|
||||
bool forced:1;
|
||||
|
||||
/* These fields are used only while building a transaction */
|
||||
Job *transaction_next, *transaction_prev;
|
||||
LIST_FIELDS(Job, transaction);
|
||||
LIST_FIELDS(Job, run_queue);
|
||||
|
||||
JobDependency *subject_list;
|
||||
JobDependency *object_list;
|
||||
@@ -83,6 +83,7 @@ struct Job {
|
||||
/* Used for graph algs as a "I have been here" marker */
|
||||
Job* marker;
|
||||
unsigned generation;
|
||||
|
||||
};
|
||||
|
||||
Job* job_new(Manager *m, JobType type, Name *name);
|
||||
@@ -102,8 +103,8 @@ int job_type_merge(JobType *a, JobType b);
|
||||
bool job_type_is_mergeable(JobType a, JobType b);
|
||||
bool job_type_is_superset(JobType a, JobType b);
|
||||
bool job_type_is_conflicting(JobType a, JobType b);
|
||||
bool job_type_is_applicable(JobType j, NameType n);
|
||||
|
||||
void job_schedule_run(Job *j);
|
||||
int job_run_and_invalidate(Job *j);
|
||||
int job_finish_and_invalidate(Job *j, bool success);
|
||||
|
||||
|
||||
82
list.h
82
list.h
@@ -6,85 +6,95 @@
|
||||
/* The head of the linked list. Use this in the structure that shall
|
||||
* contain the head of the linked list */
|
||||
#define LIST_HEAD(t,name) \
|
||||
t *name
|
||||
t *name
|
||||
|
||||
/* The pointers in the linked list's items. Use this in the item structure */
|
||||
#define LIST_FIELDS(t) \
|
||||
t *next, *prev
|
||||
#define LIST_FIELDS(t,name) \
|
||||
t *name##_next, *name##_prev
|
||||
|
||||
/* Initialize the list's head */
|
||||
#define LIST_HEAD_INIT(t,item) \
|
||||
#define LIST_HEAD_INIT(t,head) \
|
||||
do { \
|
||||
(item) = (t*) NULL; } \
|
||||
(head) = NULL; } \
|
||||
while(false)
|
||||
|
||||
/* Initialize a list item */
|
||||
#define LIST_INIT(t,item) \
|
||||
#define LIST_INIT(t,name,item) \
|
||||
do { \
|
||||
t *_item = (item); \
|
||||
assert(_item); \
|
||||
_item->prev = _item->next = NULL; \
|
||||
_item->name##_prev = _item->name##_next = NULL; \
|
||||
} while(false)
|
||||
|
||||
/* Prepend an item to the list */
|
||||
#define LIST_PREPEND(t,head,item) \
|
||||
#define LIST_PREPEND(t,name,head,item) \
|
||||
do { \
|
||||
t **_head = &(head), *_item = (item); \
|
||||
assert(_item); \
|
||||
if ((_item->next = *_head)) \
|
||||
_item->next->prev = _item; \
|
||||
_item->prev = NULL; \
|
||||
if ((_item->name##_next = *_head)) \
|
||||
_item->name##_next->name##_prev = _item; \
|
||||
_item->name##_prev = NULL; \
|
||||
*_head = _item; \
|
||||
} while(false)
|
||||
|
||||
/* Remove an item from the list */
|
||||
#define LIST_REMOVE(t,head,item) \
|
||||
#define LIST_REMOVE(t,name,head,item) \
|
||||
do { \
|
||||
t **_head = &(head), *_item = (item); \
|
||||
assert(_item); \
|
||||
if (_item->next) \
|
||||
_item->next->prev = _item->prev; \
|
||||
if (_item->prev) \
|
||||
_item->prev->next = _item->next; \
|
||||
if (_item->name##_next) \
|
||||
_item->name##_next->name##_prev = _item->name##_prev; \
|
||||
if (_item->name##_prev) \
|
||||
_item->name##_prev->name##_next = _item->name##_next; \
|
||||
else { \
|
||||
assert(*_head == _item); \
|
||||
*_head = _item->next; \
|
||||
*_head = _item->name##_next; \
|
||||
} \
|
||||
_item->next = _item->prev = NULL; \
|
||||
_item->name##_next = _item->name##_prev = NULL; \
|
||||
} while(false)
|
||||
|
||||
/* Find the head of the list */
|
||||
#define LIST_FIND_HEAD(t,item,head) \
|
||||
#define LIST_FIND_HEAD(t,name,item,head) \
|
||||
do { \
|
||||
t **_head = (head), *_item = (item); \
|
||||
*_head = _item; \
|
||||
assert(_head); \
|
||||
while ((*_head)->prev) \
|
||||
*_head = (*_head)->prev; \
|
||||
t *_item = (item); \
|
||||
assert(_item); \
|
||||
while ((_item->name##_prev) \
|
||||
_item = _item->name##_prev; \
|
||||
(head) = _item; \
|
||||
} while (false)
|
||||
|
||||
/* Find the head of the list */
|
||||
#define LIST_FIND_TAIL(t,name,item,tail) \
|
||||
do { \
|
||||
t *_item = (item); \
|
||||
assert(_item); \
|
||||
while (_item->name##_next) \
|
||||
_item = _item->name##_next; \
|
||||
(tail) = _item; \
|
||||
} while (false)
|
||||
|
||||
/* Insert an item after another one (a = where, b = what) */
|
||||
#define LIST_INSERT_AFTER(t,head,a,b) \
|
||||
#define LIST_INSERT_AFTER(t,name,head,a,b) \
|
||||
do { \
|
||||
t **_head = &(head), *_a = (a), *_b = (b); \
|
||||
assert(_b); \
|
||||
if (!_a) { \
|
||||
if ((_b->next = *_head)) \
|
||||
_b->next->prev = _b; \
|
||||
_b->prev = NULL; \
|
||||
if ((_b->name##_next = *_head)) \
|
||||
_b->name##_next->name##_prev = _b; \
|
||||
_b->name##_prev = NULL; \
|
||||
*_head = _b; \
|
||||
} else { \
|
||||
if ((_b->next = _a->next)) \
|
||||
_b->next->prev = _b; \
|
||||
_b->prev = _a; \
|
||||
_a->next = _b; \
|
||||
if ((_b->name##_next = _a->name##_next)) \
|
||||
_b->name##_next->name##_prev = _b; \
|
||||
_b->name##_prev = _a; \
|
||||
_a->name##_next = _b; \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
#define LIST_FOREACH(i,head) \
|
||||
for (i = (head); i; i = i->next)
|
||||
#define LIST_FOREACH(name,i,head) \
|
||||
for ((i) = (head); (i); (i) = (i)->name##_next)
|
||||
|
||||
#define LIST_FOREACH_SAFE(i,n,head) \
|
||||
for (i = (head); i && ((n = i->next), 1); i = n)
|
||||
#define LIST_FOREACH_SAFE(name,i,n,head) \
|
||||
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
|
||||
|
||||
#endif
|
||||
|
||||
346
load-fragment.c
346
load-fragment.c
@@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <linux/oom.h>
|
||||
|
||||
#include "name.h"
|
||||
#include "strv.h"
|
||||
@@ -44,9 +45,8 @@ static int config_parse_deps(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!*set)
|
||||
if (!(*set = set_new(trivial_hash_func, trivial_compare_func)))
|
||||
return -ENOMEM;
|
||||
if ((r = set_ensure_allocated(set, trivial_hash_func, trivial_compare_func)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = set_put(*set, other)) < 0)
|
||||
return r;
|
||||
@@ -177,12 +177,12 @@ static int config_parse_listen(
|
||||
}
|
||||
|
||||
p->fd = -1;
|
||||
LIST_PREPEND(SocketPort, s->ports, p);
|
||||
LIST_PREPEND(SocketPort, port, s->ports, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_bind(
|
||||
static int config_parse_socket_bind(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
@@ -211,6 +211,256 @@ static int config_parse_bind(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_nice(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int *i = data, priority, r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atoi(rvalue, &priority)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse nice priority: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (priority < PRIO_MIN || priority >= PRIO_MAX) {
|
||||
log_error("[%s:%u] Nice priority out of range: %s", filename, line, rvalue);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
*i = priority;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_oom_adjust(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int *i = data, oa, r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atoi(rvalue, &oa)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse OOM adjust value: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (oa < OOM_DISABLE || oa > OOM_ADJUST_MAX) {
|
||||
log_error("[%s:%u] OOM adjust value out of range: %s", filename, line, rvalue);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
*i = oa;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_umask(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
mode_t *m = data;
|
||||
long l;
|
||||
char *x = NULL;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
errno = 0;
|
||||
l = strtol(rvalue, &x, 8);
|
||||
if (!x || *x || errno) {
|
||||
log_error("[%s:%u] Failed to parse umask value: %s", filename, line, rvalue);
|
||||
return errno ? -errno : -EINVAL;
|
||||
}
|
||||
|
||||
if (l < 0000 || l > 0777) {
|
||||
log_error("[%s:%u] umask value out of range: %s", filename, line, rvalue);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
*m = (mode_t) l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_exec(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
ExecCommand **e = data, *ee, *nce = NULL;
|
||||
char **n;
|
||||
char *w;
|
||||
unsigned k;
|
||||
size_t l;
|
||||
char *state;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
k = 0;
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
k++;
|
||||
|
||||
if (!(n = new(char*, k+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
if (!(n[k++] = strndup(w, l)))
|
||||
goto fail;
|
||||
|
||||
n[k] = NULL;
|
||||
|
||||
if (!n[0] || n[0][0] != '/') {
|
||||
log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
|
||||
strv_free(n);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(nce = new0(ExecCommand, 1)))
|
||||
goto fail;
|
||||
|
||||
nce->argv = n;
|
||||
if (!(nce->path = strdup(n[0])))
|
||||
goto fail;
|
||||
|
||||
if (*e) {
|
||||
/* It's kinda important that we keep the order here */
|
||||
LIST_FIND_TAIL(ExecCommand, command, *e, ee);
|
||||
LIST_INSERT_AFTER(ExecCommand, command, *e, ee, nce);
|
||||
} else
|
||||
*e = nce;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (; k > 0; k--)
|
||||
free(n[k-1]);
|
||||
free(n);
|
||||
|
||||
free(nce);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int config_parse_usec(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
usec_t *usec = data;
|
||||
unsigned long long u;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atollu(rvalue, &u)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse time value: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* We actually assume the user configures seconds. Later on we
|
||||
* might choose to support suffixes for time values, to
|
||||
* configure bigger or smaller units */
|
||||
|
||||
*usec = u * USEC_PER_SEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_service_type(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Service *s = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(rvalue, "forking"))
|
||||
s->type = SERVICE_FORKING;
|
||||
else if (streq(rvalue, "simple"))
|
||||
s->type = SERVICE_SIMPLE;
|
||||
else {
|
||||
log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_service_restart(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Service *s = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(rvalue, "once"))
|
||||
s->restart = SERVICE_ONCE;
|
||||
else if (streq(rvalue, "on-success"))
|
||||
s->type = SERVICE_RESTART_ON_SUCCESS;
|
||||
else if (streq(rvalue, "always"))
|
||||
s->type = SERVICE_RESTART_ALWAYS;
|
||||
else {
|
||||
log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int name_load_fragment(Name *n) {
|
||||
|
||||
static const char* const section_table[_NAME_TYPE_MAX] = {
|
||||
@@ -224,32 +474,64 @@ int name_load_fragment(Name *n) {
|
||||
[NAME_SNAPSHOT] = "Snapshot"
|
||||
};
|
||||
|
||||
#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
|
||||
{ "Directory", config_parse_path, &(context).directory, section }, \
|
||||
{ "User", config_parse_string, &(context).user, section }, \
|
||||
{ "Group", config_parse_string, &(context).group, section }, \
|
||||
{ "SupplementaryGroups", config_parse_strv, &(context).supplementary_groups, section }, \
|
||||
{ "Nice", config_parse_nice, &(context).nice, section }, \
|
||||
{ "OOMAdjust", config_parse_oom_adjust, &(context).oom_adjust, section }, \
|
||||
{ "UMask", config_parse_umask, &(context).umask, section }, \
|
||||
{ "Environment", config_parse_strv, &(context).environment, section }
|
||||
|
||||
const ConfigItem items[] = {
|
||||
{ "Names", config_parse_names, &n->meta.names, "Meta" },
|
||||
{ "Description", config_parse_string, &n->meta.description, "Meta" },
|
||||
{ "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" },
|
||||
{ "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" },
|
||||
{ "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" },
|
||||
{ "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" },
|
||||
{ "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" },
|
||||
{ "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" },
|
||||
{ "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" },
|
||||
{ "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" },
|
||||
{ "ListenStream", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "ListenDatagram", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "ListenSequentialPacket", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "ListenFIFO", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "BindIPv6Only", config_parse_bind, &n->socket, "Socket" },
|
||||
{ "Backlog", config_parse_unsigned, &n->socket.backlog, "Socket" },
|
||||
{ "Names", config_parse_names, &n->meta.names, "Meta" },
|
||||
{ "Description", config_parse_string, &n->meta.description, "Meta" },
|
||||
{ "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" },
|
||||
{ "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" },
|
||||
{ "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" },
|
||||
{ "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" },
|
||||
{ "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" },
|
||||
{ "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" },
|
||||
{ "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" },
|
||||
{ "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" },
|
||||
|
||||
{ "PIDFile", config_parse_path, &n->service.pid_file, "Service" },
|
||||
{ "ExecStartPre", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_START_PRE], "Service" },
|
||||
{ "ExecStart", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_START], "Service" },
|
||||
{ "ExecStartPost", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_START_POST], "Service" },
|
||||
{ "ExecReload", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_RELOAD], "Service" },
|
||||
{ "ExecStop", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_STOP], "Service" },
|
||||
{ "ExecStopPost", config_parse_exec, &n->service.exec_command[SERVICE_EXEC_STOP_POST], "Service" },
|
||||
{ "RestartSec", config_parse_usec, &n->service.restart_usec, "Service" },
|
||||
{ "TimeoutSec", config_parse_usec, &n->service.timeout_usec, "Service" },
|
||||
{ "Type", config_parse_service_type, &n->service, "Service" },
|
||||
{ "Restart", config_parse_service_restart, &n->service, "Service" },
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(n->service.exec_context, "Service"),
|
||||
|
||||
{ "ListenStream", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "ListenDatagram", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "ListenSequentialPacket", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "ListenFIFO", config_parse_listen, &n->socket, "Socket" },
|
||||
{ "BindIPv6Only", config_parse_socket_bind, &n->socket, "Socket" },
|
||||
{ "Backlog", config_parse_unsigned, &n->socket.backlog, "Socket" },
|
||||
{ "ExecStartPre", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_START_PRE], "Socket" },
|
||||
{ "ExecStartPost", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_START_POST], "Socket" },
|
||||
{ "ExecStopPre", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_STOP_PRE], "Socket" },
|
||||
{ "ExecStopPost", config_parse_exec, &n->service.exec_command[SOCKET_EXEC_STOP_POST], "Socket" },
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(n->socket.exec_context, "Socket"),
|
||||
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(n->automount.exec_context, "Automount"),
|
||||
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
const
|
||||
#undef EXEC_CONTEXT_CONFIG_ITEMS
|
||||
|
||||
char *t;
|
||||
int r;
|
||||
void *state;
|
||||
const char *sections[3];
|
||||
Iterator i;
|
||||
|
||||
assert(n);
|
||||
assert(n->meta.load_state == NAME_STUB);
|
||||
@@ -258,12 +540,18 @@ int name_load_fragment(Name *n) {
|
||||
sections[1] = section_table[n->meta.type];
|
||||
sections[2] = NULL;
|
||||
|
||||
SET_FOREACH(t, n->meta.names, state)
|
||||
if ((r = config_parse(t, sections, items, n)) < 0)
|
||||
goto fail;
|
||||
SET_FOREACH(t, n->meta.names, i) {
|
||||
|
||||
r = 0;
|
||||
/* Try to find a name we can load this with */
|
||||
if ((r = config_parse(t, sections, items, n)) == -ENOENT)
|
||||
continue;
|
||||
|
||||
fail:
|
||||
return r;
|
||||
/* Yay, we succeeded! Now let's call this our identifier */
|
||||
if (r == 0)
|
||||
n->meta.id = t;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
2
macro.h
2
macro.h
@@ -76,4 +76,6 @@ static inline size_t ALIGN(size_t l) {
|
||||
#define memzero(x,l) (memset((x), 0, (l)))
|
||||
#define zero(x) (memzero(&(x), sizeof(x)))
|
||||
|
||||
#define char_array_0(x) x[sizeof(x)-1] = 0;
|
||||
|
||||
#endif
|
||||
|
||||
2
main.c
2
main.c
@@ -42,8 +42,6 @@ int main(int argc, char *argv[]) {
|
||||
printf("- By jobs:\n");
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
manager_run_jobs(m);
|
||||
|
||||
manager_loop(m);
|
||||
|
||||
retval = 0;
|
||||
|
||||
13
manager.h
13
manager.h
@@ -8,6 +8,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct Manager Manager;
|
||||
typedef enum ManagerEventType ManagerEventType;
|
||||
|
||||
#include "name.h"
|
||||
#include "job.h"
|
||||
@@ -15,6 +16,12 @@ typedef struct Manager Manager;
|
||||
#include "list.h"
|
||||
#include "set.h"
|
||||
|
||||
enum ManagerEventType {
|
||||
MANAGER_SIGNAL,
|
||||
MANAGER_FD,
|
||||
MANAGER_TIMER
|
||||
};
|
||||
|
||||
struct Manager {
|
||||
uint32_t current_job_id;
|
||||
|
||||
@@ -29,11 +36,15 @@ struct Manager {
|
||||
/* Names that need to be loaded */
|
||||
LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */
|
||||
|
||||
/* Jobs that need to be run */
|
||||
LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */
|
||||
|
||||
/* Jobs to be added */
|
||||
Hashmap *transaction_jobs; /* Name object => Job object list 1:1 */
|
||||
JobDependency *transaction_anchor;
|
||||
|
||||
bool dispatching_load_queue:1;
|
||||
bool dispatching_run_queue:1;
|
||||
|
||||
Hashmap *watch_pids; /* pid => Name object n:1 */
|
||||
|
||||
@@ -57,7 +68,7 @@ void manager_transaction_unlink_job(Manager *m, Job *j);
|
||||
|
||||
void manager_clear_jobs(Manager *m);
|
||||
|
||||
void manager_run_jobs(Manager *m);
|
||||
void manager_dispatch_run_queue(Manager *m);
|
||||
int manager_loop(Manager *m);
|
||||
|
||||
#endif
|
||||
|
||||
22
milestone.c
22
milestone.c
@@ -4,11 +4,7 @@
|
||||
#include "milestone.h"
|
||||
#include "load-fragment.h"
|
||||
|
||||
static NameActiveState milestone_active_state(Name *n) {
|
||||
return MILESTONE(n)->state == MILESTONE_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
|
||||
}
|
||||
|
||||
static void milestone_free_hook(Name *n) {
|
||||
static void milestone_done(Name *n) {
|
||||
Milestone *m = MILESTONE(n);
|
||||
|
||||
assert(m);
|
||||
@@ -16,17 +12,15 @@ static void milestone_free_hook(Name *n) {
|
||||
/* Nothing here for now */
|
||||
}
|
||||
|
||||
static NameActiveState milestone_active_state(Name *n) {
|
||||
return MILESTONE(n)->state == MILESTONE_DEAD ? NAME_INACTIVE : NAME_ACTIVE;
|
||||
}
|
||||
|
||||
const NameVTable milestone_vtable = {
|
||||
.suffix = ".milestone",
|
||||
|
||||
.load = name_load_fragment,
|
||||
.dump = NULL,
|
||||
.init = name_load_fragment,
|
||||
.done = milestone_done,
|
||||
|
||||
.start = NULL,
|
||||
.stop = NULL,
|
||||
.reload = NULL,
|
||||
|
||||
.active_state = milestone_active_state,
|
||||
|
||||
.free_hook = milestone_free_hook
|
||||
.active_state = milestone_active_state
|
||||
};
|
||||
|
||||
26
mount.c
26
mount.c
@@ -8,7 +8,7 @@
|
||||
#include "load-fstab.h"
|
||||
#include "load-dropin.h"
|
||||
|
||||
static int mount_load(Name *n) {
|
||||
static int mount_init(Name *n) {
|
||||
int r;
|
||||
Mount *m = MOUNT(n);
|
||||
|
||||
@@ -29,6 +29,13 @@ static int mount_load(Name *n) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static void mount_done(Name *n) {
|
||||
Mount *d = MOUNT(n);
|
||||
|
||||
assert(d);
|
||||
free(d->path);
|
||||
}
|
||||
|
||||
static void mount_dump(Name *n, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const state_table[_MOUNT_STATE_MAX] = {
|
||||
@@ -63,24 +70,13 @@ static NameActiveState mount_active_state(Name *n) {
|
||||
return table[MOUNT(n)->state];
|
||||
}
|
||||
|
||||
static void mount_free_hook(Name *n) {
|
||||
Mount *d = MOUNT(n);
|
||||
|
||||
assert(d);
|
||||
free(d->path);
|
||||
}
|
||||
|
||||
const NameVTable mount_vtable = {
|
||||
.suffix = ".mount",
|
||||
|
||||
.load = mount_load,
|
||||
.init = mount_init,
|
||||
.done = mount_done,
|
||||
|
||||
.dump = mount_dump,
|
||||
|
||||
.start = NULL,
|
||||
.stop = NULL,
|
||||
.reload = NULL,
|
||||
|
||||
.active_state = mount_active_state,
|
||||
|
||||
.free_hook = mount_free_hook
|
||||
};
|
||||
|
||||
36
name.h
36
name.h
@@ -21,8 +21,11 @@ typedef enum NameDependency NameDependency;
|
||||
#include "list.h"
|
||||
#include "socket-util.h"
|
||||
#include "execute.h"
|
||||
#include "util.h"
|
||||
|
||||
#define NAME_MAX 32
|
||||
#define DEFAULT_TIMEOUT_USEC (20*USEC_PER_SEC)
|
||||
#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
|
||||
|
||||
enum NameType {
|
||||
NAME_SERVICE = 0,
|
||||
@@ -90,6 +93,8 @@ struct Meta {
|
||||
NameType type;
|
||||
NameLoadState load_state;
|
||||
|
||||
char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
|
||||
|
||||
Set *names;
|
||||
Set *dependencies[_NAME_DEPENDENCY_MAX];
|
||||
|
||||
@@ -100,9 +105,13 @@ struct Meta {
|
||||
Job *job;
|
||||
|
||||
bool linked:1;
|
||||
bool in_load_queue:1;
|
||||
|
||||
usec_t active_enter_timestamp;
|
||||
usec_t active_exit_timestamp;
|
||||
|
||||
/* Load queue */
|
||||
LIST_FIELDS(Meta);
|
||||
LIST_FIELDS(Meta, load_queue);
|
||||
};
|
||||
|
||||
#include "service.h"
|
||||
@@ -129,21 +138,27 @@ union Name {
|
||||
struct NameVTable {
|
||||
const char *suffix;
|
||||
|
||||
int (*load)(Name *n);
|
||||
int (*init)(Name *n);
|
||||
void (*done)(Name *n);
|
||||
|
||||
void (*dump)(Name *n, FILE *f, const char *prefix);
|
||||
|
||||
int (*start)(Name *n);
|
||||
int (*stop)(Name *n);
|
||||
int (*reload)(Name *n);
|
||||
|
||||
|
||||
bool (*can_reload)(Name *n);
|
||||
|
||||
/* Boils down the more complex internal state of this name to
|
||||
* a simpler one that the engine can understand */
|
||||
NameActiveState (*active_state)(Name *n);
|
||||
|
||||
void (*fd_event)(Name *n, int fd, uint32_t events);
|
||||
void (*sigchld_event)(Name *n, pid_t pid, int code, int status);
|
||||
void (*timer_event)(Name *n, int id, uint64_t n_elapsed);
|
||||
|
||||
void (*free_hook)(Name *n);
|
||||
void (*retry)(Name *n);
|
||||
};
|
||||
|
||||
extern const NameVTable * const name_vtable[_NAME_TYPE_MAX];
|
||||
@@ -171,10 +186,10 @@ DEFINE_CAST(MOUNT, Mount);
|
||||
DEFINE_CAST(AUTOMOUNT, Automount);
|
||||
DEFINE_CAST(SNAPSHOT, Snapshot);
|
||||
|
||||
NameActiveState name_active_state(Name *name);
|
||||
|
||||
bool name_type_can_start(NameType t);
|
||||
bool name_type_can_reload(NameType t);
|
||||
bool name_can_reload(Name *n);
|
||||
#define name_can_start(n) name_type_can_start((n)->meta.type)
|
||||
|
||||
NameType name_type_from_string(const char *n);
|
||||
bool name_is_valid(const char *n);
|
||||
@@ -190,6 +205,10 @@ int name_load(Name *name);
|
||||
const char* name_id(Name *n);
|
||||
const char *name_description(Name *n);
|
||||
|
||||
int name_add_name(Name *n, const char *text);
|
||||
|
||||
NameActiveState name_active_state(Name *name);
|
||||
|
||||
void name_dump(Name *n, FILE *f, const char *prefix);
|
||||
|
||||
int name_start(Name *n);
|
||||
@@ -204,4 +223,11 @@ void name_unwatch_fd(Name *n, int fd);
|
||||
int name_watch_pid(Name *n, pid_t pid);
|
||||
void name_unwatch_pid(Name *n, pid_t pid);
|
||||
|
||||
int name_watch_timer(Name *n, usec_t delay, int *id);
|
||||
void name_unwatch_timer(Name *n, int *id);
|
||||
|
||||
char *name_change_suffix(const char *t, const char *suffix);
|
||||
|
||||
bool name_job_is_applicable(Name *n, JobType j);
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user