diff options
-rw-r--r-- | conf-parser.c | 1 | ||||
-rw-r--r-- | execute.c | 69 | ||||
-rw-r--r-- | execute.h | 4 | ||||
-rw-r--r-- | job.c | 47 | ||||
-rw-r--r-- | job.h | 4 | ||||
-rw-r--r-- | load-fragment.c | 25 | ||||
-rw-r--r-- | main.c | 6 | ||||
-rw-r--r-- | manager.c | 11 | ||||
-rw-r--r-- | name.c | 146 | ||||
-rw-r--r-- | name.h | 8 | ||||
-rw-r--r-- | service.c | 171 | ||||
-rw-r--r-- | socket.c | 105 | ||||
-rw-r--r-- | socket.h | 3 | ||||
-rw-r--r-- | test1/postfix.service | 2 | ||||
-rw-r--r-- | test1/postfix.socket | 2 | ||||
-rw-r--r-- | test1/syslog.service | 2 | ||||
-rw-r--r-- | test1/syslog.socket | 2 | ||||
-rw-r--r-- | util.c | 30 | ||||
-rw-r--r-- | util.h | 5 |
19 files changed, 422 insertions, 221 deletions
diff --git a/conf-parser.c b/conf-parser.c index 2618858331..c7f48eafef 100644 --- a/conf-parser.c +++ b/conf-parser.c @@ -12,7 +12,6 @@ #include "strv.h" #include "log.h" -#define WHITESPACE " \t\n" #define COMMENTS "#;\n" #define LINE_MAX 4096 @@ -5,6 +5,7 @@ #include <errno.h> #include <fcntl.h> #include <unistd.h> +#include <string.h> #include "execute.h" #include "strv.h" @@ -235,7 +236,7 @@ void exec_command_free_list(ExecCommand *c) { LIST_REMOVE(ExecCommand, command, c, i); free(i->path); - free(i->argv); + strv_free(i->argv); free(i); } } @@ -276,3 +277,69 @@ void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) { s->status = status; s->timestamp = now(CLOCK_REALTIME); } + +char *exec_command_line(ExecCommand *c) { + size_t k; + char *n, *p, **a; + bool first = true; + + assert(c); + assert(c->argv); + + k = 0; + STRV_FOREACH(a, c->argv) + k += strlen(*a)+3; + + if (!(n = new(char, k))) + return NULL; + + p = n; + STRV_FOREACH(a, c->argv) { + + if (!first) + *(p++) = ' '; + else + first = false; + + if (strpbrk(*a, WHITESPACE)) { + *(p++) = '\''; + p = stpcpy(p, *a); + *(p++) = '\''; + } else + p = stpcpy(p, *a); + + } + + /* FIXME: this doesn't really handle arguments that have + * spaces and ticks in them */ + + return n; +} + +void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { + char *cmd; + + assert(c); + assert(f); + + if (!prefix) + prefix = ""; + + cmd = exec_command_line(c); + + fprintf(f, + "%sCommand Line: %s\n", + prefix, cmd ? cmd : strerror(ENOMEM)); + + free(cmd); +} + +void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) { + assert(f); + + if (!prefix) + prefix = ""; + + LIST_FOREACH(command, c, c) + exec_command_dump(c, f, prefix); +} @@ -79,6 +79,10 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, void exec_command_free_list(ExecCommand *c); void exec_command_free_array(ExecCommand **c, unsigned n); +char *exec_command_line(ExecCommand *c); +void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix); +void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix); + void exec_context_init(ExecContext *c); void exec_context_done(ExecContext *c); void exec_context_dump(ExecContext *c, FILE* f, const char *prefix); @@ -61,22 +61,12 @@ JobDependency* job_dependency_new(Job *subject, Job *object, bool matters) { l->object = object; l->matters = matters; - if (subject) { - l->subject_next = subject->subject_list; - subject->subject_list = l; - } else { - l->subject_next = object->manager->transaction_anchor; - object->manager->transaction_anchor = l; - } - - if (l->subject_next) - l->subject_next->subject_prev = l; - l->subject_prev = NULL; + if (subject) + LIST_PREPEND(JobDependency, subject, subject->subject_list, l); + else + LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l); - if ((l->object_next = object->object_list)) - l->object_next->object_prev = l; - l->object_prev = NULL; - object->object_list = l; + LIST_PREPEND(JobDependency, object, object->object_list, l); return l; } @@ -84,23 +74,12 @@ JobDependency* job_dependency_new(Job *subject, Job *object, bool matters) { void job_dependency_free(JobDependency *l) { assert(l); - if (l->subject_prev) - l->subject_prev->subject_next = l->subject_next; - else if (l->subject) - l->subject->subject_list = l->subject_next; - else - l->object->manager->transaction_anchor = l->subject_next; - - if (l->subject_next) - l->subject_next->subject_prev = l->subject_prev; - - if (l->object_prev) - l->object_prev->object_next = l->object_next; + if (l->subject) + LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l); else - l->object->object_list = l->object_next; + LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l); - if (l->object_next) - l->object_next->object_prev = l->object_prev; + LIST_REMOVE(JobDependency, object, l->object->object_list, l); free(l); } @@ -110,7 +89,7 @@ void job_dependency_delete(Job *subject, Job *object, bool *matters) { assert(object); - for (l = object->object_list; l; l = l->object_next) { + LIST_FOREACH(object, l, object->object_list) { assert(l->object == object); if (l->subject == subject) @@ -158,7 +137,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) { assert(f); fprintf(f, - "%sJob %u:\n" + "%s→ Job %u:\n" "%s\tAction: %s → %s\n" "%s\tState: %s\n" "%s\tForced: %s\n", @@ -173,7 +152,7 @@ bool job_is_anchor(Job *j) { assert(j); - for (l = j->object_list; l; l = l->object_next) + LIST_FOREACH(object, l, j->object_list) if (!l->subject) return true; @@ -401,7 +380,7 @@ int job_run_and_invalidate(Job *j) { } default: - ; + assert_not_reached("Unknown job type"); } if (r == -EALREADY) @@ -77,8 +77,8 @@ struct Job { LIST_FIELDS(Job, transaction); LIST_FIELDS(Job, run_queue); - JobDependency *subject_list; - JobDependency *object_list; + LIST_HEAD(JobDependency, subject_list); + LIST_HEAD(JobDependency, object_list); /* Used for graph algs as a "I have been here" marker */ Job* marker; diff --git a/load-fragment.c b/load-fragment.c index 5570ae5913..aa1ae7867c 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -20,7 +20,7 @@ static int config_parse_deps( void *data, void *userdata) { - Set **set = data; + NameDependency d = PTR_TO_UINT(data); Name *name = userdata; char *w; size_t l; @@ -29,7 +29,6 @@ static int config_parse_deps( assert(filename); assert(lvalue); assert(rvalue); - assert(data); FOREACH_WORD(w, &l, rvalue, state) { char *t; @@ -45,10 +44,7 @@ static int config_parse_deps( if (r < 0) return r; - if ((r = set_ensure_allocated(set, trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if ((r = set_put(*set, other)) < 0) + if ((r = name_add_dependency(name, d, other)) < 0) return r; } @@ -333,6 +329,7 @@ static int config_parse_exec( if (!(n = new(char*, k+1))) return -ENOMEM; + k = 0; FOREACH_WORD_QUOTED(w, l, rvalue, state) if (!(n[k++] = strndup(w, l))) goto fail; @@ -487,14 +484,14 @@ int name_load_fragment(Name *n) { 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" }, + { "Requires", config_parse_deps, UINT_TO_PTR(NAME_REQUIRES), "Meta" }, + { "SoftRequires", config_parse_deps, UINT_TO_PTR(NAME_SOFT_REQUIRES), "Meta" }, + { "Wants", config_parse_deps, UINT_TO_PTR(NAME_WANTS), "Meta" }, + { "Requisite", config_parse_deps, UINT_TO_PTR(NAME_REQUISITE), "Meta" }, + { "SoftRequisite", config_parse_deps, UINT_TO_PTR(NAME_SOFT_REQUISITE), "Meta" }, + { "Conflicts", config_parse_deps, UINT_TO_PTR(NAME_CONFLICTS), "Meta" }, + { "Before", config_parse_deps, UINT_TO_PTR(NAME_BEFORE), "Meta" }, + { "After", config_parse_deps, UINT_TO_PTR(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" }, @@ -36,13 +36,13 @@ int main(int argc, char *argv[]) { goto finish; } - printf("- By names:\n"); + printf("→ By names:\n"); manager_dump_names(m, stdout, "\t"); - printf("- By jobs:\n"); + printf("→ By jobs:\n"); manager_dump_jobs(m, stdout, "\t"); - manager_loop(m); + /* manager_loop(m); */ retval = 0; @@ -134,7 +134,12 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi * indirectly a dependency of the anchor job via paths that * are fully marked as mattering. */ - for (l = j ? j->subject_list : m->transaction_anchor; l; l = l->subject_next) { + if (j) + l = j->subject_list; + else + l = m->transaction_anchor; + + LIST_FOREACH(subject, l, l) { /* This link does not matter */ if (!l->matters) @@ -169,7 +174,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job /* Patch us in as new owner of the JobDependency objects */ last = NULL; - for (l = other->subject_list; l; l = l->subject_next) { + LIST_FOREACH(subject, l, other->subject_list) { assert(l->subject == other); l->subject = j; last = l; @@ -185,7 +190,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job /* Patch us in as new owner of the JobDependency objects */ last = NULL; - for (l = other->object_list; l; l = l->object_next) { + LIST_FOREACH(object, l, other->object_list) { assert(l->object == other); l->object = j; last = l; @@ -13,6 +13,7 @@ #include "strv.h" #include "load-fragment.h" #include "load-dropin.h" +#include "log.h" const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { [NAME_SERVICE] = &service_vtable, @@ -183,11 +184,13 @@ static void bidi_set_free(Name *name, Set *s) { /* Frees the set and makes sure we are dropped from the * inverse pointers */ - SET_FOREACH(other, s, i) { - NameDependency d; + if (name->meta.linked) { + SET_FOREACH(other, s, i) { + NameDependency d; - for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) - set_remove(other->meta.dependencies[d], name); + for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) + set_remove(other->meta.dependencies[d], name); + } } set_free(s); @@ -211,6 +214,10 @@ void name_free(Name *name) { LIST_REMOVE(Meta, load_queue, name->meta.manager->load_queue, &name->meta); } + if (name->meta.load_state == NAME_LOADED) + if (NAME_VTABLE(name)->done) + NAME_VTABLE(name)->done(name); + /* Free data and next 'smaller' objects */ if (name->meta.job) job_free(name->meta.job); @@ -218,9 +225,6 @@ void name_free(Name *name) { for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) bidi_set_free(name, name->meta.dependencies[d]); - if (NAME_VTABLE(name)->done) - NAME_VTABLE(name)->done(name); - free(name->meta.description); while ((t = set_steal_first(name->meta.names))) @@ -239,21 +243,6 @@ NameActiveState name_active_state(Name *name) { return NAME_VTABLE(name)->active_state(name); } -static int ensure_in_set(Set **s, void *data) { - int r; - - assert(s); - assert(data); - - if ((r = set_ensure_allocated(s, trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if ((r = set_put(*s, data)) < 0) - return r; - - return 0; -} - static int ensure_merge(Set **s, Set *other) { if (!other) @@ -292,6 +281,7 @@ int name_merge(Name *name, Name *other) { /* Merge dependencies */ for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) + /* fixme, the inverse mapping is missing */ if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0) return r; @@ -307,46 +297,6 @@ int name_merge(Name *name, Name *other) { return 0; } -/* FIXME: Does not rollback on failure! */ -static int augment(Name *n) { - int r; - Iterator i; - Name *other; - - assert(n); - - /* Adds in the missing links to make all dependencies - * bidirectional. */ - - SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n)) < 0) - return r; - SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n)) < 0) - return r; - - SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n)) < 0) - return r; - - SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0) - return r; - SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0) - return r; - - SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_SOFT_REQUIRED_BY], n)) < 0) - return r; - - SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], i) - if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n)) < 0) - return r; - - return 0; -} - int name_sanitize(Name *n) { NameDependency d; @@ -356,7 +306,7 @@ int name_sanitize(Name *n) { for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) set_remove(n->meta.dependencies[d], n); - return augment(n); + return 0; } const char* name_id(Name *n) { @@ -409,14 +359,18 @@ void name_dump(Name *n, FILE *f, const char *prefix) { char *t; NameDependency d; Iterator i; + char *prefix2; assert(n); if (!prefix) prefix = ""; + prefix2 = strappend(prefix, "\t"); + if (!prefix2) + prefix2 = ""; fprintf(f, - "%sName %s:\n" + "%s→ Name %s:\n" "%s\tDescription: %s\n" "%s\tName Load State: %s\n" "%s\tName Active State: %s\n", @@ -439,19 +393,12 @@ void name_dump(Name *n, FILE *f, const char *prefix) { } if (NAME_VTABLE(n)->dump) - NAME_VTABLE(n)->dump(n, f, prefix); + NAME_VTABLE(n)->dump(n, f, prefix2); - if (n->meta.job) { - char *p; - - if (asprintf(&p, "%s\t", prefix) >= 0) - prefix = p; - else - p = NULL; + if (n->meta.job) + job_dump(n->meta.job, f, prefix2); - job_dump(n->meta.job, f, prefix); - free(p); - } + free(prefix2); } static int verify_type(Name *name) { @@ -521,14 +468,18 @@ int name_load(Name *name) { goto fail; if ((r = name_sanitize(name)) < 0) - goto fail; + goto fail_undo_init; if ((r = name_link_names(name, false)) < 0) - goto fail; + goto fail_undo_init; name->meta.load_state = NAME_LOADED; return 0; +fail_undo_init: + if (NAME_VTABLE(name)->done) + NAME_VTABLE(name)->done(name); + fail: name->meta.load_state = NAME_FAILED; return r; @@ -890,7 +841,7 @@ char *name_change_suffix(const char *t, const char *suffix) { return NULL; memcpy(n, t, a); - memcpy(n+a, t, b+1); + memcpy(n+a, suffix, b+1); return n; } @@ -919,3 +870,42 @@ bool name_job_is_applicable(Name *n, JobType j) { assert_not_reached("Invalid job type"); } } + +int name_add_dependency(Name *n, NameDependency d, Name *other) { + + static const NameDependency inverse_table[_NAME_DEPENDENCY_MAX] = { + [NAME_REQUIRES] = NAME_REQUIRED_BY, + [NAME_SOFT_REQUIRES] = NAME_SOFT_REQUIRED_BY, + [NAME_WANTS] = NAME_WANTED_BY, + [NAME_REQUISITE] = NAME_REQUIRED_BY, + [NAME_SOFT_REQUISITE] = NAME_SOFT_REQUIRED_BY, + [NAME_REQUIRED_BY] = _NAME_DEPENDENCY_INVALID, + [NAME_SOFT_REQUIRED_BY] = _NAME_DEPENDENCY_INVALID, + [NAME_WANTED_BY] = _NAME_DEPENDENCY_INVALID, + [NAME_CONFLICTS] = NAME_CONFLICTS, + [NAME_BEFORE] = NAME_AFTER, + [NAME_AFTER] = NAME_BEFORE + }; + int r; + + assert(n); + assert(d >= 0 && d < _NAME_DEPENDENCY_MAX); + assert(inverse_table[d] != _NAME_DEPENDENCY_INVALID); + assert(other); + + if ((r = set_ensure_allocated(&n->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) + return r; + + if ((r = set_ensure_allocated(&other->meta.dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0) + return r; + + if ((r = set_put(n->meta.dependencies[d], other)) < 0) + return r; + + if ((r = set_put(other->meta.dependencies[inverse_table[d]], n)) < 0) { + set_remove(n->meta.dependencies[d], other); + return r; + } + + return 0; +} @@ -75,6 +75,8 @@ enum NameDependency { NAME_WANTS, NAME_REQUISITE, NAME_SOFT_REQUISITE, + + /* Inverse of the above */ NAME_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */ NAME_SOFT_REQUIRED_BY, /* inverse of 'soft_requires' and 'soft_requisite' is 'soft_required_by' */ NAME_WANTED_BY, /* inverse of 'wants' */ @@ -85,7 +87,9 @@ enum NameDependency { /* Order */ NAME_BEFORE, /* inverse of before is after and vice versa */ NAME_AFTER, - _NAME_DEPENDENCY_MAX + + _NAME_DEPENDENCY_MAX, + _NAME_DEPENDENCY_INVALID = -1 }; struct Meta { @@ -230,4 +234,6 @@ char *name_change_suffix(const char *t, const char *suffix); bool name_job_is_applicable(Name *n, JobType j); +int name_add_dependency(Name *n, NameDependency d, Name *other); + #endif @@ -26,6 +26,33 @@ static const NameActiveState state_table[_SERVICE_STATE_MAX] = { [SERVICE_AUTO_RESTART] = NAME_ACTIVATING, }; +static void service_done(Name *n) { + Service *s = SERVICE(n); + + assert(s); + + free(s->pid_file); + s->pid_file = NULL; + + exec_context_done(&s->exec_context); + exec_command_free_array(s->exec_command, _SERVICE_EXEC_MAX); + s->control_command = NULL; + + /* This will leak a process, but at least no memory or any of + * our resources */ + if (s->main_pid > 0) { + name_unwatch_pid(n, s->main_pid); + s->main_pid = 0; + } + + if (s->control_pid > 0) { + name_unwatch_pid(n, s->control_pid); + s->control_pid = 0; + } + + name_unwatch_timer(n, &s->timer_id); +} + static int service_load_sysv(Service *s) { assert(s); @@ -63,41 +90,18 @@ static int service_init(Name *n) { if (r == -ENOENT) r = service_load_sysv(s); - if (r < 0) + if (r < 0) { + service_done(n); return r; + } /* Load dropin directory data */ - if ((r = name_load_dropin(n)) < 0) + if ((r = name_load_dropin(n)) < 0) { + service_done(n); return r; - - return 0; -} - -static void service_done(Name *n) { - Service *s = SERVICE(n); - - assert(s); - - free(s->pid_file); - s->pid_file = NULL; - - exec_context_done(&s->exec_context); - exec_command_free_array(s->exec_command, _SERVICE_EXEC_MAX); - s->control_command = NULL; - - /* This will leak a process, but at least no memory or any of - * our resources */ - if (s->main_pid > 0) { - name_unwatch_pid(n, s->main_pid); - s->main_pid = 0; - } - - if (s->control_pid > 0) { - name_unwatch_pid(n, s->control_pid); - s->control_pid = 0; } - name_unwatch_timer(n, &s->timer_id); + return 0; } static void service_dump(Name *n, FILE *f, const char *prefix) { @@ -130,9 +134,14 @@ static void service_dump(Name *n, FILE *f, const char *prefix) { ServiceExecCommand c; Service *s = SERVICE(n); + char *prefix2; assert(s); + prefix2 = strappend(prefix, "\t"); + if (!prefix2) + prefix2 = ""; + fprintf(f, "%sService State: %s\n", prefix, state_table[s->state]); @@ -146,11 +155,17 @@ static void service_dump(Name *n, FILE *f, const char *prefix) { exec_context_dump(&s->exec_context, f, prefix); for (c = 0; c < _SERVICE_EXEC_MAX; c++) { - ExecCommand *i; - LIST_FOREACH(command, i, s->exec_command[c]) - fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path); + if (!s->exec_command[c]) + continue; + + fprintf(f, "%s→ %s:\n", + prefix, command_table[c]); + + exec_command_dump_list(s->exec_command[c], f, prefix2); } + + free(prefix2); } static int service_load_pid_file(Service *s) { @@ -240,32 +255,106 @@ static void service_set_state(Service *s, ServiceState state) { name_notify(NAME(s), state_table[old_state], state_table[s->state]); } -static int service_spawn(Service *s, ExecCommand *c, bool timeout, pid_t *_pid) { +static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { + Iterator i; + int r; + int *rfds = NULL; + unsigned rn_fds = 0; + char *t; + + assert(s); + assert(fds); + assert(n_fds); + + SET_FOREACH(t, NAME(s)->meta.names, i) { + char *k; + Name *p; + int *cfds; + unsigned cn_fds; + + /* Look for all socket objects that go by any of our + * names and collect their fds */ + + if (!(k = name_change_suffix(t, ".socket"))) { + r = -ENOMEM; + goto fail; + } + + p = manager_get_name(NAME(s)->meta.manager, k); + free(k); + + if ((r = socket_collect_fds(SOCKET(p), &cfds, &cn_fds)) < 0) + goto fail; + + if (!cfds) + continue; + + if (!rfds) { + rfds = cfds; + rn_fds = cn_fds; + } else { + int *t; + + if (!(t = new(int, rn_fds+cn_fds))) { + free(cfds); + r = -ENOMEM; + goto fail; + } + + memcpy(t, rfds, rn_fds); + memcpy(t+rn_fds, cfds, cn_fds); + free(rfds); + free(cfds); + + rfds = t; + rn_fds = rn_fds+cn_fds; + } + } + + *fds = rfds; + *n_fds = rn_fds; + return 0; + +fail: + free(rfds); + return r; +} + +static int service_spawn(Service *s, ExecCommand *c, bool timeout, bool pass_fds, pid_t *_pid) { pid_t pid; int r; + int *fds = NULL; + unsigned n_fds = 0; assert(s); assert(c); assert(_pid); + if (pass_fds) + if ((r = service_collect_fds(s, &fds, &n_fds)) < 0) + goto fail; + if (timeout) { if ((r = name_watch_timer(NAME(s), s->timeout_usec, &s->timer_id)) < 0) goto fail; } else name_unwatch_timer(NAME(s), &s->timer_id); - if ((r = exec_spawn(c, &s->exec_context, NULL, 0, &pid)) < 0) + if ((r = exec_spawn(c, &s->exec_context, fds, n_fds, &pid)) < 0) goto fail; if ((r = name_watch_pid(NAME(s), pid)) < 0) /* FIXME: we need to do something here */ goto fail; + free(fds); *_pid = pid; return 0; fail: + free(fds); + if (timeout) name_unwatch_timer(NAME(s), &s->timer_id); @@ -308,7 +397,7 @@ static void service_enter_stop_post(Service *s, bool success) { if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) { - if ((r = service_spawn(s, s->control_command, true, &s->control_pid)) < 0) + if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0) goto fail; service_set_state(s, SERVICE_STOP_POST); @@ -381,7 +470,7 @@ static void service_enter_stop(Service *s, bool success) { if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) { - if ((r = service_spawn(s, s->control_command, true, &s->control_pid)) < 0) + if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0) goto fail; service_set_state(s, SERVICE_STOP); @@ -401,7 +490,7 @@ static void service_enter_start_post(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) { - if ((r = service_spawn(s, s->control_command, true, &s->control_pid)) < 0) + if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0) goto fail; service_set_state(s, SERVICE_START_POST); @@ -424,7 +513,7 @@ static void service_enter_start(Service *s) { assert(s->exec_command[SERVICE_EXEC_START]); assert(!s->exec_command[SERVICE_EXEC_START]->command_next); - if ((r = service_spawn(s, s->exec_command[SERVICE_EXEC_START], s->type == SERVICE_FORKING, &pid)) < 0) + if ((r = service_spawn(s, s->exec_command[SERVICE_EXEC_START], s->type == SERVICE_FORKING, true, &pid)) < 0) goto fail; if (s->type == SERVICE_SIMPLE) { @@ -460,7 +549,7 @@ static void service_enter_start_pre(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) { - if ((r = service_spawn(s, s->control_command, true, &s->control_pid)) < 0) + if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0) goto fail; service_set_state(s, SERVICE_START_PRE); @@ -498,7 +587,7 @@ static void service_enter_reload(Service *s) { if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) { - if ((r = service_spawn(s, s->control_command, true, &s->control_pid)) < 0) + if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0) goto fail; service_set_state(s, SERVICE_RELOAD); @@ -524,7 +613,7 @@ static void service_run_next(Service *s, bool success) { s->control_command = s->control_command->command_next; - if ((r = service_spawn(s, s->control_command, true, &s->control_pid)) < 0) + if ((r = service_spawn(s, s->control_command, true, false, &s->control_pid)) < 0) goto fail; return; @@ -27,40 +27,6 @@ static const NameActiveState state_table[_SOCKET_STATE_MAX] = { [SOCKET_MAINTAINANCE] = NAME_INACTIVE, }; -static int socket_init(Name *n) { - Socket *s = SOCKET(n); - char *t; - int r; - - /* First, reset everything to the defaults, in case this is a - * reload */ - - s->bind_ipv6_only = false; - s->backlog = SOMAXCONN; - s->timeout_usec = DEFAULT_TIMEOUT_USEC; - exec_context_init(&s->exec_context); - - if ((r = name_load_fragment_and_dropin(n)) < 0) - return r; - - if (!(t = name_change_suffix(name_id(n), ".service"))) - return -ENOMEM; - - r = manager_load_name(n->meta.manager, t, (Name**) &s->service); - free(t); - - if (r < 0) - return r; - - if ((r = set_ensure_allocated(n->meta.dependencies + NAME_BEFORE, trivial_hash_func, trivial_compare_func)) < 0) - return r; - - if ((r = set_put(n->meta.dependencies[NAME_BEFORE], s->service)) < 0) - return r; - - return 0; -} - static void socket_done(Name *n) { Socket *s = SOCKET(n); SocketPort *p; @@ -90,6 +56,45 @@ static void socket_done(Name *n) { name_unwatch_timer(n, &s->timer_id); } +static int socket_init(Name *n) { + Socket *s = SOCKET(n); + char *t; + int r; + + /* First, reset everything to the defaults, in case this is a + * reload */ + + s->state = 0; + s->timer_id = -1; + s->bind_ipv6_only = false; + s->backlog = SOMAXCONN; + s->timeout_usec = DEFAULT_TIMEOUT_USEC; + exec_context_init(&s->exec_context); + + if ((r = name_load_fragment_and_dropin(n)) < 0) + goto fail; + + if (!(t = name_change_suffix(name_id(n), ".service"))) { + r = -ENOMEM; + goto fail; + } + + r = manager_load_name(n->meta.manager, t, (Name**) &s->service); + free(t); + + if (r < 0) + goto fail; + + if ((r = name_add_dependency(n, NAME_BEFORE, NAME(s->service))) < 0) + goto fail; + + return 0; + +fail: + socket_done(n); + return r; +} + static const char* listen_lookup(int type) { if (type == SOCK_STREAM) @@ -705,6 +710,38 @@ static void socket_timer_event(Name *n, int id, uint64_t elapsed) { } } +int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { + int *rfds; + unsigned rn_fds, k; + SocketPort *p; + + assert(s); + assert(fds); + assert(n_fds); + + /* Called from the service code for requesting our fds */ + + rn_fds = 0; + LIST_FOREACH(port, p, s->ports) + if (p->fd >= 0) + rn_fds++; + + if (!(rfds = new(int, rn_fds)) < 0) + return -ENOMEM; + + k = 0; + LIST_FOREACH(port, p, s->ports) + if (p->fd >= 0) + rfds[k++] = p->fd; + + assert(k == rn_fds); + + *fds = rfds; + *n_fds = rn_fds; + + return 0; +} + const NameVTable socket_vtable = { .suffix = ".socket", @@ -75,6 +75,9 @@ struct Socket { int timer_id; }; +/* Called from the service code when collecting fds */ +int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds); + extern const NameVTable socket_vtable; #endif diff --git a/test1/postfix.service b/test1/postfix.service index 77fdc766ab..fbf39ecb58 100644 --- a/test1/postfix.service +++ b/test1/postfix.service @@ -4,4 +4,4 @@ Description=Postfix Mail Server Requires=syslog.socket [Service] -Exec=/usr/bin/postfix +ExecStart=/usr/bin/postfix diff --git a/test1/postfix.socket b/test1/postfix.socket index 1baae5b566..4c97218dad 100644 --- a/test1/postfix.socket +++ b/test1/postfix.socket @@ -3,4 +3,4 @@ Description=Postfix SMTP Socket [Socket] ListenStream=53333 -ListenFIFO=/tmp/systemd-fifo +ListenFIFO=/tmp/systemd-postfix-fifo diff --git a/test1/syslog.service b/test1/syslog.service index e26280937a..8fe47aedba 100644 --- a/test1/syslog.service +++ b/test1/syslog.service @@ -2,4 +2,4 @@ Description=System Logging Daemon [Service] -Exec=/usr/bin/rsyslogd +ExecStart=/usr/bin/rsyslogd --foobar 'waldo' diff --git a/test1/syslog.socket b/test1/syslog.socket index 8d7baa5a00..56c903c45b 100644 --- a/test1/syslog.socket +++ b/test1/syslog.socket @@ -2,5 +2,5 @@ Description=Syslog Socket [Socket] -ListenDatagram=/tmp/systemd-socket +ListenDatagram=/tmp/systemd-syslog-socket ListenStream=eth0:3456 @@ -100,9 +100,9 @@ int close_nointr(int fd) { int parse_boolean(const char *v) { assert(v); - if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) + if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) return 1; - else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off")) + else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off")) return 0; return -EINVAL; @@ -216,9 +216,6 @@ int safe_atolli(const char *s, long long int *ret_lli) { return 0; } -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n" - /* Split a string into words. */ char *split_spaces(const char *c, size_t *l, char **state) { char *current; @@ -266,6 +263,9 @@ char *split_quoted(const char *c, size_t *l, char **state) { *state = current+*l; } + /* FIXME: Cannot deal with strings that have spaces AND ticks + * in them */ + return (char*) current; } @@ -382,3 +382,23 @@ finish: fclose(f); return r; } + +char *strappend(const char *s, const char *suffix) { + size_t a, b; + char *r; + + assert(s); + assert(suffix); + + a = strlen(s); + b = strlen(suffix); + + if (!(r = new(char, a+b+1))) + return NULL; + + memcpy(r, s, a); + memcpy(r+a, suffix, b); + r[a+b] = 0; + + return r; +} @@ -18,6 +18,9 @@ typedef uint64_t usec_t; #define NSEC_PER_MSEC 1000000ULL #define NSEC_PER_USEC 1000ULL +/* What is interpreted as whitespace? */ +#define WHITESPACE " \t\n" + usec_t now(clockid_t clock); usec_t timespec_load(const struct timespec *ts); @@ -86,4 +89,6 @@ pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); int write_one_line_file(const char *fn, const char *line); int read_one_line_file(const char *fn, char **line); +char *strappend(const char *s, const char *suffix); + #endif |