core: add minimal templating system

This commit is contained in:
Lennart Poettering 2010-04-15 03:11:11 +02:00
parent 9fcc065a77
commit 9e2f7c11fb
26 changed files with 1275 additions and 490 deletions

View File

@ -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) \

View File

@ -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;

View File

@ -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))) {

View File

@ -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;

View File

@ -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,

View File

@ -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",

View File

@ -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);

14
job.c
View File

@ -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))

2
job.h
View File

@ -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;

View File

@ -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;
return r;
}
r = iterate_dir(u, path);
free(path);
while ((de = readdir(d))) {
if (r < 0)
return r;
if (ignore_file(de->d_name))
continue;
if (u->meta.instance) {
char *template;
/* Also try the template dir */
if (asprintf(&path, "%s/%s.wants/%s", *p, t, de->d_name) < 0) {
closedir(d);
if (!(template = unit_name_template(t)))
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 = asprintf(&path, "%s/%s.wants", *p, template);
free(template);
r = unit_add_dependency_by_name(u, UNIT_WANTS, path);
if (r < 0)
return -ENOMEM;
r = iterate_dir(u, path);
free(path);
if (r < 0) {
closedir(d);
if (r < 0)
return r;
}
}
closedir(d);
}
}

View File

@ -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;

4
main.c
View File

@ -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;
}

113
manager.c
View File

@ -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);
}

View File

@ -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);

37
mount.c
View File

@ -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,

109
service.c
View File

@ -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') {

View File

@ -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);
}
}

110
specifier.c Normal file
View File

@ -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));
}

37
specifier.h Normal file
View File

@ -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

View File

@ -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]);
}

View File

@ -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");

373
unit-name.c Normal file
View File

@ -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;
}

51
unit-name.h Normal file
View File

@ -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

497
unit.c
View File

@ -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 (u->meta.type != _UNIT_TYPE_INVALID &&
UNIT_VTABLE(u)->no_alias &&
!set_isempty(u->meta.names))
return -EEXIST;
if (!(s = strdup(text)))
if (!s)
return -ENOMEM;
if (!unit_name_is_valid(s)) {
r = -EINVAL;
goto fail;
}
assert_se((t = unit_name_to_type(s)) >= 0);
if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type) {
r = -EINVAL;
goto fail;
}
if ((r = unit_name_to_instance(s, &i)) < 0)
goto fail;
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) {
free(s);
if (r == -EEXIST)
return 0;
return r;
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);
/* Selects one of the names of this unit as the id */
if (unit_name_is_template(name)) {
if (!(s = set_get(u->meta.names, (char*) 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)
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 (!(other = manager_get_unit(u->meta.manager, name)))
return unit_add_name(u, name);
if (unit_name_is_template(name)) {
if (!u->meta.instance)
return -EINVAL;
return unit_merge(u, other);
if (!(s = unit_name_replace_instance(name, u->meta.instance)))
return -ENOMEM;
name = s;
}
if (!(other = manager_get_unit(u->meta.manager, name)))
r = unit_add_name(u, name);
else
r = 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) {
Unit *other;
int r;
static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) {
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)
name = file_name_from_path(path);
return 0;
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_inverse(Unit *u, UnitDependency d, const char *name) {
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(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(u, d, other);
finish:
free(s);
return r;
}
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path) {
Unit *other;
int r;
char *s;
assert(u);
assert(name || path);
if (!(name = resolve_template(u, name, path, &s)))
return -ENOMEM;
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",

38
unit.h
View File

@ -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);

9
util.c
View File

@ -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;