diff options
author | Lennart Poettering <lennart@poettering.net> | 2010-04-15 03:11:11 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2010-04-15 03:11:15 +0200 |
commit | 9e2f7c11fb6ba35ffec2274da3e2d08b10d23965 (patch) | |
tree | f25721469627d5ea25a9b2e77c2f5a13c9d7aefd | |
parent | 9fcc065a773b3804a84fe27e9089d76bfe0061fa (diff) |
core: add minimal templating system
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | dbus-job.c | 5 | ||||
-rw-r--r-- | dbus-manager.c | 16 | ||||
-rw-r--r-- | dbus-unit.c | 27 | ||||
-rw-r--r-- | device.c | 38 | ||||
-rw-r--r-- | execute.c | 21 | ||||
-rw-r--r-- | execute.h | 4 | ||||
-rw-r--r-- | job.c | 14 | ||||
-rw-r--r-- | job.h | 2 | ||||
-rw-r--r-- | load-dropin.c | 79 | ||||
-rw-r--r-- | load-fragment.c | 82 | ||||
-rw-r--r-- | main.c | 4 | ||||
-rw-r--r-- | manager.c | 113 | ||||
-rw-r--r-- | manager.h | 2 | ||||
-rw-r--r-- | mount.c | 37 | ||||
-rw-r--r-- | service.c | 109 | ||||
-rw-r--r-- | socket.c | 73 | ||||
-rw-r--r-- | specifier.c | 110 | ||||
-rw-r--r-- | specifier.h | 37 | ||||
-rw-r--r-- | target.c | 2 | ||||
-rw-r--r-- | test-engine.c | 14 | ||||
-rw-r--r-- | unit-name.c | 373 | ||||
-rw-r--r-- | unit-name.h | 51 | ||||
-rw-r--r-- | unit.c | 487 | ||||
-rw-r--r-- | unit.h | 38 | ||||
-rw-r--r-- | util.c | 9 |
26 files changed, 1269 insertions, 484 deletions
diff --git a/Makefile.am b/Makefile.am index 361786983d..8cad1420e0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -135,7 +135,11 @@ COMMON_SOURCES= \ hostname-setup.c \ hostname-setup.h \ utmp-wtmp.c \ - utmp-wtmp.h + utmp-wtmp.h \ + specifier.c \ + specifier.h \ + unit-name.c \ + unit-name.h systemd_SOURCES = \ $(COMMON_SOURCES) \ diff --git a/dbus-job.c b/dbus-job.c index 9c6a798075..b0f575b11b 100644 --- a/dbus-job.c +++ b/dbus-job.c @@ -46,7 +46,6 @@ static int bus_job_append_unit(Manager *m, DBusMessageIter *i, const char *prope Job *j = data; DBusMessageIter sub; char *p; - const char *id; assert(m); assert(i); @@ -59,9 +58,7 @@ static int bus_job_append_unit(Manager *m, DBusMessageIter *i, const char *prope if (!(p = unit_dbus_path(j->unit))) return -ENOMEM; - id = unit_id(j->unit); - - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) || + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->meta.id) || !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { free(p); return -ENOMEM; diff --git a/dbus-manager.c b/dbus-manager.c index 5f3d964777..8deb451795 100644 --- a/dbus-manager.c +++ b/dbus-manager.c @@ -175,7 +175,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection DBUS_TYPE_INVALID)) return bus_send_error_reply(m, message, &error, -EINVAL); - if ((r = manager_load_unit(m, name, &u)) < 0) + if ((r = manager_load_unit(m, name, NULL, &u)) < 0) return bus_send_error_reply(m, message, NULL, r); if (!(reply = dbus_message_new_method_return(message))) @@ -239,12 +239,11 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection HASHMAP_FOREACH_KEY(u, k, m->units, i) { char *u_path, *j_path; - const char *id, *description, *load_state, *active_state, *sub_state, *job_type; + const char *description, *load_state, *active_state, *sub_state, *job_type; DBusMessageIter sub2; uint32_t job_id; - id = unit_id(u); - if (k != id) + if (k != u->meta.id) continue; if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) @@ -273,7 +272,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection job_type = ""; } - if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &id) || + if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) || @@ -314,7 +313,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection HASHMAP_FOREACH(j, m->jobs, i) { char *u_path, *j_path; - const char *unit, *state, *type; + const char *state, *type; uint32_t id; DBusMessageIter sub2; @@ -322,7 +321,6 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection goto oom; id = (uint32_t) j->id; - unit = unit_id(j->unit); state = job_state_to_string(j->state); type = job_type_to_string(j->type); @@ -335,7 +333,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection } if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &unit) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) || @@ -434,7 +432,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection HASHMAP_FOREACH_KEY(u, k, m->units, i) { char *p; - if (k != unit_id(u)) + if (k != u->meta.id) continue; if (!(p = bus_path_escape(k))) { diff --git a/dbus-unit.c b/dbus-unit.c index 7897b857b6..6bccec14a7 100644 --- a/dbus-unit.c +++ b/dbus-unit.c @@ -61,23 +61,6 @@ static const char introspection[] = BUS_INTROSPECTABLE_INTERFACE "</node>"; -static int bus_unit_append_id(Manager *m, DBusMessageIter *i, const char *property, void *data) { - Unit *u = data; - const char *id; - - assert(m); - assert(i); - assert(property); - assert(u); - - id = unit_id(u); - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id)) - return -ENOMEM; - - return 0; -} - static int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data) { Unit *u = data; const char *d; @@ -216,7 +199,7 @@ static int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *prope static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message) { const BusProperty properties[] = { - { "org.freedesktop.systemd1.Unit", "Id", bus_unit_append_id, "s", u }, + { "org.freedesktop.systemd1.Unit", "Id", bus_property_append_string, "s", u->meta.id }, { "org.freedesktop.systemd1.Unit", "Description", bus_unit_append_description, "s", u }, { "org.freedesktop.systemd1.Unit", "LoadState", bus_unit_append_load_state, "s", &u->meta.load_state }, { "org.freedesktop.systemd1.Unit", "ActiveState", bus_unit_append_active_state, "s", u }, @@ -353,15 +336,13 @@ void bus_unit_send_change_signal(Unit *u) { if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Unit", "Changed"))) goto oom; } else { - const char *id; /* Send a new signal */ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitNew"))) goto oom; - id = unit_id(u); if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &id, + DBUS_TYPE_STRING, &u->meta.id, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) goto oom; @@ -389,7 +370,6 @@ oom: void bus_unit_send_removed_signal(Unit *u) { char *p = NULL; DBusMessage *m = NULL; - const char *id; assert(u); @@ -402,9 +382,8 @@ void bus_unit_send_removed_signal(Unit *u) { if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1", "UnitRemoved"))) goto oom; - id = unit_id(u); if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &id, + DBUS_TYPE_STRING, &u->meta.id, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) goto oom; @@ -27,6 +27,7 @@ #include "device.h" #include "strv.h" #include "log.h" +#include "unit-name.h" static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { [DEVICE_DEAD] = UNIT_INACTIVE, @@ -64,7 +65,7 @@ static void device_set_state(Device *d, DeviceState state) { d->state = state; if (state != old_state) - log_debug("%s changed %s ā %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s ā %s", UNIT(d)->meta.id, state_string_table[old_state], state_string_table[state]); unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]); } @@ -113,7 +114,7 @@ static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) { assert(dn); assert(dn[0] == '/'); - if (!(e = unit_name_escape_path(dn+1, ".device"))) + if (!(e = unit_name_build_escape(dn+1, NULL, ".device"))) return -ENOMEM; r = unit_add_name(u, e); @@ -138,7 +139,7 @@ static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) { assert(dn[0] == '/'); assert(_u); - if (!(e = unit_name_escape_path(dn+1, ".device"))) + if (!(e = unit_name_build_escape(dn+1, NULL, ".device"))) return -ENOMEM; u = manager_get_unit(m, e); @@ -303,7 +304,7 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u goto fail; } - r = unit_add_dependency_by_name(u, UNIT_WANTS, e); + r = unit_add_dependency_by_name(u, UNIT_WANTS, NULL, e); free(e); if (r < 0) @@ -356,7 +357,7 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) { return -ENOMEM; assert(sysfs[0] == '/'); - if (!(e = unit_name_escape_path(sysfs+1, ".device"))) + if (!(e = unit_name_build_escape(sysfs+1, NULL, ".device"))) return -ENOMEM; u = manager_get_unit(m, e); @@ -414,21 +415,21 @@ static int device_enumerate(Manager *m) { if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0) return -errno; - if (!(e = udev_enumerate_new(m->udev))) { - r = -ENOMEM; - goto fail; - } + /* if (!(e = udev_enumerate_new(m->udev))) { */ + /* r = -ENOMEM; */ + /* goto fail; */ + /* } */ - if (udev_enumerate_scan_devices(e) < 0) { - r = -EIO; - goto fail; - } + /* if (udev_enumerate_scan_devices(e) < 0) { */ + /* r = -EIO; */ + /* goto fail; */ + /* } */ - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) - device_process_path(m, udev_list_entry_get_name(item), false); + /* first = udev_enumerate_get_list_entry(e); */ + /* udev_list_entry_foreach(item, first) */ + /* device_process_path(m, udev_list_entry_get_name(item), false); */ - udev_enumerate_unref(e); + /* udev_enumerate_unref(e); */ return 0; fail: @@ -476,6 +477,9 @@ fail: const UnitVTable device_vtable = { .suffix = ".device", + .no_requires = true, + .no_instances = true, + .init = device_init, .load = unit_load_fragment_and_dropin_optional, .done = device_done, @@ -665,6 +665,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) { } int exec_spawn(ExecCommand *command, + char **argv, const ExecContext *context, int fds[], unsigned n_fds, bool apply_permissions, @@ -682,7 +683,10 @@ int exec_spawn(ExecCommand *command, assert(ret); assert(fds || n_fds <= 0); - if (!(line = exec_command_line(command))) + if (!argv) + argv = command->argv; + + if (!(line = exec_command_line(argv))) return -ENOMEM; log_debug("About to execute: %s", line); @@ -732,7 +736,7 @@ int exec_spawn(ExecCommand *command, goto fail; /* Now ask the question. */ - if (!(line = exec_command_line(command))) { + if (!(line = exec_command_line(argv))) { r = EXIT_MEMORY; goto fail; } @@ -950,7 +954,7 @@ int exec_spawn(ExecCommand *command, goto fail; } - execve(command->path, command->argv, final_env); + execve(command->path, argv, final_env); r = EXIT_EXEC; fail: @@ -1270,23 +1274,22 @@ void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { prefix, s->status); } -char *exec_command_line(ExecCommand *c) { +char *exec_command_line(char **argv) { size_t k; char *n, *p, **a; bool first = true; - assert(c); - assert(c->argv); + assert(argv); k = 1; - STRV_FOREACH(a, c->argv) + STRV_FOREACH(a, argv) k += strlen(*a)+3; if (!(n = new(char, k))) return NULL; p = n; - STRV_FOREACH(a, c->argv) { + STRV_FOREACH(a, argv) { if (!first) *(p++) = ' '; @@ -1324,7 +1327,7 @@ void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { p2 = strappend(prefix, "\t"); prefix2 = p2 ? p2 : prefix; - cmd = exec_command_line(c); + cmd = exec_command_line(c->argv); fprintf(f, "%sCommand Line: %s\n", @@ -163,6 +163,7 @@ typedef enum ExitStatus { } ExitStatus; int exec_spawn(ExecCommand *command, + char **argv, const ExecContext *context, int fds[], unsigned n_fds, bool apply_permissions, @@ -177,7 +178,8 @@ void exec_command_done_array(ExecCommand *c, unsigned n); void exec_command_free_list(ExecCommand *c); void exec_command_free_array(ExecCommand **c, unsigned n); -char *exec_command_line(ExecCommand *c); +char *exec_command_line(char **argv); + 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_command_append_list(ExecCommand **l, ExecCommand *e); @@ -152,9 +152,9 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s\tState: %s\n" "%s\tForced: %s\n", prefix, j->id, - prefix, unit_id(j->unit), job_type_to_string(j->type), + prefix, j->unit->meta.id, job_type_to_string(j->type), prefix, job_state_to_string(j->state), - prefix, yes_no(j->forced)); + prefix, yes_no(j->override)); } bool job_is_anchor(Job *j) { @@ -455,15 +455,15 @@ int job_finish_and_invalidate(Job *j, bool success) { assert(j); assert(j->installed); - log_debug("Job %s/%s finished, success=%s", unit_id(j->unit), job_type_to_string(j->type), yes_no(success)); + log_debug("Job %s/%s finished, success=%s", j->unit->meta.id, job_type_to_string(j->type), yes_no(success)); job_add_to_dbus_queue(j); /* Patch restart jobs so that they become normal start jobs */ if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) { log_debug("Converting job %s/%s ā %s/%s", - unit_id(j->unit), job_type_to_string(j->type), - unit_id(j->unit), job_type_to_string(JOB_START)); + j->unit->meta.id, job_type_to_string(j->type), + j->unit->meta.id, job_type_to_string(JOB_START)); j->state = JOB_RUNNING; j->type = JOB_START; @@ -490,9 +490,9 @@ int job_finish_and_invalidate(Job *j, bool success) { other->meta.job->type == JOB_RELOAD_OR_START)) job_finish_and_invalidate(other->meta.job, false); - SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRED_BY], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) if (other->meta.job && - !other->meta.job->forced && + !other->meta.job->override && (other->meta.job->type == JOB_START || other->meta.job->type == JOB_VERIFY_ACTIVE || other->meta.job->type == JOB_RELOAD_OR_START)) @@ -93,7 +93,7 @@ struct Job { bool installed:1; bool in_run_queue:1; bool matters_to_anchor:1; - bool forced:1; + bool override:1; bool in_dbus_queue:1; bool sent_dbus_new_signal:1; diff --git a/load-dropin.c b/load-dropin.c index a3c9d3c77d..89e142f9a8 100644 --- a/load-dropin.c +++ b/load-dropin.c @@ -26,6 +26,45 @@ #include "load-dropin.h" #include "log.h" #include "strv.h" +#include "unit-name.h" + +static int iterate_dir(Unit *u, const char *path) { + DIR *d; + struct dirent *de; + int r; + + if (!(d = opendir(path))) { + + if (errno == ENOENT) + return 0; + + return -errno; + } + + while ((de = readdir(d))) { + char *f; + + if (ignore_file(de->d_name)) + continue; + + if (asprintf(&f, "%s/%s", path, de->d_name) < 0) { + r = -ENOMEM; + goto finish; + } + + r = unit_add_dependency_by_name(u, UNIT_WANTS, de->d_name, f); + free(f); + + if (r < 0) + goto finish; + } + + r = 0; + +finish: + closedir(d); + return r; +} int unit_load_dropin(Unit *u) { Iterator i; @@ -38,8 +77,6 @@ int unit_load_dropin(Unit *u) { SET_FOREACH(t, u->meta.names, i) { char *path; - DIR *d; - struct dirent *de; char **p; STRV_FOREACH(p, u->meta.manager->unit_path) { @@ -47,44 +84,32 @@ int unit_load_dropin(Unit *u) { if (asprintf(&path, "%s/%s.wants", *p, t) < 0) return -ENOMEM; - if (!(d = opendir(path))) { - r = -errno; - free(path); - - if (r == -ENOENT) - continue; + r = iterate_dir(u, path); + free(path); + if (r < 0) return r; - } - free(path); + if (u->meta.instance) { + char *template; + /* Also try the template dir */ - while ((de = readdir(d))) { + if (!(template = unit_name_template(t))) + return -ENOMEM; - if (ignore_file(de->d_name)) - continue; + r = asprintf(&path, "%s/%s.wants", *p, template); + free(template); - if (asprintf(&path, "%s/%s.wants/%s", *p, t, de->d_name) < 0) { - closedir(d); + if (r < 0) return -ENOMEM; - } - - if (!unit_name_is_valid(de->d_name)) { - log_info("Name of %s is not a valid unit name. Ignoring.", path); - free(path); - continue; - } - r = unit_add_dependency_by_name(u, UNIT_WANTS, path); + r = iterate_dir(u, path); free(path); - if (r < 0) { - closedir(d); + if (r < 0) return r; - } } - closedir(d); } } diff --git a/load-fragment.c b/load-fragment.c index 47cfcd676c..5093c6dea9 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -36,6 +36,7 @@ #include "ioprio.h" #include "securebits.h" #include "missing.h" +#include "unit-name.h" #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ static int function( \ @@ -83,25 +84,22 @@ static int config_parse_deps( assert(lvalue); assert(rvalue); - if (UNIT_VTABLE(u)->refuse_requires && - (d == UNIT_REQUIRES || - d == UNIT_SOFT_REQUIRES || - d == UNIT_REQUISITE || - d == UNIT_SOFT_REQUISITE)) { - log_error("[%s:%u] Dependency of type %s not acceptable for this unit type.", filename, line, lvalue); - return -EBADMSG; - } - FOREACH_WORD(w, l, rvalue, state) { - char *t; + char *t, *k; int r; if (!(t = strndup(w, l))) return -ENOMEM; - r = unit_add_dependency_by_name(u, d, t); + k = unit_name_printf(u, t); free(t); + if (!k) + return -ENOMEM; + + r = unit_add_dependency_by_name(u, d, k, NULL); + free(k); + if (r < 0) return r; } @@ -129,15 +127,21 @@ static int config_parse_names( assert(data); FOREACH_WORD(w, l, rvalue, state) { - char *t; + char *t, *k; int r; if (!(t = strndup(w, l))) return -ENOMEM; - r = unit_merge_by_name(u, t); + k = unit_name_printf(u, t); free(t); + if (!k) + return -ENOMEM; + + r = unit_merge_by_name(u, k); + free(k); + if (r < 0) return r; } @@ -907,7 +911,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to #define FOLLOW_MAX 8 -static int open_follow(char **filename, FILE **_f, Set *names, char **_id) { +static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { unsigned c = 0; int fd, r; FILE *f; @@ -966,12 +970,12 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_id) { if (!(f = fdopen(fd, "r"))) { r = -errno; - assert(close_nointr(fd) == 0); + close_nointr_nofail(fd); return r; } *_f = f; - *_id = id; + *_final = id; return 0; } @@ -1151,10 +1155,10 @@ static int load_from_path(Unit *u, const char *path) { { "Names", config_parse_names, u, "Meta" }, { "Description", config_parse_string, &u->meta.description, "Meta" }, { "Requires", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES), "Meta" }, - { "SoftRequires", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUIRES), "Meta" }, - { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Meta" }, + { "RequiresOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES_OVERRIDABLE), "Meta" }, { "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Meta" }, - { "SoftRequisite", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUISITE), "Meta" }, + { "RequisiteOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE), "Meta" }, + { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Meta" }, { "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Meta" }, { "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Meta" }, { "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Meta" }, @@ -1336,15 +1340,14 @@ int unit_load_fragment(Unit *u) { const char *t; /* Try to find the unit under its id */ - if ((t = unit_id(u))) - if ((r = load_from_path(u, t)) < 0) - return r; + if ((r = load_from_path(u, u->meta.id)) < 0) + return r; /* Try to find an alias we can load this with */ if (u->meta.load_state == UNIT_STUB) SET_FOREACH(t, u->meta.names, i) { - if (unit_id(u) == t) + if (t == u->meta.id) continue; if ((r = load_from_path(u, t)) < 0) @@ -1353,6 +1356,39 @@ int unit_load_fragment(Unit *u) { if (u->meta.load_state != UNIT_STUB) break; } + + /* Now, follow the same logic, but look for a template */ + if (u->meta.load_state == UNIT_STUB && u->meta.instance) { + char *k; + + if (!(k = unit_name_template(u->meta.id))) + return -ENOMEM; + + r = load_from_path(u, k); + free(k); + + if (r < 0) + return r; + + if (u->meta.load_state == UNIT_STUB) + SET_FOREACH(t, u->meta.names, i) { + + if (t == u->meta.id) + continue; + + if (!(k = unit_name_template(t))) + return -ENOMEM; + + r = load_from_path(u, k); + free(k); + + if (r < 0) + return r; + + if (u->meta.load_state != UNIT_STUB) + break; + } + } } return 0; @@ -555,11 +555,11 @@ int main(int argc, char *argv[]) { log_debug("Activating default unit: %s", default_unit); - if ((r = manager_load_unit(m, default_unit, &target)) < 0) { + if ((r = manager_load_unit(m, default_unit, NULL, &target)) < 0) { log_error("Failed to load default target: %s", strerror(-r)); log_info("Trying to load rescue target..."); - if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, &target)) < 0) { + if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &target)) < 0) { log_error("Failed to load rescue target: %s", strerror(-r)); goto finish; } @@ -46,6 +46,7 @@ #include "cgroup.h" #include "mount-setup.h" #include "utmp-wtmp.h" +#include "unit-name.h" static int enable_special_signals(Manager *m) { char fd; @@ -448,7 +449,7 @@ int manager_coldplug(Manager *m) { HASHMAP_FOREACH_KEY(u, k, m->units, i) { /* ignore aliases */ - if (unit_id(u) != k) + if (u->meta.id != k) continue; if (UNIT_VTABLE(u)->coldplug) @@ -563,7 +564,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job j->type = t; j->state = JOB_WAITING; - j->forced = j->forced || other->forced; + j->override = j->override || other->override; j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor; @@ -635,7 +636,7 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) { return -ENOEXEC; /* Ok, we can drop one, so let's do so. */ - log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type)); + log_debug("Trying to fix job merging by deleting job %s/%s", d->unit->meta.id, job_type_to_string(d->type)); transaction_delete_job(m, d, true); return 0; } @@ -735,7 +736,7 @@ static void transaction_drop_redundant(Manager *m) { if (changes_something) continue; - log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("Found redundant job %s/%s, dropping.", j->unit->meta.id, job_type_to_string(j->type)); transaction_delete_job(m, j, false); again = true; break; @@ -781,17 +782,17 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned * since smart how we are we stored our way back in * there. */ - log_debug("Found ordering cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("Found ordering cycle on %s/%s", j->unit->meta.id, job_type_to_string(j->type)); for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { - log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type)); + log_debug("Walked on cycle path to %s/%s", k->unit->meta.id, job_type_to_string(k->type)); if (!k->installed && !unit_matters_to_anchor(k->unit, k)) { /* Ok, we can drop this one, so let's * do so. */ - log_debug("Breaking order cycle by deleting job %s/%s", unit_id(k->unit), job_type_to_string(k->type)); + log_debug("Breaking order cycle by deleting job %s/%s", k->unit->meta.id, job_type_to_string(k->type)); transaction_delete_unit(m, k->unit); return -EAGAIN; } @@ -872,7 +873,7 @@ static void transaction_collect_garbage(Manager *m) { if (j->object_list) continue; - log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("Garbage collecting job %s/%s", j->unit->meta.id, job_type_to_string(j->type)); transaction_delete_job(m, j, true); again = true; break; @@ -940,13 +941,13 @@ static void transaction_minimize_impact(Manager *m) { continue; if (stops_running_service) - log_debug("%s/%s would stop a running service.", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("%s/%s would stop a running service.", j->unit->meta.id, job_type_to_string(j->type)); if (changes_existing_job) - log_debug("%s/%s would change existing job.", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("%s/%s would change existing job.", j->unit->meta.id, job_type_to_string(j->type)); /* Ok, let's get rid of this */ - log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type)); + log_debug("Deleting %s/%s to minimize impact.", j->unit->meta.id, job_type_to_string(j->type)); transaction_delete_job(m, j, true); again = true; @@ -1101,7 +1102,7 @@ rollback: return r; } -static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool force, bool *is_new) { +static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool override, bool *is_new) { Job *j, *f; int r; @@ -1132,7 +1133,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool f j->generation = 0; j->marker = NULL; j->matters_to_anchor = false; - j->forced = force; + j->override = override; LIST_PREPEND(Job, transaction, f, j); @@ -1144,7 +1145,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool f if (is_new) *is_new = true; - log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type)); + log_debug("Added job %s/%s to transaction.", unit->meta.id, job_type_to_string(type)); return j; } @@ -1175,14 +1176,21 @@ void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies if (other && delete_dependencies) { log_debug("Deleting job %s/%s as dependency of job %s/%s", - unit_id(other->unit), job_type_to_string(other->type), - unit_id(j->unit), job_type_to_string(j->type)); + other->unit->meta.id, job_type_to_string(other->type), + j->unit->meta.id, job_type_to_string(j->type)); transaction_delete_job(m, other, delete_dependencies); } } } -static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit *unit, Job *by, bool matters, bool force, Job **_ret) { +static int transaction_add_job_and_dependencies( + Manager *m, + JobType type, + Unit *unit, + Job *by, + bool matters, + bool override, + Job **_ret) { Job *ret; Iterator i; Unit *dep; @@ -1200,7 +1208,7 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit * return -EBADR; /* First add the job. */ - if (!(ret = transaction_add_one_job(m, type, unit, force, &is_new))) + if (!(ret = transaction_add_one_job(m, type, unit, override, &is_new))) return -ENOMEM; /* Then, add a link to the job. */ @@ -1211,28 +1219,33 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit * /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RELOAD_OR_START) { SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR) - goto fail; - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUIRES], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, NULL)) < 0 && r != -EBADR) goto fail; + + SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, NULL)) < 0 && r != -EBADR) + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r)); + SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0) - log_warning("Cannot add dependency job for unit %s, ignoring: %s", unit_id(dep), strerror(-r)); + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, NULL)) < 0) + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r)); + SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR) - goto fail; - SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUISITE], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, NULL)) < 0 && r != -EBADR) goto fail; + + SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i) + if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, NULL)) < 0 && r != -EBADR) + log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r)); + SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i) - if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, NULL)) < 0 && r != -EBADR) goto fail; } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i) - if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, NULL)) < 0 && r != -EBADR) goto fail; } @@ -1248,7 +1261,7 @@ fail: return r; } -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret) { +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, Job **_ret) { int r; Job *ret; @@ -1257,9 +1270,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for assert(unit); assert(mode < _JOB_MODE_MAX); - log_debug("Trying to enqueue job %s/%s", unit_id(unit), job_type_to_string(type)); + log_debug("Trying to enqueue job %s/%s", unit->meta.id, job_type_to_string(type)); - if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret)) < 0) { + if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, &ret)) < 0) { transaction_abort(m); return r; } @@ -1267,7 +1280,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for if ((r = transaction_activate(m, mode)) < 0) return r; - log_debug("Enqueued job %s/%s as %u", unit_id(unit), job_type_to_string(type), (unsigned) ret->id); + log_debug("Enqueued job %s/%s as %u", unit->meta.id, job_type_to_string(type), (unsigned) ret->id); if (_ret) *_ret = ret; @@ -1275,7 +1288,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for return 0; } -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, Job **_ret) { +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, Job **_ret) { Unit *unit; int r; @@ -1284,10 +1297,10 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode assert(name); assert(mode < _JOB_MODE_MAX); - if ((r = manager_load_unit(m, name, &unit)) < 0) + if ((r = manager_load_unit(m, name, NULL, &unit)) < 0) return r; - return manager_add_job(m, type, unit, mode, force, _ret); + return manager_add_job(m, type, unit, mode, override, _ret); } Job *manager_get_job(Manager *m, uint32_t id) { @@ -1329,19 +1342,24 @@ unsigned manager_dispatch_load_queue(Manager *m) { return n; } -int manager_load_unit(Manager *m, const char *path, Unit **_ret) { +int manager_load_unit(Manager *m, const char *name, const char *path, Unit **_ret) { Unit *ret; int r; - const char *name; assert(m); - assert(path); - assert(_ret); + assert(name || path); /* This will load the service information files, but not actually * start any services or anything. */ - name = file_name_from_path(path); + if (path && !is_path(path)) + return -EINVAL; + + if (!name) + name = file_name_from_path(path); + + if (!unit_name_is_valid(name)) + return -EINVAL; if ((ret = manager_get_unit(m, name))) { *_ret = ret; @@ -1351,12 +1369,11 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) { if (!(ret = unit_new(m))) return -ENOMEM; - if (is_path(path)) { + if (path) if (!(ret->meta.fragment_path = strdup(path))) { unit_free(ret); return -ENOMEM; } - } if ((r = unit_add_name(ret, name)) < 0) { unit_free(ret); @@ -1368,7 +1385,9 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) { manager_dispatch_load_queue(m); - *_ret = unit_follow_merge(ret); + if (_ret) + *_ret = unit_follow_merge(ret); + return 0; } @@ -1392,7 +1411,7 @@ void manager_dump_units(Manager *s, FILE *f, const char *prefix) { assert(f); HASHMAP_FOREACH_KEY(u, t, s->units, i) - if (unit_id(u) == t) + if (u->meta.id == t) unit_dump(u, f, prefix); } @@ -1512,7 +1531,7 @@ static int manager_dispatch_sigchld(Manager *m) { if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) continue; - log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, unit_id(u)); + log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, u->meta.id); UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); } @@ -196,7 +196,7 @@ Unit *manager_get_unit(Manager *m, const char *name); int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u); int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j); -int manager_load_unit(Manager *m, const char *path_or_name, Unit **_ret); +int manager_load_unit(Manager *m, const char *name, const char *path, Unit **_ret); int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret); int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, Job **_ret); @@ -32,6 +32,7 @@ #include "log.h" #include "strv.h" #include "mount-setup.h" +#include "unit-name.h" static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = UNIT_INACTIVE, @@ -156,10 +157,10 @@ static int mount_add_node_links(Mount *m) { if (!path_startswith(what, "/dev/")) return 0; - if (!(e = unit_name_escape_path(what+1, ".device"))) + if (!(e = unit_name_build_escape(what+1, NULL, ".device"))) return -ENOMEM; - r = manager_load_unit(UNIT(m)->meta.manager, e, &device); + r = manager_load_unit(UNIT(m)->meta.manager, e, NULL, &device); free(e); if (r < 0) @@ -268,7 +269,7 @@ static int mount_add_target_links(Mount *m) { else target = SPECIAL_LOCAL_FS_TARGET; - if ((r = manager_load_unit(UNIT(m)->meta.manager, target, &u)) < 0) + if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &u)) < 0) return r; if (handle) @@ -337,7 +338,7 @@ static void mount_set_state(Mount *m, MountState state) { } if (state != old_state) - log_debug("%s changed %s ā %s", unit_id(UNIT(m)), state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s ā %s", UNIT(m)->meta.id, state_string_table[old_state], state_string_table[state]); unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state]); } @@ -366,6 +367,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { goto fail; if ((r = exec_spawn(c, + NULL, &m->exec_context, NULL, 0, true, @@ -492,7 +494,7 @@ static void mount_enter_signal(Mount *m, MountState state, bool success) { return; fail: - log_warning("%s failed to kill processes: %s", unit_id(UNIT(m)), strerror(-r)); + log_warning("%s failed to kill processes: %s", UNIT(m)->meta.id, strerror(-r)); if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) mount_enter_mounted(m, false); @@ -528,7 +530,7 @@ static void mount_enter_unmounting(Mount *m, bool success) { return; fail: - log_warning("%s failed to run umount exectuable: %s", unit_id(UNIT(m)), strerror(-r)); + log_warning("%s failed to run umount exectuable: %s", UNIT(m)->meta.id, strerror(-r)); mount_enter_mounted(m, false); } @@ -574,7 +576,7 @@ static void mount_enter_mounting(Mount *m, bool success) { return; fail: - log_warning("%s failed to run mount exectuable: %s", unit_id(UNIT(m)), strerror(-r)); + log_warning("%s failed to run mount exectuable: %s", UNIT(m)->meta.id, strerror(-r)); mount_enter_dead(m, false); } @@ -745,7 +747,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { exec_status_fill(&m->control_command->exec_status, pid, code, status); m->control_pid = 0; - log_debug("%s control process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status); + log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); /* Note that mount(8) returning and the kernel sending us a * mount table change event might happen out-of-order. If an @@ -800,39 +802,39 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) { case MOUNT_MOUNTING: case MOUNT_MOUNTING_DONE: - log_warning("%s mounting timed out. Stopping.", unit_id(u)); + log_warning("%s mounting timed out. Stopping.", u->meta.id); mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, false); break; case MOUNT_REMOUNTING: - log_warning("%s remounting timed out. Stopping.", unit_id(u)); + log_warning("%s remounting timed out. Stopping.", u->meta.id); mount_enter_signal(m, MOUNT_REMOUNTING_SIGTERM, false); break; case MOUNT_UNMOUNTING: - log_warning("%s unmounting timed out. Stopping.", unit_id(u)); + log_warning("%s unmounting timed out. Stopping.", u->meta.id); mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, false); break; case MOUNT_MOUNTING_SIGTERM: - log_warning("%s mounting timed out. Killing.", unit_id(u)); + log_warning("%s mounting timed out. Killing.", u->meta.id); mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, false); break; case MOUNT_REMOUNTING_SIGTERM: - log_warning("%s remounting timed out. Killing.", unit_id(u)); + log_warning("%s remounting timed out. Killing.", u->meta.id); mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, false); break; case MOUNT_UNMOUNTING_SIGTERM: - log_warning("%s unmounting timed out. Killing.", unit_id(u)); + log_warning("%s unmounting timed out. Killing.", u->meta.id); mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, false); break; case MOUNT_MOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGKILL: - log_warning("%s mount process still around after SIGKILL. Ignoring.", unit_id(u)); + log_warning("%s mount process still around after SIGKILL. Ignoring.", u->meta.id); if (m->from_proc_self_mountinfo) mount_enter_mounted(m, false); @@ -879,7 +881,7 @@ static int mount_add_one( if (streq(where, "/")) e = strdup("-.mount"); else - e = unit_name_escape_path(where+1, ".mount"); + e = unit_name_build_escape(where+1, NULL, ".mount"); if (!e) return -ENOMEM; @@ -1245,7 +1247,7 @@ int mount_path_is_mounted(Manager *m, const char* path) { char *e, *slash; Unit *u; - if (!(e = unit_name_escape_path(t+1, ".mount"))) { + if (!(e = unit_name_build_escape(t+1, NULL, ".mount"))) { r = -ENOMEM; goto finish; } @@ -1281,6 +1283,7 @@ const UnitVTable mount_vtable = { .suffix = ".mount", .no_alias = true, + .no_instances = true, .init = mount_init, .load = mount_load, @@ -30,6 +30,7 @@ #include "load-dropin.h" #include "log.h" #include "strv.h" +#include "unit-name.h" #define COMMENTS "#;\n" #define NEWLINES "\n\r" @@ -282,7 +283,7 @@ static int priority_from_rcd(Service *s, const char *init_script) { s->sysv_start_priority = a*10 + b; - log_debug("Determined priority %i from link farm for %s", s->sysv_start_priority, unit_id(UNIT(s))); + log_debug("Determined priority %i from link farm for %s", s->sysv_start_priority, UNIT(s)->meta.id); closedir(d); return 0; @@ -485,8 +486,8 @@ static int service_load_sysv_path(Service *s, const char *path) { if (unit_name_to_type(m) == UNIT_SERVICE) r = unit_add_name(u, m); else { - if ((r = unit_add_dependency_by_name_inverse(u, UNIT_REQUIRES, m)) >= 0) - r = unit_add_dependency_by_name(u, UNIT_BEFORE, m); + if ((r = unit_add_dependency_by_name_inverse(u, UNIT_REQUIRES, m, NULL)) >= 0) + r = unit_add_dependency_by_name(u, UNIT_BEFORE, m, NULL); } free(m); @@ -519,7 +520,7 @@ static int service_load_sysv_path(Service *s, const char *path) { if (r == 0) continue; - r = unit_add_dependency_by_name(u, UNIT_AFTER, m); + r = unit_add_dependency_by_name(u, UNIT_AFTER, m, NULL); free(m); if (r < 0) @@ -596,13 +597,13 @@ static int service_load_sysv_path(Service *s, const char *path) { * needed as soon as at least one non-LSB script is used. */ if (s->sysv_start_priority < 0) { - log_debug("%s has no chkconfig header, trying to determine SysV priority from link farm.", unit_id(u)); + log_debug("%s has no chkconfig header, trying to determine SysV priority from link farm.", u->meta.id); if ((r = priority_from_rcd(s, file_name_from_path(path))) < 0) goto finish; if (s->sysv_start_priority < 0) - log_warning("%s has neither a chkconfig header nor a directory link, cannot order unit!", unit_id(u)); + log_warning("%s has neither a chkconfig header nor a directory link, cannot order unit!", u->meta.id); } if ((r = sysv_exec_commands(s)) < 0) @@ -615,8 +616,8 @@ static int service_load_sysv_path(Service *s, const char *path) { * needed for early boot) and don't create any links * to it. */ - if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_BASIC_TARGET)) < 0 || - (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_BASIC_TARGET)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL)) < 0 || + (r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL)) < 0) goto finish; } @@ -680,13 +681,13 @@ static int service_load_sysv(Service *s) { if (strv_isempty(UNIT(s)->meta.manager->sysvinit_path)) return 0; - if ((t = unit_id(UNIT(s)))) + if ((t = UNIT(s)->meta.id)) if ((r = service_load_sysv_name(s, t)) < 0) return r; if (UNIT(s)->meta.load_state == UNIT_STUB) SET_FOREACH(t, UNIT(s)->meta.names, i) { - if (t == unit_id(UNIT(s))) + if (t == UNIT(s)->meta.id) continue; if ((r == service_load_sysv_name(s, t)) < 0) @@ -737,7 +738,7 @@ static int service_verify(Service *s) { return 0; if (!s->exec_command[SERVICE_EXEC_START]) { - log_error("%s lacks ExecStart setting. Refusing.", unit_id(UNIT(s))); + log_error("%s lacks ExecStart setting. Refusing.", UNIT(s)->meta.id); return -EINVAL; } @@ -1020,7 +1021,7 @@ static void service_set_state(Service *s, ServiceState state) { service_notify_sockets_dead(s); if (old_state != state) - log_debug("%s changed %s ā %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state)); + log_debug("%s changed %s ā %s", UNIT(s)->meta.id, service_state_to_string(old_state), service_state_to_string(state)); unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); } @@ -1099,6 +1100,7 @@ static int service_spawn( int r; int *fds = NULL; unsigned n_fds = 0; + char **argv; assert(s); assert(c); @@ -1114,14 +1116,23 @@ static int service_spawn( } else unit_unwatch_timer(UNIT(s), &s->timer_watch); - if ((r = exec_spawn(c, - &s->exec_context, - fds, n_fds, - apply_permissions, - apply_chroot, - UNIT(s)->meta.manager->confirm_spawn, - UNIT(s)->meta.cgroup_bondings, - &pid)) < 0) + if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { + r = -ENOMEM; + goto fail; + } + + r = exec_spawn(c, + argv, + &s->exec_context, + fds, n_fds, + apply_permissions, + apply_chroot, + UNIT(s)->meta.manager->confirm_spawn, + UNIT(s)->meta.cgroup_bondings, + &pid); + + strv_free(argv); + if (r < 0) goto fail; if ((r = unit_watch_pid(UNIT(s), pid)) < 0) @@ -1198,7 +1209,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) { return; fail: - log_warning("%s failed to run install restart timer: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run install restart timer: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_dead(s, false, false); } @@ -1231,7 +1242,7 @@ static void service_enter_stop_post(Service *s, bool success) { return; fail: - log_warning("%s failed to run stop-post executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run stop-post executable: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); } @@ -1291,7 +1302,7 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { return; fail: - log_warning("%s failed to kill processes: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to kill processes: %s", UNIT(s)->meta.id, strerror(-r)); if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL) service_enter_stop_post(s, false); @@ -1325,7 +1336,7 @@ static void service_enter_stop(Service *s, bool success) { return; fail: - log_warning("%s failed to run stop executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run stop executable: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_signal(s, SERVICE_STOP_SIGTERM, false); } @@ -1367,7 +1378,7 @@ static void service_enter_start_post(Service *s) { return; fail: - log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run start-post executable: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_stop(s, false); } @@ -1429,7 +1440,7 @@ static void service_enter_start(Service *s) { return; fail: - log_warning("%s failed to run start exectuable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run start exectuable: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); } @@ -1457,7 +1468,7 @@ static void service_enter_start_pre(Service *s) { return; fail: - log_warning("%s failed to run start-pre executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run start-pre executable: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_dead(s, false, true); } @@ -1470,12 +1481,12 @@ static void service_enter_restart(Service *s) { if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, NULL)) < 0) goto fail; - log_debug("%s scheduled restart job.", unit_id(UNIT(s))); + log_debug("%s scheduled restart job.", UNIT(s)->meta.id); return; fail: - log_warning("%s failed to schedule restart job: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to schedule restart job: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_dead(s, false, false); } @@ -1503,7 +1514,7 @@ static void service_enter_reload(Service *s) { return; fail: - log_warning("%s failed to run reload executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run reload executable: %s", UNIT(s)->meta.id, strerror(-r)); service_enter_stop(s, false); } @@ -1533,7 +1544,7 @@ static void service_run_next(Service *s, bool success) { return; fail: - log_warning("%s failed to run spawn next executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run spawn next executable: %s", UNIT(s)->meta.id, strerror(-r)); if (s->state == SERVICE_START_PRE) service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); @@ -1570,7 +1581,7 @@ static int service_start(Unit *u) { /* Make sure we don't enter a busy loop of some kind. */ if (!ratelimit_test(&s->ratelimit)) { - log_warning("%s start request repeated too quickly, refusing to start.", unit_id(u)); + log_warning("%s start request repeated too quickly, refusing to start.", u->meta.id); return -EAGAIN; } @@ -1664,7 +1675,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status; } - log_debug("%s: main process exited, code=%s, status=%i", unit_id(u), sigchld_code_to_string(code), status); + log_debug("%s: main process exited, code=%s, status=%i", u->meta.id, sigchld_code_to_string(code), status); /* The service exited, so the service is officially * gone. */ @@ -1711,7 +1722,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { exec_status_fill(&s->control_command->exec_status, pid, code, status); s->control_pid = 0; - log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status); + log_debug("%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); /* If we are shutting things down anyway we * don't care about failing commands. */ @@ -1721,14 +1732,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* There is another command to * * execute, so let's do that. */ - log_debug("%s running next command for state %s", unit_id(u), service_state_to_string(s->state)); + log_debug("%s running next command for state %s", u->meta.id, service_state_to_string(s->state)); service_run_next(s, success); } else { /* No further commands for this step, so let's * figure out what to do next */ - log_debug("%s got final SIGCHLD for state %s", unit_id(u), service_state_to_string(s->state)); + log_debug("%s got final SIGCHLD for state %s", u->meta.id, service_state_to_string(s->state)); switch (s->state) { @@ -1769,7 +1780,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { * executed. */ if ((r = service_load_pid_file(s)) < 0) - log_warning("%s: failed to load PID file %s: %s", unit_id(UNIT(s)), s->pid_file, strerror(-r)); + log_warning("%s: failed to load PID file %s: %s", UNIT(s)->meta.id, s->pid_file, strerror(-r)); } /* Fall through */ @@ -1822,23 +1833,23 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { case SERVICE_START_PRE: case SERVICE_START: - log_warning("%s operation timed out. Terminating.", unit_id(u)); + log_warning("%s operation timed out. Terminating.", u->meta.id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); break; case SERVICE_START_POST: case SERVICE_RELOAD: - log_warning("%s operation timed out. Stopping.", unit_id(u)); + log_warning("%s operation timed out. Stopping.", u->meta.id); service_enter_stop(s, false); break; case SERVICE_STOP: - log_warning("%s stopping timed out. Terminating.", unit_id(u)); + log_warning("%s stopping timed out. Terminating.", u->meta.id); service_enter_signal(s, SERVICE_STOP_SIGTERM, false); break; case SERVICE_STOP_SIGTERM: - log_warning("%s stopping timed out. Killing.", unit_id(u)); + log_warning("%s stopping timed out. Killing.", u->meta.id); service_enter_signal(s, SERVICE_STOP_SIGKILL, false); break; @@ -1847,27 +1858,27 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { * Must be something we cannot kill, so let's just be * weirded out and continue */ - log_warning("%s still around after SIGKILL. Ignoring.", unit_id(u)); + log_warning("%s still around after SIGKILL. Ignoring.", u->meta.id); service_enter_stop_post(s, false); break; case SERVICE_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", unit_id(u)); + log_warning("%s stopping timed out (2). Terminating.", u->meta.id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); break; case SERVICE_FINAL_SIGTERM: - log_warning("%s stopping timed out (2). Killing.", unit_id(u)); + log_warning("%s stopping timed out (2). Killing.", u->meta.id); service_enter_signal(s, SERVICE_FINAL_SIGKILL, false); break; case SERVICE_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", unit_id(u)); + log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", u->meta.id); service_enter_dead(s, false, true); break; case SERVICE_AUTO_RESTART: - log_debug("%s holdoff time over, scheduling restart.", unit_id(u)); + log_debug("%s holdoff time over, scheduling restart.", u->meta.id); service_enter_restart(s); break; @@ -1881,7 +1892,7 @@ static void service_cgroup_notify_event(Unit *u) { assert(u); - log_debug("%s: cgroup is empty", unit_id(u)); + log_debug("%s: cgroup is empty", u->meta.id); switch (s->state) { @@ -1964,10 +1975,10 @@ static int service_enumerate(Manager *m) { goto finish; } - if ((r = manager_load_unit(m, name, &service)) < 0) + if ((r = manager_load_unit(m, name, NULL, &service)) < 0) goto finish; - if ((r = manager_load_unit(m, rcnd_table[i+1], &runlevel)) < 0) + if ((r = manager_load_unit(m, rcnd_table[i+1], NULL, &runlevel)) < 0) goto finish; if (de->d_name[0] == 'S') { @@ -32,6 +32,7 @@ #include "log.h" #include "load-dropin.h" #include "load-fragment.h" +#include "strv.h" static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, @@ -384,7 +385,7 @@ static void socket_set_state(Socket *s, SocketState state) { socket_unwatch_fds(s); if (state != old_state) - log_debug("%s changed %s ā %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s ā %s", s->meta.id, state_string_table[old_state], state_string_table[state]); unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); } @@ -392,6 +393,7 @@ static void socket_set_state(Socket *s, SocketState state) { static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; + char **argv; assert(s); assert(c); @@ -400,14 +402,23 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) goto fail; - if ((r = exec_spawn(c, - &s->exec_context, - NULL, 0, - true, - true, - UNIT(s)->meta.manager->confirm_spawn, - UNIT(s)->meta.cgroup_bondings, - &pid)) < 0) + if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) { + r = -ENOMEM; + goto fail; + } + + r = exec_spawn(c, + argv, + &s->exec_context, + NULL, 0, + true, + true, + UNIT(s)->meta.manager->confirm_spawn, + UNIT(s)->meta.cgroup_bondings, + &pid); + + strv_free(argv); + if (r < 0) goto fail; if ((r = unit_watch_pid(UNIT(s), pid)) < 0) @@ -455,7 +466,7 @@ static void socket_enter_stop_post(Socket *s, bool success) { return; fail: - log_warning("%s failed to run stop-post executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run stop-post executable: %s", s->meta.id, strerror(-r)); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); } @@ -500,7 +511,7 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) { return; fail: - log_warning("%s failed to kill processes: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to kill processes: %s", s->meta.id, strerror(-r)); if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) socket_enter_stop_post(s, false); @@ -528,7 +539,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) { return; fail: - log_warning("%s failed to run stop-pre executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run stop-pre executable: %s", s->meta.id, strerror(-r)); socket_enter_stop_post(s, false); } @@ -537,7 +548,7 @@ static void socket_enter_listening(Socket *s) { assert(s); if ((r = socket_watch_fds(s)) < 0) { - log_warning("%s failed to watch sockets: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to watch sockets: %s", s->meta.id, strerror(-r)); goto fail; } @@ -553,7 +564,7 @@ static void socket_enter_start_post(Socket *s) { assert(s); if ((r = socket_open_fds(s)) < 0) { - log_warning("%s failed to listen on sockets: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to listen on sockets: %s", s->meta.id, strerror(-r)); goto fail; } @@ -561,7 +572,7 @@ static void socket_enter_start_post(Socket *s) { if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) { if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) { - log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run start-post executable: %s", s->meta.id, strerror(-r)); goto fail; } @@ -592,7 +603,7 @@ static void socket_enter_start_pre(Socket *s) { return; fail: - log_warning("%s failed to run start-pre exectuable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run start-pre exectuable: %s", s->meta.id, strerror(-r)); socket_enter_dead(s, false); } @@ -608,7 +619,7 @@ static void socket_enter_running(Socket *s) { return; fail: - log_warning("%s failed to queue socket startup job: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to queue socket startup job: %s", s->meta.id, strerror(-r)); socket_enter_stop_pre(s, false); } @@ -632,7 +643,7 @@ static void socket_run_next(Socket *s, bool success) { return; fail: - log_warning("%s failed to run spawn next executable: %s", unit_id(UNIT(s)), strerror(-r)); + log_warning("%s failed to run spawn next executable: %s", s->meta.id, strerror(-r)); if (s->state == SOCKET_START_POST) socket_enter_stop_pre(s, false); @@ -722,7 +733,7 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { assert(s); - log_debug("Incoming traffic on %s", unit_id(u)); + log_debug("Incoming traffic on %s", u->meta.id); if (events != EPOLLIN) socket_enter_stop_pre(s, false); @@ -746,16 +757,16 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { exec_status_fill(&s->control_command->exec_status, pid, code, status); s->control_pid = 0; - log_debug("%s control process exited, code=%s status=%i", unit_id(u), sigchld_code_to_string(code), status); + log_debug("%s control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); if (s->control_command->command_next && success) { - log_debug("%s running next command for state %s", unit_id(u), state_string_table[s->state]); + log_debug("%s running next command for state %s", u->meta.id, state_string_table[s->state]); socket_run_next(s, success); } else { /* No further commands for this step, so let's figure * out what to do next */ - log_debug("%s got final SIGCHLD for state %s", unit_id(u), state_string_table[s->state]); + log_debug("%s got final SIGCHLD for state %s", u->meta.id, state_string_table[s->state]); switch (s->state) { @@ -801,41 +812,41 @@ static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) { switch (s->state) { case SOCKET_START_PRE: - log_warning("%s starting timed out. Terminating.", unit_id(u)); + log_warning("%s starting timed out. Terminating.", u->meta.id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); case SOCKET_START_POST: - log_warning("%s starting timed out. Stopping.", unit_id(u)); + log_warning("%s starting timed out. Stopping.", u->meta.id); socket_enter_stop_pre(s, false); break; case SOCKET_STOP_PRE: - log_warning("%s stopping timed out. Terminating.", unit_id(u)); + log_warning("%s stopping timed out. Terminating.", u->meta.id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, false); break; case SOCKET_STOP_PRE_SIGTERM: - log_warning("%s stopping timed out. Killing.", unit_id(u)); + log_warning("%s stopping timed out. Killing.", u->meta.id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, false); break; case SOCKET_STOP_PRE_SIGKILL: - log_warning("%s still around after SIGKILL. Ignoring.", unit_id(u)); + log_warning("%s still around after SIGKILL. Ignoring.", u->meta.id); socket_enter_stop_post(s, false); break; case SOCKET_STOP_POST: - log_warning("%s stopping timed out (2). Terminating.", unit_id(u)); + log_warning("%s stopping timed out (2). Terminating.", u->meta.id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false); break; case SOCKET_FINAL_SIGTERM: - log_warning("%s stopping timed out (2). Killing.", unit_id(u)); + log_warning("%s stopping timed out (2). Killing.", u->meta.id); socket_enter_signal(s, SOCKET_FINAL_SIGKILL, false); break; case SOCKET_FINAL_SIGKILL: - log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", unit_id(u)); + log_warning("%s still around after SIGKILL (2). Entering maintainance mode.", u->meta.id); socket_enter_dead(s, false); break; @@ -882,7 +893,7 @@ void socket_notify_service_dead(Socket *s) { /* The service is dead. Dang. */ if (s->state == SOCKET_RUNNING) { - log_debug("%s got notified about service death.", unit_id(UNIT(s))); + log_debug("%s got notified about service death.", s->meta.id); socket_enter_listening(s); } } diff --git a/specifier.c b/specifier.c new file mode 100644 index 0000000000..a25f33a047 --- /dev/null +++ b/specifier.c @@ -0,0 +1,110 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <string.h> + +#include "macro.h" +#include "util.h" +#include "specifier.h" + +/* + * Generic infrastructure for replacing %x style specifiers in + * strings. Will call a callback for each replacement. + * + */ + +char *specifier_printf(const char *text, const Specifier table[], void *userdata) { + char *r, *t; + const char *f; + bool percent = false; + size_t l; + + assert(text); + assert(table); + + l = strlen(text); + if (!(r = new(char, l+1))) + return NULL; + + t = r; + + for (f = text; *f; f++, l--) { + + if (percent) { + if (*f == '%') + *(t++) = '%'; + else { + const Specifier *i; + + for (i = table; i->specifier; i++) + if (i->specifier == *f) + break; + + if (i->lookup) { + char *n, *w; + size_t k, j; + + if (!(w = i->lookup(i->specifier, i->data, userdata))) { + free(r); + return NULL; + } + + j = t - r; + k = strlen(w); + + if (!(n = new(char, j + k + (l - (f - text)) + 1))) { + free(r); + free(w); + return NULL; + } + + memcpy(n, r, j); + memcpy(n + j, w, k); + + free(r); + free(w); + + r = n; + t = n + j + k; + } else { + *(t++) = '%'; + *(t++) = *f; + } + } + + percent = false; + } else if (*f == '%') + percent = true; + else + *(t++) = *f; + } + + *t = 0; + return r; +} + +/* Generic handler for simple string replacements */ + +char* specifier_string(char specifier, void *data, void *userdata) { + assert(data); + + return strdup(strempty(data)); +} diff --git a/specifier.h b/specifier.h new file mode 100644 index 0000000000..4b3b94c857 --- /dev/null +++ b/specifier.h @@ -0,0 +1,37 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foospecifierhfoo +#define foospecifierhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef char* (*SpecifierCallback)(char specifier, void *data, void *userdata); + +typedef struct Specifier { + const char specifier; + const SpecifierCallback lookup; + void *data; +} Specifier; + +char *specifier_printf(const char *text, const Specifier table[], void *userdata); + +char* specifier_string(char specifier, void *data, void *userdata); + +#endif @@ -65,7 +65,7 @@ static void target_set_state(Target *t, TargetState state) { t->state = state; if (state != old_state) - log_debug("%s changed %s ā %s", unit_id(UNIT(t)), state_string_table[old_state], state_string_table[state]); + log_debug("%s changed %s ā %s", UNIT(t)->meta.id, state_string_table[old_state], state_string_table[state]); unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]); } diff --git a/test-engine.c b/test-engine.c index 1a2f8930d8..27e16f3484 100644 --- a/test-engine.c +++ b/test-engine.c @@ -36,9 +36,9 @@ int main(int argc, char *argv[]) { assert_se(manager_new(MANAGER_INIT, false, &m) >= 0); printf("Load1:\n"); - assert_se(manager_load_unit(m, "a.service", &a) == 0); - assert_se(manager_load_unit(m, "b.service", &b) == 0); - assert_se(manager_load_unit(m, "c.service", &c) == 0); + assert_se(manager_load_unit(m, "a.service", NULL, &a) == 0); + assert_se(manager_load_unit(m, "b.service", NULL, &b) == 0); + assert_se(manager_load_unit(m, "c.service", NULL, &c) == 0); manager_dump_units(m, stdout, "\t"); printf("Test1: (Trivial)\n"); @@ -47,8 +47,8 @@ int main(int argc, char *argv[]) { printf("Load2:\n"); manager_clear_jobs(m); - assert_se(manager_load_unit(m, "d.service", &d) == 0); - assert_se(manager_load_unit(m, "e.service", &e) == 0); + assert_se(manager_load_unit(m, "d.service", NULL, &d) == 0); + assert_se(manager_load_unit(m, "e.service", NULL, &e) == 0); manager_dump_units(m, stdout, "\t"); printf("Test2: (Cyclic Order, Unfixable)\n"); @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) { manager_dump_jobs(m, stdout, "\t"); printf("Load3:\n"); - assert_se(manager_load_unit(m, "g.service", &g) == 0); + assert_se(manager_load_unit(m, "g.service", NULL, &g) == 0); manager_dump_units(m, stdout, "\t"); printf("Test5: (Colliding transaction, fail)\n"); @@ -86,7 +86,7 @@ int main(int argc, char *argv[]) { manager_dump_jobs(m, stdout, "\t"); printf("Load4:\n"); - assert_se(manager_load_unit(m, "h.service", &h) == 0); + assert_se(manager_load_unit(m, "h.service", NULL, &h) == 0); manager_dump_units(m, stdout, "\t"); printf("Test10: (Unmeargable job type of auxiliary job, fail)\n"); diff --git a/unit-name.c b/unit-name.c new file mode 100644 index 0000000000..219997b681 --- /dev/null +++ b/unit-name.c @@ -0,0 +1,373 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <string.h> + +#include "unit.h" +#include "unit-name.h" + +#define VALID_CHARS \ + "0123456789" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "-_.\\" + +UnitType unit_name_to_type(const char *n) { + UnitType t; + + assert(n); + + for (t = 0; t < _UNIT_TYPE_MAX; t++) + if (endswith(n, unit_vtable[t]->suffix)) + return t; + + return _UNIT_TYPE_INVALID; +} + +bool unit_name_is_valid(const char *n) { + UnitType t; + const char *e, *i, *at; + + /* Valid formats: + * + * string@instance.suffix + * string.suffix + */ + + assert(n); + + if (strlen(n) >= UNIT_NAME_MAX) + return false; + + t = unit_name_to_type(n); + if (t < 0 || t >= _UNIT_TYPE_MAX) + return false; + + assert_se(e = strrchr(n, '.')); + + if (e == n) + return false; + + for (i = n, at = NULL; i < e; i++) { + + if (*i == '@' && !at) + at = i; + + if (!strchr("@" VALID_CHARS, *i)) + return false; + } + + if (at) { + if (at == n) + return false; + + if (at[1] == '.') + return false; + } + + return true; +} + +bool unit_instance_is_valid(const char *i) { + assert(i); + + /* The max length depends on the length of the string, so we + * don't really check this here. */ + + if (i[0] == 0) + return false; + + /* We allow additional @ in the instance string, we do not + * allow them in the prefix! */ + + for (; *i; i++) + if (!strchr("@" VALID_CHARS, *i)) + return false; + + return true; +} + +bool unit_prefix_is_valid(const char *p) { + + /* We don't allow additional @ in the instance string */ + + if (p[0] == 0) + return false; + + for (; *p; p++) + if (!strchr(VALID_CHARS, *p)) + return false; + + return true; +} + +int unit_name_to_instance(const char *n, char **instance) { + const char *p, *d; + char *i; + + assert(n); + assert(instance); + + /* Everything past the first @ and before the last . is the instance */ + if (!(p = strchr(n, '@'))) { + *instance = NULL; + return 0; + } + + assert_se(d = strrchr(n, '.')); + assert(p < d); + + if (!(i = strndup(p+1, d-p-1))) + return -ENOMEM; + + *instance = i; + return 0; +} + +char *unit_name_to_prefix_and_instance(const char *n) { + const char *d; + + assert(n); + + assert_se(d = strrchr(n, '.')); + + return strndup(n, d - n); +} + +char *unit_name_to_prefix(const char *n) { + const char *p; + + if ((p = strchr(n, '@'))) + return strndup(n, p - n); + + return unit_name_to_prefix_and_instance(n); +} + +char *unit_name_change_suffix(const char *n, const char *suffix) { + char *e, *r; + size_t a, b; + + assert(n); + assert(unit_name_is_valid(n)); + assert(suffix); + + assert_se(e = strrchr(n, '.')); + a = e - n; + b = strlen(suffix); + + if (!(r = new(char, a + b + 1))) + return NULL; + + memcpy(r, n, a); + memcpy(r+a, suffix, b+1); + + return r; +} + +char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { + char *r; + + assert(prefix); + assert(unit_prefix_is_valid(prefix)); + assert(!instance || unit_instance_is_valid(instance)); + assert(suffix); + assert(unit_name_to_type(suffix) >= 0); + + if (!instance) + return strappend(prefix, suffix); + + if (asprintf(&r, "%s@%s%s", prefix, instance, suffix) < 0) + return NULL; + + return r; +} + +static char* do_escape(const char *f, char *t) { + assert(f); + assert(t); + + for (; *f; f++) { + if (*f == '/') + *(t++) = '.'; + else if (*f == '.' || *f == '\\' || !strchr(VALID_CHARS, *f)) { + *(t++) = '\\'; + *(t++) = 'x'; + *(t++) = hexchar(*f > 4); + *(t++) = hexchar(*f); + } else + *(t++) = *f; + } + + return t; +} + +char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) { + char *r, *t; + size_t a, b, c; + + assert(prefix); + assert(suffix); + assert(unit_name_to_type(suffix) >= 0); + + /* Takes a arbitrary string for prefix and instance plus a + * suffix and makes a nice string suitable as unit name of it, + * escaping all weird chars on the way. + * + * / becomes ., and all chars not alloweed in a unit name get + * escaped as \xFF, including \ and ., of course. This + * escaping is hence reversible. + * + * This is primarily useful to make nice unit names from + * strings, but is actually useful for any kind of string. + */ + + a = strlen(prefix); + c = strlen(suffix); + + if (instance) { + b = strlen(instance); + + if (!(r = new(char, a*4 + 1 + b*4 + c + 1))) + return NULL; + + t = do_escape(prefix, r); + *(t++) = '@'; + t = do_escape(instance, t); + } else { + + if (!(r = new(char, a*4 + c + 1))) + return NULL; + + t = do_escape(prefix, r); + } + + strcpy(t, suffix); + return r; +} + +char *unit_name_escape(const char *f) { + char *r, *t; + + if (!(r = new(char, strlen(f)*4+1))) + return NULL; + + t = do_escape(f, r); + *t = 0; + + return r; + +} + +char *unit_name_unescape(const char *f) { + char *r, *t; + + assert(f); + + if (!(r = strdup(f))) + return NULL; + + for (t = r; *f; f++) { + if (*f == '.') + *(t++) = '/'; + else if (*f == '\\') { + int a, b; + + if ((a = unhexchar(f[1])) < 0 || + (b = unhexchar(f[2])) < 0) { + /* Invalid escape code, let's take it literal then */ + *(t++) = '\\'; + } else { + *(t++) = (char) ((a << 4) | b); + f += 2; + } + } else + *(t++) = *f; + } + + *t = 0; + + return r; +} + +bool unit_name_is_template(const char *n) { + const char *p; + + assert(n); + + if (!(p = strchr(n, '@'))) + return false; + + return p[1] == '.'; +} + +char *unit_name_replace_instance(const char *f, const char *i) { + const char *p, *e; + char *r, *k; + size_t a; + + assert(f); + + p = strchr(f, '@'); + assert_se(e = strrchr(f, '.')); + + a = p - f; + + if (p) { + size_t b; + + b = strlen(i); + + if (!(r = new(char, a + 1 + b + strlen(e) + 1))) + return NULL; + + k = mempcpy(r, f, a + 1); + k = mempcpy(k, i, b); + } else { + + if (!(r = new(char, a + strlen(e) + 1))) + return NULL; + + k = mempcpy(r, f, a); + } + + strcpy(k, e); + return r; +} + +char *unit_name_template(const char *f) { + const char *p, *e; + char *r; + size_t a; + + if (!(p = strchr(f, '@'))) + return strdup(f); + + assert_se(e = strrchr(f, '.')); + a = p - f + 1; + + if (!(r = new(char, a + strlen(e) + 1))) + return NULL; + + strcpy(mempcpy(r, f, a), e); + return r; + +} diff --git a/unit-name.h b/unit-name.h new file mode 100644 index 0000000000..587741ba41 --- /dev/null +++ b/unit-name.h @@ -0,0 +1,51 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foounitnamehfoo +#define foounitnamehfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "unit.h" + +UnitType unit_name_to_type(const char *n); + +int unit_name_to_instance(const char *n, char **instance); +char* unit_name_to_prefix(const char *n); +char* unit_name_to_prefix_and_instance(const char *n); + +bool unit_name_is_valid(const char *n); +bool unit_prefix_is_valid(const char *p); +bool unit_instance_is_valid(const char *i); + +char *unit_name_change_suffix(const char *n, const char *suffix); + +char *unit_name_build(const char *prefix, const char *instance, const char *suffix); +char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix); + +char *unit_name_escape(const char *f); +char *unit_name_unescape(const char *f); + +bool unit_name_is_template(const char *n); + +char *unit_name_replace_instance(const char *f, const char *i); + +char *unit_name_template(const char *f); + +#endif @@ -35,6 +35,8 @@ #include "load-fragment.h" #include "load-dropin.h" #include "log.h" +#include "unit-name.h" +#include "specifier.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -47,71 +49,6 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SNAPSHOT] = &snapshot_vtable }; -UnitType unit_name_to_type(const char *n) { - UnitType t; - - assert(n); - - for (t = 0; t < _UNIT_TYPE_MAX; t++) - if (endswith(n, unit_vtable[t]->suffix)) - return t; - - return _UNIT_TYPE_INVALID; -} - -#define VALID_CHARS \ - "0123456789" \ - "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "-_.\\" - -bool unit_name_is_valid(const char *n) { - UnitType t; - const char *e, *i; - - assert(n); - - if (strlen(n) >= UNIT_NAME_MAX) - return false; - - t = unit_name_to_type(n); - if (t < 0 || t >= _UNIT_TYPE_MAX) - return false; - - if (!(e = strrchr(n, '.'))) - return false; - - if (e == n) - return false; - - for (i = n; i < e; i++) - if (!strchr(VALID_CHARS, *i)) - return false; - - return true; -} - -char *unit_name_change_suffix(const char *n, const char *suffix) { - char *e, *r; - size_t a, b; - - assert(n); - assert(unit_name_is_valid(n)); - assert(suffix); - - assert_se(e = strrchr(n, '.')); - a = e - n; - b = strlen(suffix); - - if (!(r = new(char, a + b + 1))) - return NULL; - - memcpy(r, n, a); - memcpy(r+a, suffix, b+1); - - return r; -} - Unit *unit_new(Manager *m) { Unit *u; @@ -140,74 +77,114 @@ bool unit_has_name(Unit *u, const char *name) { int unit_add_name(Unit *u, const char *text) { UnitType t; - char *s; + char *s = NULL, *i = NULL; int r; assert(u); assert(text); - if (!unit_name_is_valid(text)) - return -EINVAL; + if (unit_name_is_template(text)) { + if (!u->meta.instance) + return -EINVAL; - if ((t = unit_name_to_type(text)) == _UNIT_TYPE_INVALID) - return -EINVAL; + s = unit_name_replace_instance(text, u->meta.instance); + } else + s = strdup(text); - if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) - return -EINVAL; + if (!s) + return -ENOMEM; - if (u->meta.type != _UNIT_TYPE_INVALID && - UNIT_VTABLE(u)->no_alias && - !set_isempty(u->meta.names)) - return -EEXIST; + if (!unit_name_is_valid(s)) { + r = -EINVAL; + goto fail; + } - if (!(s = strdup(text))) - return -ENOMEM; + assert_se((t = unit_name_to_type(s)) >= 0); - if ((r = set_put(u->meta.names, s)) < 0) { - free(s); + if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) { + r = -EINVAL; + goto fail; + } - if (r == -EEXIST) - return 0; + if ((r = unit_name_to_instance(s, &i)) < 0) + goto fail; - return r; + if (i && unit_vtable[t]->no_instances) + goto fail; + + if (u->meta.type != _UNIT_TYPE_INVALID && !streq_ptr(u->meta.instance, i)) { + r = -EINVAL; + goto fail; + } + + if (unit_vtable[t]->no_alias && + !set_isempty(u->meta.names) && + !set_get(u->meta.names, s)) { + r = -EEXIST; + goto fail; + } + + if ((r = set_put(u->meta.names, s)) < 0) { + if (r == -EEXIST) + r = 0; + goto fail; } if ((r = hashmap_put(u->meta.manager->units, s, u)) < 0) { set_remove(u->meta.names, s); - free(s); - return r; + goto fail; } if (u->meta.type == _UNIT_TYPE_INVALID) { - LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta); u->meta.type = t; + u->meta.id = s; + u->meta.instance = i; + + LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta); if (UNIT_VTABLE(u)->init) UNIT_VTABLE(u)->init(u); - } - - if (!u->meta.id) - u->meta.id = s; + } else + free(i); unit_add_to_dbus_queue(u); return 0; + +fail: + free(s); + free(i); + + return r; } int unit_choose_id(Unit *u, const char *name) { - char *s; + char *s, *t = NULL; assert(u); assert(name); + if (unit_name_is_template(name)) { + + if (!u->meta.instance) + return -EINVAL; + + if (!(t = unit_name_replace_instance(name, u->meta.instance))) + return -ENOMEM; + + name = t; + } + /* Selects one of the names of this unit as the id */ + s = set_get(u->meta.names, (char*) name); + free(t); - if (!(s = set_get(u->meta.names, (char*) name))) + if (!s) return -ENOENT; u->meta.id = s; - unit_add_to_dbus_queue(u); + return 0; } @@ -322,6 +299,8 @@ void unit_free(Unit *u) { free(t); set_free(u->meta.names); + free(u->meta.instance); + free(u); } @@ -409,13 +388,17 @@ int unit_merge(Unit *u, Unit *other) { assert(u); assert(other); assert(u->meta.manager == other->meta.manager); + assert(u->meta.type != _UNIT_TYPE_INVALID); other = unit_follow_merge(other); if (other == u) return 0; - if (u->meta.type != u->meta.type) + if (u->meta.type != other->meta.type) + return -EINVAL; + + if (!streq_ptr(u->meta.instance, other->meta.instance)) return -EINVAL; if (other->meta.load_state != UNIT_STUB && @@ -452,14 +435,29 @@ int unit_merge(Unit *u, Unit *other) { int unit_merge_by_name(Unit *u, const char *name) { Unit *other; + int r; + char *s = NULL; assert(u); assert(name); + if (unit_name_is_template(name)) { + if (!u->meta.instance) + return -EINVAL; + + if (!(s = unit_name_replace_instance(name, u->meta.instance))) + return -ENOMEM; + + name = s; + } + if (!(other = manager_get_unit(u->meta.manager, name))) - return unit_add_name(u, name); + r = unit_add_name(u, name); + else + r = unit_merge(u, other); - return unit_merge(u, other); + free(s); + return r; } Unit* unit_follow_merge(Unit *u) { @@ -483,32 +481,23 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { /* If syslog or kernel logging is requested, make sure our own * logging daemon is run first. */ - if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL)) < 0) return r; if (u->meta.manager->running_as != MANAGER_SESSION) - if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0) + if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET, NULL)) < 0) return r; return 0; } -const char* unit_id(Unit *u) { - assert(u); - - if (u->meta.id) - return u->meta.id; - - return set_first(u->meta.names); -} - const char *unit_description(Unit *u) { assert(u); if (u->meta.description) return u->meta.description; - return unit_id(u); + return u->meta.id; } void unit_dump(Unit *u, FILE *f, const char *prefix) { @@ -521,6 +510,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; assert(u); + assert(u->meta.type >= 0); if (!prefix) prefix = ""; @@ -530,12 +520,14 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sā Unit %s:\n" "%s\tDescription: %s\n" + "%s\tInstance: %s\n" "%s\tUnit Load State: %s\n" "%s\tUnit Active State: %s\n" "%s\tActive Enter Timestamp: %s\n" "%s\tActive Exit Timestamp: %s\n", - prefix, unit_id(u), + prefix, u->meta.id, prefix, unit_description(u), + prefix, strna(u->meta.instance), prefix, unit_load_state_to_string(u->meta.load_state), prefix, unit_active_state_to_string(unit_active_state(u)), prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->meta.active_enter_timestamp)), @@ -551,7 +543,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { Unit *other; SET_FOREACH(other, u->meta.dependencies[d], i) - fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other)); + fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->meta.id); } if (u->meta.load_state == UNIT_LOADED) { @@ -571,7 +563,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { } else if (u->meta.load_state == UNIT_MERGED) fprintf(f, "%s\tMerged into: %s\n", - prefix, unit_id(u->meta.merged_into)); + prefix, u->meta.merged_into->meta.id); if (u->meta.job) job_dump(u->meta.job, f, prefix2); @@ -657,7 +649,7 @@ fail: u->meta.load_state = UNIT_FAILED; unit_add_to_dbus_queue(u); - log_error("Failed to load configuration for %s: %s", unit_id(u), strerror(-r)); + log_error("Failed to load configuration for %s: %s", u->meta.id, strerror(-r)); return r; } @@ -776,7 +768,7 @@ static void unit_check_uneeded(Unit *u) { if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) return; - SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRED_BY], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) return; @@ -784,7 +776,7 @@ static void unit_check_uneeded(Unit *u) { if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) return; - log_debug("Service %s is not needed anymore. Stopping.", unit_id(u)); + log_debug("Service %s is not needed anymore. Stopping.", u->meta.id); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ manager_add_job(u->meta.manager, JOB_STOP, u, JOB_FAIL, true, NULL); @@ -801,7 +793,7 @@ static void retroactively_start_dependencies(Unit *u) { if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); - SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRES], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); @@ -836,7 +828,7 @@ static void retroactively_stop_dependencies(Unit *u) { SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_uneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRES], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_uneeded(other); SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i) @@ -845,7 +837,7 @@ static void retroactively_stop_dependencies(Unit *u) { SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_uneeded(other); - SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUISITE], i) + SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_uneeded(other); } @@ -1150,12 +1142,12 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) { static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = UNIT_REQUIRED_BY, - [UNIT_SOFT_REQUIRES] = UNIT_SOFT_REQUIRED_BY, + [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, [UNIT_WANTS] = UNIT_WANTED_BY, [UNIT_REQUISITE] = UNIT_REQUIRED_BY, - [UNIT_SOFT_REQUISITE] = UNIT_SOFT_REQUIRED_BY, + [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, - [UNIT_SOFT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID, + [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID, [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID, [UNIT_CONFLICTS] = UNIT_CONFLICTS, [UNIT_BEFORE] = UNIT_AFTER, @@ -1173,6 +1165,14 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) { if (u == other) return 0; + if (UNIT_VTABLE(u)->no_requires && + (d == UNIT_REQUIRES || + d == UNIT_REQUIRES_OVERRIDABLE || + d == UNIT_REQUISITE || + d == UNIT_REQUISITE_OVERRIDABLE)) { + return -EINVAL; + } + if ((r = set_ensure_allocated(&u->meta.dependencies[d], trivial_hash_func, trivial_compare_func)) < 0) return r; @@ -1191,30 +1191,79 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) { return 0; } -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name) { +static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) { + char *s; + + assert(u); + assert(name || path); + + if (!name) + name = file_name_from_path(path); + + if (!unit_name_is_template(name)) { + *p = NULL; + return name; + } + + if (u->meta.instance) + s = unit_name_replace_instance(name, u->meta.instance); + else { + char *i; + + if (!(i = unit_name_to_prefix(u->meta.id))) + return NULL; + + s = unit_name_replace_instance(name, i); + free(i); + } + + if (!s) + return NULL; + + *p = s; + return s; +} + +int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path) { Unit *other; int r; + char *s; - if ((r = manager_load_unit(u->meta.manager, name, &other)) < 0) - return r; + assert(u); + assert(name || path); - if ((r = unit_add_dependency(u, d, other)) < 0) - return r; + if (!(name = resolve_template(u, name, path, &s))) + return -ENOMEM; - return 0; + if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0) + goto finish; + + r = unit_add_dependency(u, d, other); + +finish: + free(s); + return r; } -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name) { +int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path) { Unit *other; int r; + char *s; - if ((r = manager_load_unit(u->meta.manager, name, &other)) < 0) - return r; + assert(u); + assert(name || path); - if ((r = unit_add_dependency(other, d, u)) < 0) - return r; + if (!(name = resolve_template(u, name, path, &s))) + return -ENOMEM; - return 0; + if ((r = manager_load_unit(u->meta.manager, name, path, &other)) < 0) + goto finish; + + r = unit_add_dependency(other, d, u); + +finish: + free(s); + return r; } int set_unit_path(const char *p) { @@ -1246,54 +1295,12 @@ int set_unit_path(const char *p) { return 0; } -char *unit_name_escape_path(const char *path, const char *suffix) { - char *r, *t; - const char *f; - size_t a, b; - - assert(path); - - /* Takes a path and a suffix and prefix and makes a nice - * string suitable as unit name of it, escaping all weird - * chars on the way. - * - * / becomes ., and all chars not alloweed in a unit name get - * escaped as \xFF, including \ and ., of course. This - * escaping is hence reversible. - */ - - if (!suffix) - suffix = ""; - - a = strlen(path); - b = strlen(suffix); - - if (!(r = new(char, a*4+b+1))) - return NULL; - - for (f = path, t = r; *f; f++) { - if (*f == '/') - *(t++) = '.'; - else if (*f == '.' || *f == '\\' || !strchr(VALID_CHARS, *f)) { - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(*f > 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - memcpy(t, suffix, b+1); - - return r; -} - char *unit_dbus_path(Unit *u) { char *p, *e; assert(u); - if (!(e = bus_path_escape(unit_id(u)))) + if (!(e = bus_path_escape(u->meta.id))) return NULL; if (asprintf(&p, "/org/freedesktop/systemd1/unit/%s", e) < 0) { @@ -1335,7 +1342,7 @@ static char *default_cgroup_path(Unit *u) { assert(u); - if (asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, unit_id(u)) < 0) + if (asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id) < 0) return NULL; return p; @@ -1462,20 +1469,134 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { assert(type); assert(_found); - if (!(t = unit_name_change_suffix(unit_id(u), type))) + if (!(t = unit_name_change_suffix(u->meta.id, type))) return -ENOMEM; assert(!unit_has_name(u, t)); - r = manager_load_unit(u->meta.manager, t, _found); + r = manager_load_unit(u->meta.manager, t, NULL, _found); free(t); - if (r >= 0) - assert(*_found != u); + assert(r < 0 || *_found != u); return r; } +static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + return unit_name_to_prefix_and_instance(u->meta.id); +} + +static char *specifier_prefix(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + return unit_name_to_prefix(u->meta.id); +} + +static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) { + Unit *u = userdata; + char *p, *r; + + assert(u); + + if (!(p = unit_name_to_prefix(u->meta.id))) + return NULL; + + r = unit_name_unescape(p); + free(p); + + return r; +} + +static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) { + Unit *u = userdata; + assert(u); + + if (u->meta.instance) + return unit_name_unescape(u->meta.instance); + + return strdup(""); +} + +char *unit_name_printf(Unit *u, const char* format) { + + /* + * This will use the passed string as format string and + * replace the following specifiers: + * + * %n: the full id of the unit (foo@bar.waldo) + * %N: the id of the unit without the suffix (foo@bar) + * %p: the prefix (foo) + * %i: the instance (bar) + */ + + const Specifier table[] = { + { 'n', specifier_string, u->meta.id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'i', specifier_string, u->meta.instance }, + { 0, NULL, NULL } + }; + + assert(u); + assert(format); + + return specifier_printf(format, table, u); +} + +char *unit_full_printf(Unit *u, const char *format) { + + /* This is similar to unit_name_printf() but also supports + * unescaping */ + + const Specifier table[] = { + { 'n', specifier_string, u->meta.id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'P', specifier_prefix_unescaped, NULL }, + { 'i', specifier_string, u->meta.instance }, + { 'I', specifier_instance_unescaped, NULL }, + { 0, NULL, NULL } + }; + + assert(u); + assert(format); + + return specifier_printf(format, table, u); +} + +char **unit_full_printf_strv(Unit *u, char **l) { + size_t n; + char **r, **i, **j; + + /* Applies unit_full_printf to every entry in l */ + + assert(u); + + n = strv_length(l); + if (!(r = new(char*, n+1))) + return NULL; + + for (i = l, j = r; *i; i++, j++) + if (!(*j = unit_full_printf(u, *i))) + goto fail; + + *j = NULL; + return r; + +fail: + j--; + while (j >= r) + free(*j); + + free(r); + + return NULL; +} + static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_TIMER] = "timer", @@ -1509,12 +1630,12 @@ DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = "Requires", - [UNIT_SOFT_REQUIRES] = "SoftRequires", + [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable", [UNIT_WANTS] = "Wants", [UNIT_REQUISITE] = "Requisite", - [UNIT_SOFT_REQUISITE] = "SoftRequisite", + [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable", [UNIT_REQUIRED_BY] = "RequiredBy", - [UNIT_SOFT_REQUIRED_BY] = "SoftRequiredBy", + [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable", [UNIT_WANTED_BY] = "WantedBy", [UNIT_CONFLICTS] = "Conflicts", [UNIT_BEFORE] = "Before", @@ -99,21 +99,21 @@ static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) { enum UnitDependency { /* Positive dependencies */ UNIT_REQUIRES, - UNIT_SOFT_REQUIRES, - UNIT_WANTS, + UNIT_REQUIRES_OVERRIDABLE, UNIT_REQUISITE, - UNIT_SOFT_REQUISITE, + UNIT_REQUISITE_OVERRIDABLE, + UNIT_WANTS, /* Inverse of the above */ - UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */ - UNIT_SOFT_REQUIRED_BY, /* inverse of 'soft_requires' and 'soft_requisite' is 'soft_required_by' */ - UNIT_WANTED_BY, /* inverse of 'wants' */ + UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */ + UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'soft_requires' and 'soft_requisite' is 'soft_required_by' */ + UNIT_WANTED_BY, /* inverse of 'wants' */ /* Negative dependencies */ - UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */ + UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicts' */ /* Order */ - UNIT_BEFORE, /* inverse of before is after and vice versa */ + UNIT_BEFORE, /* inverse of before is after and vice versa */ UNIT_AFTER, _UNIT_DEPENDENCY_MAX, @@ -132,6 +132,7 @@ struct Meta { Unit *merged_into; char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ + char *instance; Set *names; Set *dependencies[_UNIT_DEPENDENCY_MAX]; @@ -203,7 +204,10 @@ struct UnitVTable { /* If true units of this types can never have "Requires" * dependencies, because state changes can only be observed, * not triggered */ - bool refuse_requires:1; + bool no_requires:1; + + /* Instances make no sense for this type */ + bool no_instances:1; /* This should reset all type-specific variables. This should * not allocate memory, and is either called with 0 @@ -284,17 +288,14 @@ DEFINE_CAST(MOUNT, Mount); DEFINE_CAST(AUTOMOUNT, Automount); DEFINE_CAST(SNAPSHOT, Snapshot); -UnitType unit_name_to_type(const char *n); -bool unit_name_is_valid(const char *n); -char *unit_name_change_suffix(const char *n, const char *suffix); - Unit *unit_new(Manager *m); void unit_free(Unit *u); int unit_add_name(Unit *u, const char *name); + int unit_add_dependency(Unit *u, UnitDependency d, Unit *other); -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name); -int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name); +int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename); +int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename); int unit_add_exec_dependencies(Unit *u, ExecContext *c); @@ -319,7 +320,6 @@ int unit_load_fragment_and_dropin(Unit *u); int unit_load_fragment_and_dropin_optional(Unit *u); int unit_load(Unit *unit); -const char* unit_id(Unit *u); const char *unit_description(Unit *u); bool unit_has_name(Unit *u, const char *name); @@ -352,12 +352,14 @@ bool unit_job_is_applicable(Unit *u, JobType j); int set_unit_path(const char *p); -char *unit_name_escape_path(const char *path, const char *suffix); - char *unit_dbus_path(Unit *u); int unit_load_related_unit(Unit *u, const char *type, Unit **_found); +char *unit_name_printf(Unit *u, const char* text); +char *unit_full_printf(Unit *u, const char *text); +char **unit_full_printf_strv(Unit *u, char **l); + const char *unit_type_to_string(UnitType i); UnitType unit_type_from_string(const char *s); @@ -1041,16 +1041,15 @@ char *bus_path_escape(const char *s) { return r; } -char *bus_path_unescape(const char *s) { +char *bus_path_unescape(const char *f) { char *r, *t; - const char *f; - assert(s); + assert(f); - if (!(r = new(char, strlen(s)+1))) + if (!(r = strdup(f))) return NULL; - for (f = s, t = r; *f; f++) { + for (t = r; *f; f++) { if (*f == '_') { int a, b; |