+
+char *unit_dbus_path(Unit *u) {
+ char *p, *e;
+
+ assert(u);
+
+ if (!(e = bus_path_escape(u->meta.id)))
+ return NULL;
+
+ if (asprintf(&p, "/org/freedesktop/systemd1/unit/%s", e) < 0) {
+ free(e);
+ return NULL;
+ }
+
+ free(e);
+ return p;
+}
+
+int unit_add_cgroup(Unit *u, CGroupBonding *b) {
+ CGroupBonding *l;
+ int r;
+
+ assert(u);
+ assert(b);
+ assert(b->path);
+
+ /* Ensure this hasn't been added yet */
+ assert(!b->unit);
+
+ l = hashmap_get(u->meta.manager->cgroup_bondings, b->path);
+ LIST_PREPEND(CGroupBonding, by_path, l, b);
+
+ if ((r = hashmap_replace(u->meta.manager->cgroup_bondings, b->path, l)) < 0) {
+ LIST_REMOVE(CGroupBonding, by_path, l, b);
+ return r;
+ }
+
+ LIST_PREPEND(CGroupBonding, by_unit, u->meta.cgroup_bondings, b);
+ b->unit = u;
+
+ return 0;
+}
+
+static char *default_cgroup_path(Unit *u) {
+ char *p;
+ int r;
+
+ assert(u);
+
+ if (u->meta.instance) {
+ char *t;
+
+ if (!(t = unit_name_template(u->meta.id)))
+ return NULL;
+
+ r = asprintf(&p, "%s/%s/%s", u->meta.manager->cgroup_hierarchy, t, u->meta.instance);
+ free(t);
+ } else
+ r = asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id);
+
+ return r < 0 ? NULL : p;
+}
+
+int unit_add_cgroup_from_text(Unit *u, const char *name) {
+ size_t n;
+ char *controller = NULL, *path = NULL;
+ CGroupBonding *b = NULL;
+ int r;
+
+ assert(u);
+ assert(name);
+
+ /* Detect controller name */
+ n = strcspn(name, ":");
+
+ if (name[n] == 0 ||
+ (name[n] == ':' && name[n+1] == 0)) {
+
+ /* Only controller name, no path? */
+
+ if (!(path = default_cgroup_path(u)))
+ return -ENOMEM;
+
+ } else {
+ const char *p;
+
+ /* Controller name, and path. */
+ p = name+n+1;
+
+ if (!path_is_absolute(p))
+ return -EINVAL;
+
+ if (!(path = strdup(p)))
+ return -ENOMEM;
+ }
+
+ if (n > 0)
+ controller = strndup(name, n);
+ else
+ controller = strdup(u->meta.manager->cgroup_controller);
+
+ if (!controller) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ if (!(b = new0(CGroupBonding, 1))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ b->controller = controller;
+ b->path = path;
+ b->only_us = false;
+ b->clean_up = false;
+
+ if ((r = unit_add_cgroup(u, b)) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ free(path);
+ free(controller);
+ free(b);
+
+ return r;
+}
+
+int unit_add_default_cgroup(Unit *u) {
+ CGroupBonding *b;
+ int r = -ENOMEM;
+
+ assert(u);
+
+ /* Adds in the default cgroup data, if it wasn't specified yet */
+
+ if (unit_get_default_cgroup(u))
+ return 0;
+
+ if (!(b = new0(CGroupBonding, 1)))
+ return -ENOMEM;
+
+ if (!(b->controller = strdup(u->meta.manager->cgroup_controller)))
+ goto fail;
+
+ if (!(b->path = default_cgroup_path(u)))
+ goto fail;
+
+ b->clean_up = true;
+ b->only_us = true;
+
+ if ((r = unit_add_cgroup(u, b)) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ free(b->path);
+ free(b->controller);
+ free(b);
+
+ return r;
+}
+
+CGroupBonding* unit_get_default_cgroup(Unit *u) {
+ assert(u);
+
+ return cgroup_bonding_find_list(u->meta.cgroup_bondings, u->meta.manager->cgroup_controller);
+}
+
+int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
+ char *t;
+ int r;
+
+ assert(u);
+ assert(type);
+ assert(_found);
+
+ 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, NULL, _found);
+ free(t);
+
+ assert(r < 0 || *_found != u);
+
+ return r;
+}
+
+int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
+ Unit *found;
+ char *t;
+
+ assert(u);
+ assert(type);
+ assert(_found);
+
+ if (!(t = unit_name_change_suffix(u->meta.id, type)))
+ return -ENOMEM;
+
+ assert(!unit_has_name(u, t));
+
+ found = manager_get_unit(u->meta.manager, t);
+ free(t);
+
+ if (!found)
+ return -ENOENT;
+
+ *_found = found;
+ return 0;
+}
+
+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;
+}
+
+int unit_watch_bus_name(Unit *u, const char *name) {
+ assert(u);
+ assert(name);
+
+ /* Watch a specific name on the bus. We only support one unit
+ * watching each name for now. */
+
+ return hashmap_put(u->meta.manager->watch_bus, name, u);
+}
+
+void unit_unwatch_bus_name(Unit *u, const char *name) {
+ assert(u);
+ assert(name);
+
+ hashmap_remove_value(u->meta.manager->watch_bus, name, u);
+}
+
+bool unit_can_serialize(Unit *u) {
+ assert(u);
+
+ return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
+}
+
+int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!unit_can_serialize(u))
+ return 0;
+
+ if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
+ return r;
+
+ /* End marker */
+ fputc('\n', f);
+ return 0;
+}
+
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
+ va_list ap;
+
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(format);
+
+ fputs(key, f);
+ fputc('=', f);
+
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+
+ fputc('\n', f);
+}
+
+void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(value);
+
+ fprintf(f, "%s=%s\n", key, value);
+}
+
+int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!unit_can_serialize(u))
+ return 0;
+
+ for (;;) {
+ char line[1024], *l, *v;
+ size_t k;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ return 0;
+ return -errno;
+ }
+
+ l = strstrip(line);
+
+ /* End marker */
+ if (l[0] == 0)
+ return 0;
+
+ k = strcspn(l, "=");
+
+ if (l[k] == '=') {
+ l[k] = 0;
+ v = l+k+1;
+ } else
+ v = l+k;
+
+ if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
+ return r;
+ }
+}
+
+
+static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
+ [UNIT_SERVICE] = "service",
+ [UNIT_TIMER] = "timer",
+ [UNIT_SOCKET] = "socket",
+ [UNIT_TARGET] = "target",
+ [UNIT_DEVICE] = "device",
+ [UNIT_MOUNT] = "mount",
+ [UNIT_AUTOMOUNT] = "automount",
+ [UNIT_SNAPSHOT] = "snapshot",
+ [UNIT_SWAP] = "swap"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
+
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+ [UNIT_STUB] = "stub",
+ [UNIT_LOADED] = "loaded",
+ [UNIT_FAILED] = "failed",
+ [UNIT_MERGED] = "merged"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+
+static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
+ [UNIT_ACTIVE] = "active",
+ [UNIT_INACTIVE] = "inactive",
+ [UNIT_ACTIVATING] = "activating",
+ [UNIT_DEACTIVATING] = "deactivating"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
+
+static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
+ [UNIT_REQUIRES] = "Requires",
+ [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
+ [UNIT_WANTS] = "Wants",
+ [UNIT_REQUISITE] = "Requisite",
+ [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
+ [UNIT_REQUIRED_BY] = "RequiredBy",
+ [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
+ [UNIT_WANTED_BY] = "WantedBy",
+ [UNIT_CONFLICTS] = "Conflicts",
+ [UNIT_BEFORE] = "Before",
+ [UNIT_AFTER] = "After",
+ [UNIT_REFERENCES] = "References",
+ [UNIT_REFERENCED_BY] = "ReferencedBy"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
+
+static const char* const kill_mode_table[_KILL_MODE_MAX] = {
+ [KILL_CONTROL_GROUP] = "control-group",
+ [KILL_PROCESS_GROUP] = "process-group",
+ [KILL_PROCESS] = "process",
+ [KILL_NONE] = "none"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);