mirror of
https://github.com/systemd/systemd.git
synced 2025-01-26 14:04:03 +03:00
reload: implement reload/reexec logic
This commit is contained in:
parent
0d9068141e
commit
a16e112358
10
Makefile.am
10
Makefile.am
@ -32,7 +32,11 @@ AM_CPPFLAGS = \
|
||||
-DSYSTEM_SYSVRCND_PATH=\"$(SYSTEM_SYSVRCND_PATH)\" \
|
||||
-DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
|
||||
-DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \
|
||||
-DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\"
|
||||
-DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" \
|
||||
-DSYSTEMD_BINARY_PATH=\"$(sbindir)/systemd\"
|
||||
|
||||
# -DSYSTEMD_BINARY_PATH=\"/home/lennart/projects/systemd/systemd\"
|
||||
|
||||
|
||||
sbin_PROGRAMS = \
|
||||
systemd
|
||||
@ -155,7 +159,9 @@ COMMON_SOURCES= \
|
||||
specifier.c \
|
||||
specifier.h \
|
||||
unit-name.c \
|
||||
unit-name.h
|
||||
unit-name.h \
|
||||
fdset.c \
|
||||
fdset.h
|
||||
|
||||
systemd_SOURCES = \
|
||||
$(COMMON_SOURCES) \
|
||||
|
210
automount.c
210
automount.c
@ -43,21 +43,7 @@ static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE,
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_DEAD] = "dead",
|
||||
[AUTOMOUNT_WAITING] = "waiting",
|
||||
[AUTOMOUNT_RUNNING] = "running",
|
||||
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
static char *automount_name_from_where(const char *where) {
|
||||
assert(where);
|
||||
|
||||
if (streq(where, "/"))
|
||||
return strdup("-.automount");
|
||||
|
||||
return unit_name_build_escape(where+1, NULL, ".automount");
|
||||
}
|
||||
static int open_dev_autofs(Manager *m);
|
||||
|
||||
static void automount_init(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
@ -66,6 +52,7 @@ static void automount_init(Unit *u) {
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
a->pipe_watch.fd = a->pipe_fd = -1;
|
||||
a->pipe_watch.type = WATCH_INVALID;
|
||||
}
|
||||
|
||||
static void repeat_unmout(const char *path) {
|
||||
@ -95,7 +82,12 @@ static void unmount_autofs(Automount *a) {
|
||||
close_nointr_nofail(a->pipe_fd);
|
||||
a->pipe_fd = -1;
|
||||
|
||||
repeat_unmout(a->where);
|
||||
/* If we reload/reexecute things we keep the mount point
|
||||
* around */
|
||||
if (a->where &&
|
||||
(UNIT(a)->meta.manager->exit_code != MANAGER_RELOAD &&
|
||||
UNIT(a)->meta.manager->exit_code != MANAGER_REEXECUTE))
|
||||
repeat_unmout(a->where);
|
||||
}
|
||||
|
||||
static void automount_done(Unit *u) {
|
||||
@ -106,10 +98,11 @@ static void automount_done(Unit *u) {
|
||||
unmount_autofs(a);
|
||||
a->mount = NULL;
|
||||
|
||||
if (a->tokens) {
|
||||
set_free(a->tokens);
|
||||
a->tokens = NULL;
|
||||
}
|
||||
free(a->where);
|
||||
a->where = NULL;
|
||||
|
||||
set_free(a->tokens);
|
||||
a->tokens = NULL;
|
||||
}
|
||||
|
||||
static int automount_verify(Automount *a) {
|
||||
@ -120,14 +113,7 @@ static int automount_verify(Automount *a) {
|
||||
if (UNIT(a)->meta.load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
if (!a->where) {
|
||||
log_error("%s lacks Where setting. Refusing.", UNIT(a)->meta.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
path_kill_slashes(a->where);
|
||||
|
||||
if (!(e = automount_name_from_where(a->where)))
|
||||
if (!(e = unit_name_from_path(a->where, ".automount")))
|
||||
return -ENOMEM;
|
||||
|
||||
b = unit_has_name(UNIT(a), e);
|
||||
@ -154,6 +140,12 @@ static int automount_load(Unit *u) {
|
||||
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
|
||||
if (!a->where)
|
||||
if (!(a->where = unit_name_to_path(u->meta.id)))
|
||||
return -ENOMEM;
|
||||
|
||||
path_kill_slashes(a->where);
|
||||
|
||||
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
|
||||
return r;
|
||||
|
||||
@ -176,19 +168,51 @@ static void automount_set_state(Automount *a, AutomountState state) {
|
||||
unmount_autofs(a);
|
||||
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", UNIT(a)->meta.id, state_string_table[old_state], state_string_table[state]);
|
||||
log_debug("%s changed %s → %s",
|
||||
UNIT(a)->meta.id,
|
||||
automount_state_to_string(old_state),
|
||||
automount_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Automount *s = AUTOMOUNT(u);
|
||||
static int automount_coldplug(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(a);
|
||||
assert(a->state == AUTOMOUNT_DEAD);
|
||||
|
||||
if (a->deserialized_state != a->state) {
|
||||
|
||||
if ((r = open_dev_autofs(u->meta.manager)) < 0)
|
||||
return r;
|
||||
|
||||
if (a->deserialized_state == AUTOMOUNT_WAITING ||
|
||||
a->deserialized_state == AUTOMOUNT_RUNNING) {
|
||||
|
||||
assert(a->pipe_fd >= 0);
|
||||
|
||||
if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
automount_set_state(a, a->deserialized_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(a);
|
||||
|
||||
fprintf(f,
|
||||
"%sAutomount State: %s\n",
|
||||
prefix, state_string_table[s->state]);
|
||||
"%sAutomount State: %s\n"
|
||||
"%sWhere: %s\n",
|
||||
prefix, automount_state_to_string(a->state),
|
||||
prefix, a->where);
|
||||
}
|
||||
|
||||
static void automount_enter_dead(Automount *a, bool success) {
|
||||
@ -208,7 +232,7 @@ static int open_dev_autofs(Manager *m) {
|
||||
if (m->dev_autofs_fd >= 0)
|
||||
return m->dev_autofs_fd;
|
||||
|
||||
if ((m->dev_autofs_fd = open("/dev/autofs", O_RDONLY)) < 0) {
|
||||
if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) {
|
||||
log_error("Failed to open /dev/autofs: %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
@ -254,6 +278,7 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fd_cloexec(param->ioctlfd, true);
|
||||
r = param->ioctlfd;
|
||||
|
||||
finish:
|
||||
@ -383,10 +408,6 @@ static void automount_enter_waiting(Automount *a) {
|
||||
|
||||
if (a->tokens)
|
||||
set_clear(a->tokens);
|
||||
else if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->meta.manager)) < 0) {
|
||||
r = dev_autofs_fd;
|
||||
@ -396,7 +417,7 @@ static void automount_enter_waiting(Automount *a) {
|
||||
/* We knowingly ignore the results of this call */
|
||||
mkdir_p(a->where, 0555);
|
||||
|
||||
if (pipe2(p, O_NONBLOCK) < 0) {
|
||||
if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
@ -521,7 +542,94 @@ static int automount_stop(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
void *p;
|
||||
Iterator i;
|
||||
|
||||
assert(a);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", automount_state_to_string(a->state));
|
||||
unit_serialize_item(u, f, "failure", yes_no(a->failure));
|
||||
unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id);
|
||||
|
||||
SET_FOREACH(p, a->tokens, i)
|
||||
unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
|
||||
|
||||
if (a->pipe_fd >= 0) {
|
||||
int copy;
|
||||
|
||||
if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0)
|
||||
return copy;
|
||||
|
||||
unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
AutomountState state;
|
||||
|
||||
if ((state = automount_state_from_string(value)) < 0)
|
||||
log_debug("Failed to parse state value %s", value);
|
||||
else
|
||||
a->deserialized_state = state;
|
||||
} else if (streq(key, "failure")) {
|
||||
int b;
|
||||
|
||||
if ((b = parse_boolean(value)) < 0)
|
||||
log_debug("Failed to parse failure value %s", value);
|
||||
else
|
||||
a->failure = b || a->failure;
|
||||
} else if (streq(key, "dev-id")) {
|
||||
unsigned d;
|
||||
|
||||
if (safe_atou(value, &d) < 0)
|
||||
log_debug("Failed to parse dev-id value %s", value);
|
||||
else
|
||||
a->dev_id = (unsigned) d;
|
||||
} else if (streq(key, "token")) {
|
||||
unsigned token;
|
||||
|
||||
if (safe_atou(value, &token) < 0)
|
||||
log_debug("Failed to parse token value %s", value);
|
||||
else {
|
||||
if (!a->tokens)
|
||||
if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func)))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0)
|
||||
return r;
|
||||
}
|
||||
} else if (streq(key, "pipe-fd")) {
|
||||
int fd;
|
||||
|
||||
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
|
||||
log_debug("Failed to parse pipe-fd value %s", value);
|
||||
else {
|
||||
if (a->pipe_fd >= 0)
|
||||
close_nointr_nofail(a->pipe_fd);
|
||||
|
||||
a->pipe_fd = fdset_remove(fds, fd);
|
||||
}
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState automount_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_translation_table[AUTOMOUNT(u)->state];
|
||||
}
|
||||
@ -529,7 +637,7 @@ static UnitActiveState automount_active_state(Unit *u) {
|
||||
static const char *automount_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_string_table[AUTOMOUNT(u)->state];
|
||||
return automount_state_to_string(AUTOMOUNT(u)->state);
|
||||
}
|
||||
|
||||
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
|
||||
@ -557,6 +665,12 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
|
||||
case autofs_ptype_missing_direct:
|
||||
log_debug("Got direct mount request for %s", packet.v5_packet.name);
|
||||
|
||||
if (!a->tokens)
|
||||
if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
|
||||
log_error("Failed to allocate token set.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) {
|
||||
log_error("Failed to remember token: %s", strerror(-r));
|
||||
goto fail;
|
||||
@ -583,6 +697,15 @@ static void automount_shutdown(Manager *m) {
|
||||
close_nointr_nofail(m->dev_autofs_fd);
|
||||
}
|
||||
|
||||
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
|
||||
[AUTOMOUNT_DEAD] = "dead",
|
||||
[AUTOMOUNT_WAITING] = "waiting",
|
||||
[AUTOMOUNT_RUNNING] = "running",
|
||||
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
|
||||
|
||||
const UnitVTable automount_vtable = {
|
||||
.suffix = ".automount",
|
||||
|
||||
@ -593,11 +716,16 @@ const UnitVTable automount_vtable = {
|
||||
.load = automount_load,
|
||||
.done = automount_done,
|
||||
|
||||
.coldplug = automount_coldplug,
|
||||
|
||||
.dump = automount_dump,
|
||||
|
||||
.start = automount_start,
|
||||
.stop = automount_stop,
|
||||
|
||||
.serialize = automount_serialize,
|
||||
.deserialize_item = automount_deserialize_item,
|
||||
|
||||
.active_state = automount_active_state,
|
||||
.sub_state_to_string = automount_sub_state_to_string,
|
||||
|
||||
|
@ -38,7 +38,7 @@ typedef enum AutomountState {
|
||||
struct Automount {
|
||||
Meta meta;
|
||||
|
||||
AutomountState state;
|
||||
AutomountState state, deserialized_state;
|
||||
|
||||
char *where;
|
||||
|
||||
@ -57,4 +57,7 @@ extern const UnitVTable automount_vtable;
|
||||
|
||||
int automount_send_ready(Automount *a, int status);
|
||||
|
||||
const char* automount_state_to_string(AutomountState i);
|
||||
AutomountState automount_state_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
|
11
cgroup.c
11
cgroup.c
@ -478,7 +478,7 @@ int manager_setup_cgroup(Manager *m) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_shutdown_cgroup(Manager *m) {
|
||||
int manager_shutdown_cgroup(Manager *m, bool delete) {
|
||||
struct cgroup *cg;
|
||||
int r;
|
||||
|
||||
@ -495,11 +495,10 @@ int manager_shutdown_cgroup(Manager *m) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
|
||||
log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
|
||||
r = translate_error(r, errno);
|
||||
goto finish;
|
||||
}
|
||||
/* Often enough we won't be able to delete the cgroup we
|
||||
* ourselves are in, hence ignore all errors here */
|
||||
if (delete)
|
||||
cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE);
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
|
2
cgroup.h
2
cgroup.h
@ -75,7 +75,7 @@ char *cgroup_bonding_to_string(CGroupBonding *b);
|
||||
#include "manager.h"
|
||||
|
||||
int manager_setup_cgroup(Manager *m);
|
||||
int manager_shutdown_cgroup(Manager *m);
|
||||
int manager_shutdown_cgroup(Manager *m, bool delete);
|
||||
|
||||
int cgroup_notify_empty(Manager *m, const char *group);
|
||||
|
||||
|
@ -56,6 +56,9 @@
|
||||
" <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"Reload\"/>" \
|
||||
" <method name=\"Reexecute\"/>" \
|
||||
" <method name=\"Exit\"/>" \
|
||||
" <signal name=\"UnitNew\">" \
|
||||
" <arg name=\"id\" type=\"s\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\"/>" \
|
||||
@ -504,6 +507,37 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection
|
||||
|
||||
free(introspection);
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
|
||||
|
||||
assert(!m->queued_message);
|
||||
|
||||
/* Instead of sending the reply back right away, we
|
||||
* just remember that we need to and then send it
|
||||
* after the reload is finished. That way the caller
|
||||
* knows when the reload finished. */
|
||||
|
||||
if (!(m->queued_message = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
m->exit_code = MANAGER_RELOAD;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
m->exit_code = MANAGER_REEXECUTE;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
|
||||
|
||||
if (m->running_as == MANAGER_INIT)
|
||||
return bus_send_error_reply(m, message, NULL, -ENOTSUP);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
m->exit_code = MANAGER_EXIT;
|
||||
|
||||
} else
|
||||
return bus_default_message_handler(m, message, NULL, properties);
|
||||
|
||||
|
17
dbus.c
17
dbus.c
@ -381,6 +381,18 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection,
|
||||
unsigned bus_dispatch(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->queued_message) {
|
||||
/* If we cannot get rid of this message we won't
|
||||
* dispatch any D-Bus messages, so that we won't end
|
||||
* up wanting to queue another message. */
|
||||
|
||||
if (!dbus_connection_send(m->api_bus, m->queued_message, NULL))
|
||||
return 0;
|
||||
|
||||
dbus_message_unref(m->queued_message);
|
||||
m->queued_message = NULL;
|
||||
}
|
||||
|
||||
if (m->request_api_bus_dispatch) {
|
||||
if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE)
|
||||
m->request_api_bus_dispatch = false;
|
||||
@ -655,6 +667,11 @@ void bus_done_api(Manager *m) {
|
||||
|
||||
if (m->name_data_slot >= 0)
|
||||
dbus_pending_call_free_data_slot(&m->name_data_slot);
|
||||
|
||||
if (m->queued_message) {
|
||||
dbus_message_unref(m->queued_message);
|
||||
m->queued_message = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bus_done_system(Manager *m) {
|
||||
|
87
device.c
87
device.c
@ -35,11 +35,6 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
|
||||
[DEVICE_AVAILABLE] = UNIT_ACTIVE
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_DEVICE_STATE_MAX] = {
|
||||
[DEVICE_DEAD] = "dead",
|
||||
[DEVICE_AVAILABLE] = "available"
|
||||
};
|
||||
|
||||
static void device_done(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
@ -49,15 +44,6 @@ static void device_done(Unit *u) {
|
||||
d->sysfs = NULL;
|
||||
}
|
||||
|
||||
static void device_init(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
d->state = 0;
|
||||
}
|
||||
|
||||
static void device_set_state(Device *d, DeviceState state) {
|
||||
DeviceState old_state;
|
||||
assert(d);
|
||||
@ -66,7 +52,10 @@ static void device_set_state(Device *d, DeviceState state) {
|
||||
d->state = state;
|
||||
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", UNIT(d)->meta.id, state_string_table[old_state], state_string_table[state]);
|
||||
log_debug("%s changed %s → %s",
|
||||
UNIT(d)->meta.id,
|
||||
device_state_to_string(old_state),
|
||||
device_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
@ -91,7 +80,7 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
fprintf(f,
|
||||
"%sDevice State: %s\n"
|
||||
"%sSysfs Path: %s\n",
|
||||
prefix, state_string_table[d->state],
|
||||
prefix, device_state_to_string(d->state),
|
||||
prefix, strna(d->sysfs));
|
||||
}
|
||||
|
||||
@ -104,7 +93,7 @@ static UnitActiveState device_active_state(Unit *u) {
|
||||
static const char *device_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_string_table[DEVICE(u)->state];
|
||||
return device_state_to_string(DEVICE(u)->state);
|
||||
}
|
||||
|
||||
static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
|
||||
@ -115,7 +104,7 @@ static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
|
||||
assert(dn);
|
||||
assert(dn[0] == '/');
|
||||
|
||||
if (!(e = unit_name_build_escape(dn+1, NULL, ".device")))
|
||||
if (!(e = unit_name_from_path(dn, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
r = unit_add_name(u, e);
|
||||
@ -140,7 +129,7 @@ static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
|
||||
assert(dn[0] == '/');
|
||||
assert(_u);
|
||||
|
||||
if (!(e = unit_name_build_escape(dn+1, NULL, ".device")))
|
||||
if (!(e = unit_name_from_path(dn, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
u = manager_get_unit(m, e);
|
||||
@ -308,7 +297,7 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) {
|
||||
return -ENOMEM;
|
||||
|
||||
assert(sysfs[0] == '/');
|
||||
if (!(e = unit_name_build_escape(sysfs+1, NULL, ".device")))
|
||||
if (!(e = unit_name_from_path(sysfs, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
u = manager_get_unit(m, e);
|
||||
@ -328,11 +317,15 @@ static int device_process_removed_device(Manager *m, struct udev_device *dev) {
|
||||
static void device_shutdown(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->udev_monitor)
|
||||
if (m->udev_monitor) {
|
||||
udev_monitor_unref(m->udev_monitor);
|
||||
m->udev_monitor = NULL;
|
||||
}
|
||||
|
||||
if (m->udev)
|
||||
if (m->udev) {
|
||||
udev_unref(m->udev);
|
||||
m->udev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int device_enumerate(Manager *m) {
|
||||
@ -343,29 +336,31 @@ static int device_enumerate(Manager *m) {
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!(m->udev = udev_new()))
|
||||
return -ENOMEM;
|
||||
if (!m->udev) {
|
||||
if (!(m->udev = udev_new()))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
|
||||
r = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m->udev_watch.type = WATCH_UDEV;
|
||||
m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
|
||||
|
||||
zero(ev);
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = &m->udev_watch;
|
||||
|
||||
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
|
||||
r = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m->udev_watch.type = WATCH_UDEV;
|
||||
m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
|
||||
|
||||
zero(ev);
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = &m->udev_watch;
|
||||
|
||||
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;
|
||||
@ -425,6 +420,13 @@ fail:
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
|
||||
static const char* const device_state_table[_DEVICE_STATE_MAX] = {
|
||||
[DEVICE_DEAD] = "dead",
|
||||
[DEVICE_AVAILABLE] = "available"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
|
||||
|
||||
const UnitVTable device_vtable = {
|
||||
.suffix = ".device",
|
||||
|
||||
@ -432,7 +434,6 @@ const UnitVTable device_vtable = {
|
||||
.no_instances = true,
|
||||
.no_snapshots = true,
|
||||
|
||||
.init = device_init,
|
||||
.load = unit_load_fragment_and_dropin_optional,
|
||||
.done = device_done,
|
||||
.coldplug = device_coldplug,
|
||||
|
3
device.h
3
device.h
@ -47,4 +47,7 @@ extern const UnitVTable device_vtable;
|
||||
|
||||
void device_fd_event(Manager *m, int events);
|
||||
|
||||
const char* device_state_to_string(DeviceState i);
|
||||
DeviceState device_state_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
|
21
execute.c
21
execute.c
@ -1063,30 +1063,9 @@ void exec_context_init(ExecContext *c) {
|
||||
assert(c);
|
||||
|
||||
c->umask = 0002;
|
||||
c->oom_adjust = 0;
|
||||
c->oom_adjust_set = false;
|
||||
c->nice = 0;
|
||||
c->nice_set = false;
|
||||
c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
|
||||
c->ioprio_set = false;
|
||||
c->cpu_sched_policy = SCHED_OTHER;
|
||||
c->cpu_sched_priority = 0;
|
||||
c->cpu_sched_set = false;
|
||||
CPU_ZERO(&c->cpu_affinity);
|
||||
c->cpu_affinity_set = false;
|
||||
c->timer_slack_ns = 0;
|
||||
c->timer_slack_ns_set = false;
|
||||
|
||||
c->cpu_sched_reset_on_fork = false;
|
||||
c->non_blocking = false;
|
||||
|
||||
c->std_input = 0;
|
||||
c->std_output = 0;
|
||||
c->std_error = 0;
|
||||
c->syslog_priority = LOG_DAEMON|LOG_INFO;
|
||||
|
||||
c->secure_bits = 0;
|
||||
c->capability_bounding_set_drop = 0;
|
||||
}
|
||||
|
||||
void exec_context_done(ExecContext *c) {
|
||||
|
162
fdset.c
Normal file
162
fdset.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*-*- 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 <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "set.h"
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
#include "fdset.h"
|
||||
|
||||
#define MAKE_SET(s) ((Set*) s)
|
||||
#define MAKE_FDSET(s) ((FDSet*) s)
|
||||
|
||||
/* Make sure we can distuingish fd 0 and NULL */
|
||||
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
|
||||
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
|
||||
|
||||
FDSet *fdset_new(void) {
|
||||
return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func));
|
||||
}
|
||||
|
||||
void fdset_free(FDSet *s) {
|
||||
void *p;
|
||||
|
||||
while ((p = set_steal_first(MAKE_SET(s)))) {
|
||||
/* Valgrind's fd might have ended up in this set here,
|
||||
* due to fdset_new_fill(). We'll ignore all failures
|
||||
* here, so that the EBADFD that valgrind will return
|
||||
* us on close() doesn't influence us */
|
||||
|
||||
log_warning("Closing left-over fd %i", PTR_TO_FD(p));
|
||||
close_nointr(PTR_TO_FD(p));
|
||||
}
|
||||
|
||||
set_free(MAKE_SET(s));
|
||||
}
|
||||
|
||||
int fdset_put(FDSet *s, int fd) {
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
return set_put(MAKE_SET(s), FD_TO_PTR(fd));
|
||||
}
|
||||
|
||||
int fdset_put_dup(FDSet *s, int fd) {
|
||||
int copy, r;
|
||||
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((r = fdset_put(s, copy)) < 0) {
|
||||
close_nointr_nofail(copy);
|
||||
return r;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool fdset_contains(FDSet *s, int fd) {
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
|
||||
}
|
||||
|
||||
int fdset_remove(FDSet *s, int fd) {
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
|
||||
}
|
||||
|
||||
int fdset_new_fill(FDSet **_s) {
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
int r = 0;
|
||||
FDSet *s;
|
||||
|
||||
assert(_s);
|
||||
|
||||
/* Creates an fdsets and fills in all currently open file
|
||||
* descriptors. */
|
||||
|
||||
if (!(d = opendir("/proc/self/fd")))
|
||||
return -errno;
|
||||
|
||||
if (!(s = fdset_new())) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
int fd = -1;
|
||||
|
||||
if (ignore_file(de->d_name))
|
||||
continue;
|
||||
|
||||
if ((r = safe_atoi(de->d_name, &fd)) < 0)
|
||||
goto finish;
|
||||
|
||||
if (fd < 3)
|
||||
continue;
|
||||
|
||||
if (fd == dirfd(d))
|
||||
continue;
|
||||
|
||||
if ((r = fdset_put(s, fd)) < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
*_s = s;
|
||||
s = NULL;
|
||||
|
||||
finish:
|
||||
closedir(d);
|
||||
|
||||
/* We won't close the fds here! */
|
||||
if (s)
|
||||
set_free(MAKE_SET(s));
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
int fdset_cloexec(FDSet *fds, bool b) {
|
||||
Iterator i;
|
||||
void *p;
|
||||
int r;
|
||||
|
||||
assert(fds);
|
||||
|
||||
SET_FOREACH(p, MAKE_SET(fds), i)
|
||||
if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
40
fdset.h
Normal file
40
fdset.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foofdsethfoo
|
||||
#define foofdsethfoo
|
||||
|
||||
/***
|
||||
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 struct FDSet FDSet;
|
||||
|
||||
FDSet* fdset_new(void);
|
||||
void fdset_free(FDSet *s);
|
||||
|
||||
int fdset_put(FDSet *s, int fd);
|
||||
int fdset_put_dup(FDSet *s, int fd);
|
||||
|
||||
bool fdset_contains(FDSet *s, int fd);
|
||||
int fdset_remove(FDSet *s, int fd);
|
||||
|
||||
int fdset_new_fill(FDSet **_s);
|
||||
|
||||
int fdset_cloexec(FDSet *fds, bool b);
|
||||
|
||||
#endif
|
@ -246,7 +246,7 @@ static void fifo_free(Fifo *f) {
|
||||
if (f->server)
|
||||
epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
|
||||
|
||||
assert_se(close_nointr(f->fd) == 0);
|
||||
close_nointr_nofail(f->fd);
|
||||
}
|
||||
|
||||
free(f);
|
||||
@ -302,7 +302,7 @@ static void server_done(Server *s) {
|
||||
fifo_free(s->fifos);
|
||||
|
||||
if (s->epoll_fd >= 0)
|
||||
assert_se(close_nointr(s->epoll_fd) == 0);
|
||||
close_nointr_nofail(s->epoll_fd);
|
||||
|
||||
if (s->bus)
|
||||
dbus_connection_unref(s->bus);
|
||||
|
4
log.c
4
log.c
@ -43,7 +43,7 @@ static int kmsg_fd = -1;
|
||||
void log_close_kmsg(void) {
|
||||
|
||||
if (kmsg_fd >= 0) {
|
||||
close_nointr(kmsg_fd);
|
||||
close_nointr_nofail(kmsg_fd);
|
||||
kmsg_fd = -1;
|
||||
}
|
||||
}
|
||||
@ -71,7 +71,7 @@ int log_open_kmsg(void) {
|
||||
void log_close_syslog(void) {
|
||||
|
||||
if (syslog_fd >= 0) {
|
||||
close_nointr(syslog_fd);
|
||||
close_nointr_nofail(syslog_fd);
|
||||
syslog_fd = -1;
|
||||
}
|
||||
}
|
||||
|
14
logger.c
14
logger.c
@ -283,7 +283,7 @@ static void stream_free(Stream *s) {
|
||||
if (s->server)
|
||||
epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL);
|
||||
|
||||
assert_se(close_nointr(s->fd) == 0);
|
||||
close_nointr_nofail(s->fd);
|
||||
}
|
||||
|
||||
free(s->process);
|
||||
@ -305,12 +305,12 @@ static int stream_new(Server *s, int server_fd) {
|
||||
|
||||
if (s->n_streams >= STREAMS_MAX) {
|
||||
log_warning("Too many connections, refusing connection.");
|
||||
assert_se(close_nointr(fd) == 0);
|
||||
close_nointr_nofail(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(stream = new0(Stream, 1))) {
|
||||
assert_se(close_nointr(fd) == 0);
|
||||
close_nointr_nofail(fd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -399,16 +399,16 @@ static void server_done(Server *s) {
|
||||
stream_free(s->streams);
|
||||
|
||||
for (i = 0; i < s->n_server_fd; i++)
|
||||
assert_se(close_nointr(SERVER_FD_START+i) == 0);
|
||||
close_nointr_nofail(SERVER_FD_START+i);
|
||||
|
||||
if (s->syslog_fd >= 0)
|
||||
assert_se(close_nointr(s->syslog_fd) == 0);
|
||||
close_nointr_nofail(s->syslog_fd);
|
||||
|
||||
if (s->epoll_fd >= 0)
|
||||
assert_se(close_nointr(s->epoll_fd) == 0);
|
||||
close_nointr_nofail(s->epoll_fd);
|
||||
|
||||
if (s->kmsg_fd >= 0)
|
||||
assert_se(close_nointr(s->kmsg_fd) == 0);
|
||||
close_nointr_nofail(s->kmsg_fd);
|
||||
}
|
||||
|
||||
static int server_init(Server *s, unsigned n_sockets) {
|
||||
|
268
main.c
268
main.c
@ -37,6 +37,7 @@
|
||||
#include "mount-setup.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "load-fragment.h"
|
||||
#include "fdset.h"
|
||||
|
||||
static enum {
|
||||
ACTION_RUN,
|
||||
@ -51,8 +52,8 @@ static ManagerRunningAs running_as = _MANAGER_RUNNING_AS_INVALID;
|
||||
static bool dump_core = true;
|
||||
static bool crash_shell = false;
|
||||
static int crash_chvt = -1;
|
||||
|
||||
static bool confirm_spawn = false;
|
||||
static FILE* serialization = NULL;
|
||||
|
||||
_noreturn static void freeze(void) {
|
||||
for (;;)
|
||||
@ -202,10 +203,10 @@ static int console_setup(void) {
|
||||
|
||||
finish:
|
||||
if (tty_fd >= 0)
|
||||
close_nointr(tty_fd);
|
||||
close_nointr_nofail(tty_fd);
|
||||
|
||||
if (null_fd >= 0)
|
||||
close_nointr(null_fd);
|
||||
close_nointr_nofail(null_fd);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -351,19 +352,21 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_RUNNING_AS,
|
||||
ARG_TEST,
|
||||
ARG_DUMP_CONFIGURATION_ITEMS,
|
||||
ARG_CONFIRM_SPAWN
|
||||
ARG_CONFIRM_SPAWN,
|
||||
ARG_DESERIALIZE
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
|
||||
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
|
||||
{ "default", required_argument, NULL, ARG_DEFAULT },
|
||||
{ "running-as", required_argument, NULL, ARG_RUNNING_AS },
|
||||
{ "test", no_argument, NULL, ARG_TEST },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
|
||||
{ "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
|
||||
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
|
||||
{ "default", required_argument, NULL, ARG_DEFAULT },
|
||||
{ "running-as", required_argument, NULL, ARG_RUNNING_AS },
|
||||
{ "test", no_argument, NULL, ARG_TEST },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
|
||||
{ "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN },
|
||||
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c, r;
|
||||
@ -425,6 +428,28 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
confirm_spawn = true;
|
||||
break;
|
||||
|
||||
case ARG_DESERIALIZE: {
|
||||
int fd;
|
||||
FILE *f;
|
||||
|
||||
if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
|
||||
log_error("Failed to parse deserialize option %s.", optarg);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!(f = fdopen(fd, "r"))) {
|
||||
log_error("Failed to open serialization fd: %m");
|
||||
return r;
|
||||
}
|
||||
|
||||
if (serialization)
|
||||
fclose(serialization);
|
||||
|
||||
serialization = f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'h':
|
||||
action = ACTION_HELP;
|
||||
break;
|
||||
@ -456,11 +481,67 @@ static int help(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) {
|
||||
FILE *f = NULL;
|
||||
FDSet *fds = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(_f);
|
||||
assert(_fds);
|
||||
|
||||
if ((r = manager_open_serialization(&f)) < 0) {
|
||||
log_error("Failed to create serialization faile: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(fds = fdset_new())) {
|
||||
r = -ENOMEM;
|
||||
log_error("Failed to allocate fd set: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = manager_serialize(m, f, fds)) < 0) {
|
||||
log_error("Failed to serialize state: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fseeko(f, 0, SEEK_SET) < 0) {
|
||||
log_error("Failed to rewind serialization fd: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = fd_cloexec(fileno(f), false)) < 0) {
|
||||
log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = fdset_cloexec(fds, false)) < 0) {
|
||||
log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*_f = f;
|
||||
*_fds = fds;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fdset_free(fds);
|
||||
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Manager *m = NULL;
|
||||
Unit *target = NULL;
|
||||
Job *job = NULL;
|
||||
int r, retval = 1;
|
||||
FDSet *fds = NULL;
|
||||
bool reexecute = false;
|
||||
|
||||
if (getpid() == 1)
|
||||
running_as = MANAGER_INIT;
|
||||
@ -484,9 +565,6 @@ int main(int argc, char *argv[]) {
|
||||
ignore_signal(SIGKILL);
|
||||
ignore_signal(SIGPIPE);
|
||||
|
||||
/* Close all open files */
|
||||
assert_se(close_all_fds(NULL, 0) == 0);
|
||||
|
||||
if (running_as != MANAGER_SESSION)
|
||||
if (parse_proc_cmdline() < 0)
|
||||
goto finish;
|
||||
@ -507,6 +585,17 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
assert_se(action == ACTION_RUN || action == ACTION_TEST);
|
||||
|
||||
/* Remember open file descriptors for later deserialization */
|
||||
if (serialization) {
|
||||
if ((r = fdset_new_fill(&fds)) < 0) {
|
||||
log_error("Failed to allocate fd set: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
|
||||
} else
|
||||
close_all_fds(NULL, 0);
|
||||
|
||||
/* Set up PATH unless it is already set */
|
||||
setenv("PATH",
|
||||
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
@ -548,53 +637,83 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = manager_coldplug(m)) < 0) {
|
||||
log_error("Failed to retrieve coldplug information: %s", strerror(-r));
|
||||
goto finish;
|
||||
if ((r = manager_startup(m, serialization, fds)) < 0)
|
||||
log_error("Failed to fully startup daemon: %s", strerror(-r));
|
||||
|
||||
if (fds) {
|
||||
/* This will close all file descriptors that were opened, but
|
||||
* not claimed by any unit. */
|
||||
|
||||
fdset_free(fds);
|
||||
fds = NULL;
|
||||
}
|
||||
|
||||
log_debug("Activating default unit: %s", default_unit);
|
||||
if (serialization) {
|
||||
fclose(serialization);
|
||||
serialization = NULL;
|
||||
} else {
|
||||
log_debug("Activating default unit: %s", default_unit);
|
||||
|
||||
if ((r = manager_load_unit(m, default_unit, NULL, &target)) < 0) {
|
||||
log_error("Failed to load default target: %s", strerror(-r));
|
||||
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, NULL, &target)) < 0) {
|
||||
log_error("Failed to load rescue target: %s", strerror(-r));
|
||||
log_info("Trying to load rescue target...");
|
||||
if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &target)) < 0) {
|
||||
log_error("Failed to load rescue target: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == ACTION_TEST) {
|
||||
printf("→ By units:\n");
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
}
|
||||
|
||||
if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) {
|
||||
log_error("Failed to start default target: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (action == ACTION_TEST) {
|
||||
printf("→ By jobs:\n");
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
retval = 0;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == ACTION_TEST) {
|
||||
printf("→ By units:\n");
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
for (;;) {
|
||||
if ((r = manager_loop(m)) < 0) {
|
||||
log_error("Failed to run mainloop: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
switch (m->exit_code) {
|
||||
|
||||
case MANAGER_EXIT:
|
||||
retval = 0;
|
||||
log_debug("Exit.");
|
||||
goto finish;
|
||||
|
||||
case MANAGER_RELOAD:
|
||||
if ((r = manager_reload(m)) < 0)
|
||||
log_error("Failed to reload: %s", strerror(-r));
|
||||
break;
|
||||
|
||||
case MANAGER_REEXECUTE:
|
||||
|
||||
if (prepare_reexecute(m, &serialization, &fds) < 0)
|
||||
goto finish;
|
||||
|
||||
reexecute = true;
|
||||
log_debug("Reexecuting.");
|
||||
goto finish;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown exit code.");
|
||||
}
|
||||
}
|
||||
|
||||
if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) {
|
||||
log_error("Failed to start default target: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (action == ACTION_TEST) {
|
||||
printf("→ By jobs:\n");
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
if (getpid() == 1)
|
||||
pause();
|
||||
|
||||
retval = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = manager_loop(m)) < 0) {
|
||||
log_error("Failed to run mainloop: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
log_debug("Exit.");
|
||||
|
||||
finish:
|
||||
if (m)
|
||||
manager_free(m);
|
||||
@ -603,6 +722,49 @@ finish:
|
||||
|
||||
dbus_shutdown();
|
||||
|
||||
if (reexecute) {
|
||||
const char *args[11];
|
||||
unsigned i = 0;
|
||||
char sfd[16];
|
||||
|
||||
assert(serialization);
|
||||
assert(fds);
|
||||
|
||||
args[i++] = SYSTEMD_BINARY_PATH;
|
||||
|
||||
args[i++] = "--log-level";
|
||||
args[i++] = log_level_to_string(log_get_max_level());
|
||||
|
||||
args[i++] = "--log-target";
|
||||
args[i++] = log_target_to_string(log_get_target());
|
||||
|
||||
args[i++] = "--running-as";
|
||||
args[i++] = manager_running_as_to_string(running_as);
|
||||
|
||||
snprintf(sfd, sizeof(sfd), "%i", fileno(serialization));
|
||||
char_array_0(sfd);
|
||||
|
||||
args[i++] = "--deserialize";
|
||||
args[i++] = sfd;
|
||||
|
||||
if (confirm_spawn)
|
||||
args[i++] = "--confirm-spawn";
|
||||
|
||||
args[i++] = NULL;
|
||||
|
||||
assert(i <= ELEMENTSOF(args));
|
||||
|
||||
execv(args[0], (char* const*) args);
|
||||
|
||||
log_error("Failed to reexecute: %m");
|
||||
}
|
||||
|
||||
if (serialization)
|
||||
fclose(serialization);
|
||||
|
||||
if (fds)
|
||||
fdset_free(fds);
|
||||
|
||||
if (getpid() == 1)
|
||||
freeze();
|
||||
|
||||
|
252
manager.c
252
manager.c
@ -35,6 +35,8 @@
|
||||
#include <libcgroup.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "manager.h"
|
||||
#include "hashmap.h"
|
||||
@ -323,6 +325,7 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) {
|
||||
m->running_as = running_as;
|
||||
m->confirm_spawn = confirm_spawn;
|
||||
m->name_data_slot = -1;
|
||||
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
|
||||
|
||||
m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1;
|
||||
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
|
||||
@ -386,10 +389,9 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) {
|
||||
return n;
|
||||
}
|
||||
|
||||
void manager_free(Manager *m) {
|
||||
UnitType c;
|
||||
Unit *u;
|
||||
static void manager_clear_jobs_and_units(Manager *m) {
|
||||
Job *j;
|
||||
Unit *u;
|
||||
|
||||
assert(m);
|
||||
|
||||
@ -398,14 +400,22 @@ void manager_free(Manager *m) {
|
||||
|
||||
while ((u = hashmap_first(m->units)))
|
||||
unit_free(u);
|
||||
}
|
||||
|
||||
manager_dispatch_cleanup_queue(m);
|
||||
void manager_free(Manager *m) {
|
||||
UnitType c;
|
||||
|
||||
assert(m);
|
||||
|
||||
manager_clear_jobs_and_units(m);
|
||||
|
||||
for (c = 0; c < _UNIT_TYPE_MAX; c++)
|
||||
if (unit_vtable[c]->shutdown)
|
||||
unit_vtable[c]->shutdown(m);
|
||||
|
||||
manager_shutdown_cgroup(m);
|
||||
/* If we reexecute ourselves, we keep the root cgroup
|
||||
* around */
|
||||
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
|
||||
|
||||
bus_done_api(m);
|
||||
bus_done_system(m);
|
||||
@ -417,9 +427,9 @@ void manager_free(Manager *m) {
|
||||
hashmap_free(m->watch_bus);
|
||||
|
||||
if (m->epoll_fd >= 0)
|
||||
close_nointr(m->epoll_fd);
|
||||
close_nointr_nofail(m->epoll_fd);
|
||||
if (m->signal_watch.fd >= 0)
|
||||
close_nointr(m->signal_watch.fd);
|
||||
close_nointr_nofail(m->signal_watch.fd);
|
||||
|
||||
strv_free(m->unit_path);
|
||||
strv_free(m->sysvinit_path);
|
||||
@ -428,30 +438,36 @@ void manager_free(Manager *m) {
|
||||
free(m->cgroup_controller);
|
||||
free(m->cgroup_hierarchy);
|
||||
|
||||
assert(hashmap_isempty(m->cgroup_bondings));
|
||||
hashmap_free(m->cgroup_bondings);
|
||||
|
||||
free(m);
|
||||
}
|
||||
|
||||
int manager_coldplug(Manager *m) {
|
||||
int r;
|
||||
int manager_enumerate(Manager *m) {
|
||||
int r = 0, q;
|
||||
UnitType c;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Let's ask every type to load all units from disk/kernel
|
||||
* that it might know */
|
||||
for (c = 0; c < _UNIT_TYPE_MAX; c++)
|
||||
if (unit_vtable[c]->enumerate)
|
||||
if ((q = unit_vtable[c]->enumerate(m)) < 0)
|
||||
r = q;
|
||||
|
||||
manager_dispatch_load_queue(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_coldplug(Manager *m) {
|
||||
int r = 0, q;
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
char *k;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* First, let's ask every type to load all units from
|
||||
* disk/kernel that it might know */
|
||||
for (c = 0; c < _UNIT_TYPE_MAX; c++)
|
||||
if (unit_vtable[c]->enumerate)
|
||||
if ((r = unit_vtable[c]->enumerate(m)) < 0)
|
||||
return r;
|
||||
|
||||
manager_dispatch_load_queue(m);
|
||||
|
||||
/* Then, let's set up their initial state. */
|
||||
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
|
||||
|
||||
@ -460,15 +476,35 @@ int manager_coldplug(Manager *m) {
|
||||
continue;
|
||||
|
||||
if (UNIT_VTABLE(u)->coldplug)
|
||||
if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
|
||||
return r;
|
||||
if ((q = UNIT_VTABLE(u)->coldplug(u)) < 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
|
||||
int r, q;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* First, enumerate what we can from all config files */
|
||||
r = manager_enumerate(m);
|
||||
|
||||
/* Second, deserialize if there is something to deserialize */
|
||||
if (serialization)
|
||||
if ((q = manager_deserialize(m, serialization, fds)) < 0)
|
||||
r = q;
|
||||
|
||||
/* Third, fire things up! */
|
||||
if ((q = manager_coldplug(m)) < 0)
|
||||
r = q;
|
||||
|
||||
/* Now that the initial devices are available, let's see if we
|
||||
* can write the utmp file */
|
||||
manager_write_utmp_reboot(m);
|
||||
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
|
||||
@ -1553,7 +1589,7 @@ static void manager_start_target(Manager *m, const char *name) {
|
||||
log_error("Failed to enqueue %s job: %s", name, strerror(-r));
|
||||
}
|
||||
|
||||
static int manager_process_signal_fd(Manager *m, bool *quit) {
|
||||
static int manager_process_signal_fd(Manager *m) {
|
||||
ssize_t n;
|
||||
struct signalfd_siginfo sfsi;
|
||||
bool sigchld = false;
|
||||
@ -1586,7 +1622,7 @@ static int manager_process_signal_fd(Manager *m, bool *quit) {
|
||||
break;
|
||||
}
|
||||
|
||||
*quit = true;
|
||||
m->exit_code = MANAGER_EXIT;
|
||||
return 0;
|
||||
|
||||
case SIGWINCH:
|
||||
@ -1628,6 +1664,10 @@ static int manager_process_signal_fd(Manager *m, bool *quit) {
|
||||
break;
|
||||
}
|
||||
|
||||
case SIGHUP:
|
||||
m->exit_code = MANAGER_RELOAD;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo));
|
||||
}
|
||||
@ -1639,7 +1679,7 @@ static int manager_process_signal_fd(Manager *m, bool *quit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
|
||||
static int process_event(Manager *m, struct epoll_event *ev) {
|
||||
int r;
|
||||
Watch *w;
|
||||
|
||||
@ -1656,7 +1696,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
|
||||
if (ev->events != EPOLLIN)
|
||||
return -EINVAL;
|
||||
|
||||
if ((r = manager_process_signal_fd(m, quit)) < 0)
|
||||
if ((r = manager_process_signal_fd(m)) < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
@ -1711,13 +1751,13 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
|
||||
|
||||
int manager_loop(Manager *m) {
|
||||
int r;
|
||||
bool quit = false;
|
||||
|
||||
RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000);
|
||||
|
||||
assert(m);
|
||||
m->exit_code = MANAGER_RUNNING;
|
||||
|
||||
do {
|
||||
while (m->exit_code == MANAGER_RUNNING) {
|
||||
struct epoll_event event;
|
||||
int n;
|
||||
|
||||
@ -1752,11 +1792,11 @@ int manager_loop(Manager *m) {
|
||||
|
||||
assert(n == 1);
|
||||
|
||||
if ((r = process_event(m, &event, &quit)) < 0)
|
||||
if ((r = process_event(m, &event)) < 0)
|
||||
return r;
|
||||
} while (!quit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return m->exit_code;
|
||||
}
|
||||
|
||||
int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
|
||||
@ -1907,6 +1947,156 @@ void manager_dispatch_bus_query_pid_done(
|
||||
UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid);
|
||||
}
|
||||
|
||||
int manager_open_serialization(FILE **_f) {
|
||||
char *path;
|
||||
mode_t saved_umask;
|
||||
int fd;
|
||||
FILE *f;
|
||||
|
||||
assert(_f);
|
||||
|
||||
if (asprintf(&path, "/dev/shm/systemd-%u.dump-XXXXXX", (unsigned) getpid()) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
saved_umask = umask(0077);
|
||||
fd = mkostemp(path, O_RDWR|O_CLOEXEC);
|
||||
umask(saved_umask);
|
||||
|
||||
if (fd < 0) {
|
||||
free(path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
unlink(path);
|
||||
|
||||
log_debug("Serializing state to %s", path);
|
||||
free(path);
|
||||
|
||||
if (!(f = fdopen(fd, "w+")) < 0)
|
||||
return -errno;
|
||||
|
||||
*_f = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
const char *t;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
|
||||
if (u->meta.id != t)
|
||||
continue;
|
||||
|
||||
if (!unit_can_serialize(u))
|
||||
continue;
|
||||
|
||||
/* Start marker */
|
||||
fputs(u->meta.id, f);
|
||||
fputc('\n', f);
|
||||
|
||||
if ((r = unit_serialize(u, f, fds)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ferror(f))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
|
||||
int r = 0;
|
||||
|
||||
assert(m);
|
||||
assert(f);
|
||||
|
||||
log_debug("Deserializing state...");
|
||||
|
||||
for (;;) {
|
||||
Unit *u;
|
||||
char name[UNIT_NAME_MAX+2];
|
||||
|
||||
/* Start marker */
|
||||
if (!fgets(name, sizeof(name), f)) {
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
char_array_0(name);
|
||||
|
||||
if ((r = manager_load_unit(m, strstrip(name), NULL, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_deserialize(u, f, fds)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ferror(f))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_reload(Manager *m) {
|
||||
int r, q;
|
||||
FILE *f;
|
||||
FDSet *fds;
|
||||
|
||||
assert(m);
|
||||
|
||||
if ((r = manager_open_serialization(&f)) < 0)
|
||||
return r;
|
||||
|
||||
if (!(fds = fdset_new())) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = manager_serialize(m, f, fds)) < 0)
|
||||
goto finish;
|
||||
|
||||
if (fseeko(f, 0, SEEK_SET) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* From here on there is no way back. */
|
||||
manager_clear_jobs_and_units(m);
|
||||
|
||||
/* First, enumerate what we can from all config files */
|
||||
if ((q = manager_enumerate(m)) < 0)
|
||||
r = q;
|
||||
|
||||
/* Second, deserialize our stored data */
|
||||
if ((q = manager_deserialize(m, f, fds)) < 0)
|
||||
r = q;
|
||||
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
|
||||
/* Third, fire things up! */
|
||||
if ((q = manager_coldplug(m)) < 0)
|
||||
r = q;
|
||||
|
||||
finish:
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
if (fds)
|
||||
fdset_free(fds);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
|
||||
[MANAGER_INIT] = "init",
|
||||
[MANAGER_SYSTEM] = "system",
|
||||
|
27
manager.h
27
manager.h
@ -25,13 +25,23 @@
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "fdset.h"
|
||||
|
||||
typedef struct Manager Manager;
|
||||
typedef enum WatchType WatchType;
|
||||
typedef struct Watch Watch;
|
||||
|
||||
typedef enum ManagerExitCode {
|
||||
MANAGER_RUNNING,
|
||||
MANAGER_EXIT,
|
||||
MANAGER_RELOAD,
|
||||
MANAGER_REEXECUTE,
|
||||
_MANAGER_EXIT_CODE_MAX,
|
||||
_MANAGER_EXIT_CODE_INVALID = -1
|
||||
} ManagerExitCode;
|
||||
|
||||
typedef enum ManagerRunningAs {
|
||||
MANAGER_INIT, /* root and pid=1 */
|
||||
MANAGER_SYSTEM, /* root and pid!=1 */
|
||||
@ -155,6 +165,8 @@ struct Manager {
|
||||
|
||||
bool confirm_spawn:1;
|
||||
|
||||
ManagerExitCode exit_code;
|
||||
|
||||
Hashmap *watch_pids; /* pid => Unit object n:1 */
|
||||
|
||||
int epoll_fd;
|
||||
@ -179,6 +191,10 @@ struct Manager {
|
||||
/* Data specific to the D-Bus subsystem */
|
||||
DBusConnection *api_bus, *system_bus;
|
||||
Set *subscribed;
|
||||
DBusMessage *queued_message; /* This is used during reloading:
|
||||
* before the reload we queue the
|
||||
* reply message here, and
|
||||
* afterwards we send it */
|
||||
|
||||
Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */
|
||||
int32_t name_data_slot;
|
||||
@ -198,7 +214,9 @@ struct Manager {
|
||||
int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **m);
|
||||
void manager_free(Manager *m);
|
||||
|
||||
int manager_enumerate(Manager *m);
|
||||
int manager_coldplug(Manager *m);
|
||||
int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
|
||||
|
||||
Job *manager_get_job(Manager *m, uint32_t id);
|
||||
Unit *manager_get_unit(Manager *m, const char *name);
|
||||
@ -230,6 +248,13 @@ void manager_write_utmp_runlevel(Manager *m, Unit *t);
|
||||
void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner);
|
||||
void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid);
|
||||
|
||||
int manager_open_serialization(FILE **_f);
|
||||
|
||||
int manager_serialize(Manager *m, FILE *f, FDSet *fds);
|
||||
int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
|
||||
|
||||
int manager_reload(Manager *m);
|
||||
|
||||
const char *manager_running_as_to_string(ManagerRunningAs i);
|
||||
ManagerRunningAs manager_running_as_from_string(const char *s);
|
||||
|
||||
|
429
mount.c
429
mount.c
@ -52,32 +52,27 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
|
||||
[MOUNT_MAINTAINANCE] = UNIT_INACTIVE,
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_MOUNT_STATE_MAX] = {
|
||||
[MOUNT_DEAD] = "dead",
|
||||
[MOUNT_MOUNTING] = "mounting",
|
||||
[MOUNT_MOUNTING_DONE] = "mounting-done",
|
||||
[MOUNT_MOUNTED] = "mounted",
|
||||
[MOUNT_REMOUNTING] = "remounting",
|
||||
[MOUNT_UNMOUNTING] = "unmounting",
|
||||
[MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
|
||||
[MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
|
||||
[MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
|
||||
[MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
|
||||
[MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
|
||||
[MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
|
||||
[MOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
static void mount_init(Unit *u) {
|
||||
Mount *m = MOUNT(u);
|
||||
|
||||
static char *mount_name_from_where(const char *where) {
|
||||
assert(where);
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
if (streq(where, "/"))
|
||||
return strdup("-.mount");
|
||||
m->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
exec_context_init(&m->exec_context);
|
||||
|
||||
return unit_name_build_escape(where+1, NULL, ".mount");
|
||||
/* We need to make sure that /bin/mount is always called in
|
||||
* the same process group as us, so that the autofs kernel
|
||||
* side doesn't send us another mount request while we are
|
||||
* already trying to comply its last one. */
|
||||
m->exec_context.no_setsid = true;
|
||||
|
||||
m->timer_watch.type = WATCH_INVALID;
|
||||
|
||||
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
static void service_unwatch_control_pid(Mount *m) {
|
||||
static void mount_unwatch_control_pid(Mount *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->control_pid <= 0)
|
||||
@ -113,47 +108,11 @@ static void mount_done(Unit *u) {
|
||||
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
|
||||
m->control_command = NULL;
|
||||
|
||||
service_unwatch_control_pid(m);
|
||||
mount_unwatch_control_pid(m);
|
||||
|
||||
unit_unwatch_timer(u, &m->timer_watch);
|
||||
}
|
||||
|
||||
static void mount_init(Unit *u) {
|
||||
Mount *m = MOUNT(u);
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
m->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
exec_context_init(&m->exec_context);
|
||||
|
||||
/* We need to make sure that /bin/mount is always called in
|
||||
* the same process group as us, so that the autofs kernel
|
||||
* side doesn't send us another mount request while we are
|
||||
* already trying to comply its last one. */
|
||||
m->exec_context.no_setsid = true;
|
||||
|
||||
m->timer_watch.type = WATCH_INVALID;
|
||||
}
|
||||
|
||||
static int mount_notify_automount(Mount *m, int status) {
|
||||
Unit *p;
|
||||
char *k;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!(k = unit_name_change_suffix(UNIT(m)->meta.id, ".automount")))
|
||||
return -ENOMEM;
|
||||
|
||||
p = manager_get_unit(UNIT(m)->meta.manager, k);
|
||||
free(k);
|
||||
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
return automount_send_ready(AUTOMOUNT(p), status);
|
||||
}
|
||||
|
||||
static int mount_add_node_links(Mount *m) {
|
||||
Unit *device;
|
||||
char *e;
|
||||
@ -262,10 +221,9 @@ static bool mount_test_option(const char *haystack, const char *needle) {
|
||||
static int mount_add_target_links(Mount *m) {
|
||||
const char *target;
|
||||
MountParameters *p;
|
||||
Unit *u;
|
||||
Unit *tu;
|
||||
int r;
|
||||
bool noauto;
|
||||
bool handle;
|
||||
bool noauto, handle, automount;
|
||||
|
||||
assert(m);
|
||||
|
||||
@ -278,8 +236,9 @@ static int mount_add_target_links(Mount *m) {
|
||||
|
||||
noauto = mount_test_option(p->options, MNTOPT_NOAUTO);
|
||||
handle = mount_test_option(p->options, "comment=systemd.mount");
|
||||
automount = mount_test_option(p->options, "comment=systemd.automount");
|
||||
|
||||
if (noauto && !handle)
|
||||
if (noauto && !handle && !automount)
|
||||
return 0;
|
||||
|
||||
if (mount_test_option(p->options, "_netdev") ||
|
||||
@ -288,14 +247,28 @@ static int mount_add_target_links(Mount *m) {
|
||||
else
|
||||
target = SPECIAL_LOCAL_FS_TARGET;
|
||||
|
||||
if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &u)) < 0)
|
||||
if ((r = manager_load_unit(UNIT(m)->meta.manager, target, NULL, &tu)) < 0)
|
||||
return r;
|
||||
|
||||
if (handle)
|
||||
if ((r = unit_add_dependency(u, UNIT_WANTS, UNIT(m))) < 0)
|
||||
if (automount) {
|
||||
Unit *am;
|
||||
|
||||
if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0)
|
||||
return r;
|
||||
|
||||
return unit_add_dependency(UNIT(m), UNIT_BEFORE, u);
|
||||
if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(am))) < 0)
|
||||
return r;
|
||||
|
||||
return unit_add_dependency(UNIT(am), UNIT_BEFORE, tu);
|
||||
|
||||
} else {
|
||||
|
||||
if (handle)
|
||||
if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m))) < 0)
|
||||
return r;
|
||||
|
||||
return unit_add_dependency(UNIT(m), UNIT_BEFORE, tu);
|
||||
}
|
||||
}
|
||||
|
||||
static int mount_verify(Mount *m) {
|
||||
@ -306,14 +279,7 @@ static int mount_verify(Mount *m) {
|
||||
if (UNIT(m)->meta.load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
if (!m->where) {
|
||||
log_error("%s lacks Where setting. Refusing.", UNIT(m)->meta.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
path_kill_slashes(m->where);
|
||||
|
||||
if (!(e = mount_name_from_where(m->where)))
|
||||
if (!(e = unit_name_from_path(m->where, ".mount")))
|
||||
return -ENOMEM;
|
||||
|
||||
b = unit_has_name(UNIT(m), e);
|
||||
@ -340,6 +306,12 @@ static int mount_load(Unit *u) {
|
||||
/* This is a new unit? Then let's add in some extras */
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
|
||||
if (!m->where)
|
||||
if (!(m->where = unit_name_to_path(u->meta.id)))
|
||||
return -ENOMEM;
|
||||
|
||||
path_kill_slashes(m->where);
|
||||
|
||||
/* Minor validity checking */
|
||||
if ((m->parameters_fragment.options || m->parameters_fragment.fstype) && !m->parameters_fragment.what)
|
||||
return -EBADMSG;
|
||||
@ -363,6 +335,18 @@ static int mount_load(Unit *u) {
|
||||
return mount_verify(m);
|
||||
}
|
||||
|
||||
static int mount_notify_automount(Mount *m, int status) {
|
||||
Unit *p;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if ((r = unit_get_related_unit(UNIT(m), ".automount", &p)) < 0)
|
||||
return r == -ENOENT ? 0 : r;
|
||||
|
||||
return automount_send_ready(AUTOMOUNT(p), status);
|
||||
}
|
||||
|
||||
static void mount_set_state(Mount *m, MountState state) {
|
||||
MountState old_state;
|
||||
assert(m);
|
||||
@ -381,8 +365,9 @@ static void mount_set_state(Mount *m, MountState state) {
|
||||
state != MOUNT_REMOUNTING_SIGTERM &&
|
||||
state != MOUNT_REMOUNTING_SIGKILL) {
|
||||
unit_unwatch_timer(UNIT(m), &m->timer_watch);
|
||||
service_unwatch_control_pid(m);
|
||||
mount_unwatch_control_pid(m);
|
||||
m->control_command = NULL;
|
||||
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
if (state == MOUNT_MOUNTED ||
|
||||
@ -400,23 +385,98 @@ static void mount_set_state(Mount *m, MountState state) {
|
||||
mount_notify_automount(m, -ENODEV);
|
||||
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", UNIT(m)->meta.id, state_string_table[old_state], state_string_table[state]);
|
||||
log_debug("%s changed %s → %s",
|
||||
UNIT(m)->meta.id,
|
||||
mount_state_to_string(old_state),
|
||||
mount_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int mount_coldplug(Unit *u) {
|
||||
Mount *m = MOUNT(u);
|
||||
MountState new_state = MOUNT_DEAD;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(m->state == MOUNT_DEAD);
|
||||
|
||||
if (m->from_proc_self_mountinfo)
|
||||
mount_set_state(m, MOUNT_MOUNTED);
|
||||
if (m->deserialized_state != m->state)
|
||||
new_state = m->deserialized_state;
|
||||
else if (m->from_proc_self_mountinfo)
|
||||
new_state = MOUNT_MOUNTED;
|
||||
|
||||
if (new_state != m->state) {
|
||||
|
||||
if (new_state == MOUNT_MOUNTING ||
|
||||
new_state == MOUNT_MOUNTING_DONE ||
|
||||
new_state == MOUNT_REMOUNTING ||
|
||||
new_state == MOUNT_UNMOUNTING ||
|
||||
new_state == MOUNT_MOUNTING_SIGTERM ||
|
||||
new_state == MOUNT_MOUNTING_SIGKILL ||
|
||||
new_state == MOUNT_UNMOUNTING_SIGTERM ||
|
||||
new_state == MOUNT_UNMOUNTING_SIGKILL ||
|
||||
new_state == MOUNT_REMOUNTING_SIGTERM ||
|
||||
new_state == MOUNT_REMOUNTING_SIGKILL) {
|
||||
|
||||
if (m->control_pid <= 0)
|
||||
return -EBADMSG;
|
||||
|
||||
if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
mount_set_state(m, new_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mount_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Mount *m = MOUNT(u);
|
||||
MountParameters *p;
|
||||
|
||||
assert(m);
|
||||
assert(f);
|
||||
|
||||
if (m->from_proc_self_mountinfo)
|
||||
p = &m->parameters_proc_self_mountinfo;
|
||||
else if (m->from_fragment)
|
||||
p = &m->parameters_fragment;
|
||||
else
|
||||
p = &m->parameters_etc_fstab;
|
||||
|
||||
fprintf(f,
|
||||
"%sMount State: %s\n"
|
||||
"%sWhere: %s\n"
|
||||
"%sWhat: %s\n"
|
||||
"%sFile System Type: %s\n"
|
||||
"%sOptions: %s\n"
|
||||
"%sFrom /etc/fstab: %s\n"
|
||||
"%sFrom /proc/self/mountinfo: %s\n"
|
||||
"%sFrom fragment: %s\n"
|
||||
"%sKillMode: %s\n",
|
||||
prefix, mount_state_to_string(m->state),
|
||||
prefix, m->where,
|
||||
prefix, strna(p->what),
|
||||
prefix, strna(p->fstype),
|
||||
prefix, strna(p->options),
|
||||
prefix, yes_no(m->from_etc_fstab),
|
||||
prefix, yes_no(m->from_proc_self_mountinfo),
|
||||
prefix, yes_no(m->from_fragment),
|
||||
prefix, kill_mode_to_string(m->kill_mode));
|
||||
|
||||
if (m->control_pid > 0)
|
||||
fprintf(f,
|
||||
"%sControl PID: %llu\n",
|
||||
prefix, (unsigned long long) m->control_pid);
|
||||
|
||||
exec_context_dump(&m->exec_context, f, prefix);
|
||||
}
|
||||
|
||||
static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
|
||||
pid_t pid;
|
||||
int r;
|
||||
@ -453,48 +513,6 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void mount_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Mount *m = MOUNT(u);
|
||||
MountParameters *p;
|
||||
|
||||
assert(m);
|
||||
assert(f);
|
||||
|
||||
if (m->from_proc_self_mountinfo)
|
||||
p = &m->parameters_proc_self_mountinfo;
|
||||
else if (m->from_fragment)
|
||||
p = &m->parameters_fragment;
|
||||
else
|
||||
p = &m->parameters_etc_fstab;
|
||||
|
||||
fprintf(f,
|
||||
"%sMount State: %s\n"
|
||||
"%sWhere: %s\n"
|
||||
"%sWhat: %s\n"
|
||||
"%sFile System Type: %s\n"
|
||||
"%sOptions: %s\n"
|
||||
"%sFrom /etc/fstab: %s\n"
|
||||
"%sFrom /proc/self/mountinfo: %s\n"
|
||||
"%sFrom fragment: %s\n"
|
||||
"%sKillMode: %s\n",
|
||||
prefix, state_string_table[m->state],
|
||||
prefix, m->where,
|
||||
prefix, strna(p->what),
|
||||
prefix, strna(p->fstype),
|
||||
prefix, strna(p->options),
|
||||
prefix, yes_no(m->from_etc_fstab),
|
||||
prefix, yes_no(m->from_proc_self_mountinfo),
|
||||
prefix, yes_no(m->from_fragment),
|
||||
prefix, kill_mode_to_string(m->kill_mode));
|
||||
|
||||
if (m->control_pid > 0)
|
||||
fprintf(f,
|
||||
"%sControl PID: %llu\n",
|
||||
prefix, (unsigned long long) m->control_pid);
|
||||
|
||||
exec_context_dump(&m->exec_context, f, prefix);
|
||||
}
|
||||
|
||||
static void mount_enter_dead(Mount *m, bool success) {
|
||||
assert(m);
|
||||
|
||||
@ -565,7 +583,6 @@ fail:
|
||||
}
|
||||
|
||||
static void mount_enter_unmounting(Mount *m, bool success) {
|
||||
ExecCommand *c;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -573,18 +590,19 @@ static void mount_enter_unmounting(Mount *m, bool success) {
|
||||
if (!success)
|
||||
m->failure = true;
|
||||
|
||||
m->control_command = c = m->exec_command + MOUNT_EXEC_UNMOUNT;
|
||||
m->control_command_id = MOUNT_EXEC_UNMOUNT;
|
||||
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
|
||||
|
||||
if ((r = exec_command_set(
|
||||
c,
|
||||
m->control_command,
|
||||
"/bin/umount",
|
||||
m->where,
|
||||
NULL)) < 0)
|
||||
goto fail;
|
||||
|
||||
service_unwatch_control_pid(m);
|
||||
mount_unwatch_control_pid(m);
|
||||
|
||||
if ((r = mount_spawn(m, c, &m->control_pid)) < 0)
|
||||
if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
mount_set_state(m, MOUNT_UNMOUNTING);
|
||||
@ -597,16 +615,16 @@ fail:
|
||||
}
|
||||
|
||||
static void mount_enter_mounting(Mount *m) {
|
||||
ExecCommand *c;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
m->control_command = c = m->exec_command + MOUNT_EXEC_MOUNT;
|
||||
m->control_command_id = MOUNT_EXEC_MOUNT;
|
||||
m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
|
||||
|
||||
if (m->from_fragment)
|
||||
r = exec_command_set(
|
||||
c,
|
||||
m->control_command,
|
||||
"/bin/mount",
|
||||
m->parameters_fragment.what,
|
||||
m->where,
|
||||
@ -615,7 +633,7 @@ static void mount_enter_mounting(Mount *m) {
|
||||
NULL);
|
||||
else if (m->from_etc_fstab)
|
||||
r = exec_command_set(
|
||||
c,
|
||||
m->control_command,
|
||||
"/bin/mount",
|
||||
m->where,
|
||||
NULL);
|
||||
@ -625,9 +643,9 @@ static void mount_enter_mounting(Mount *m) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
service_unwatch_control_pid(m);
|
||||
mount_unwatch_control_pid(m);
|
||||
|
||||
if ((r = mount_spawn(m, c, &m->control_pid)) < 0)
|
||||
if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
mount_set_state(m, MOUNT_MOUNTING);
|
||||
@ -646,7 +664,6 @@ static void mount_enter_mounting_done(Mount *m) {
|
||||
}
|
||||
|
||||
static void mount_enter_remounting(Mount *m, bool success) {
|
||||
ExecCommand *c;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -654,7 +671,8 @@ static void mount_enter_remounting(Mount *m, bool success) {
|
||||
if (!success)
|
||||
m->failure = true;
|
||||
|
||||
m->control_command = c = m->exec_command + MOUNT_EXEC_REMOUNT;
|
||||
m->control_command_id = MOUNT_EXEC_REMOUNT;
|
||||
m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
|
||||
|
||||
if (m->from_fragment) {
|
||||
char *buf = NULL;
|
||||
@ -671,7 +689,7 @@ static void mount_enter_remounting(Mount *m, bool success) {
|
||||
o = "remount";
|
||||
|
||||
r = exec_command_set(
|
||||
c,
|
||||
m->control_command,
|
||||
"/bin/mount",
|
||||
m->parameters_fragment.what,
|
||||
m->where,
|
||||
@ -682,7 +700,7 @@ static void mount_enter_remounting(Mount *m, bool success) {
|
||||
free(buf);
|
||||
} else if (m->from_etc_fstab)
|
||||
r = exec_command_set(
|
||||
c,
|
||||
m->control_command,
|
||||
"/bin/mount",
|
||||
m->where,
|
||||
"-o", "remount",
|
||||
@ -695,9 +713,9 @@ static void mount_enter_remounting(Mount *m, bool success) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
service_unwatch_control_pid(m);
|
||||
mount_unwatch_control_pid(m);
|
||||
|
||||
if ((r = mount_spawn(m, c, &m->control_pid)) < 0)
|
||||
if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
|
||||
goto fail;
|
||||
|
||||
mount_set_state(m, MOUNT_REMOUNTING);
|
||||
@ -729,7 +747,6 @@ static int mount_start(Unit *u) {
|
||||
assert(m->state == MOUNT_DEAD || m->state == MOUNT_MAINTAINANCE);
|
||||
|
||||
m->failure = false;
|
||||
|
||||
mount_enter_mounting(m);
|
||||
return 0;
|
||||
}
|
||||
@ -775,6 +792,72 @@ static int mount_reload(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Mount *m = MOUNT(u);
|
||||
|
||||
assert(m);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", mount_state_to_string(m->state));
|
||||
unit_serialize_item(u, f, "failure", yes_no(m->failure));
|
||||
|
||||
if (m->control_pid > 0)
|
||||
unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) m->control_pid);
|
||||
|
||||
if (m->control_command_id >= 0)
|
||||
unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Mount *m = MOUNT(u);
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(key);
|
||||
assert(value);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
MountState state;
|
||||
|
||||
if ((state = mount_state_from_string(value)) < 0)
|
||||
log_debug("Failed to parse state value %s", value);
|
||||
else
|
||||
m->deserialized_state = state;
|
||||
} else if (streq(key, "failure")) {
|
||||
int b;
|
||||
|
||||
if ((b = parse_boolean(value)) < 0)
|
||||
log_debug("Failed to parse failure value %s", value);
|
||||
else
|
||||
m->failure = b || m->failure;
|
||||
|
||||
} else if (streq(key, "control-pid")) {
|
||||
unsigned pid;
|
||||
|
||||
if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
|
||||
log_debug("Failed to parse control-pid value %s", value);
|
||||
else
|
||||
m->control_pid = (pid_t) pid;
|
||||
} else if (streq(key, "control-command")) {
|
||||
MountExecCommand id;
|
||||
|
||||
if ((id = mount_exec_command_from_string(value)) < 0)
|
||||
log_debug("Failed to parse exec-command value %s", value);
|
||||
else {
|
||||
m->control_command_id = id;
|
||||
m->control_command = m->exec_command + id;
|
||||
}
|
||||
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState mount_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
@ -784,7 +867,7 @@ static UnitActiveState mount_active_state(Unit *u) {
|
||||
static const char *mount_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_string_table[MOUNT(u)->state];
|
||||
return mount_state_to_string(MOUNT(u)->state);
|
||||
}
|
||||
|
||||
static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
@ -798,11 +881,14 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
m->failure = m->failure || !success;
|
||||
|
||||
assert(m->control_pid == pid);
|
||||
assert(m->control_command);
|
||||
|
||||
exec_status_fill(&m->control_command->exec_status, pid, code, status);
|
||||
m->control_pid = 0;
|
||||
|
||||
if (m->control_command) {
|
||||
exec_status_fill(&m->control_command->exec_status, pid, code, status);
|
||||
m->control_command = NULL;
|
||||
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
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
|
||||
@ -937,9 +1023,7 @@ static int mount_add_one(
|
||||
if (where[0] != '/')
|
||||
return 0;
|
||||
|
||||
e = mount_name_from_where(where);
|
||||
|
||||
if (!e)
|
||||
if (!(e = unit_name_from_path(where, ".mount")))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(u = manager_get_unit(m, e))) {
|
||||
@ -1172,8 +1256,10 @@ finish:
|
||||
static void mount_shutdown(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->proc_self_mountinfo)
|
||||
if (m->proc_self_mountinfo) {
|
||||
fclose(m->proc_self_mountinfo);
|
||||
m->proc_self_mountinfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mount_enumerate(Manager *m) {
|
||||
@ -1181,18 +1267,20 @@ static int mount_enumerate(Manager *m) {
|
||||
struct epoll_event ev;
|
||||
assert(m);
|
||||
|
||||
if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "r")))
|
||||
return -errno;
|
||||
if (!m->proc_self_mountinfo) {
|
||||
if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
|
||||
return -errno;
|
||||
|
||||
m->mount_watch.type = WATCH_MOUNT;
|
||||
m->mount_watch.fd = fileno(m->proc_self_mountinfo);
|
||||
m->mount_watch.type = WATCH_MOUNT;
|
||||
m->mount_watch.fd = fileno(m->proc_self_mountinfo);
|
||||
|
||||
zero(ev);
|
||||
ev.events = EPOLLERR;
|
||||
ev.data.ptr = &m->mount_watch;
|
||||
zero(ev);
|
||||
ev.events = EPOLLERR;
|
||||
ev.data.ptr = &m->mount_watch;
|
||||
|
||||
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
|
||||
return -errno;
|
||||
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((r = mount_load_etc_fstab(m)) < 0)
|
||||
goto fail;
|
||||
@ -1303,7 +1391,7 @@ int mount_path_is_mounted(Manager *m, const char* path) {
|
||||
char *e, *slash;
|
||||
Unit *u;
|
||||
|
||||
if (!(e = mount_name_from_where(t))) {
|
||||
if (!(e = unit_name_from_path(t, ".mount"))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
@ -1335,6 +1423,32 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
|
||||
[MOUNT_DEAD] = "dead",
|
||||
[MOUNT_MOUNTING] = "mounting",
|
||||
[MOUNT_MOUNTING_DONE] = "mounting-done",
|
||||
[MOUNT_MOUNTED] = "mounted",
|
||||
[MOUNT_REMOUNTING] = "remounting",
|
||||
[MOUNT_UNMOUNTING] = "unmounting",
|
||||
[MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
|
||||
[MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
|
||||
[MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
|
||||
[MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
|
||||
[MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
|
||||
[MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
|
||||
[MOUNT_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
|
||||
|
||||
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
|
||||
[MOUNT_EXEC_MOUNT] = "ExecMount",
|
||||
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
|
||||
[MOUNT_EXEC_REMOUNT] = "ExecRemount",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand);
|
||||
|
||||
const UnitVTable mount_vtable = {
|
||||
.suffix = ".mount",
|
||||
|
||||
@ -1353,6 +1467,9 @@ const UnitVTable mount_vtable = {
|
||||
.stop = mount_stop,
|
||||
.reload = mount_reload,
|
||||
|
||||
.serialize = mount_serialize,
|
||||
.deserialize_item = mount_deserialize_item,
|
||||
|
||||
.active_state = mount_active_state,
|
||||
.sub_state_to_string = mount_sub_state_to_string,
|
||||
|
||||
|
9
mount.h
9
mount.h
@ -84,11 +84,12 @@ struct Mount {
|
||||
ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
MountState state;
|
||||
MountState state, deserialized_state;
|
||||
|
||||
KillMode kill_mode;
|
||||
|
||||
ExecCommand* control_command;
|
||||
MountExecCommand control_command_id;
|
||||
pid_t control_pid;
|
||||
|
||||
Watch timer_watch;
|
||||
@ -100,4 +101,10 @@ void mount_fd_event(Manager *m, int events);
|
||||
|
||||
int mount_path_is_mounted(Manager *m, const char* path);
|
||||
|
||||
const char* mount_state_to_string(MountState i);
|
||||
MountState mount_state_from_string(const char *s);
|
||||
|
||||
const char* mount_exec_command_to_string(MountExecCommand i);
|
||||
MountExecCommand mount_exec_command_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
|
226
service.c
226
service.c
@ -66,6 +66,25 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
|
||||
};
|
||||
|
||||
static void service_init(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
s->restart_usec = DEFAULT_RESTART_USEC;
|
||||
s->timer_watch.type = WATCH_INVALID;
|
||||
s->sysv_start_priority = -1;
|
||||
s->socket_fd = -1;
|
||||
|
||||
exec_context_init(&s->exec_context);
|
||||
|
||||
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
|
||||
|
||||
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
static void service_unwatch_control_pid(Service *s) {
|
||||
assert(s);
|
||||
|
||||
@ -735,23 +754,6 @@ static int service_add_bus_name(Service *s) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static void service_init(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
s->restart_usec = DEFAULT_RESTART_USEC;
|
||||
s->timer_watch.type = WATCH_INVALID;
|
||||
s->sysv_start_priority = -1;
|
||||
s->socket_fd = -1;
|
||||
|
||||
exec_context_init(&s->exec_context);
|
||||
|
||||
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
|
||||
}
|
||||
|
||||
static int service_verify(Service *s) {
|
||||
assert(s);
|
||||
|
||||
@ -1047,6 +1049,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
state != SERVICE_FINAL_SIGKILL) {
|
||||
service_unwatch_control_pid(s);
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
if (state == SERVICE_DEAD ||
|
||||
@ -1071,6 +1074,66 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int service_coldplug(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SERVICE_DEAD);
|
||||
|
||||
if (s->deserialized_state != s->state) {
|
||||
|
||||
if (s->deserialized_state == SERVICE_START_PRE ||
|
||||
s->deserialized_state == SERVICE_START ||
|
||||
s->deserialized_state == SERVICE_START_POST ||
|
||||
s->deserialized_state == SERVICE_RELOAD ||
|
||||
s->deserialized_state == SERVICE_STOP ||
|
||||
s->deserialized_state == SERVICE_STOP_SIGTERM ||
|
||||
s->deserialized_state == SERVICE_STOP_SIGKILL ||
|
||||
s->deserialized_state == SERVICE_STOP_POST ||
|
||||
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
|
||||
s->deserialized_state == SERVICE_FINAL_SIGKILL ||
|
||||
s->deserialized_state == SERVICE_AUTO_RESTART)
|
||||
if ((r = unit_watch_timer(UNIT(s),
|
||||
s->deserialized_state == SERVICE_AUTO_RESTART ?
|
||||
s->restart_usec :
|
||||
s->timeout_usec,
|
||||
&s->timer_watch)) < 0)
|
||||
return r;
|
||||
|
||||
if ((s->deserialized_state == SERVICE_START &&
|
||||
(s->type == SERVICE_FORKING ||
|
||||
s->type == SERVICE_DBUS)) ||
|
||||
s->deserialized_state == SERVICE_START_POST ||
|
||||
s->deserialized_state == SERVICE_RUNNING ||
|
||||
s->deserialized_state == SERVICE_RELOAD ||
|
||||
s->deserialized_state == SERVICE_STOP ||
|
||||
s->deserialized_state == SERVICE_STOP_SIGTERM ||
|
||||
s->deserialized_state == SERVICE_STOP_SIGKILL)
|
||||
if (s->main_pid > 0)
|
||||
if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0)
|
||||
return r;
|
||||
|
||||
if (s->deserialized_state == SERVICE_START_PRE ||
|
||||
s->deserialized_state == SERVICE_START ||
|
||||
s->deserialized_state == SERVICE_START_POST ||
|
||||
s->deserialized_state == SERVICE_RELOAD ||
|
||||
s->deserialized_state == SERVICE_STOP ||
|
||||
s->deserialized_state == SERVICE_STOP_SIGTERM ||
|
||||
s->deserialized_state == SERVICE_STOP_SIGKILL ||
|
||||
s->deserialized_state == SERVICE_STOP_POST ||
|
||||
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
|
||||
s->deserialized_state == SERVICE_FINAL_SIGKILL)
|
||||
if (s->control_pid > 0)
|
||||
if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
|
||||
return r;
|
||||
|
||||
service_set_state(s, s->deserialized_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
|
||||
Iterator i;
|
||||
int r;
|
||||
@ -1279,6 +1342,7 @@ static void service_enter_stop_post(Service *s, bool success) {
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SERVICE_EXEC_STOP_POST;
|
||||
if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) {
|
||||
if ((r = service_spawn(s,
|
||||
s->control_command,
|
||||
@ -1374,6 +1438,7 @@ static void service_enter_stop(Service *s, bool success) {
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SERVICE_EXEC_STOP;
|
||||
if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) {
|
||||
if ((r = service_spawn(s,
|
||||
s->control_command,
|
||||
@ -1417,6 +1482,7 @@ static void service_enter_start_post(Service *s) {
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SERVICE_EXEC_START_POST;
|
||||
if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) {
|
||||
if ((r = service_spawn(s,
|
||||
s->control_command,
|
||||
@ -1478,6 +1544,7 @@ static void service_enter_start(Service *s) {
|
||||
|
||||
s->control_pid = pid;
|
||||
|
||||
s->control_command_id = SERVICE_EXEC_START;
|
||||
s->control_command = s->exec_command[SERVICE_EXEC_START];
|
||||
service_set_state(s, SERVICE_START);
|
||||
|
||||
@ -1511,6 +1578,7 @@ static void service_enter_start_pre(Service *s) {
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SERVICE_EXEC_START_PRE;
|
||||
if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
|
||||
if ((r = service_spawn(s,
|
||||
s->control_command,
|
||||
@ -1557,6 +1625,7 @@ static void service_enter_reload(Service *s) {
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SERVICE_EXEC_RELOAD;
|
||||
if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) {
|
||||
if ((r = service_spawn(s,
|
||||
s->control_command,
|
||||
@ -1703,6 +1772,112 @@ static bool service_can_reload(Unit *u) {
|
||||
return !!s->exec_command[SERVICE_EXEC_RELOAD];
|
||||
}
|
||||
|
||||
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(u);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", service_state_to_string(s->state));
|
||||
unit_serialize_item(u, f, "failure", yes_no(s->failure));
|
||||
|
||||
if (s->control_pid > 0)
|
||||
unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) (s->control_pid));
|
||||
|
||||
if (s->main_pid > 0)
|
||||
unit_serialize_item_format(u, f, "main-pid", "%u", (unsigned) (s->main_pid));
|
||||
|
||||
unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
|
||||
|
||||
/* There's a minor uncleanliness here: if there are multiple
|
||||
* commands attached here, we will start from the first one
|
||||
* again */
|
||||
if (s->control_command_id >= 0)
|
||||
unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(s->control_command_id));
|
||||
|
||||
if (s->socket_fd >= 0) {
|
||||
int copy;
|
||||
|
||||
if ((copy = fdset_put_dup(fds, s->socket_fd)) < 0)
|
||||
return copy;
|
||||
|
||||
unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Service *s = SERVICE(u);
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(key);
|
||||
assert(value);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
ServiceState state;
|
||||
|
||||
if ((state = service_state_from_string(value)) < 0)
|
||||
log_debug("Failed to parse state value %s", value);
|
||||
else
|
||||
s->deserialized_state = state;
|
||||
} else if (streq(key, "failure")) {
|
||||
int b;
|
||||
|
||||
if ((b = parse_boolean(value)) < 0)
|
||||
log_debug("Failed to parse failure value %s", value);
|
||||
else
|
||||
s->failure = b || s->failure;
|
||||
} else if (streq(key, "control-pid")) {
|
||||
unsigned pid;
|
||||
|
||||
if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
|
||||
log_debug("Failed to parse control-pid value %s", value);
|
||||
else
|
||||
s->control_pid = (pid_t) pid;
|
||||
} else if (streq(key, "main-pid")) {
|
||||
unsigned pid;
|
||||
|
||||
if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
|
||||
log_debug("Failed to parse main-pid value %s", value);
|
||||
else
|
||||
s->main_pid = (pid_t) pid;
|
||||
} else if (streq(key, "main-pid-known")) {
|
||||
int b;
|
||||
|
||||
if ((b = parse_boolean(value)) < 0)
|
||||
log_debug("Failed to parse main-pid-known value %s", value);
|
||||
else
|
||||
s->main_pid_known = b;
|
||||
} else if (streq(key, "control-command")) {
|
||||
ServiceExecCommand id;
|
||||
|
||||
if ((id = service_exec_command_from_string(value)) < 0)
|
||||
log_debug("Failed to parse exec-command value %s", value);
|
||||
else {
|
||||
s->control_command_id = id;
|
||||
s->control_command = s->exec_command[id];
|
||||
}
|
||||
} else if (streq(key, "socket-fd")) {
|
||||
int fd;
|
||||
|
||||
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
|
||||
log_debug("Failed to parse socket-fd value %s", value);
|
||||
else {
|
||||
|
||||
if (s->socket_fd >= 0)
|
||||
close_nointr_nofail(s->socket_fd);
|
||||
s->socket_fd = fdset_remove(fds, fd);
|
||||
}
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState service_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
@ -1777,9 +1952,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
}
|
||||
|
||||
} else if (s->control_pid == pid) {
|
||||
assert(s->control_command);
|
||||
|
||||
exec_status_fill(&s->control_command->exec_status, pid, code, status);
|
||||
if (s->control_command)
|
||||
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", u->meta.id, sigchld_code_to_string(code), status);
|
||||
@ -1787,7 +1963,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
/* If we are shutting things down anyway we
|
||||
* don't care about failing commands. */
|
||||
|
||||
if (s->control_command->command_next && success) {
|
||||
if (s->control_command && s->control_command->command_next && success) {
|
||||
|
||||
/* There is another command to *
|
||||
* execute, so let's do that. */
|
||||
@ -1799,6 +1975,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
/* No further commands for this step, so let's
|
||||
* figure out what to do next */
|
||||
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||||
|
||||
log_debug("%s got final SIGCHLD for state %s", u->meta.id, service_state_to_string(s->state));
|
||||
|
||||
switch (s->state) {
|
||||
@ -2218,8 +2397,10 @@ const UnitVTable service_vtable = {
|
||||
.suffix = ".service",
|
||||
|
||||
.init = service_init,
|
||||
.load = service_load,
|
||||
.done = service_done,
|
||||
.load = service_load,
|
||||
|
||||
.coldplug = service_coldplug,
|
||||
|
||||
.dump = service_dump,
|
||||
|
||||
@ -2229,6 +2410,9 @@ const UnitVTable service_vtable = {
|
||||
|
||||
.can_reload = service_can_reload,
|
||||
|
||||
.serialize = service_serialize,
|
||||
.deserialize_item = service_deserialize_item,
|
||||
|
||||
.active_state = service_active_state,
|
||||
.sub_state_to_string = service_sub_state_to_string,
|
||||
|
||||
|
@ -94,13 +94,14 @@ struct Service {
|
||||
bool root_directory_start_only;
|
||||
bool valid_no_process;
|
||||
|
||||
ServiceState state;
|
||||
ServiceState state, deserialized_state;
|
||||
|
||||
KillMode kill_mode;
|
||||
|
||||
ExecStatus main_exec_status;
|
||||
|
||||
ExecCommand *control_command;
|
||||
ServiceExecCommand control_command_id;
|
||||
pid_t main_pid, control_pid;
|
||||
bool main_pid_known:1;
|
||||
|
||||
|
151
snapshot.c
151
snapshot.c
@ -31,31 +31,30 @@ static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
|
||||
[SNAPSHOT_ACTIVE] = UNIT_ACTIVE
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_SNAPSHOT_STATE_MAX] = {
|
||||
[SNAPSHOT_DEAD] = "dead",
|
||||
[SNAPSHOT_ACTIVE] = "active"
|
||||
};
|
||||
static void snapshot_set_state(Snapshot *s, SnapshotState state) {
|
||||
SnapshotState old_state;
|
||||
assert(s);
|
||||
|
||||
static int snapshot_load(Unit *u) {
|
||||
Iterator i;
|
||||
Unit *other;
|
||||
int r;
|
||||
old_state = s->state;
|
||||
s->state = state;
|
||||
|
||||
assert(u);
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s",
|
||||
UNIT(s)->meta.id,
|
||||
snapshot_state_to_string(old_state),
|
||||
snapshot_state_to_string(state));
|
||||
|
||||
HASHMAP_FOREACH(other, u->meta.manager->units, i) {
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
if (UNIT_VTABLE(other)->no_snapshots)
|
||||
continue;
|
||||
static int snapshot_coldplug(Unit *u) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0)
|
||||
return r;
|
||||
assert(s);
|
||||
assert(s->state == SNAPSHOT_DEAD);
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
u->meta.load_state = UNIT_LOADED;
|
||||
if (s->deserialized_state != s->state)
|
||||
snapshot_set_state(s, s->deserialized_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -69,23 +68,10 @@ static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
fprintf(f,
|
||||
"%sSnapshot State: %s\n"
|
||||
"%sClean Up: %s\n",
|
||||
prefix, state_string_table[s->state],
|
||||
prefix, snapshot_state_to_string(s->state),
|
||||
prefix, yes_no(s->cleanup));
|
||||
}
|
||||
|
||||
static void snapshot_set_state(Snapshot *s, SnapshotState state) {
|
||||
SnapshotState old_state;
|
||||
assert(s);
|
||||
|
||||
old_state = s->state;
|
||||
s->state = state;
|
||||
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", UNIT(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]);
|
||||
}
|
||||
|
||||
static int snapshot_start(Unit *u) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
@ -110,6 +96,60 @@ static int snapshot_stop(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
Unit *other;
|
||||
Iterator i;
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
|
||||
unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i)
|
||||
unit_serialize_item(u, f, "requires", other->meta.id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(key);
|
||||
assert(value);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
SnapshotState state;
|
||||
|
||||
if ((state = snapshot_state_from_string(value)) < 0)
|
||||
log_debug("Failed to parse state value %s", value);
|
||||
else
|
||||
s->deserialized_state = state;
|
||||
|
||||
} else if (streq(key, "cleanup")) {
|
||||
|
||||
if ((r = parse_boolean(value)) < 0)
|
||||
log_debug("Failed to parse cleanup value %s", value);
|
||||
else
|
||||
s->cleanup = r;
|
||||
|
||||
} else if (streq(key, "requires")) {
|
||||
|
||||
if ((r = unit_add_dependency_by_name(u, UNIT_AFTER, value, NULL)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, value, NULL)) < 0)
|
||||
return r;
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState snapshot_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
@ -119,13 +159,15 @@ static UnitActiveState snapshot_active_state(Unit *u) {
|
||||
static const char *snapshot_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_string_table[SNAPSHOT(u)->state];
|
||||
return snapshot_state_to_string(SNAPSHOT(u)->state);
|
||||
}
|
||||
|
||||
int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) {
|
||||
Unit *u;
|
||||
Iterator i;
|
||||
Unit *other, *u = NULL;
|
||||
char *n = NULL;
|
||||
int r;
|
||||
const char *k;
|
||||
|
||||
assert(m);
|
||||
assert(_s);
|
||||
@ -159,12 +201,36 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) {
|
||||
free(n);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto fail;
|
||||
|
||||
HASHMAP_FOREACH_KEY(other, k, m->units, i) {
|
||||
|
||||
if (UNIT_VTABLE(other)->no_snapshots)
|
||||
continue;
|
||||
|
||||
if (k != other->meta.id)
|
||||
continue;
|
||||
|
||||
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
|
||||
continue;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_REQUIRES, other)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_AFTER, other)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SNAPSHOT(u)->cleanup = cleanup;
|
||||
*_s = SNAPSHOT(u);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (u)
|
||||
unit_add_to_cleanup_queue(u);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void snapshot_remove(Snapshot *s) {
|
||||
@ -173,6 +239,13 @@ void snapshot_remove(Snapshot *s) {
|
||||
unit_add_to_cleanup_queue(UNIT(s));
|
||||
}
|
||||
|
||||
static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
|
||||
[SNAPSHOT_DEAD] = "dead",
|
||||
[SNAPSHOT_ACTIVE] = "active"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
|
||||
|
||||
const UnitVTable snapshot_vtable = {
|
||||
.suffix = ".snapshot",
|
||||
|
||||
@ -180,13 +253,17 @@ const UnitVTable snapshot_vtable = {
|
||||
.no_instances = true,
|
||||
.no_snapshots = true,
|
||||
|
||||
.load = snapshot_load,
|
||||
.load = unit_load_nop,
|
||||
.coldplug = snapshot_coldplug,
|
||||
|
||||
.dump = snapshot_dump,
|
||||
|
||||
.start = snapshot_start,
|
||||
.stop = snapshot_stop,
|
||||
|
||||
.serialize = snapshot_serialize,
|
||||
.deserialize_item = snapshot_deserialize_item,
|
||||
|
||||
.active_state = snapshot_active_state,
|
||||
.sub_state_to_string = snapshot_sub_state_to_string,
|
||||
|
||||
|
@ -36,7 +36,7 @@ typedef enum SnapshotState {
|
||||
struct Snapshot {
|
||||
Meta meta;
|
||||
|
||||
SnapshotState state;
|
||||
SnapshotState state, deserialized_state;
|
||||
|
||||
bool cleanup;
|
||||
};
|
||||
@ -46,4 +46,7 @@ extern const UnitVTable snapshot_vtable;
|
||||
int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **s);
|
||||
void snapshot_remove(Snapshot *s);
|
||||
|
||||
const char* snapshot_state_to_string(SnapshotState i);
|
||||
SnapshotState snapshot_state_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
|
@ -316,7 +316,7 @@ int socket_address_listen(
|
||||
if ((r = socket_address_verify(a)) < 0)
|
||||
return r;
|
||||
|
||||
if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK, 0)) < 0)
|
||||
if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
|
||||
@ -370,7 +370,7 @@ int socket_address_listen(
|
||||
|
||||
fail:
|
||||
r = -errno;
|
||||
close_nointr(fd);
|
||||
close_nointr_nofail(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -381,3 +381,77 @@ bool socket_address_can_accept(const SocketAddress *a) {
|
||||
a->type == SOCK_STREAM ||
|
||||
a->type == SOCK_SEQPACKET;
|
||||
}
|
||||
|
||||
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
/* Invalid addresses are unequal to all */
|
||||
if (socket_address_verify(a) < 0 ||
|
||||
socket_address_verify(b) < 0)
|
||||
return false;
|
||||
|
||||
if (a->type != b->type)
|
||||
return false;
|
||||
|
||||
if (a->size != b->size)
|
||||
return false;
|
||||
|
||||
if (socket_address_family(a) != socket_address_family(b))
|
||||
return false;
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
|
||||
case AF_INET:
|
||||
if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
case AF_UNIX:
|
||||
|
||||
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.un.sun_path[0]) {
|
||||
if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
|
||||
return false;
|
||||
} else {
|
||||
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Cannot compare, so we assume the addresses are different */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
|
||||
}
|
||||
|
@ -70,4 +70,8 @@ int socket_address_listen(
|
||||
mode_t socket_mode,
|
||||
int *ret);
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s);
|
||||
|
||||
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
|
||||
|
||||
#endif
|
||||
|
331
socket.c
331
socket.c
@ -52,20 +52,22 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
|
||||
[SOCKET_MAINTAINANCE] = UNIT_INACTIVE,
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_SOCKET_STATE_MAX] = {
|
||||
[SOCKET_DEAD] = "dead",
|
||||
[SOCKET_START_PRE] = "start-pre",
|
||||
[SOCKET_START_POST] = "start-post",
|
||||
[SOCKET_LISTENING] = "listening",
|
||||
[SOCKET_RUNNING] = "running",
|
||||
[SOCKET_STOP_PRE] = "stop-pre",
|
||||
[SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
|
||||
[SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
|
||||
[SOCKET_STOP_POST] = "stop-post",
|
||||
[SOCKET_FINAL_SIGTERM] = "final-sigterm",
|
||||
[SOCKET_FINAL_SIGKILL] = "final-sigkill",
|
||||
[SOCKET_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
static void socket_init(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->timer_watch.type = WATCH_INVALID;
|
||||
s->backlog = SOMAXCONN;
|
||||
s->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
s->directory_mode = 0755;
|
||||
s->socket_mode = 0666;
|
||||
|
||||
exec_context_init(&s->exec_context);
|
||||
|
||||
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
static void socket_unwatch_control_pid(Socket *s) {
|
||||
assert(s);
|
||||
@ -86,8 +88,11 @@ static void socket_done(Unit *u) {
|
||||
while ((p = s->ports)) {
|
||||
LIST_REMOVE(SocketPort, port, s->ports, p);
|
||||
|
||||
if (p->fd >= 0)
|
||||
close_nointr(p->fd);
|
||||
if (p->fd >= 0) {
|
||||
unit_unwatch_fd(UNIT(s), &p->fd_watch);
|
||||
close_nointr_nofail(p->fd);
|
||||
}
|
||||
|
||||
free(p->path);
|
||||
free(p);
|
||||
}
|
||||
@ -106,21 +111,6 @@ static void socket_done(Unit *u) {
|
||||
unit_unwatch_timer(u, &s->timer_watch);
|
||||
}
|
||||
|
||||
static void socket_init(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->timer_watch.type = WATCH_INVALID;
|
||||
s->backlog = SOMAXCONN;
|
||||
s->timeout_usec = DEFAULT_TIMEOUT_USEC;
|
||||
s->directory_mode = 0755;
|
||||
s->socket_mode = 0666;
|
||||
|
||||
exec_context_init(&s->exec_context);
|
||||
}
|
||||
|
||||
static bool have_non_accept_socket(Socket *s) {
|
||||
SocketPort *p;
|
||||
|
||||
@ -196,13 +186,6 @@ static const char* listen_lookup(int type) {
|
||||
|
||||
static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
static const char* const command_table[_SOCKET_EXEC_COMMAND_MAX] = {
|
||||
[SOCKET_EXEC_START_PRE] = "StartPre",
|
||||
[SOCKET_EXEC_START_POST] = "StartPost",
|
||||
[SOCKET_EXEC_STOP_PRE] = "StopPre",
|
||||
[SOCKET_EXEC_STOP_POST] = "StopPost"
|
||||
};
|
||||
|
||||
SocketExecCommand c;
|
||||
Socket *s = SOCKET(u);
|
||||
SocketPort *p;
|
||||
@ -222,7 +205,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%sKillMode: %s\n"
|
||||
"%sSocketMode: %04o\n"
|
||||
"%sDirectoryMode: %04o\n",
|
||||
prefix, state_string_table[s->state],
|
||||
prefix, socket_state_to_string(s->state),
|
||||
prefix, yes_no(s->bind_ipv6_only),
|
||||
prefix, s->backlog,
|
||||
prefix, kill_mode_to_string(s->kill_mode),
|
||||
@ -269,7 +252,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
continue;
|
||||
|
||||
fprintf(f, "%s→ %s:\n",
|
||||
prefix, command_table[c]);
|
||||
prefix, socket_exec_command_to_string(c));
|
||||
|
||||
exec_command_dump_list(s->exec_command[c], f, prefix2);
|
||||
}
|
||||
@ -307,7 +290,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
b = ntohl(remote.in.sin_addr.s_addr);
|
||||
|
||||
if (asprintf(&r,
|
||||
"%u-%u.%u.%u.%u-%u-%u.%u.%u.%u-%u",
|
||||
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
||||
nr,
|
||||
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
|
||||
ntohs(local.in.sin_port),
|
||||
@ -322,7 +305,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
|
||||
|
||||
if (asprintf(&r,
|
||||
"%u-%s-%u-%s-%u",
|
||||
"%u-%s:%u-%s:%u",
|
||||
nr,
|
||||
inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
|
||||
ntohs(local.in6.sin6_port),
|
||||
@ -368,7 +351,15 @@ static void socket_close_fds(Socket *s) {
|
||||
continue;
|
||||
|
||||
unit_unwatch_fd(UNIT(s), &p->fd_watch);
|
||||
assert_se(close_nointr(p->fd) >= 0);
|
||||
close_nointr_nofail(p->fd);
|
||||
|
||||
/* One little note: we should never delete any sockets
|
||||
* in the file system here! After all some other
|
||||
* process we spawned might still have a reference of
|
||||
* this fd and wants to continue to use it. Therefore
|
||||
* we delete sockets in the file system before we
|
||||
* create a new one, not after we stopped using
|
||||
* one! */
|
||||
|
||||
p->fd = -1;
|
||||
}
|
||||
@ -490,8 +481,12 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
unit_unwatch_timer(UNIT(s), &s->timer_watch);
|
||||
socket_unwatch_control_pid(s);
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
|
||||
}
|
||||
|
||||
if (state != SOCKET_LISTENING)
|
||||
socket_unwatch_fds(s);
|
||||
|
||||
if (state != SOCKET_START_POST &&
|
||||
state != SOCKET_LISTENING &&
|
||||
state != SOCKET_RUNNING &&
|
||||
@ -500,15 +495,62 @@ static void socket_set_state(Socket *s, SocketState state) {
|
||||
state != SOCKET_STOP_PRE_SIGKILL)
|
||||
socket_close_fds(s);
|
||||
|
||||
if (state != SOCKET_LISTENING)
|
||||
socket_unwatch_fds(s);
|
||||
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s", s->meta.id, state_string_table[old_state], state_string_table[state]);
|
||||
log_debug("%s changed %s → %s",
|
||||
s->meta.id,
|
||||
socket_state_to_string(old_state),
|
||||
socket_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int socket_coldplug(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SOCKET_DEAD);
|
||||
|
||||
if (s->deserialized_state != s->state) {
|
||||
|
||||
if (s->deserialized_state == SOCKET_START_PRE ||
|
||||
s->deserialized_state == SOCKET_START_POST ||
|
||||
s->deserialized_state == SOCKET_STOP_PRE ||
|
||||
s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
|
||||
s->deserialized_state == SOCKET_STOP_PRE_SIGKILL ||
|
||||
s->deserialized_state == SOCKET_STOP_POST ||
|
||||
s->deserialized_state == SOCKET_FINAL_SIGTERM ||
|
||||
s->deserialized_state == SOCKET_FINAL_SIGKILL) {
|
||||
|
||||
if (s->control_pid <= 0)
|
||||
return -EBADMSG;
|
||||
|
||||
if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (s->deserialized_state == SOCKET_START_POST ||
|
||||
s->deserialized_state == SOCKET_LISTENING ||
|
||||
s->deserialized_state == SOCKET_RUNNING ||
|
||||
s->deserialized_state == SOCKET_STOP_PRE ||
|
||||
s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
|
||||
s->deserialized_state == SOCKET_STOP_PRE_SIGKILL)
|
||||
if ((r = socket_open_fds(s)) < 0)
|
||||
return r;
|
||||
|
||||
if (s->deserialized_state == SOCKET_LISTENING)
|
||||
if ((r = socket_watch_fds(s)) < 0)
|
||||
return r;
|
||||
|
||||
socket_set_state(s, s->deserialized_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
|
||||
pid_t pid;
|
||||
int r;
|
||||
@ -574,6 +616,8 @@ static void socket_enter_stop_post(Socket *s, bool success) {
|
||||
|
||||
socket_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SOCKET_EXEC_STOP_POST;
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) {
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
@ -647,6 +691,8 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
|
||||
|
||||
socket_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SOCKET_EXEC_STOP_PRE;
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) {
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
@ -689,6 +735,8 @@ static void socket_enter_start_post(Socket *s) {
|
||||
|
||||
socket_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SOCKET_EXEC_START_POST;
|
||||
|
||||
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", s->meta.id, strerror(-r));
|
||||
@ -711,6 +759,8 @@ static void socket_enter_start_pre(Socket *s) {
|
||||
|
||||
socket_unwatch_control_pid(s);
|
||||
|
||||
s->control_command_id = SOCKET_EXEC_START_PRE;
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) {
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
|
||||
goto fail;
|
||||
@ -876,6 +926,142 @@ static int socket_stop(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Socket *s = SOCKET(u);
|
||||
SocketPort *p;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
|
||||
unit_serialize_item(u, f, "failure", yes_no(s->failure));
|
||||
unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
|
||||
|
||||
if (s->control_pid > 0)
|
||||
unit_serialize_item_format(u, f, "control-pid", "%u", (unsigned) s->control_pid);
|
||||
|
||||
if (s->control_command_id >= 0)
|
||||
unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
|
||||
|
||||
LIST_FOREACH(port, p, s->ports) {
|
||||
int copy;
|
||||
|
||||
if (p->fd < 0)
|
||||
continue;
|
||||
|
||||
if ((copy = fdset_put_dup(fds, p->fd)) < 0)
|
||||
return copy;
|
||||
|
||||
if (p->type == SOCKET_SOCKET) {
|
||||
char *t;
|
||||
|
||||
if ((r = socket_address_print(&p->address, &t)) < 0)
|
||||
return r;
|
||||
|
||||
unit_serialize_item_format(u, f, "socket", "%i %s", copy, t);
|
||||
free(t);
|
||||
} else {
|
||||
assert(p->type == SOCKET_FIFO);
|
||||
unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Socket *s = SOCKET(u);
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(key);
|
||||
assert(value);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
SocketState state;
|
||||
|
||||
if ((state = socket_state_from_string(value)) < 0)
|
||||
log_debug("Failed to parse state value %s", value);
|
||||
else
|
||||
s->deserialized_state = state;
|
||||
} else if (streq(key, "failure")) {
|
||||
int b;
|
||||
|
||||
if ((b = parse_boolean(value)) < 0)
|
||||
log_debug("Failed to parse failure value %s", value);
|
||||
else
|
||||
s->failure = b || s->failure;
|
||||
|
||||
} else if (streq(key, "n-accepted")) {
|
||||
unsigned k;
|
||||
|
||||
if ((r = safe_atou(value, &k)) < 0)
|
||||
log_debug("Failed to parse n-accepted value %s", value);
|
||||
else
|
||||
s->n_accepted += k;
|
||||
} else if (streq(key, "control-pid")) {
|
||||
unsigned pid;
|
||||
|
||||
if ((r = safe_atou(value, &pid)) < 0 || pid <= 0)
|
||||
log_debug("Failed to parse control-pid value %s", value);
|
||||
else
|
||||
s->control_pid = (pid_t) pid;
|
||||
} else if (streq(key, "control-command")) {
|
||||
SocketExecCommand id;
|
||||
|
||||
if ((id = socket_exec_command_from_string(value)) < 0)
|
||||
log_debug("Failed to parse exec-command value %s", value);
|
||||
else {
|
||||
s->control_command_id = id;
|
||||
s->control_command = s->exec_command[id];
|
||||
}
|
||||
} else if (streq(key, "fifo")) {
|
||||
int fd, skip = 0;
|
||||
SocketPort *p;
|
||||
|
||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||
log_debug("Failed to parse fifo value %s", value);
|
||||
else {
|
||||
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (streq(p->path, value+skip))
|
||||
break;
|
||||
|
||||
if (p) {
|
||||
if (p->fd >= 0)
|
||||
close_nointr_nofail(p->fd);
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (streq(key, "socket")) {
|
||||
int fd, skip = 0;
|
||||
SocketPort *p;
|
||||
|
||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||
log_debug("Failed to parse socket value %s", value);
|
||||
else {
|
||||
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (socket_address_is(&p->address, value+skip))
|
||||
break;
|
||||
|
||||
if (p) {
|
||||
if (p->fd >= 0)
|
||||
close_nointr_nofail(p->fd);
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
}
|
||||
}
|
||||
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState socket_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
@ -885,7 +1071,7 @@ static UnitActiveState socket_active_state(Unit *u) {
|
||||
static const char *socket_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_string_table[SOCKET(u)->state];
|
||||
return socket_state_to_string(SOCKET(u)->state);
|
||||
}
|
||||
|
||||
static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
|
||||
@ -918,6 +1104,7 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("cfd=%i", cfd);
|
||||
socket_enter_running(s, cfd);
|
||||
return;
|
||||
|
||||
@ -936,21 +1123,24 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
s->failure = s->failure || !success;
|
||||
|
||||
assert(s->control_pid == pid);
|
||||
assert(s->control_command);
|
||||
|
||||
exec_status_fill(&s->control_command->exec_status, pid, code, status);
|
||||
s->control_pid = 0;
|
||||
|
||||
if (s->control_command)
|
||||
exec_status_fill(&s->control_command->exec_status, pid, 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", u->meta.id, state_string_table[s->state]);
|
||||
if (s->control_command && s->control_command->command_next && success) {
|
||||
log_debug("%s running next command for state %s", u->meta.id, socket_state_to_string(s->state));
|
||||
socket_run_next(s, success);
|
||||
} else {
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
|
||||
|
||||
/* No further commands for this step, so let's figure
|
||||
* out what to do next */
|
||||
|
||||
log_debug("%s got final SIGCHLD for state %s", u->meta.id, state_string_table[s->state]);
|
||||
log_debug("%s got final SIGCHLD for state %s", u->meta.id, socket_state_to_string(s->state));
|
||||
|
||||
switch (s->state) {
|
||||
|
||||
@ -1082,18 +1272,49 @@ void socket_notify_service_dead(Socket *s) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
|
||||
[SOCKET_DEAD] = "dead",
|
||||
[SOCKET_START_PRE] = "start-pre",
|
||||
[SOCKET_START_POST] = "start-post",
|
||||
[SOCKET_LISTENING] = "listening",
|
||||
[SOCKET_RUNNING] = "running",
|
||||
[SOCKET_STOP_PRE] = "stop-pre",
|
||||
[SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
|
||||
[SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
|
||||
[SOCKET_STOP_POST] = "stop-post",
|
||||
[SOCKET_FINAL_SIGTERM] = "final-sigterm",
|
||||
[SOCKET_FINAL_SIGKILL] = "final-sigkill",
|
||||
[SOCKET_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
|
||||
|
||||
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
|
||||
[SOCKET_EXEC_START_PRE] = "StartPre",
|
||||
[SOCKET_EXEC_START_POST] = "StartPost",
|
||||
[SOCKET_EXEC_STOP_PRE] = "StopPre",
|
||||
[SOCKET_EXEC_STOP_POST] = "StopPost"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
|
||||
|
||||
const UnitVTable socket_vtable = {
|
||||
.suffix = ".socket",
|
||||
|
||||
.init = socket_init,
|
||||
.load = socket_load,
|
||||
.done = socket_done,
|
||||
.load = socket_load,
|
||||
|
||||
.coldplug = socket_coldplug,
|
||||
|
||||
.dump = socket_dump,
|
||||
|
||||
.start = socket_start,
|
||||
.stop = socket_stop,
|
||||
|
||||
.serialize = socket_serialize,
|
||||
.deserialize_item = socket_deserialize_item,
|
||||
|
||||
.active_state = socket_active_state,
|
||||
.sub_state_to_string = socket_sub_state_to_string,
|
||||
|
||||
|
9
socket.h
9
socket.h
@ -91,11 +91,12 @@ struct Socket {
|
||||
|
||||
Service *service;
|
||||
|
||||
SocketState state;
|
||||
SocketState state, deserialized_state;
|
||||
|
||||
KillMode kill_mode;
|
||||
|
||||
ExecCommand* control_command;
|
||||
SocketExecCommand control_command_id;
|
||||
pid_t control_pid;
|
||||
|
||||
char *bind_to_device;
|
||||
@ -117,4 +118,10 @@ void socket_notify_service_dead(Socket *s);
|
||||
|
||||
extern const UnitVTable socket_vtable;
|
||||
|
||||
const char* socket_state_to_string(SocketState i);
|
||||
SocketState socket_state_from_string(const char *s);
|
||||
|
||||
const char* socket_exec_command_to_string(SocketExecCommand i);
|
||||
SocketExecCommand socket_exec_command_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
|
@ -85,7 +85,9 @@ int main (string[] args) {
|
||||
" reload [NAME...] Reload on or more units\n" +
|
||||
" monitor Monitor unit/job changes\n" +
|
||||
" dump Dump servier status\n" +
|
||||
" snapshot [NAME] Create a snapshot\n");
|
||||
" snapshot [NAME] Create a snapshot\n" +
|
||||
" daemon-reload Reload daemon configuration\n" +
|
||||
" daemon-reexecute Reexecute daemon\n");
|
||||
|
||||
try {
|
||||
context.parse(ref args);
|
||||
@ -236,7 +238,13 @@ int main (string[] args) {
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
stdout.printf("%s\n", u.id);
|
||||
} else {
|
||||
} else if (args[1] == "daemon-reload")
|
||||
manager.reload();
|
||||
else if (args[1] == "daemon-reexecute" || args[1] == "daemon-reexec")
|
||||
manager.reexecute();
|
||||
else if (args[1] == "daemon-exit")
|
||||
manager.exit();
|
||||
else {
|
||||
stderr.printf("Unknown command %s.\n", args[1]);
|
||||
return 1;
|
||||
}
|
||||
|
@ -57,6 +57,10 @@ public interface Manager : DBus.Object {
|
||||
|
||||
public abstract string dump() throws DBus.Error;
|
||||
|
||||
public abstract void reload() throws DBus.Error;
|
||||
public abstract void reexecute() throws DBus.Error;
|
||||
public abstract void exit() throws DBus.Error;
|
||||
|
||||
public abstract ObjectPath create_snapshot(string name, bool cleanup = false) throws DBus.Error;
|
||||
|
||||
public abstract signal void unit_new(string id, ObjectPath path);
|
||||
|
92
target.c
92
target.c
@ -33,10 +33,33 @@ static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
|
||||
[TARGET_ACTIVE] = UNIT_ACTIVE
|
||||
};
|
||||
|
||||
static const char* const state_string_table[_TARGET_STATE_MAX] = {
|
||||
[TARGET_DEAD] = "dead",
|
||||
[TARGET_ACTIVE] = "active"
|
||||
};
|
||||
static void target_set_state(Target *t, TargetState state) {
|
||||
TargetState old_state;
|
||||
assert(t);
|
||||
|
||||
old_state = t->state;
|
||||
t->state = state;
|
||||
|
||||
if (state != old_state)
|
||||
log_debug("%s changed %s → %s",
|
||||
UNIT(t)->meta.id,
|
||||
target_state_to_string(old_state),
|
||||
target_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int target_coldplug(Unit *u) {
|
||||
Target *t = TARGET(u);
|
||||
|
||||
assert(t);
|
||||
assert(t->state == TARGET_DEAD);
|
||||
|
||||
if (t->deserialized_state != t->state)
|
||||
target_set_state(t, t->deserialized_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void target_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Target *t = TARGET(u);
|
||||
@ -46,20 +69,7 @@ static void target_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
fprintf(f,
|
||||
"%sTarget State: %s\n",
|
||||
prefix, state_string_table[t->state]);
|
||||
}
|
||||
|
||||
static void target_set_state(Target *t, TargetState state) {
|
||||
TargetState old_state;
|
||||
assert(t);
|
||||
|
||||
old_state = t->state;
|
||||
t->state = state;
|
||||
|
||||
if (state != old_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]);
|
||||
prefix, target_state_to_string(t->state));
|
||||
}
|
||||
|
||||
static int target_start(Unit *u) {
|
||||
@ -82,6 +92,39 @@ static int target_stop(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int target_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Target *s = TARGET(u);
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", target_state_to_string(s->state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Target *s = TARGET(u);
|
||||
|
||||
assert(u);
|
||||
assert(key);
|
||||
assert(value);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
TargetState state;
|
||||
|
||||
if ((state = target_state_from_string(value)) < 0)
|
||||
log_debug("Failed to parse state value %s", value);
|
||||
else
|
||||
s->deserialized_state = state;
|
||||
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState target_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
@ -91,7 +134,7 @@ static UnitActiveState target_active_state(Unit *u) {
|
||||
static const char *target_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_string_table[TARGET(u)->state];
|
||||
return target_state_to_string(TARGET(u)->state);
|
||||
}
|
||||
|
||||
int target_get_runlevel(Target *t) {
|
||||
@ -123,16 +166,27 @@ int target_get_runlevel(Target *t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const target_state_table[_TARGET_STATE_MAX] = {
|
||||
[TARGET_DEAD] = "dead",
|
||||
[TARGET_ACTIVE] = "active"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
|
||||
|
||||
const UnitVTable target_vtable = {
|
||||
.suffix = ".target",
|
||||
|
||||
.load = unit_load_fragment_and_dropin,
|
||||
.coldplug = target_coldplug,
|
||||
|
||||
.dump = target_dump,
|
||||
|
||||
.start = target_start,
|
||||
.stop = target_stop,
|
||||
|
||||
.serialize = target_serialize,
|
||||
.deserialize_item = target_deserialize_item,
|
||||
|
||||
.active_state = target_active_state,
|
||||
.sub_state_to_string = target_sub_state_to_string,
|
||||
|
||||
|
5
target.h
5
target.h
@ -36,11 +36,14 @@ typedef enum TargetState {
|
||||
struct Target {
|
||||
Meta meta;
|
||||
|
||||
TargetState state;
|
||||
TargetState state, deserialized_state;
|
||||
};
|
||||
|
||||
extern const UnitVTable target_vtable;
|
||||
|
||||
int target_get_runlevel(Target *t);
|
||||
|
||||
const char* target_state_to_string(TargetState i);
|
||||
TargetState target_state_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
|
40
unit-name.c
40
unit-name.c
@ -371,3 +371,43 @@ char *unit_name_template(const char *f) {
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
char *unit_name_from_path(const char *path, const char *suffix) {
|
||||
assert(path);
|
||||
assert(suffix);
|
||||
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
|
||||
if (path[0] == 0)
|
||||
return strappend("-", suffix);
|
||||
|
||||
return unit_name_build_escape(path, NULL, suffix);
|
||||
}
|
||||
|
||||
char *unit_name_to_path(const char *name) {
|
||||
char *w, *e;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (!(w = unit_name_to_prefix(name)))
|
||||
return NULL;
|
||||
|
||||
e = unit_name_unescape(w);
|
||||
free(w);
|
||||
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
if (e[0] != '/') {
|
||||
w = strappend("/", e);
|
||||
free(e);
|
||||
|
||||
if (!w)
|
||||
return NULL;
|
||||
|
||||
e = w;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
@ -48,4 +48,7 @@ char *unit_name_replace_instance(const char *f, const char *i);
|
||||
|
||||
char *unit_name_template(const char *f);
|
||||
|
||||
char *unit_name_from_path(const char *path, const char *suffix);
|
||||
char *unit_name_to_path(const char *name);
|
||||
|
||||
#endif
|
||||
|
126
unit.c
126
unit.c
@ -615,6 +615,16 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Common implementation for multiple backends */
|
||||
int unit_load_nop(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
if (u->meta.load_state == UNIT_STUB)
|
||||
u->meta.load_state = UNIT_LOADED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unit_load(Unit *u) {
|
||||
int r;
|
||||
|
||||
@ -1109,7 +1119,7 @@ void unit_unwatch_timer(Unit *u, Watch *w) {
|
||||
assert(w->type == WATCH_TIMER && w->data.unit == u);
|
||||
|
||||
assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
|
||||
assert_se(close_nointr(w->fd) == 0);
|
||||
close_nointr_nofail(w->fd);
|
||||
|
||||
w->fd = -1;
|
||||
w->type = WATCH_INVALID;
|
||||
@ -1495,6 +1505,29 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
|
||||
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);
|
||||
@ -1627,6 +1660,97 @@ void unit_unwatch_bus_name(Unit *u, const char *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",
|
||||
|
31
unit.h
31
unit.h
@ -209,24 +209,24 @@ struct UnitVTable {
|
||||
/* Instances make no sense for this type */
|
||||
bool no_instances:1;
|
||||
|
||||
/* Execlude this type from snapshots */
|
||||
/* Exclude this type from snapshots */
|
||||
bool no_snapshots:1;
|
||||
|
||||
/* This should reset all type-specific variables. This should
|
||||
* not allocate memory, and is either called with 0
|
||||
* initialized data, or with data left from done() */
|
||||
* not allocate memory, and is called with zero-initialized
|
||||
* data. It should hence only initialize variables that need
|
||||
* to be set != 0. */
|
||||
void (*init)(Unit *u);
|
||||
|
||||
/* This should free all type-specific variables. It should be
|
||||
* idempotent. */
|
||||
void (*done)(Unit *u);
|
||||
|
||||
/* Actually load data from disk. This may fail, and should set
|
||||
* load_state to UNIT_LOADED, UNIT_MERGED or leave it at
|
||||
* UNIT_STUB if no configuration could be found. */
|
||||
int (*load)(Unit *u);
|
||||
|
||||
/* This should free all type-specific variables. It should be
|
||||
* idempotent. There's no need to reset variables that deal
|
||||
* with dynamic memory/resources. */
|
||||
void (*done)(Unit *u);
|
||||
|
||||
/* If a a lot of units got created via enumerate(), this is
|
||||
* where to actually set the state and call unit_notify(). */
|
||||
int (*coldplug)(Unit *u);
|
||||
@ -239,6 +239,13 @@ struct UnitVTable {
|
||||
|
||||
bool (*can_reload)(Unit *u);
|
||||
|
||||
/* Write all data that cannot be restored from other sources
|
||||
* away using unit_serialize_item() */
|
||||
int (*serialize)(Unit *u, FILE *f, FDSet *fds);
|
||||
|
||||
/* Restore one item from the serialization */
|
||||
int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
|
||||
|
||||
/* Boils down the more complex internal state of this unit to
|
||||
* a simpler one that the engine can understand */
|
||||
UnitActiveState (*active_state)(Unit *u);
|
||||
@ -333,6 +340,7 @@ Unit *unit_follow_merge(Unit *u);
|
||||
|
||||
int unit_load_fragment_and_dropin(Unit *u);
|
||||
int unit_load_fragment_and_dropin_optional(Unit *u);
|
||||
int unit_load_nop(Unit *u);
|
||||
int unit_load(Unit *unit);
|
||||
|
||||
const char *unit_description(Unit *u);
|
||||
@ -373,11 +381,18 @@ int set_unit_path(const char *p);
|
||||
char *unit_dbus_path(Unit *u);
|
||||
|
||||
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
|
||||
int unit_get_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);
|
||||
|
||||
bool unit_can_serialize(Unit *u);
|
||||
int unit_serialize(Unit *u, FILE *f, FDSet *fds);
|
||||
void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr(4,5);
|
||||
void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
|
||||
int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
|
||||
|
||||
const char *unit_type_to_string(UnitType i);
|
||||
UnitType unit_type_from_string(const char *s);
|
||||
|
||||
|
10
util.c
10
util.c
@ -1215,7 +1215,7 @@ int close_all_fds(const int except[], unsigned n_except) {
|
||||
while ((de = readdir(d))) {
|
||||
int fd = -1;
|
||||
|
||||
if (de->d_name[0] == '.')
|
||||
if (ignore_file(de->d_name))
|
||||
continue;
|
||||
|
||||
if ((r = safe_atoi(de->d_name, &fd)) < 0)
|
||||
@ -1324,7 +1324,7 @@ int chvt(int vt) {
|
||||
if (ioctl(fd, VT_ACTIVATE, vt) < 0)
|
||||
r = -errno;
|
||||
|
||||
close_nointr(r);
|
||||
close_nointr_nofail(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1612,7 +1612,7 @@ int acquire_terminal(const char *name, bool fail, bool force) {
|
||||
}
|
||||
|
||||
if (notify >= 0)
|
||||
close_nointr(notify);
|
||||
close_nointr_nofail(notify);
|
||||
|
||||
if ((r = reset_terminal(fd)) < 0)
|
||||
log_warning("Failed to reset terminal: %s", strerror(-r));
|
||||
@ -1621,10 +1621,10 @@ int acquire_terminal(const char *name, bool fail, bool force) {
|
||||
|
||||
fail:
|
||||
if (fd >= 0)
|
||||
close_nointr(fd);
|
||||
close_nointr_nofail(fd);
|
||||
|
||||
if (notify >= 0)
|
||||
close_nointr(notify);
|
||||
close_nointr_nofail(notify);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user