mount: implement mounting properly

This also includes code that writes utmp/wtmp records when applicable,
making use the mount infrastructure to detct when those files are
accessible.

Finally, this also introduces a --dump-configuration-items switch.
This commit is contained in:
Lennart Poettering 2010-04-10 17:53:17 +02:00
parent 108736d0d3
commit e537352b9b
21 changed files with 1695 additions and 404 deletions

View file

@ -106,7 +106,9 @@ COMMON_SOURCES= \
mount-setup.c \
mount-setup.h \
hostname-setup.c \
hostname-setup.h
hostname-setup.h \
utmp-wtmp.c \
utmp-wtmp.h
systemd_SOURCES = \
$(COMMON_SOURCES) \

View file

@ -26,108 +26,80 @@
#include "load-fragment.h"
#include "load-dropin.h"
static int automount_init(Unit *u, UnitLoadState *new_state) {
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
[AUTOMOUNT_WAITING] = UNIT_ACTIVE,
[AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
[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 void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
a->state = 0;
a->mount = NULL;
}
static int automount_load(Unit *u) {
int r;
Automount *a = AUTOMOUNT(u);
assert(a);
exec_context_init(&a->exec_context);
assert(u);
assert(u->meta.load_state == UNIT_STUB);
/* Load a .automount file */
if ((r = unit_load_fragment(u, new_state)) < 0)
if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
return r;
if (*new_state == UNIT_STUB)
*new_state = UNIT_LOADED;
if (u->meta.load_state == UNIT_LOADED) {
/* Load drop-in directory data */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
return r;
if (*new_state == UNIT_LOADED) {
if ((r = unit_load_related_unit(u, ".mount", (Unit**) &a->mount)) < 0)
return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(a->mount))) < 0)
return r;
if ((r = unit_add_exec_dependencies(u, &a->exec_context)) < 0)
return r;
if ((r = unit_add_default_cgroup(u)) < 0)
return r;
}
return 0;
}
static void automount_done(Unit *u) {
Automount *d = AUTOMOUNT(u);
Automount *a = AUTOMOUNT(u);
assert(d);
free(d->path);
assert(a);
a->mount = NULL;
}
static void automount_dump(Unit *u, FILE *f, const char *prefix) {
static const char* const state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_START_PRE] = "start-pre",
[AUTOMOUNT_START_POST] = "start-post",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
[AUTOMOUNT_STOP_PRE] = "stop-pre",
[AUTOMOUNT_STOP_POST] = "stop-post",
[AUTOMOUNT_MAINTAINANCE] = "maintainance"
};
static const char* const command_table[_AUTOMOUNT_EXEC_MAX] = {
[AUTOMOUNT_EXEC_START_PRE] = "StartPre",
[AUTOMOUNT_EXEC_START_POST] = "StartPost",
[AUTOMOUNT_EXEC_STOP_PRE] = "StopPre",
[AUTOMOUNT_EXEC_STOP_POST] = "StopPost"
};
AutomountExecCommand c;
Automount *s = AUTOMOUNT(u);
assert(s);
fprintf(f,
"%sAutomount State: %s\n"
"%sPath: %s\n",
prefix, state_table[s->state],
prefix, s->path);
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _AUTOMOUNT_EXEC_MAX; c++) {
ExecCommand *i;
LIST_FOREACH(command, i, s->exec_command[c])
fprintf(f, "%s%s: %s\n", prefix, command_table[c], i->path);
}
"%sAutomount State: %s\n",
prefix, state_string_table[s->state]);
}
static UnitActiveState automount_active_state(Unit *u) {
static const UnitActiveState table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
[AUTOMOUNT_START_PRE] = UNIT_ACTIVATING,
[AUTOMOUNT_START_POST] = UNIT_ACTIVATING,
[AUTOMOUNT_WAITING] = UNIT_ACTIVE,
[AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
[AUTOMOUNT_STOP_PRE] = UNIT_DEACTIVATING,
[AUTOMOUNT_STOP_POST] = UNIT_DEACTIVATING,
[AUTOMOUNT_MAINTAINANCE] = UNIT_INACTIVE,
};
return table[AUTOMOUNT(u)->state];
return state_translation_table[AUTOMOUNT(u)->state];
}
const UnitVTable automount_vtable = {
.suffix = ".mount",
.no_alias = true,
.init = automount_init,
.load = automount_load,
.done = automount_done,
.dump = automount_dump,

View file

@ -28,34 +28,17 @@ typedef struct Automount Automount;
typedef enum AutomountState {
AUTOMOUNT_DEAD,
AUTOMOUNT_START_PRE,
AUTOMOUNT_START_POST,
AUTOMOUNT_WAITING,
AUTOMOUNT_RUNNING,
AUTOMOUNT_STOP_PRE,
AUTOMOUNT_STOP_POST,
AUTOMOUNT_MAINTAINANCE,
_AUTOMOUNT_STATE_MAX
_AUTOMOUNT_STATE_MAX,
_AUTOMOUNT_STATE_INVALID = -1
} AutomountState;
typedef enum AutomountExecCommand {
AUTOMOUNT_EXEC_START_PRE,
AUTOMOUNT_EXEC_START_POST,
AUTOMOUNT_EXEC_STOP_PRE,
AUTOMOUNT_EXEC_STOP_POST,
_AUTOMOUNT_EXEC_MAX
} AutomountExecCommand;
struct Automount {
Meta meta;
AutomountState state;
char *path;
ExecCommand* exec_command[_AUTOMOUNT_EXEC_MAX];
ExecContext exec_context;
pid_t contol_pid;
Mount *mount;
};

View file

@ -42,20 +42,29 @@ static void device_done(Unit *u) {
Device *d = DEVICE(u);
assert(d);
free(d->sysfs);
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);
if (state == d->state)
return;
old_state = d->state;
d->state = state;
log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
if (state != old_state)
log_debug("%s changed %s → %s", unit_id(UNIT(d)), state_string_table[old_state], state_string_table[state]);
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state]);
}
@ -231,15 +240,16 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u
if ((r = device_add_escaped_name(u, sysfs, true)) < 0)
goto fail;
unit_add_to_load_queue(u);
} else
delete = false;
if (!(DEVICE(u)->sysfs))
if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
r = -ENOMEM;
goto fail;
}
unit_add_to_load_queue(u);
} else
delete = false;
if (dn)
if ((r = device_add_escaped_name(u, dn, true)) < 0)
goto fail;
@ -460,7 +470,8 @@ fail:
const UnitVTable device_vtable = {
.suffix = ".device",
.init = unit_load_fragment_and_dropin_optional,
.init = device_init,
.load = unit_load_fragment_and_dropin_optional,
.done = device_done,
.coldplug = device_coldplug,

View file

@ -56,6 +56,15 @@ static int config_parse_deps(
assert(lvalue);
assert(rvalue);
if (UNIT_VTABLE(u)->refuse_requires &&
(d == UNIT_REQUIRES ||
d == UNIT_SOFT_REQUIRES ||
d == UNIT_REQUISITE ||
d == UNIT_SOFT_REQUISITE)) {
log_error("[%s:%u] Dependency of type %s not acceptable for this unit type.", filename, line, lvalue);
return -EBADMSG;
}
FOREACH_WORD(w, l, rvalue, state) {
char *t;
int r;
@ -1116,7 +1125,80 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
return 0;
}
static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
static void dump_items(FILE *f, const ConfigItem *items) {
const ConfigItem *i;
const char *prev_section = NULL;
bool not_first = false;
struct {
ConfigParserCallback callback;
const char *rvalue;
} table[] = {
{ config_parse_int, "INTEGER" },
{ config_parse_unsigned, "UNSIGNED" },
{ config_parse_size, "SIZE" },
{ config_parse_bool, "BOOLEAN" },
{ config_parse_string, "STRING" },
{ config_parse_path, "PATH" },
{ config_parse_strv, "STRING [...]" },
{ config_parse_nice, "NICE" },
{ config_parse_oom_adjust, "OOMADJUST" },
{ config_parse_io_class, "IOCLASS" },
{ config_parse_io_priority, "IOPRIORITY" },
{ config_parse_cpu_sched_policy, "CPUSCHEDPOLICY" },
{ config_parse_cpu_sched_prio, "CPUSCHEDPRIO" },
{ config_parse_cpu_affinity, "CPUAFFINITY" },
{ config_parse_mode, "MODE" },
{ config_parse_output, "OUTPUT" },
{ config_parse_input, "INPUT" },
{ config_parse_facility, "FACILITY" },
{ config_parse_level, "LEVEL" },
{ config_parse_capabilities, "CAPABILITIES" },
{ config_parse_secure_bits, "SECUREBITS" },
{ config_parse_bounding_set, "BOUNDINGSET" },
{ config_parse_timer_slack_ns, "TIMERSLACK" },
{ config_parse_limit, "LIMIT" },
{ config_parse_cgroup, "CGROUP [...]" },
{ config_parse_deps, "UNIT [...]" },
{ config_parse_names, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
{ config_parse_service_type, "SERVICETYPE" },
{ config_parse_service_restart, "SERVICERESTART" },
{ config_parse_sysv_priority, "SYSVPRIORITY" },
{ config_parse_kill_mode, "KILLMODE" },
{ config_parse_listen, "SOCKET [...]" },
{ config_parse_socket_bind, "SOCKETBIND" },
{ config_parse_bindtodevice, "NETWORKINTERFACE" }
};
assert(f);
assert(items);
for (i = items; i->lvalue; i++) {
unsigned j;
const char *rvalue = "OTHER";
if (!streq_ptr(i->section, prev_section)) {
if (!not_first)
not_first = true;
else
fputc('\n', f);
fprintf(f, "[%s]\n", i->section);
prev_section = i->section;
}
for (j = 0; j < ELEMENTSOF(table); j++)
if (i->parse == table[j].callback) {
rvalue = table[j].rvalue;
break;
}
fprintf(f, "%s=%s\n", i->lvalue, rvalue);
}
}
static int load_from_path(Unit *u, const char *path) {
static const char* const section_table[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = "Service",
@ -1223,7 +1305,13 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
{ "KillMode", config_parse_kill_mode, &u->socket.kill_mode, "Socket" },
EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"),
{ "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" },
{ "Where", config_parse_path, &u->mount.where, "Mount" },
{ "Options", config_parse_string, &u->mount.parameters_fragment.options, "Mount" },
{ "Type", config_parse_string, &u->mount.parameters_fragment.fstype, "Mount" },
{ "TimeoutSec", config_parse_usec, &u->mount.timeout_usec, "Mount" },
{ "KillMode", config_parse_kill_mode, &u->mount.kill_mode, "Mount" },
EXEC_CONTEXT_CONFIG_ITEMS(u->mount.exec_context, "Mount"),
{ NULL, NULL, NULL, NULL }
};
@ -1238,8 +1326,14 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
char *filename = NULL, *id = NULL;
Unit *merged;
if (!u) {
/* Dirty dirty hack. */
dump_items((FILE*) path, items);
return 0;
}
assert(u);
assert(new_state);
assert(path);
sections[0] = "Meta";
sections[1] = section_table[u->meta.type];
@ -1306,7 +1400,7 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
goto finish;
if (merged != u) {
*new_state = UNIT_MERGED;
u->meta.load_state = UNIT_MERGED;
r = 0;
goto finish;
}
@ -1319,7 +1413,7 @@ static int load_from_path(Unit *u, const char *path, UnitLoadState *new_state) {
u->meta.fragment_path = filename;
filename = NULL;
*new_state = UNIT_LOADED;
u->meta.load_state = UNIT_LOADED;
r = 0;
finish:
@ -1335,16 +1429,14 @@ finish:
return r;
}
int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
int unit_load_fragment(Unit *u) {
int r;
assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB);
if (u->meta.fragment_path) {
if ((r = load_from_path(u, u->meta.fragment_path, new_state)) < 0)
if ((r = load_from_path(u, u->meta.fragment_path)) < 0)
return r;
} else {
@ -1353,23 +1445,29 @@ int unit_load_fragment(Unit *u, UnitLoadState *new_state) {
/* Try to find the unit under its id */
if ((t = unit_id(u)))
if ((r = load_from_path(u, t, new_state)) < 0)
if ((r = load_from_path(u, t)) < 0)
return r;
/* Try to find an alias we can load this with */
if (*new_state == UNIT_STUB)
if (u->meta.load_state == UNIT_STUB)
SET_FOREACH(t, u->meta.names, i) {
if (unit_id(u) == t)
continue;
if ((r = load_from_path(u, t, new_state)) < 0)
if ((r = load_from_path(u, t)) < 0)
return r;
if (*new_state != UNIT_STUB)
if (u->meta.load_state != UNIT_STUB)
break;
}
}
return 0;
}
void unit_dump_config_items(FILE *f) {
/* OK, this wins a prize for extreme ugliness. */
load_from_path(NULL, (const void*) f);
}

View file

@ -26,6 +26,8 @@
/* Read service data from .desktop file style configuration fragments */
int unit_load_fragment(Unit *u, UnitLoadState *new_state);
int unit_load_fragment(Unit *u);
void unit_dump_config_items(FILE *f);
#endif

35
main.c
View file

@ -38,7 +38,8 @@
static enum {
ACTION_RUN,
ACTION_HELP,
ACTION_TEST
ACTION_TEST,
ACTION_DUMP_CONFIGURATION_ITEMS
} action = ACTION_RUN;
static char *default_unit = NULL;
@ -138,7 +139,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LOG_TARGET,
ARG_DEFAULT,
ARG_RUNNING_AS,
ARG_TEST
ARG_TEST,
ARG_DUMP_CONFIGURATION_ITEMS
};
static const struct option options[] = {
@ -148,6 +150,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "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 },
{ NULL, 0, NULL, 0 }
};
@ -202,6 +205,10 @@ static int parse_argv(int argc, char *argv[]) {
action = ACTION_TEST;
break;
case ARG_DUMP_CONFIGURATION_ITEMS:
action = ACTION_DUMP_CONFIGURATION_ITEMS;
break;
case 'h':
action = ACTION_HELP;
break;
@ -220,12 +227,13 @@ static int parse_argv(int argc, char *argv[]) {
static int help(void) {
printf("%s [options]\n\n"
" -h --help Show this help\n"
" --default=UNIT Set default unit\n"
" --log-level=LEVEL Set log level\n"
" --log-target=TARGET Set log target (console, syslog, kmsg)\n"
" --running-as=AS Set running as (init, system, session)\n"
" --test Determine startup sequence, dump it and exit\n",
" -h --help Show this help\n"
" --default=UNIT Set default unit\n"
" --log-level=LEVEL Set log level\n"
" --log-target=TARGET Set log target (console, syslog, kmsg)\n"
" --running-as=AS Set running as (init, system, session)\n"
" --test Determine startup sequence, dump it and exit\n"
" --dump-configuration-items Dump understood unit configuration items\n",
__progname);
return 0;
@ -270,12 +278,18 @@ int main(int argc, char *argv[]) {
if (action == ACTION_HELP) {
retval = help();
goto finish;
} else if (action == ACTION_DUMP_CONFIGURATION_ITEMS) {
unit_dump_config_items(stdout);
retval = 0;
goto finish;
}
assert_se(action == ACTION_RUN || action == ACTION_TEST);
/* Set up PATH unless it is already set */
setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", false);
setenv("PATH",
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
running_as == MANAGER_INIT);
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
@ -296,8 +310,7 @@ int main(int argc, char *argv[]) {
log_debug("systemd running in %s mode.", manager_running_as_to_string(running_as));
if (running_as == MANAGER_INIT)
if (hostname_setup() < 0)
goto finish;
hostname_setup();
if ((r = manager_new(running_as, &m)) < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));

View file

@ -27,6 +27,7 @@
#include <sys/signalfd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <utmpx.h>
#include <sys/poll.h>
#include <sys/reboot.h>
#include <sys/ioctl.h>
@ -42,6 +43,7 @@
#include "ratelimit.h"
#include "cgroup.h"
#include "mount-setup.h"
#include "utmp-wtmp.h"
static int manager_setup_signals(Manager *m) {
sigset_t mask;
@ -291,6 +293,8 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
if (!(m = new0(Manager, 1)))
return -ENOMEM;
m->boot_timestamp = now(CLOCK_REALTIME);
m->running_as = running_as;
m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@ -428,6 +432,10 @@ int manager_coldplug(Manager *m) {
return r;
}
/* Now that the initial devices are available, let's see if we
* can write the utmp file */
manager_write_utmp_reboot(m);
return 0;
}
@ -1704,6 +1712,72 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
return 0;
}
static bool manager_utmp_good(Manager *m) {
int r;
assert(m);
if ((r = mount_path_is_mounted(m, _PATH_UTMPX)) <= 0) {
if (r < 0)
log_warning("Failed to determine whether " _PATH_UTMPX " is mounted: %s", strerror(-r));
return false;
}
return true;
}
void manager_write_utmp_reboot(Manager *m) {
int r;
assert(m);
if (m->utmp_reboot_written)
return;
if (m->running_as != MANAGER_INIT)
return;
if (!manager_utmp_good(m))
return;
if ((r = utmp_put_reboot(m->boot_timestamp)) < 0) {
if (r != -ENOENT && r != -EROFS)
log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
return;
}
m->utmp_reboot_written = true;
}
void manager_write_utmp_runlevel(Manager *m, Unit *u) {
int runlevel, r;
assert(m);
assert(u);
if (u->meta.type != UNIT_TARGET)
return;
if (m->running_as != MANAGER_INIT)
return;
if (!manager_utmp_good(m))
return;
if ((runlevel = target_get_runlevel(TARGET(u))) <= 0)
return;
if ((r = utmp_put_runlevel(0, runlevel, 0)) < 0) {
if (r != -ENOENT && r != -EROFS)
log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
}
}
static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_INIT] = "init",
[MANAGER_SYSTEM] = "system",

View file

@ -145,6 +145,8 @@ struct Manager {
bool request_api_bus_dispatch:1;
bool request_system_bus_dispatch:1;
bool utmp_reboot_written:1;
Hashmap *watch_pids; /* pid => Unit object n:1 */
int epoll_fd;
@ -172,6 +174,8 @@ struct Manager {
Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
char *cgroup_controller;
char *cgroup_hierarchy;
usec_t boot_timestamp;
};
int manager_new(ManagerRunningAs running_as, Manager **m);
@ -204,4 +208,8 @@ int manager_loop(Manager *m);
const char *manager_running_as_to_string(ManagerRunningAs i);
ManagerRunningAs manager_running_as_from_string(const char *s);
void manager_write_utmp_reboot(Manager *m);
void manager_write_utmp_runlevel(Manager *m, Unit *t);
#endif

1038
mount.c

File diff suppressed because it is too large Load diff

57
mount.h
View file

@ -28,31 +28,76 @@ typedef struct Mount Mount;
typedef enum MountState {
MOUNT_DEAD,
MOUNT_MOUNTING,
MOUNT_MOUNTING, /* /bin/mount is running, but the mount is not done yet. */
MOUNT_MOUNTING_DONE, /* /bin/mount is running, and the mount is done. */
MOUNT_MOUNTED,
MOUNT_REMOUNTING,
MOUNT_UNMOUNTING,
MOUNT_MOUNTING_SIGTERM,
MOUNT_MOUNTING_SIGKILL,
MOUNT_REMOUNTING_SIGTERM,
MOUNT_REMOUNTING_SIGKILL,
MOUNT_UNMOUNTING_SIGTERM,
MOUNT_UNMOUNTING_SIGKILL,
MOUNT_MAINTAINANCE,
_MOUNT_STATE_MAX
_MOUNT_STATE_MAX,
_MOUNT_STATE_INVALID = -1
} MountState;
typedef enum MountExecCommand {
MOUNT_EXEC_MOUNT,
MOUNT_EXEC_UNMOUNT,
MOUNT_EXEC_REMOUNT,
_MOUNT_EXEC_COMMAND_MAX,
_MOUNT_EXEC_COMMAND_INVALID = -1
} MountExecCommand;
typedef struct MountParameters {
char *what;
char *options;
char *fstype;
} MountParameters;
struct Mount {
Meta meta;
MountState state;
char *where;
char *what, *where;
MountParameters parameters_etc_fstab;
MountParameters parameters_proc_self_mountinfo;
MountParameters parameters_fragment;
bool from_etc_fstab:1;
bool from_proc_self_mountinfo:1;
bool from_fragment:1;
/* Used while looking for mount points that vanished or got
* added from/to /proc/self/mountinfo */
bool still_exists:1;
bool just_created:1;
bool is_mounted:1;
bool just_mounted:1;
bool just_changed:1;
bool failure:1;
usec_t timeout_usec;
ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
ExecContext exec_context;
MountState state;
KillMode kill_mode;
ExecCommand* control_command;
pid_t control_pid;
Watch timer_watch;
};
extern const UnitVTable mount_vtable;
void mount_fd_event(Manager *m, int events);
int mount_path_is_mounted(Manager *m, const char* path);
#endif

View file

@ -78,7 +78,7 @@ static void service_done(Unit *u) {
s->sysv_runlevels = NULL;
exec_context_done(&s->exec_context);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_MAX);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
/* This will leak a process, but at least no memory or any of
@ -280,7 +280,7 @@ static int priority_from_rcd(Service *s, const char *init_script) {
return 0;
}
static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *new_state) {
static int service_load_sysv_path(Service *s, const char *path) {
FILE *f;
Unit *u;
unsigned line = 0;
@ -294,7 +294,6 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
assert(s);
assert(path);
assert(new_state);
u = UNIT(s);
@ -607,7 +606,7 @@ static int service_load_sysv_path(Service *s, const char *path, UnitLoadState *n
goto finish;
}
*new_state = UNIT_LOADED;
u->meta.load_state = UNIT_LOADED;
r = 0;
finish:
@ -618,7 +617,7 @@ finish:
return r;
}
static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *new_state) {
static int service_load_sysv_name(Service *s, const char *name) {
char **p;
assert(s);
@ -634,26 +633,25 @@ static int service_load_sysv_name(Service *s, const char *name, UnitLoadState *n
assert(endswith(path, ".service"));
path[strlen(path)-8] = 0;
r = service_load_sysv_path(s, path, new_state);
r = service_load_sysv_path(s, path);
free(path);
if (r < 0)
return r;
if (*new_state != UNIT_STUB)
if ((UNIT(s)->meta.load_state != UNIT_STUB))
break;
}
return 0;
}
static int service_load_sysv(Service *s, UnitLoadState *new_state) {
static int service_load_sysv(Service *s) {
const char *t;
Iterator i;
int r;
assert(s);
assert(new_state);
/* Load service data from SysV init scripts, preferably with
* LSB headers ... */
@ -662,31 +660,29 @@ static int service_load_sysv(Service *s, UnitLoadState *new_state) {
return 0;
if ((t = unit_id(UNIT(s))))
if ((r = service_load_sysv_name(s, t, new_state)) < 0)
if ((r = service_load_sysv_name(s, t)) < 0)
return r;
if (*new_state == UNIT_STUB)
if (UNIT(s)->meta.load_state == UNIT_STUB)
SET_FOREACH(t, UNIT(s)->meta.names, i) {
if ((r == service_load_sysv_name(s, t, new_state)) < 0)
if (t == unit_id(UNIT(s)))
continue;
if ((r == service_load_sysv_name(s, t)) < 0)
return r;
if (*new_state != UNIT_STUB)
if (UNIT(s)->meta.load_state != UNIT_STUB)
break;
}
return 0;
}
static int service_init(Unit *u, UnitLoadState *new_state) {
int r;
static void service_init(Unit *u) {
Service *s = SERVICE(u);
assert(s);
assert(new_state);
assert(*new_state == UNIT_STUB);
/* First, reset everything to the defaults, in case this is a
* reload */
assert(u);
assert(u->meta.load_state == UNIT_STUB);
s->type = 0;
s->restart = 0;
@ -711,18 +707,25 @@ static int service_init(Unit *u, UnitLoadState *new_state) {
s->failure = false;
RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5);
}
static int service_load(Unit *u) {
int r;
Service *s = SERVICE(u);
assert(s);
/* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0)
if ((r = unit_load_fragment(u)) < 0)
return r;
/* Load a classic init script as a fallback, if we couldn't find anything */
if (*new_state == UNIT_STUB)
if ((r = service_load_sysv(s, new_state)) < 0)
if (u->meta.load_state == UNIT_STUB)
if ((r = service_load_sysv(s)) < 0)
return r;
/* Still nothing found? Then let's give up */
if (*new_state == UNIT_STUB)
if (u->meta.load_state == UNIT_STUB)
return -ENOENT;
/* We were able to load something, then let's add in the
@ -731,7 +734,7 @@ static int service_init(Unit *u, UnitLoadState *new_state) {
return r;
/* This is a new unit? Then let's add in some extras */
if (*new_state == UNIT_LOADED) {
if (u->meta.load_state == UNIT_LOADED) {
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r;
@ -788,7 +791,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _SERVICE_EXEC_MAX; c++) {
for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
if (!s->exec_command[c])
continue;
@ -904,7 +907,7 @@ fail:
}
static int service_notify_sockets(Service *s) {
static int service_notify_sockets_dead(Service *s) {
Iterator i;
Set *set;
Socket *sock;
@ -966,19 +969,14 @@ static void service_set_state(Service *s, ServiceState state) {
state != SERVICE_STOP_SIGKILL &&
state != SERVICE_STOP_POST &&
state != SERVICE_FINAL_SIGTERM &&
state != SERVICE_FINAL_SIGKILL)
state != SERVICE_FINAL_SIGKILL) {
if (s->control_pid > 0) {
unit_unwatch_pid(UNIT(s), s->control_pid);
s->control_pid = 0;
}
if (state != SERVICE_START_PRE &&
state != SERVICE_START &&
state != SERVICE_START_POST &&
state != SERVICE_RELOAD &&
state != SERVICE_STOP &&
state != SERVICE_STOP_POST)
s->control_command = NULL;
}
if (state == SERVICE_DEAD ||
state == SERVICE_STOP ||
@ -989,12 +987,10 @@ static void service_set_state(Service *s, ServiceState state) {
state == SERVICE_FINAL_SIGKILL ||
state == SERVICE_MAINTAINANCE ||
state == SERVICE_AUTO_RESTART)
service_notify_sockets(s);
service_notify_sockets_dead(s);
if (old_state == state)
return;
log_debug("%s changed %s → %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state));
if (old_state != state)
log_debug("%s changed %s → %s", unit_id(UNIT(s)), service_state_to_string(old_state), service_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
}
@ -1489,12 +1485,22 @@ static int service_stop(Unit *u) {
assert(s);
/* Cannot do this now */
if (s->state == SERVICE_START_PRE ||
s->state == SERVICE_START ||
s->state == SERVICE_START_POST ||
s->state == SERVICE_RELOAD)
return -EAGAIN;
/* Already on it */
if (s->state == SERVICE_STOP ||
s->state == SERVICE_STOP_SIGTERM ||
s->state == SERVICE_STOP_SIGKILL ||
s->state == SERVICE_STOP_POST ||
s->state == SERVICE_FINAL_SIGTERM ||
s->state == SERVICE_FINAL_SIGKILL)
return 0;
if (s->state == SERVICE_AUTO_RESTART) {
service_set_state(s, SERVICE_DEAD);
return 0;
@ -1966,7 +1972,7 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
static const char* const service_exec_command_table[_SERVICE_EXEC_MAX] = {
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
[SERVICE_EXEC_START] = "ExecStart",
[SERVICE_EXEC_START_POST] = "ExecStartPost",
@ -1981,6 +1987,7 @@ const UnitVTable service_vtable = {
.suffix = ".service",
.init = service_init,
.load = service_load,
.done = service_done,
.dump = service_dump,

View file

@ -69,8 +69,8 @@ typedef enum ServiceExecCommand {
SERVICE_EXEC_RELOAD,
SERVICE_EXEC_STOP,
SERVICE_EXEC_STOP_POST,
_SERVICE_EXEC_MAX,
_SERVICE_EXEC_INVALID = -1
_SERVICE_EXEC_COMMAND_MAX,
_SERVICE_EXEC_COMMAND_INVALID = -1
} ServiceExecCommand;
struct Service {
@ -85,7 +85,7 @@ struct Service {
usec_t restart_usec;
usec_t timeout_usec;
ExecCommand* exec_command[_SERVICE_EXEC_MAX];
ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
ExecContext exec_context;
bool permissions_start_only;
@ -102,16 +102,16 @@ struct Service {
pid_t main_pid, control_pid;
bool main_pid_known:1;
bool sysv_has_lsb:1;
bool failure:1; /* if we shut down, remember why */
Watch timer_watch;
bool sysv_has_lsb:1;
char *sysv_path;
int sysv_start_priority;
char *sysv_runlevels;
RateLimit ratelimit;
Watch timer_watch;
};
extern const UnitVTable service_vtable;

102
socket.c
View file

@ -79,7 +79,7 @@ static void socket_done(Unit *u) {
}
exec_context_done(&s->exec_context);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_MAX);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
if (s->control_pid > 0) {
@ -90,17 +90,16 @@ static void socket_done(Unit *u) {
s->service = NULL;
free(s->bind_to_device);
s->bind_to_device = NULL;
unit_unwatch_timer(u, &s->timer_watch);
}
static int socket_init(Unit *u, UnitLoadState *new_state) {
static void socket_init(Unit *u) {
Socket *s = SOCKET(u);
char *t;
int r;
/* First, reset everything to the defaults, in case this is a
* reload */
assert(u);
assert(u->meta.load_state == UNIT_STUB);
s->state = 0;
s->timer_watch.type = WATCH_INVALID;
@ -112,27 +111,24 @@ static int socket_init(Unit *u, UnitLoadState *new_state) {
s->kill_mode = 0;
s->failure = false;
s->control_pid = 0;
s->service = NULL;
exec_context_init(&s->exec_context);
}
if ((r = unit_load_fragment(u, new_state)) < 0)
return r;
static int socket_load(Unit *u) {
Socket *s = SOCKET(u);
int r;
if (*new_state == UNIT_STUB)
return -ENOENT;
assert(u);
assert(u->meta.load_state == UNIT_STUB);
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
if ((r = unit_load_fragment_and_dropin(u)) < 0)
return r;
/* This is a new unit? Then let's add in some extras */
if (*new_state == UNIT_LOADED) {
if (u->meta.load_state == UNIT_LOADED) {
if (!(t = unit_name_change_suffix(unit_id(u), ".service")))
return -ENOMEM;
r = manager_load_unit(u->meta.manager, t, (Unit**) &s->service);
free(t);
if (r < 0)
if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service)))
return r;
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
@ -163,7 +159,7 @@ 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_MAX] = {
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",
@ -226,7 +222,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _SOCKET_EXEC_MAX; c++) {
for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) {
if (!s->exec_command[c])
continue;
@ -363,27 +359,16 @@ static void socket_set_state(Socket *s, SocketState state) {
state != SOCKET_STOP_PRE_SIGKILL &&
state != SOCKET_STOP_POST &&
state != SOCKET_STOP_POST_SIGTERM &&
state != SOCKET_STOP_POST_SIGKILL)
state != SOCKET_STOP_POST_SIGKILL) {
unit_unwatch_timer(UNIT(s), &s->timer_watch);
if (state != SOCKET_START_PRE &&
state != SOCKET_START_POST &&
state != SOCKET_STOP_PRE &&
state != SOCKET_STOP_PRE_SIGTERM &&
state != SOCKET_STOP_PRE_SIGKILL &&
state != SOCKET_STOP_POST &&
state != SOCKET_STOP_POST_SIGTERM &&
state != SOCKET_STOP_POST_SIGKILL)
if (s->control_pid > 0) {
unit_unwatch_pid(UNIT(s), s->control_pid);
s->control_pid = 0;
}
if (state != SOCKET_START_PRE &&
state != SOCKET_START_POST &&
state != SOCKET_STOP_PRE &&
state != SOCKET_STOP_POST)
s->control_command = NULL;
}
if (state != SOCKET_START_POST &&
state != SOCKET_LISTENING &&
@ -396,15 +381,13 @@ static void socket_set_state(Socket *s, SocketState state) {
if (state != SOCKET_LISTENING)
socket_unwatch_fds(s);
if (state == old_state)
return;
log_debug("%s changed %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]);
if (state != old_state)
log_debug("%s changed %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]);
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
}
static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
@ -412,11 +395,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
assert(c);
assert(_pid);
if (timeout) {
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
goto fail;
} else
unit_unwatch_timer(UNIT(s), &s->timer_watch);
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
goto fail;
if ((r = exec_spawn(c,
&s->exec_context,
@ -436,8 +416,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
return 0;
fail:
if (timeout)
unit_unwatch_timer(UNIT(s), &s->timer_watch);
unit_unwatch_timer(UNIT(s), &s->timer_watch);
return r;
}
@ -459,7 +438,7 @@ static void socket_enter_stop_post(Socket *s, bool success) {
s->failure = true;
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]))
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
socket_set_state(s, SOCKET_STOP_POST);
@ -502,7 +481,6 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) {
r = -errno;
goto fail;
}
}
socket_set_state(s, state);
@ -529,7 +507,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
s->failure = true;
if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]))
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
socket_set_state(s, SOCKET_STOP_PRE);
@ -570,7 +548,7 @@ static void socket_enter_start_post(Socket *s) {
}
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST]))
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0) {
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) {
log_warning("%s failed to run start-post executable: %s", unit_id(UNIT(s)), strerror(-r));
goto fail;
}
@ -591,7 +569,7 @@ static void socket_enter_start_pre(Socket *s) {
assert(s);
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]))
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
socket_set_state(s, SOCKET_START_PRE);
@ -634,7 +612,7 @@ static void socket_run_next(Socket *s, bool success) {
s->control_command = s->control_command->command_next;
if ((r = socket_spawn(s, s->control_command, true, &s->control_pid)) < 0)
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
goto fail;
return;
@ -671,6 +649,13 @@ static int socket_start(Unit *u) {
if (s->service->meta.load_state != UNIT_LOADED)
return -ENOENT;
/* If the service is alredy actvie we cannot start the
* socket */
if (s->service->state != SERVICE_DEAD &&
s->service->state != SERVICE_MAINTAINANCE &&
s->service->state != SERVICE_AUTO_RESTART)
return -EBUSY;
assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE);
s->failure = false;
@ -689,6 +674,15 @@ static int socket_stop(Unit *u) {
s->state == SOCKET_START_POST)
return -EAGAIN;
/* Already on it */
if (s->state == SOCKET_STOP_PRE ||
s->state == SOCKET_STOP_PRE_SIGTERM ||
s->state == SOCKET_STOP_PRE_SIGKILL ||
s->state == SOCKET_STOP_POST ||
s->state == SOCKET_STOP_POST_SIGTERM ||
s->state == SOCKET_STOP_POST_SIGTERM)
return 0;
assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
socket_enter_stop_pre(s, true);
@ -781,14 +775,13 @@ static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
assert(s);
assert(elapsed == 1);
assert(w == &s->timer_watch);
switch (s->state) {
case SOCKET_START_PRE:
case SOCKET_START_POST:
log_warning("%s operation timed out. Stopping.", unit_id(u));
log_warning("%s starting timed out. Stopping.", unit_id(u));
socket_enter_stop_pre(s, false);
break;
@ -874,6 +867,7 @@ const UnitVTable socket_vtable = {
.suffix = ".socket",
.init = socket_init,
.load = socket_load,
.done = socket_done,
.dump = socket_dump,

View file

@ -41,7 +41,8 @@ typedef enum SocketState {
SOCKET_STOP_POST_SIGTERM,
SOCKET_STOP_POST_SIGKILL,
SOCKET_MAINTAINANCE,
_SOCKET_STATE_MAX
_SOCKET_STATE_MAX,
_SOCKET_STATE_INVALID = -1
} SocketState;
typedef enum SocketExecCommand {
@ -49,12 +50,15 @@ typedef enum SocketExecCommand {
SOCKET_EXEC_START_POST,
SOCKET_EXEC_STOP_PRE,
SOCKET_EXEC_STOP_POST,
_SOCKET_EXEC_MAX
_SOCKET_EXEC_COMMAND_MAX,
_SOCKET_EXEC_COMMAND_INVALID = -1
} SocketExecCommand;
typedef enum SocketType {
SOCKET_SOCKET,
SOCKET_FIFO
SOCKET_FIFO,
_SOCKET_FIFO_MAX,
_SOCKET_FIFO_INVALID = -1
} SocketType;
typedef struct SocketPort SocketPort;
@ -82,7 +86,7 @@ struct Socket {
usec_t timeout_usec;
ExecCommand* exec_command[_SOCKET_EXEC_MAX];
ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
ExecContext exec_context;
Service *service;

View file

@ -37,6 +37,15 @@ static const char* const state_string_table[_TARGET_STATE_MAX] = {
[TARGET_ACTIVE] = "active"
};
static void target_init(Unit *u) {
Target *t = TARGET(u);
assert(t);
assert(u->meta.load_state == UNIT_STUB);
t->state = 0;
}
static void target_dump(Unit *u, FILE *f, const char *prefix) {
Target *t = TARGET(u);
@ -52,13 +61,11 @@ static void target_set_state(Target *t, TargetState state) {
TargetState old_state;
assert(t);
if (state == t->state)
return;
old_state = t->state;
t->state = state;
log_debug("%s changed %s → %s", unit_id(UNIT(t)), state_string_table[old_state], state_string_table[state]);
if (state != old_state)
log_debug("%s changed %s → %s", unit_id(UNIT(t)), state_string_table[old_state], state_string_table[state]);
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]);
}
@ -121,7 +128,8 @@ int target_get_runlevel(Target *t) {
const UnitVTable target_vtable = {
.suffix = ".target",
.init = unit_load_fragment_and_dropin,
.init = target_init,
.load = unit_load_fragment_and_dropin,
.dump = target_dump,

View file

@ -44,7 +44,7 @@ static UnitActiveState timer_active_state(Unit *u) {
const UnitVTable timer_vtable = {
.suffix = ".timer",
.init = unit_load_fragment_and_dropin,
.load = unit_load_fragment_and_dropin,
.done = timer_done,
.active_state = timer_active_state

94
unit.c
View file

@ -155,6 +155,11 @@ int unit_add_name(Unit *u, const char *text) {
if (u->meta.type != _UNIT_TYPE_INVALID && t != u->meta.type)
return -EINVAL;
if (u->meta.type != _UNIT_TYPE_INVALID &&
UNIT_VTABLE(u)->no_alias &&
!set_isempty(u->meta.names))
return -EEXIST;
if (!(s = strdup(text)))
return -ENOMEM;
@ -173,10 +178,14 @@ int unit_add_name(Unit *u, const char *text) {
return r;
}
if (u->meta.type == _UNIT_TYPE_INVALID)
if (u->meta.type == _UNIT_TYPE_INVALID) {
LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta);
u->meta.type = t;
u->meta.type = t;
if (UNIT_VTABLE(u)->init)
UNIT_VTABLE(u)->init(u);
}
if (!u->meta.id)
u->meta.id = s;
@ -278,9 +287,6 @@ void unit_free(Unit *u) {
bus_unit_send_removed_signal(u);
/* Detach from next 'bigger' objects */
cgroup_bonding_free_list(u->meta.cgroup_bondings);
SET_FOREACH(t, u->meta.names, i)
hashmap_remove_value(u->meta.manager->units, t, u);
@ -296,13 +302,15 @@ void unit_free(Unit *u) {
if (u->meta.in_cleanup_queue)
LIST_REMOVE(Meta, cleanup_queue, u->meta.manager->cleanup_queue, &u->meta);
/* Free data and next 'smaller' objects */
if (u->meta.job)
job_free(u->meta.job);
if (u->meta.load_state != UNIT_STUB)
if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u);
/* Free data and next 'smaller' objects */
if (u->meta.job)
job_free(u->meta.job);
cgroup_bonding_free_list(u->meta.cgroup_bondings);
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
bidi_set_free(u, u->meta.dependencies[d]);
@ -401,9 +409,6 @@ int unit_merge(Unit *u, Unit *other) {
if (other == u)
return 0;
/* This merges 'other' into 'unit'. FIXME: This does not
* rollback on failure. */
if (u->meta.type != u->meta.type)
return -EINVAL;
@ -569,18 +574,16 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) {
int unit_load_fragment_and_dropin(Unit *u) {
int r;
assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
/* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0)
if ((r = unit_load_fragment(u)) < 0)
return r;
if (*new_state == UNIT_STUB)
if (u->meta.load_state == UNIT_STUB)
return -ENOENT;
/* Load drop-in directory data */
@ -591,22 +594,20 @@ int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state) {
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) {
int unit_load_fragment_and_dropin_optional(Unit *u) {
int r;
assert(u);
assert(new_state);
assert(*new_state == UNIT_STUB || *new_state == UNIT_LOADED);
/* Same as unit_load_fragment_and_dropin(), but whether
* something can be loaded or not doesn't matter. */
/* Load a .service file */
if ((r = unit_load_fragment(u, new_state)) < 0)
if ((r = unit_load_fragment(u)) < 0)
return r;
if (*new_state == UNIT_STUB)
*new_state = UNIT_LOADED;
if (u->meta.load_state == UNIT_STUB)
u->meta.load_state = UNIT_LOADED;
/* Load drop-in directory data */
if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
@ -617,7 +618,6 @@ int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state) {
int unit_load(Unit *u) {
int r;
UnitLoadState res;
assert(u);
@ -626,21 +626,21 @@ int unit_load(Unit *u) {
u->meta.in_load_queue = false;
}
if (u->meta.type == _UNIT_TYPE_INVALID)
return -EINVAL;
if (u->meta.load_state != UNIT_STUB)
return 0;
if (UNIT_VTABLE(u)->init) {
res = UNIT_STUB;
if ((r = UNIT_VTABLE(u)->init(u, &res)) < 0)
if (UNIT_VTABLE(u)->load)
if ((r = UNIT_VTABLE(u)->load(u)) < 0)
goto fail;
}
if (res == UNIT_STUB) {
if (u->meta.load_state == UNIT_STUB) {
r = -ENOENT;
goto fail;
}
u->meta.load_state = res;
assert((u->meta.load_state != UNIT_MERGED) == !u->meta.merged_into);
unit_add_to_dbus_queue(unit_follow_merge(u));
@ -711,9 +711,6 @@ int unit_stop(Unit *u) {
if (!UNIT_VTABLE(u)->stop)
return -EBADR;
if (state == UNIT_DEACTIVATING)
return 0;
unit_add_to_dbus_queue(u);
return UNIT_VTABLE(u)->stop(u);
}
@ -940,25 +937,38 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
retroactively_stop_dependencies(u);
}
if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
/* Some names are special */
if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
log_info("D-Bus became available, trying to reconnect.");
/* The bus just got started, hence try to connect to it. */
/* The bus just might have become available,
* hence try to connect to it, if we aren't
* yet connected. */
bus_init_system(u->meta.manager);
bus_init_api(u->meta.manager);
}
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) {
/* The syslog daemon just got started, hence try to connect to it. */
log_info("Syslog became available, trying to reconnect.");
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE))
/* The syslog daemon just might have become
* available, hence try to connect to it, if
* we aren't yet connected. */
log_open_syslog();
}
} else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
if (u->meta.type == UNIT_MOUNT)
/* Another directory became available, let's
* check if that is enough to write our utmp
* entry. */
manager_write_utmp_reboot(u->meta.manager);
if (u->meta.type == UNIT_TARGET)
/* A target got activated, maybe this is a runlevel? */
manager_write_utmp_runlevel(u->meta.manager, u);
} else if (!UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE))
/* The syslog daemon just got terminated, hence try to disconnect from it. */
/* The syslog daemon might just have
* terminated, hence try to disconnect from
* it. */
log_close_syslog();
/* We don't care about D-Bus here, since we'll get an

29
unit.h
View file

@ -196,8 +196,31 @@ union Unit {
struct UnitVTable {
const char *suffix;
int (*init)(Unit *u, UnitLoadState *new_state);
/* Can units of this type have multiple names? */
bool no_alias:1;
/* If true units of this types can never have "Requires"
* dependencies, because state changes can only be observed,
* not triggered */
bool refuse_requires: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() */
void (*init)(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);
void (*dump)(Unit *u, FILE *f, const char *prefix);
@ -285,8 +308,8 @@ int unit_merge_by_name(Unit *u, const char *other);
Unit *unit_follow_merge(Unit *u);
int unit_load_fragment_and_dropin(Unit *u, UnitLoadState *new_state);
int unit_load_fragment_and_dropin_optional(Unit *u, UnitLoadState *new_state);
int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit);
const char* unit_id(Unit *u);

210
utmp-wtmp.c Normal file
View file

@ -0,0 +1,210 @@
/*-*- 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 <utmpx.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <sys/utsname.h>
#include "macro.h"
#include "utmp-wtmp.h"
int utmp_get_runlevel(int *runlevel, int *previous) {
struct utmpx lookup, *found;
int r;
const char *e;
assert(runlevel);
/* If these values are set in the environment this takes
* precedence. Presumably, sysvinit does this to work around a
* race condition that would otherwise exist where we'd always
* go to disk and hence might read runlevel data that might be
* very new and does not apply to the current script being
* executed. */
if ((e = getenv("RUNLEVEL")) && e[0] > 0) {
*runlevel = e[0];
if (previous) {
/* $PREVLEVEL seems to be an Upstart thing */
if ((e = getenv("PREVLEVEL")) && e[0] > 0)
*previous = e[0];
else
*previous = 0;
}
return 0;
}
if (utmpxname(_PATH_UTMPX) < 0)
return -errno;
setutxent();
zero(lookup);
lookup.ut_type = RUN_LVL;
if (!(found = getutxid(&lookup)))
r = -errno;
else {
int a, b;
a = found->ut_pid & 0xFF;
b = (found->ut_pid >> 8) & 0xFF;
if (a < 0 || b < 0)
r = -EIO;
else {
*runlevel = a;
if (previous)
*previous = b;
r = 0;
}
}
endutxent();
return r;
}
static void init_entry(struct utmpx *store, usec_t timestamp) {
struct utsname uts;
assert(store);
zero(*store);
zero(uts);
if (timestamp <= 0)
timestamp = now(CLOCK_REALTIME);
store->ut_tv.tv_sec = timestamp / USEC_PER_SEC;
store->ut_tv.tv_usec = timestamp % USEC_PER_SEC;
if (uname(&uts) >= 0)
strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
strncpy(store->ut_id, "~~", sizeof(store->ut_id));
}
static int write_entry_utmp(const struct utmpx *store) {
int r;
assert(store);
/* utmp is similar to wtmp, but there is only one entry for
* each entry type resp. user; i.e. basically a key/value
* table. */
if (utmpxname(_PATH_UTMPX) < 0)
return -errno;
setutxent();
if (!pututxline(store))
r = -errno;
else
r = 0;
endutxent();
return r;
}
static int write_entry_wtmp(const struct utmpx *store) {
assert(store);
/* wtmp is a simple append-only file where each entry is
simply appended to * the end; i.e. basically a log. */
errno = 0;
updwtmpx(_PATH_WTMPX, store);
return -errno;
}
static int write_entry_both(const struct utmpx *store) {
int r, s;
r = write_entry_utmp(store);
s = write_entry_wtmp(store);
if (r >= 0)
r = s;
/* If utmp/wtmp have been disabled, that's a good thing, hence
* ignore the errors */
if (r == -ENOENT)
r = 0;
return r;
}
int utmp_put_shutdown(usec_t timestamp) {
struct utmpx store;
init_entry(&store, timestamp);
store.ut_type = RUN_LVL;
strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
return write_entry_both(&store);
}
int utmp_put_reboot(usec_t timestamp) {
struct utmpx store;
init_entry(&store, timestamp);
store.ut_type = BOOT_TIME;
strncpy(store.ut_user, "reboot", sizeof(store.ut_type));
return write_entry_both(&store);
}
int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous) {
struct utmpx store;
int r;
assert(runlevel > 0);
if (previous <= 0) {
/* Find the old runlevel automatically */
if ((r = utmp_get_runlevel(&previous, NULL)) < 0)
return r;
if (previous == runlevel)
return 0;
}
init_entry(&store, timestamp);
store.ut_type = RUN_LVL;
store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
return write_entry_both(&store);
}

33
utmp-wtmp.h Normal file
View file

@ -0,0 +1,33 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef fooutmpwtmphfoo
#define fooutmpwtmphfoo
/***
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 "util.h"
int utmp_get_runlevel(int *runlevel, int *previous);
int utmp_put_shutdown(usec_t timestamp);
int utmp_put_reboot(usec_t timestamp);
int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous);
#endif