build-sys: move source files to subdirectory
This commit is contained in:
+784
@@ -0,0 +1,784 @@
|
||||
/*-*- 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 <limits.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/auto_fs4.h>
|
||||
#include <linux/auto_dev-ioctl.h>
|
||||
|
||||
#include "unit.h"
|
||||
#include "automount.h"
|
||||
#include "load-fragment.h"
|
||||
#include "load-dropin.h"
|
||||
#include "unit-name.h"
|
||||
#include "dbus-automount.h"
|
||||
|
||||
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 int open_dev_autofs(Manager *m);
|
||||
|
||||
static void automount_init(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(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) {
|
||||
assert(path);
|
||||
|
||||
for (;;) {
|
||||
|
||||
if (umount2(path, MNT_DETACH) >= 0)
|
||||
continue;
|
||||
|
||||
if (errno != EINVAL)
|
||||
log_error("Failed to unmount: %m");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void unmount_autofs(Automount *a) {
|
||||
assert(a);
|
||||
|
||||
if (a->pipe_fd < 0)
|
||||
return;
|
||||
|
||||
automount_send_ready(a, -EHOSTDOWN);
|
||||
|
||||
unit_unwatch_fd(UNIT(a), &a->pipe_watch);
|
||||
close_nointr_nofail(a->pipe_fd);
|
||||
a->pipe_fd = -1;
|
||||
|
||||
/* 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) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(a);
|
||||
|
||||
unmount_autofs(a);
|
||||
a->mount = NULL;
|
||||
|
||||
free(a->where);
|
||||
a->where = NULL;
|
||||
|
||||
set_free(a->tokens);
|
||||
a->tokens = NULL;
|
||||
}
|
||||
|
||||
int automount_add_one_mount_link(Automount *a, Mount *m) {
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(m);
|
||||
|
||||
if (a->meta.load_state != UNIT_LOADED ||
|
||||
m->meta.load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
if (!path_startswith(a->where, m->where))
|
||||
return 0;
|
||||
|
||||
if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, UNIT(a), true)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency(UNIT(a), UNIT_REQUIRES, UNIT(m), true)) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int automount_add_mount_links(Automount *a) {
|
||||
Meta *other;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
|
||||
LIST_FOREACH(units_per_type, other, a->meta.manager->units_per_type[UNIT_MOUNT])
|
||||
if ((r = automount_add_one_mount_link(a, (Mount*) other)) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int automount_verify(Automount *a) {
|
||||
bool b;
|
||||
char *e;
|
||||
assert(a);
|
||||
|
||||
if (UNIT(a)->meta.load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
if (!(e = unit_name_from_path(a->where, ".automount")))
|
||||
return -ENOMEM;
|
||||
|
||||
b = unit_has_name(UNIT(a), e);
|
||||
free(e);
|
||||
|
||||
if (!b) {
|
||||
log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->meta.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int automount_load(Unit *u) {
|
||||
int r;
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
/* Load a .automount file */
|
||||
if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
|
||||
return r;
|
||||
|
||||
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 = automount_add_mount_links(a)) < 0)
|
||||
return r;
|
||||
|
||||
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), true)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return automount_verify(a);
|
||||
}
|
||||
|
||||
static void automount_set_state(Automount *a, AutomountState state) {
|
||||
AutomountState old_state;
|
||||
assert(a);
|
||||
|
||||
old_state = a->state;
|
||||
a->state = state;
|
||||
|
||||
if (state != AUTOMOUNT_WAITING &&
|
||||
state != AUTOMOUNT_RUNNING)
|
||||
unmount_autofs(a);
|
||||
|
||||
if (state != old_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 int automount_coldplug(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
int r;
|
||||
|
||||
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"
|
||||
"%sWhere: %s\n",
|
||||
prefix, automount_state_to_string(a->state),
|
||||
prefix, a->where);
|
||||
}
|
||||
|
||||
static void automount_enter_dead(Automount *a, bool success) {
|
||||
assert(a);
|
||||
|
||||
if (!success)
|
||||
a->failure = true;
|
||||
|
||||
automount_set_state(a, a->failure ? AUTOMOUNT_MAINTAINANCE : AUTOMOUNT_DEAD);
|
||||
}
|
||||
|
||||
static int open_dev_autofs(Manager *m) {
|
||||
struct autofs_dev_ioctl param;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (m->dev_autofs_fd >= 0)
|
||||
return m->dev_autofs_fd;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
init_autofs_dev_ioctl(¶m);
|
||||
if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) {
|
||||
close_nointr_nofail(m->dev_autofs_fd);
|
||||
m->dev_autofs_fd = -1;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor);
|
||||
|
||||
return m->dev_autofs_fd;
|
||||
}
|
||||
|
||||
static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
|
||||
struct autofs_dev_ioctl *param;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(dev_autofs_fd >= 0);
|
||||
assert(where);
|
||||
|
||||
l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1;
|
||||
|
||||
if (!(param = malloc(l)))
|
||||
return -ENOMEM;
|
||||
|
||||
init_autofs_dev_ioctl(param);
|
||||
param->size = l;
|
||||
param->ioctlfd = -1;
|
||||
param->openmount.devid = devid;
|
||||
strcpy(param->path, where);
|
||||
|
||||
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (param->ioctlfd < 0) {
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fd_cloexec(param->ioctlfd, true);
|
||||
r = param->ioctlfd;
|
||||
|
||||
finish:
|
||||
free(param);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
|
||||
uint32_t major, minor;
|
||||
struct autofs_dev_ioctl param;
|
||||
|
||||
assert(dev_autofs_fd >= 0);
|
||||
assert(ioctl_fd >= 0);
|
||||
|
||||
init_autofs_dev_ioctl(¶m);
|
||||
param.ioctlfd = ioctl_fd;
|
||||
|
||||
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0)
|
||||
return -errno;
|
||||
|
||||
major = param.protover.version;
|
||||
|
||||
init_autofs_dev_ioctl(¶m);
|
||||
param.ioctlfd = ioctl_fd;
|
||||
|
||||
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0)
|
||||
return -errno;
|
||||
|
||||
minor = param.protosubver.sub_version;
|
||||
|
||||
log_debug("Autofs protocol version %i.%i", major, minor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
|
||||
struct autofs_dev_ioctl param;
|
||||
|
||||
assert(dev_autofs_fd >= 0);
|
||||
assert(ioctl_fd >= 0);
|
||||
|
||||
init_autofs_dev_ioctl(¶m);
|
||||
param.ioctlfd = ioctl_fd;
|
||||
param.timeout.timeout = sec;
|
||||
|
||||
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) {
|
||||
struct autofs_dev_ioctl param;
|
||||
|
||||
assert(dev_autofs_fd >= 0);
|
||||
assert(ioctl_fd >= 0);
|
||||
|
||||
init_autofs_dev_ioctl(¶m);
|
||||
param.ioctlfd = ioctl_fd;
|
||||
|
||||
if (status) {
|
||||
param.fail.token = token;
|
||||
param.fail.status = status;
|
||||
} else
|
||||
param.ready.token = token;
|
||||
|
||||
if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int automount_send_ready(Automount *a, int status) {
|
||||
int ioctl_fd, r;
|
||||
unsigned token;
|
||||
|
||||
assert(a);
|
||||
assert(status <= 0);
|
||||
|
||||
if (set_isempty(a->tokens))
|
||||
return 0;
|
||||
|
||||
if ((ioctl_fd = open_ioctl_fd(UNIT(a)->meta.manager->dev_autofs_fd, a->where, a->dev_id)) < 0) {
|
||||
r = ioctl_fd;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (status)
|
||||
log_debug("Sending failure: %s", strerror(-status));
|
||||
else
|
||||
log_debug("Sending success.");
|
||||
|
||||
/* Autofs thankfully does not hand out 0 as a token */
|
||||
while ((token = PTR_TO_UINT(set_steal_first(a->tokens)))) {
|
||||
int k;
|
||||
|
||||
/* Autofs fun fact II:
|
||||
*
|
||||
* if you pass a positive status code here, the kernel will
|
||||
* freeze! Yay! */
|
||||
|
||||
if ((k = autofs_send_ready(UNIT(a)->meta.manager->dev_autofs_fd,
|
||||
ioctl_fd,
|
||||
token,
|
||||
status)) < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
fail:
|
||||
if (ioctl_fd >= 0)
|
||||
close_nointr_nofail(ioctl_fd);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void automount_enter_waiting(Automount *a) {
|
||||
int p[2] = { -1, -1 };
|
||||
char name[32], options[128];
|
||||
bool mounted = false;
|
||||
int r, ioctl_fd = -1, dev_autofs_fd;
|
||||
struct stat st;
|
||||
|
||||
assert(a);
|
||||
assert(a->pipe_fd < 0);
|
||||
assert(a->where);
|
||||
|
||||
if (a->tokens)
|
||||
set_clear(a->tokens);
|
||||
|
||||
if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->meta.manager)) < 0) {
|
||||
r = dev_autofs_fd;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* We knowingly ignore the results of this call */
|
||||
mkdir_p(a->where, 0555);
|
||||
|
||||
if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp());
|
||||
char_array_0(options);
|
||||
|
||||
snprintf(name, sizeof(name), "systemd-%u", (unsigned) getpid());
|
||||
char_array_0(name);
|
||||
|
||||
if (mount(name, a->where, "autofs", 0, options) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mounted = true;
|
||||
|
||||
close_nointr_nofail(p[1]);
|
||||
p[1] = -1;
|
||||
|
||||
if (stat(a->where, &st) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev)) < 0) {
|
||||
r = ioctl_fd;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = autofs_protocol(dev_autofs_fd, ioctl_fd)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300)) < 0)
|
||||
goto fail;
|
||||
|
||||
/* Autofs fun fact:
|
||||
*
|
||||
* Unless we close the ioctl fd here, for some weird reason
|
||||
* the direct mount will not receive events from the
|
||||
* kernel. */
|
||||
|
||||
close_nointr_nofail(ioctl_fd);
|
||||
ioctl_fd = -1;
|
||||
|
||||
if ((r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch)) < 0)
|
||||
goto fail;
|
||||
|
||||
a->pipe_fd = p[0];
|
||||
a->dev_id = st.st_dev;
|
||||
|
||||
automount_set_state(a, AUTOMOUNT_WAITING);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
assert_se(close_pipe(p) == 0);
|
||||
|
||||
if (ioctl_fd >= 0)
|
||||
close_nointr_nofail(ioctl_fd);
|
||||
|
||||
if (mounted)
|
||||
repeat_unmout(a->where);
|
||||
|
||||
log_error("Failed to initialize automounter: %s", strerror(-r));
|
||||
automount_enter_dead(a, false);
|
||||
}
|
||||
|
||||
static void automount_enter_runnning(Automount *a) {
|
||||
int r;
|
||||
struct stat st;
|
||||
|
||||
assert(a);
|
||||
assert(a->mount);
|
||||
|
||||
/* Before we do anything, let's see if somebody is playing games with us? */
|
||||
|
||||
if (stat(a->where, &st) < 0) {
|
||||
log_warning("%s failed stat automount point: %m", a->meta.id);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
|
||||
log_info("%s's automount point already active?", a->meta.id);
|
||||
else if ((r = manager_add_job(UNIT(a)->meta.manager, JOB_START, UNIT(a->mount), JOB_REPLACE, true, NULL)) < 0) {
|
||||
log_warning("%s failed to queue mount startup job: %s", a->meta.id, strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
automount_set_state(a, AUTOMOUNT_RUNNING);
|
||||
return;
|
||||
|
||||
fail:
|
||||
automount_enter_dead(a, false);
|
||||
}
|
||||
|
||||
static int automount_start(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(a);
|
||||
|
||||
if (path_is_mount_point(a->where)) {
|
||||
log_error("Path %s is already a mount point, refusing start for %s", a->where, u->meta.id);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_MAINTAINANCE);
|
||||
|
||||
a->failure = false;
|
||||
automount_enter_waiting(a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int automount_stop(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(a);
|
||||
|
||||
assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING);
|
||||
|
||||
automount_enter_dead(a, true);
|
||||
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];
|
||||
}
|
||||
|
||||
static const char *automount_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return automount_state_to_string(AUTOMOUNT(u)->state);
|
||||
}
|
||||
|
||||
static bool automount_check_gc(Unit *u) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
|
||||
assert(a);
|
||||
|
||||
return UNIT_VTABLE(UNIT(a->mount))->check_gc(UNIT(a->mount));
|
||||
}
|
||||
|
||||
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
|
||||
Automount *a = AUTOMOUNT(u);
|
||||
union autofs_v5_packet_union packet;
|
||||
ssize_t l;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(fd == a->pipe_fd);
|
||||
|
||||
if (events != EPOLLIN) {
|
||||
log_error("Got invalid poll event on pipe.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((l = loop_read(a->pipe_fd, &packet, sizeof(packet))) != sizeof(packet)) {
|
||||
log_error("Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (packet.hdr.type) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
automount_enter_runnning(a);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_error("Received unknown automount request %i", packet.hdr.type);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
automount_enter_dead(a, false);
|
||||
}
|
||||
|
||||
static void automount_shutdown(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->dev_autofs_fd >= 0)
|
||||
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",
|
||||
|
||||
.no_alias = true,
|
||||
.no_instances = true,
|
||||
|
||||
.init = automount_init,
|
||||
.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,
|
||||
|
||||
.check_gc = automount_check_gc,
|
||||
|
||||
.fd_event = automount_fd_event,
|
||||
|
||||
.bus_message_handler = bus_automount_message_handler,
|
||||
|
||||
.shutdown = automount_shutdown
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooautomounthfoo
|
||||
#define fooautomounthfoo
|
||||
|
||||
/***
|
||||
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 Automount Automount;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum AutomountState {
|
||||
AUTOMOUNT_DEAD,
|
||||
AUTOMOUNT_WAITING,
|
||||
AUTOMOUNT_RUNNING,
|
||||
AUTOMOUNT_MAINTAINANCE,
|
||||
_AUTOMOUNT_STATE_MAX,
|
||||
_AUTOMOUNT_STATE_INVALID = -1
|
||||
} AutomountState;
|
||||
|
||||
struct Automount {
|
||||
Meta meta;
|
||||
|
||||
AutomountState state, deserialized_state;
|
||||
|
||||
char *where;
|
||||
|
||||
Mount *mount;
|
||||
|
||||
int pipe_fd;
|
||||
Watch pipe_watch;
|
||||
dev_t dev_id;
|
||||
|
||||
Set *tokens;
|
||||
|
||||
bool failure:1;
|
||||
};
|
||||
|
||||
extern const UnitVTable automount_vtable;
|
||||
|
||||
int automount_send_ready(Automount *a, int status);
|
||||
|
||||
int automount_add_one_mount_link(Automount *a, Mount *m);
|
||||
|
||||
const char* automount_state_to_string(AutomountState i);
|
||||
AutomountState automount_state_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
/*-*- 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 <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "cgroup.h"
|
||||
#include "log.h"
|
||||
|
||||
static int translate_error(int error, int _errno) {
|
||||
|
||||
switch (error) {
|
||||
|
||||
case ECGROUPNOTCOMPILED:
|
||||
case ECGROUPNOTMOUNTED:
|
||||
case ECGROUPNOTEXIST:
|
||||
case ECGROUPNOTCREATED:
|
||||
return -ENOENT;
|
||||
|
||||
case ECGINVAL:
|
||||
return -EINVAL;
|
||||
|
||||
case ECGROUPNOTALLOWED:
|
||||
return -EPERM;
|
||||
|
||||
case ECGOTHER:
|
||||
return -_errno;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int cgroup_bonding_realize(CGroupBonding *b) {
|
||||
int r;
|
||||
|
||||
assert(b);
|
||||
assert(b->path);
|
||||
assert(b->controller);
|
||||
|
||||
if (b->cgroup)
|
||||
return 0;
|
||||
|
||||
if (!(b->cgroup = cgroup_new_cgroup(b->path)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!cgroup_add_controller(b->cgroup, b->controller)) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (b->inherit)
|
||||
r = cgroup_create_cgroup_from_parent(b->cgroup, true);
|
||||
else
|
||||
r = cgroup_create_cgroup(b->cgroup, true);
|
||||
|
||||
if (r != 0) {
|
||||
r = translate_error(r, errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
cgroup_free(&b->cgroup);
|
||||
b->cgroup = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
int cgroup_bonding_realize_list(CGroupBonding *first) {
|
||||
CGroupBonding *b;
|
||||
|
||||
LIST_FOREACH(by_unit, b, first) {
|
||||
int r;
|
||||
|
||||
if ((r = cgroup_bonding_realize(b)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cgroup_bonding_free(CGroupBonding *b) {
|
||||
assert(b);
|
||||
|
||||
if (b->unit) {
|
||||
CGroupBonding *f;
|
||||
|
||||
LIST_REMOVE(CGroupBonding, by_unit, b->unit->meta.cgroup_bondings, b);
|
||||
|
||||
assert_se(f = hashmap_get(b->unit->meta.manager->cgroup_bondings, b->path));
|
||||
LIST_REMOVE(CGroupBonding, by_path, f, b);
|
||||
|
||||
if (f)
|
||||
hashmap_replace(b->unit->meta.manager->cgroup_bondings, b->path, f);
|
||||
else
|
||||
hashmap_remove(b->unit->meta.manager->cgroup_bondings, b->path);
|
||||
}
|
||||
|
||||
if (b->cgroup) {
|
||||
if (b->only_us && b->clean_up && cgroup_bonding_is_empty(b) > 0)
|
||||
cgroup_delete_cgroup_ext(b->cgroup, true);
|
||||
|
||||
cgroup_free(&b->cgroup);
|
||||
}
|
||||
|
||||
free(b->controller);
|
||||
free(b->path);
|
||||
free(b);
|
||||
}
|
||||
|
||||
void cgroup_bonding_free_list(CGroupBonding *first) {
|
||||
CGroupBonding *b, *n;
|
||||
|
||||
LIST_FOREACH_SAFE(by_unit, b, n, first)
|
||||
cgroup_bonding_free(b);
|
||||
}
|
||||
|
||||
int cgroup_bonding_install(CGroupBonding *b, pid_t pid) {
|
||||
int r;
|
||||
|
||||
assert(b);
|
||||
assert(pid >= 0);
|
||||
|
||||
if (pid == 0)
|
||||
pid = getpid();
|
||||
|
||||
if (!b->cgroup)
|
||||
return -ENOENT;
|
||||
|
||||
if ((r = cgroup_attach_task_pid(b->cgroup, pid)))
|
||||
return translate_error(r, errno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) {
|
||||
CGroupBonding *b;
|
||||
|
||||
LIST_FOREACH(by_unit, b, first) {
|
||||
int r;
|
||||
|
||||
if ((r = cgroup_bonding_install(b, pid)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cgroup_bonding_kill(CGroupBonding *b, int sig) {
|
||||
int r;
|
||||
Set *s;
|
||||
bool done;
|
||||
bool killed = false;
|
||||
|
||||
assert(b);
|
||||
assert(sig > 0);
|
||||
|
||||
if (!b->only_us)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
|
||||
return -ENOMEM;
|
||||
|
||||
do {
|
||||
void *iterator;
|
||||
pid_t pid;
|
||||
|
||||
done = true;
|
||||
|
||||
if ((r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid)) != 0) {
|
||||
if (r == ECGEOF) {
|
||||
r = 0;
|
||||
goto kill_done;
|
||||
} else {
|
||||
r = translate_error(r, errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (set_get(s, INT_TO_PTR(pid)) != INT_TO_PTR(pid)) {
|
||||
|
||||
/* If we haven't killed this process
|
||||
* yet, kill it */
|
||||
|
||||
if (kill(pid, sig) < 0 && errno != ESRCH) {
|
||||
r = -errno;
|
||||
break;
|
||||
}
|
||||
|
||||
killed = true;
|
||||
done = false;
|
||||
|
||||
if ((r = set_put(s, INT_TO_PTR(pid))) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((r = cgroup_get_task_next(&iterator, &pid)) != 0) {
|
||||
|
||||
if (r == ECGEOF)
|
||||
r = 0;
|
||||
else
|
||||
r = translate_error(r, errno);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kill_done:
|
||||
assert_se(cgroup_get_task_end(&iterator) == 0);
|
||||
|
||||
/* To avoid racing against processes which fork
|
||||
* quicker than we can kill them we repeat this until
|
||||
* no new pids need to be killed. */
|
||||
|
||||
} while (!done && r >= 0);
|
||||
|
||||
set_free(s);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return killed ? 0 : -ESRCH;
|
||||
}
|
||||
|
||||
int cgroup_bonding_kill_list(CGroupBonding *first, int sig) {
|
||||
CGroupBonding *b;
|
||||
int r = -EAGAIN;
|
||||
|
||||
LIST_FOREACH(by_unit, b, first) {
|
||||
if ((r = cgroup_bonding_kill(b, sig)) < 0) {
|
||||
if (r == -EAGAIN || -ESRCH)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
|
||||
* cannot know */
|
||||
int cgroup_bonding_is_empty(CGroupBonding *b) {
|
||||
void *iterator;
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
assert(b);
|
||||
|
||||
r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid);
|
||||
|
||||
if (r == 0 || r == ECGEOF)
|
||||
cgroup_get_task_end(&iterator);
|
||||
|
||||
/* Hmm, no PID in this group? Then it is definitely empty */
|
||||
if (r == ECGEOF)
|
||||
return 1;
|
||||
|
||||
/* Some error? Let's return it */
|
||||
if (r != 0)
|
||||
return translate_error(r, errno);
|
||||
|
||||
/* It's not empty, and we are the only user, then it is
|
||||
* definitely not empty */
|
||||
if (b->only_us)
|
||||
return 0;
|
||||
|
||||
/* There are PIDs in the group but we aren't the only users,
|
||||
* hence we cannot say */
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
int cgroup_bonding_is_empty_list(CGroupBonding *first) {
|
||||
CGroupBonding *b;
|
||||
|
||||
LIST_FOREACH(by_unit, b, first) {
|
||||
int r;
|
||||
|
||||
if ((r = cgroup_bonding_is_empty(b)) < 0) {
|
||||
/* If this returned -EAGAIN, then we don't know if the
|
||||
* group is empty, so let's see if another group can
|
||||
* tell us */
|
||||
|
||||
if (r != -EAGAIN)
|
||||
return r;
|
||||
} else
|
||||
return r;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int install_release_agent(Manager *m, const char *mount_point) {
|
||||
char *p, *c, *sc;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(mount_point);
|
||||
|
||||
if (asprintf(&p, "%s/release_agent", mount_point) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((r = read_one_line_file(p, &c)) < 0) {
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
sc = strstrip(c);
|
||||
|
||||
if (sc[0] == 0) {
|
||||
if ((r = write_one_line_file(p, CGROUP_AGENT_PATH "\n" )) < 0) {
|
||||
free(p);
|
||||
free(c);
|
||||
return r;
|
||||
}
|
||||
} else if (!streq(sc, CGROUP_AGENT_PATH)) {
|
||||
free(p);
|
||||
free(c);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
free(c);
|
||||
free(p);
|
||||
|
||||
if (asprintf(&p, "%s/notify_on_release", mount_point) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((r = read_one_line_file(p, &c)) < 0) {
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
sc = strstrip(c);
|
||||
|
||||
if (streq(sc, "0")) {
|
||||
if ((r = write_one_line_file(p, "1\n")) < 0) {
|
||||
free(p);
|
||||
free(c);
|
||||
return r;
|
||||
}
|
||||
} else if (!streq(sc, "1")) {
|
||||
free(p);
|
||||
free(c);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
free(p);
|
||||
free(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_hierarchy_cgroup(Manager *m) {
|
||||
struct cgroup *cg;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = cgroup_create_cgroup(cg, true)) != 0) {
|
||||
log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r));
|
||||
r = translate_error(r, errno);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = cgroup_attach_task(cg)) != 0) {
|
||||
log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r));
|
||||
r = translate_error(r, errno);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
cgroup_free(&cg);
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_setup_cgroup(Manager *m) {
|
||||
char *mp, *cp;
|
||||
int r;
|
||||
pid_t pid;
|
||||
char suffix[32];
|
||||
|
||||
assert(m);
|
||||
|
||||
if ((r = cgroup_init()) != 0) {
|
||||
log_error("Failed to initialize libcg: %s", cgroup_strerror(r));
|
||||
return translate_error(r, errno);
|
||||
}
|
||||
|
||||
free(m->cgroup_controller);
|
||||
if (!(m->cgroup_controller = strdup("debug")))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &mp)))
|
||||
return translate_error(r, errno);
|
||||
|
||||
pid = getpid();
|
||||
|
||||
if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) {
|
||||
free(mp);
|
||||
return translate_error(r, errno);
|
||||
}
|
||||
|
||||
snprintf(suffix, sizeof(suffix), "/systemd-%u", (unsigned) pid);
|
||||
char_array_0(suffix);
|
||||
|
||||
free(m->cgroup_hierarchy);
|
||||
|
||||
if (endswith(cp, suffix))
|
||||
/* We probably got reexecuted and can continue to use our root cgroup */
|
||||
m->cgroup_hierarchy = cp;
|
||||
else {
|
||||
/* We need a new root cgroup */
|
||||
|
||||
m->cgroup_hierarchy = NULL;
|
||||
r = asprintf(&m->cgroup_hierarchy, "%s%s", streq(cp, "/") ? "" : cp, suffix);
|
||||
free(cp);
|
||||
|
||||
if (r < 0) {
|
||||
free(mp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Using cgroup controller <%s>, hierarchy mounted at <%s>, using root group <%s>.",
|
||||
m->cgroup_controller,
|
||||
mp,
|
||||
m->cgroup_hierarchy);
|
||||
|
||||
if ((r = install_release_agent(m, mp)) < 0)
|
||||
log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
|
||||
else
|
||||
log_debug("Installed release agent, or already installed.");
|
||||
|
||||
free(mp);
|
||||
|
||||
if ((r = create_hierarchy_cgroup(m)) < 0)
|
||||
log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
|
||||
else
|
||||
log_debug("Created root group.");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_shutdown_cgroup(Manager *m, bool delete) {
|
||||
struct cgroup *cg;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!m->cgroup_hierarchy)
|
||||
return 0;
|
||||
|
||||
if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
|
||||
r = -ENOMEM;
|
||||
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:
|
||||
cgroup_free(&cg);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
int cgroup_notify_empty(Manager *m, const char *group) {
|
||||
CGroupBonding *l, *b;
|
||||
|
||||
assert(m);
|
||||
assert(group);
|
||||
|
||||
if (!(l = hashmap_get(m->cgroup_bondings, group)))
|
||||
return 0;
|
||||
|
||||
LIST_FOREACH(by_path, b, l) {
|
||||
int t;
|
||||
|
||||
if (!b->unit)
|
||||
continue;
|
||||
|
||||
if ((t = cgroup_bonding_is_empty_list(b)) < 0) {
|
||||
|
||||
/* If we don't know, we don't know */
|
||||
if (t != -EAGAIN)
|
||||
log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t > 0)
|
||||
if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
|
||||
UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
|
||||
CGroupBonding *b;
|
||||
|
||||
assert(controller);
|
||||
|
||||
LIST_FOREACH(by_unit, b, first)
|
||||
if (streq(b->controller, controller))
|
||||
return b;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *cgroup_bonding_to_string(CGroupBonding *b) {
|
||||
char *r;
|
||||
|
||||
assert(b);
|
||||
|
||||
if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
|
||||
return NULL;
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foocgrouphfoo
|
||||
#define foocgrouphfoo
|
||||
|
||||
/***
|
||||
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 <libcgroup.h>
|
||||
|
||||
typedef struct CGroupBonding CGroupBonding;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
/* Binds a cgroup to a name */
|
||||
struct CGroupBonding {
|
||||
char *controller;
|
||||
char *path;
|
||||
|
||||
Unit *unit;
|
||||
|
||||
struct cgroup *cgroup;
|
||||
|
||||
/* For the Unit::cgroup_bondings list */
|
||||
LIST_FIELDS(CGroupBonding, by_unit);
|
||||
|
||||
/* For the Manager::cgroup_bondings hashmap */
|
||||
LIST_FIELDS(CGroupBonding, by_path);
|
||||
|
||||
/* When shutting down, remove cgroup? */
|
||||
bool clean_up:1;
|
||||
|
||||
/* When our tasks are the only ones in this group */
|
||||
bool only_us:1;
|
||||
|
||||
/* Inherit parameters from parent group */
|
||||
bool inherit:1;
|
||||
};
|
||||
|
||||
int cgroup_bonding_realize(CGroupBonding *b);
|
||||
int cgroup_bonding_realize_list(CGroupBonding *first);
|
||||
|
||||
void cgroup_bonding_free(CGroupBonding *b);
|
||||
void cgroup_bonding_free_list(CGroupBonding *first);
|
||||
|
||||
int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
|
||||
int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
|
||||
|
||||
int cgroup_bonding_kill(CGroupBonding *b, int sig);
|
||||
int cgroup_bonding_kill_list(CGroupBonding *first, int sig);
|
||||
|
||||
int cgroup_bonding_is_empty(CGroupBonding *b);
|
||||
int cgroup_bonding_is_empty_list(CGroupBonding *first);
|
||||
|
||||
CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller);
|
||||
|
||||
char *cgroup_bonding_to_string(CGroupBonding *b);
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
int manager_setup_cgroup(Manager *m);
|
||||
int manager_shutdown_cgroup(Manager *m, bool delete);
|
||||
|
||||
int cgroup_notify_empty(Manager *m, const char *group);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
/*-*- 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 <dbus/dbus.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DBusError error;
|
||||
DBusConnection *bus = NULL;
|
||||
DBusMessage *m = NULL;
|
||||
int r = 1;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (argc != 2) {
|
||||
log_error("Incorrect number of arguments.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
|
||||
log_error("Failed to get D-Bus connection: %s", error.message);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) {
|
||||
log_error("Could not allocate signal message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_STRING, &argv[1],
|
||||
DBUS_TYPE_INVALID)) {
|
||||
log_error("Could not attach group information to signal message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!dbus_connection_send(bus, m, NULL)) {
|
||||
log_error("Failed to send signal message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (bus)
|
||||
dbus_connection_unref(bus);
|
||||
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
|
||||
dbus_error_free(&error);
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
#include "log.h"
|
||||
|
||||
#define COMMENTS "#;\n"
|
||||
#define LINE_MAX 4096
|
||||
|
||||
/* Run the user supplied parser for an assignment */
|
||||
static int next_assignment(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const ConfigItem *t,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *userdata) {
|
||||
|
||||
assert(filename);
|
||||
assert(t);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
for (; t->parse; t++) {
|
||||
|
||||
if (t->lvalue && !streq(lvalue, t->lvalue))
|
||||
continue;
|
||||
|
||||
if (t->section && !section)
|
||||
continue;
|
||||
|
||||
if (t->section && !streq(section, t->section))
|
||||
continue;
|
||||
|
||||
return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
|
||||
}
|
||||
|
||||
/* Warn about unknown non-extension fields. */
|
||||
if (!startswith(lvalue, "X-"))
|
||||
log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a variable assignment line */
|
||||
static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) {
|
||||
char *e;
|
||||
|
||||
l = strstrip(l);
|
||||
|
||||
if (!*l)
|
||||
return 0;
|
||||
|
||||
if (strchr(COMMENTS, *l))
|
||||
return 0;
|
||||
|
||||
if (startswith(l, ".include ")) {
|
||||
char *fn;
|
||||
int r;
|
||||
|
||||
if (!(fn = file_in_same_dir(filename, strstrip(l+9))))
|
||||
return -ENOMEM;
|
||||
|
||||
r = config_parse(fn, NULL, sections, t, userdata);
|
||||
free(fn);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (*l == '[') {
|
||||
size_t k;
|
||||
char *n;
|
||||
|
||||
k = strlen(l);
|
||||
assert(k > 0);
|
||||
|
||||
if (l[k-1] != ']') {
|
||||
log_error("[%s:%u] Invalid section header.", filename, line);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (!(n = strndup(l+1, k-2)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (sections && !strv_contains((char**) sections, n)) {
|
||||
log_error("[%s:%u] Unknown section '%s'.", filename, line, n);
|
||||
free(n);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
free(*section);
|
||||
*section = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(e = strchr(l, '='))) {
|
||||
log_error("[%s:%u] Missing '='.", filename, line);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
*e = 0;
|
||||
e++;
|
||||
|
||||
return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata);
|
||||
}
|
||||
|
||||
/* Go through the file and parse each line */
|
||||
int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) {
|
||||
unsigned line = 0;
|
||||
char *section = NULL;
|
||||
int r;
|
||||
bool ours = false;
|
||||
|
||||
assert(filename);
|
||||
assert(t);
|
||||
|
||||
if (!f) {
|
||||
if (!(f = fopen(filename, "re"))) {
|
||||
r = -errno;
|
||||
log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ours = true;
|
||||
}
|
||||
|
||||
while (!feof(f)) {
|
||||
char l[LINE_MAX];
|
||||
|
||||
if (!fgets(l, sizeof(l), f)) {
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
r = -errno;
|
||||
log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
free(section);
|
||||
|
||||
if (f && ours)
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int config_parse_int(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int *i = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atoi(rvalue, i)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_unsigned(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
unsigned *u = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atou(rvalue, u)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_size(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
size_t *sz = data;
|
||||
unsigned u;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atou(rvalue, &u)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
*sz = (size_t) u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_bool(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int k;
|
||||
bool *b = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((k = parse_boolean(rvalue)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
|
||||
return k;
|
||||
}
|
||||
|
||||
*b = !!k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_string(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char **s = data;
|
||||
char *n;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (*rvalue) {
|
||||
if (!(n = strdup(rvalue)))
|
||||
return -ENOMEM;
|
||||
} else
|
||||
n = NULL;
|
||||
|
||||
free(*s);
|
||||
*s = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_path(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char **s = data;
|
||||
char *n;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (!path_is_absolute(rvalue)) {
|
||||
log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(n = strdup(rvalue)))
|
||||
return -ENOMEM;
|
||||
|
||||
free(*s);
|
||||
*s = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_strv(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char*** sv = data;
|
||||
char **n;
|
||||
char *w;
|
||||
unsigned k;
|
||||
size_t l;
|
||||
char *state;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
k = strv_length(*sv);
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
k++;
|
||||
|
||||
if (!(n = new(char*, k+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (*sv)
|
||||
for (k = 0; (*sv)[k]; k++)
|
||||
n[k] = (*sv)[k];
|
||||
else
|
||||
k = 0;
|
||||
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
if (!(n[k++] = strndup(w, l)))
|
||||
goto fail;
|
||||
|
||||
n[k] = NULL;
|
||||
free(*sv);
|
||||
*sv = n;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (; k > 0; k--)
|
||||
free(n[k-1]);
|
||||
free(n);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int config_parse_path_strv(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char*** sv = data;
|
||||
char **n;
|
||||
char *w;
|
||||
unsigned k;
|
||||
size_t l;
|
||||
char *state;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
k = strv_length(*sv);
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
k++;
|
||||
|
||||
if (!(n = new(char*, k+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
k = 0;
|
||||
if (*sv)
|
||||
for (; (*sv)[k]; k++)
|
||||
n[k] = (*sv)[k];
|
||||
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
|
||||
if (!(n[k] = strndup(w, l))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(n[k])) {
|
||||
log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
k++;
|
||||
}
|
||||
|
||||
n[k] = NULL;
|
||||
free(*sv);
|
||||
*sv = n;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(n[k]);
|
||||
for (; k > 0; k--)
|
||||
free(n[k-1]);
|
||||
free(n);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooconfparserhfoo
|
||||
#define fooconfparserhfoo
|
||||
|
||||
/***
|
||||
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 <stdio.h>
|
||||
|
||||
/* An abstract parser for simple, line based, shallow configuration
|
||||
* files consisting of variable assignments only. */
|
||||
|
||||
typedef int (*ConfigParserCallback)(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
|
||||
/* Wraps info for parsing a specific configuration variable */
|
||||
typedef struct ConfigItem {
|
||||
const char *lvalue; /* name of the variable */
|
||||
ConfigParserCallback parse; /* Function that is called to parse the variable's value */
|
||||
void *data; /* Where to store the variable's data */
|
||||
const char *section;
|
||||
} ConfigItem;
|
||||
|
||||
/* The configuration file parsing routine. Expects a table of
|
||||
* config_items in *t that is terminated by an item where lvalue is
|
||||
* NULL */
|
||||
int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata);
|
||||
|
||||
/* Generic parsers */
|
||||
int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-automount.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Automount\">"
|
||||
" <property name=\"Where\" type=\"s\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
DBusHandlerResult bus_automount_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Automount", "Where", bus_property_append_string, "s", u->automount.where },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusautomounthfoo
|
||||
#define foodbusautomounthfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_automount_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-device.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Device\">"
|
||||
" <property name=\"SysFSPath\" type=\"s\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
DBusHandlerResult bus_device_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Device", "SysFSPath", bus_property_append_string, "s", u->device.sysfs },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusdevicehfoo
|
||||
#define foodbusdevicehfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_device_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*-*- 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 <dbus/dbus.h>
|
||||
|
||||
#include "dbus-execute.h"
|
||||
|
||||
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
|
||||
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
|
||||
@@ -0,0 +1,79 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusexecutehfoo
|
||||
#define foodbusexecutehfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
#define BUS_EXEC_CONTEXT_INTERFACE \
|
||||
" <property name=\"Environment\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"UMask\" type=\"u\" access=\"read\"/>" \
|
||||
" <property name=\"WorkingDirectory\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"CPUSchedulingResetOnFork\" type=\"b\" access=\"read\"/>" \
|
||||
" <property name=\"NonBlocking\" type=\"b\" access=\"read\"/>" \
|
||||
" <property name=\"StandardInput\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"StandardOutput\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"StandardError\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"TTYPath\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"SyslogPriority\" type=\"i\" access=\"read\"/>" \
|
||||
" <property name=\"SyslogIdentifier\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"SecureBits\" type=\"i\" access=\"read\"/>" \
|
||||
" <property name=\"CapabilityBoundingSetDrop\" type=\"t\" access=\"read\"/>" \
|
||||
" <property name=\"User\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"Group\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"SupplementaryGroups\" type=\"as\" access=\"read\"/>"
|
||||
|
||||
#define BUS_EXEC_CONTEXT_PROPERTIES(interface, context) \
|
||||
{ interface, "Environment", bus_property_append_strv, "as", (context).environment }, \
|
||||
{ interface, "UMask", bus_property_append_mode, "u", &(context).umask }, \
|
||||
/* RLimits */ \
|
||||
{ interface, "WorkingDirectory", bus_property_append_string, "s", (context).working_directory }, \
|
||||
{ interface, "RootDirectory", bus_property_append_string, "s", (context).root_directory }, \
|
||||
/* OOM Adjust */ \
|
||||
/* Nice */ \
|
||||
/* IOPrio */ \
|
||||
/* CPUSchedPolicy */ \
|
||||
/* CPUSchedPriority */ \
|
||||
/* CPUAffinity */ \
|
||||
/* TimerSlackNS */ \
|
||||
{ interface, "CPUSchedulingResetOnFork", bus_property_append_bool, "b", &(context).cpu_sched_reset_on_fork }, \
|
||||
{ interface, "NonBlocking", bus_property_append_bool, "b", &(context).non_blocking }, \
|
||||
{ interface, "StandardInput", bus_execute_append_input, "s", &(context).std_input }, \
|
||||
{ interface, "StandardOutput", bus_execute_append_output, "s", &(context).std_output }, \
|
||||
{ interface, "StandardError", bus_execute_append_output, "s", &(context).std_error }, \
|
||||
{ interface, "TTYPath", bus_property_append_string, "s", (context).tty_path }, \
|
||||
{ interface, "SyslogPriority", bus_property_append_int, "i", &(context).syslog_priority }, \
|
||||
{ interface, "SyslogIdentifier", bus_property_append_string, "s", (context).syslog_identifier }, \
|
||||
/* CAPABILITIES */ \
|
||||
{ interface, "SecureBits", bus_property_append_int, "i", &(context).secure_bits }, \
|
||||
{ interface, "CapabilityBoundingSetDrop", bus_property_append_uint64, "t", &(context).capability_bounding_set_drop }, \
|
||||
{ interface, "User", bus_property_append_string, "s", (context).user }, \
|
||||
{ interface, "Group", bus_property_append_string, "s", (context).group }, \
|
||||
{ interface, "SupplementaryGroups", bus_property_append_strv, "as", (context).supplementary_groups }
|
||||
|
||||
int bus_execute_append_output(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_execute_append_input(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
|
||||
#endif
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
/*-*- 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 "dbus.h"
|
||||
#include "log.h"
|
||||
#include "dbus-job.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
" <interface name=\"org.freedesktop.systemd1.Job\">"
|
||||
" <method name=\"Cancel\"/>"
|
||||
" <signal name=\"Changed\"/>"
|
||||
" <property name=\"Id\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"Unit\" type=\"(so)\" access=\"read\"/>"
|
||||
" <property name=\"JobType\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"State\" type=\"s\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
|
||||
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
|
||||
|
||||
static int bus_job_append_unit(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Job *j = data;
|
||||
DBusMessageIter sub;
|
||||
char *p;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(j);
|
||||
|
||||
if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(p = unit_dbus_path(j->unit)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->meta.id) ||
|
||||
!dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
|
||||
free(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
if (!dbus_message_iter_close_container(i, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
{ "org.freedesktop.systemd1.Job", "Id", bus_property_append_uint32, "u", &j->id },
|
||||
{ "org.freedesktop.systemd1.Job", "State", bus_job_append_state, "s", &j->state },
|
||||
{ "org.freedesktop.systemd1.Job", "JobType", bus_job_append_type, "s", &j->type },
|
||||
{ "org.freedesktop.systemd1.Job", "Unit", bus_job_append_unit, "(so)", j },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
DBusMessage *reply = NULL;
|
||||
Manager *m = j->manager;
|
||||
|
||||
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
job_free(j);
|
||||
|
||||
} else
|
||||
return bus_default_message_handler(j->manager, message, introspection, properties);
|
||||
|
||||
if (reply) {
|
||||
if (!dbus_connection_send(m->api_bus, reply, NULL))
|
||||
goto oom;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
oom:
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
|
||||
static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
|
||||
Manager *m = data;
|
||||
Job *j;
|
||||
int r;
|
||||
|
||||
assert(connection);
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
log_debug("Got D-Bus request: %s.%s() on %s",
|
||||
dbus_message_get_interface(message),
|
||||
dbus_message_get_member(message),
|
||||
dbus_message_get_path(message));
|
||||
|
||||
if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) {
|
||||
|
||||
if (r == -ENOMEM)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
if (r == -ENOENT)
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
}
|
||||
|
||||
return bus_job_message_dispatch(j, message);
|
||||
}
|
||||
|
||||
const DBusObjectPathVTable bus_job_vtable = {
|
||||
.message_function = bus_job_message_handler
|
||||
};
|
||||
|
||||
void bus_job_send_change_signal(Job *j) {
|
||||
char *p = NULL;
|
||||
DBusMessage *m = NULL;
|
||||
|
||||
assert(j);
|
||||
assert(j->in_dbus_queue);
|
||||
|
||||
LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
|
||||
j->in_dbus_queue = false;
|
||||
|
||||
if (set_isempty(j->manager->subscribed)) {
|
||||
j->sent_dbus_new_signal = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(p = job_dbus_path(j)))
|
||||
goto oom;
|
||||
|
||||
if (j->sent_dbus_new_signal) {
|
||||
/* Send a change signal */
|
||||
|
||||
if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Job", "Changed")))
|
||||
goto oom;
|
||||
} else {
|
||||
/* Send a new signal */
|
||||
|
||||
if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew")))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_UINT32, &j->id,
|
||||
DBUS_TYPE_OBJECT_PATH, &p,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (!dbus_connection_send(j->manager->api_bus, m, NULL))
|
||||
goto oom;
|
||||
|
||||
free(p);
|
||||
dbus_message_unref(m);
|
||||
|
||||
j->sent_dbus_new_signal = true;
|
||||
|
||||
return;
|
||||
|
||||
oom:
|
||||
free(p);
|
||||
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
|
||||
log_error("Failed to allocate job change signal.");
|
||||
}
|
||||
|
||||
void bus_job_send_removed_signal(Job *j) {
|
||||
char *p = NULL;
|
||||
DBusMessage *m = NULL;
|
||||
|
||||
assert(j);
|
||||
|
||||
if (set_isempty(j->manager->subscribed) || !j->sent_dbus_new_signal)
|
||||
return;
|
||||
|
||||
if (!(p = job_dbus_path(j)))
|
||||
goto oom;
|
||||
|
||||
if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved")))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_UINT32, &j->id,
|
||||
DBUS_TYPE_OBJECT_PATH, &p,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_connection_send(j->manager->api_bus, m, NULL))
|
||||
goto oom;
|
||||
|
||||
free(p);
|
||||
dbus_message_unref(m);
|
||||
|
||||
return;
|
||||
|
||||
oom:
|
||||
free(p);
|
||||
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
|
||||
log_error("Failed to allocate job remove signal.");
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusjobhfoo
|
||||
#define foodbusjobhfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
void bus_job_send_change_signal(Job *j);
|
||||
void bus_job_send_removed_signal(Job *j);
|
||||
|
||||
extern const DBusObjectPathVTable bus_job_vtable;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,657 @@
|
||||
/*-*- 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 "dbus.h"
|
||||
#include "log.h"
|
||||
#include "dbus-manager.h"
|
||||
#include "strv.h"
|
||||
|
||||
#define INTROSPECTION_BEGIN \
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
|
||||
"<node>" \
|
||||
" <interface name=\"org.freedesktop.systemd1.Manager\">" \
|
||||
" <method name=\"GetUnit\">" \
|
||||
" <arg name=\"name\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"LoadUnit\">" \
|
||||
" <arg name=\"name\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"GetJob\">" \
|
||||
" <arg name=\"id\" type=\"u\" direction=\"in\"/>" \
|
||||
" <arg name=\"job\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"ClearJobs\"/>" \
|
||||
" <method name=\"ListUnits\">" \
|
||||
" <arg name=\"units\" type=\"a(sssssouso)\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"ListJobs\">" \
|
||||
" <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"Subscribe\"/>" \
|
||||
" <method name=\"Unsubscribe\"/>" \
|
||||
" <method name=\"Dump\"/>" \
|
||||
" <method name=\"CreateSnapshot\">" \
|
||||
" <arg name=\"name\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"Reload\"/>" \
|
||||
" <method name=\"Reexecute\"/>" \
|
||||
" <method name=\"Exit\"/>" \
|
||||
" <method name=\"SetEnvironment\">" \
|
||||
" <arg name=\"names\" type=\"as\" direction=\"in\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"UnsetEnvironment\">" \
|
||||
" <arg name=\"names\" type=\"as\" direction=\"in\"/>" \
|
||||
" </method>" \
|
||||
" <signal name=\"UnitNew\">" \
|
||||
" <arg name=\"id\" type=\"s\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\"/>" \
|
||||
" </signal>" \
|
||||
" <signal name=\"UnitRemoved\">" \
|
||||
" <arg name=\"id\" type=\"s\"/>" \
|
||||
" <arg name=\"unit\" type=\"o\"/>" \
|
||||
" </signal>" \
|
||||
" <signal name=\"JobNew\">" \
|
||||
" <arg name=\"id\" type=\"u\"/>" \
|
||||
" <arg name=\"job\" type=\"o\"/>" \
|
||||
" </signal>" \
|
||||
" <signal name=\"JobRemoved\">" \
|
||||
" <arg name=\"id\" type=\"u\"/>" \
|
||||
" <arg name=\"job\" type=\"o\"/>" \
|
||||
" </signal>" \
|
||||
" <property name=\"Version\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"RunningAs\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"BootTimestamp\" type=\"t\" access=\"read\"/>" \
|
||||
" <property name=\"LogLevel\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"LogTarget\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"NNames\" type=\"u\" access=\"read\"/>" \
|
||||
" <property name=\"NJobs\" type=\"u\" access=\"read\"/>" \
|
||||
" <property name=\"Environment\" type=\"as\" access=\"read\"/>" \
|
||||
" </interface>" \
|
||||
BUS_PROPERTIES_INTERFACE \
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
|
||||
#define INTROSPECTION_END \
|
||||
"</node>"
|
||||
|
||||
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs);
|
||||
|
||||
static int bus_manager_append_log_target(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
const char *t;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
|
||||
t = log_target_to_string(log_get_target());
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_manager_append_log_level(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
const char *t;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
|
||||
t = log_level_to_string(log_get_max_level());
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_manager_append_n_names(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
uint32_t u;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
|
||||
u = hashmap_size(m->units);
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_manager_append_n_jobs(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
uint32_t u;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
|
||||
u = hashmap_size(m->jobs);
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
|
||||
Manager *m = data;
|
||||
|
||||
const BusProperty properties[] = {
|
||||
{ "org.freedesktop.systemd1.Manager", "Version", bus_property_append_string, "s", PACKAGE_STRING },
|
||||
{ "org.freedesktop.systemd1.Manager", "RunningAs", bus_manager_append_running_as, "s", &m->running_as },
|
||||
{ "org.freedesktop.systemd1.Manager", "BootTimestamp", bus_property_append_uint64, "t", &m->boot_timestamp },
|
||||
{ "org.freedesktop.systemd1.Manager", "LogLevel", bus_manager_append_log_level, "s", NULL },
|
||||
{ "org.freedesktop.systemd1.Manager", "LogTarget", bus_manager_append_log_target, "s", NULL },
|
||||
{ "org.freedesktop.systemd1.Manager", "NNames", bus_manager_append_n_names, "u", NULL },
|
||||
{ "org.freedesktop.systemd1.Manager", "NJobs", bus_manager_append_n_jobs, "u", NULL },
|
||||
{ "org.freedesktop.systemd1.Manager", "Environment", bus_property_append_strv, "as", m->environment },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int r;
|
||||
DBusError error;
|
||||
DBusMessage *reply = NULL;
|
||||
char * path = NULL;
|
||||
|
||||
assert(connection);
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
log_debug("Got D-Bus request: %s.%s() on %s",
|
||||
dbus_message_get_interface(message),
|
||||
dbus_message_get_member(message),
|
||||
dbus_message_get_path(message));
|
||||
|
||||
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
|
||||
const char *name;
|
||||
Unit *u;
|
||||
|
||||
if (!dbus_message_get_args(
|
||||
message,
|
||||
&error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID))
|
||||
return bus_send_error_reply(m, message, &error, -EINVAL);
|
||||
|
||||
if (!(u = manager_get_unit(m, name)))
|
||||
return bus_send_error_reply(m, message, NULL, -ENOENT);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
if (!(path = unit_dbus_path(u)))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(
|
||||
reply,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
|
||||
const char *name;
|
||||
Unit *u;
|
||||
|
||||
if (!dbus_message_get_args(
|
||||
message,
|
||||
&error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID))
|
||||
return bus_send_error_reply(m, message, &error, -EINVAL);
|
||||
|
||||
if ((r = manager_load_unit(m, name, NULL, &u)) < 0)
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
if (!(path = unit_dbus_path(u)))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(
|
||||
reply,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
|
||||
uint32_t id;
|
||||
Job *j;
|
||||
|
||||
if (!dbus_message_get_args(
|
||||
message,
|
||||
&error,
|
||||
DBUS_TYPE_UINT32, &id,
|
||||
DBUS_TYPE_INVALID))
|
||||
return bus_send_error_reply(m, message, &error, -EINVAL);
|
||||
|
||||
if (!(j = manager_get_job(m, id)))
|
||||
return bus_send_error_reply(m, message, NULL, -ENOENT);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
if (!(path = job_dbus_path(j)))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(
|
||||
reply,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
|
||||
|
||||
manager_clear_jobs(m);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
|
||||
DBusMessageIter iter, sub;
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
const char *k;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssssouso)", &sub))
|
||||
goto oom;
|
||||
|
||||
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
|
||||
char *u_path, *j_path;
|
||||
const char *description, *load_state, *active_state, *sub_state, *job_type;
|
||||
DBusMessageIter sub2;
|
||||
uint32_t job_id;
|
||||
|
||||
if (k != u->meta.id)
|
||||
continue;
|
||||
|
||||
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
|
||||
goto oom;
|
||||
|
||||
description = unit_description(u);
|
||||
load_state = unit_load_state_to_string(u->meta.load_state);
|
||||
active_state = unit_active_state_to_string(unit_active_state(u));
|
||||
sub_state = unit_sub_state_to_string(u);
|
||||
|
||||
if (!(u_path = unit_dbus_path(u)))
|
||||
goto oom;
|
||||
|
||||
if (u->meta.job) {
|
||||
job_id = (uint32_t) u->meta.job->id;
|
||||
|
||||
if (!(j_path = job_dbus_path(u->meta.job))) {
|
||||
free(u_path);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
job_type = job_type_to_string(u->meta.job->type);
|
||||
} else {
|
||||
job_id = 0;
|
||||
j_path = u_path;
|
||||
job_type = "";
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &job_type) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
|
||||
free(u_path);
|
||||
if (u->meta.job)
|
||||
free(j_path);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
free(u_path);
|
||||
if (u->meta.job)
|
||||
free(j_path);
|
||||
|
||||
if (!dbus_message_iter_close_container(&sub, &sub2))
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_close_container(&iter, &sub))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
|
||||
DBusMessageIter iter, sub;
|
||||
Iterator i;
|
||||
Job *j;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
|
||||
goto oom;
|
||||
|
||||
HASHMAP_FOREACH(j, m->jobs, i) {
|
||||
char *u_path, *j_path;
|
||||
const char *state, *type;
|
||||
uint32_t id;
|
||||
DBusMessageIter sub2;
|
||||
|
||||
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
|
||||
goto oom;
|
||||
|
||||
id = (uint32_t) j->id;
|
||||
state = job_state_to_string(j->state);
|
||||
type = job_type_to_string(j->type);
|
||||
|
||||
if (!(j_path = job_dbus_path(j)))
|
||||
goto oom;
|
||||
|
||||
if (!(u_path = unit_dbus_path(j->unit))) {
|
||||
free(j_path);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
|
||||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
|
||||
free(j_path);
|
||||
free(u_path);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
free(j_path);
|
||||
free(u_path);
|
||||
|
||||
if (!dbus_message_iter_close_container(&sub, &sub2))
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_close_container(&iter, &sub))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
|
||||
char *client;
|
||||
|
||||
if (!(client = strdup(dbus_message_get_sender(message))))
|
||||
goto oom;
|
||||
|
||||
r = set_put(m->subscribed, client);
|
||||
|
||||
if (r < 0)
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
|
||||
char *client;
|
||||
|
||||
if (!(client = set_remove(m->subscribed, (char*) dbus_message_get_sender(message))))
|
||||
return bus_send_error_reply(m, message, NULL, -ENOENT);
|
||||
|
||||
free(client);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
|
||||
FILE *f;
|
||||
char *dump = NULL;
|
||||
size_t size;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
if (!(f = open_memstream(&dump, &size)))
|
||||
goto oom;
|
||||
|
||||
manager_dump_units(m, f, NULL);
|
||||
manager_dump_jobs(m, f, NULL);
|
||||
|
||||
if (ferror(f)) {
|
||||
fclose(f);
|
||||
free(dump);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
|
||||
free(dump);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
free(dump);
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
|
||||
const char *name;
|
||||
dbus_bool_t cleanup;
|
||||
Snapshot *s;
|
||||
|
||||
if (!dbus_message_get_args(
|
||||
message,
|
||||
&error,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_BOOLEAN, &cleanup,
|
||||
DBUS_TYPE_INVALID))
|
||||
return bus_send_error_reply(m, message, &error, -EINVAL);
|
||||
|
||||
if (name && name[0] == 0)
|
||||
name = NULL;
|
||||
|
||||
if ((r = snapshot_create(m, name, cleanup, &s)) < 0)
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
if (!(path = unit_dbus_path(UNIT(s))))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(
|
||||
reply,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
|
||||
char *introspection = NULL;
|
||||
FILE *f;
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
Job *j;
|
||||
const char *k;
|
||||
size_t size;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
/* We roll our own introspection code here, instead of
|
||||
* relying on bus_default_message_handler() because we
|
||||
* need to generate our introspection string
|
||||
* dynamically. */
|
||||
|
||||
if (!(f = open_memstream(&introspection, &size)))
|
||||
goto oom;
|
||||
|
||||
fputs(INTROSPECTION_BEGIN, f);
|
||||
|
||||
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
|
||||
char *p;
|
||||
|
||||
if (k != u->meta.id)
|
||||
continue;
|
||||
|
||||
if (!(p = bus_path_escape(k))) {
|
||||
fclose(f);
|
||||
free(introspection);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
fprintf(f, "<node name=\"unit/%s\"/>", p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(j, m->jobs, i)
|
||||
fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
|
||||
|
||||
fputs(INTROSPECTION_END, f);
|
||||
|
||||
if (ferror(f)) {
|
||||
fclose(f);
|
||||
free(introspection);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (!introspection)
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
|
||||
free(introspection);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
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 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
|
||||
char **l = NULL, **e = NULL;
|
||||
|
||||
if ((r = bus_parse_strv(message, &l)) < 0) {
|
||||
if (r == -ENOMEM)
|
||||
goto oom;
|
||||
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
}
|
||||
|
||||
e = strv_env_merge(m->environment, l, NULL);
|
||||
strv_free(l);
|
||||
|
||||
if (!e)
|
||||
goto oom;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message))) {
|
||||
strv_free(e);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
strv_free(m->environment);
|
||||
m->environment = e;
|
||||
|
||||
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
|
||||
char **l = NULL, **e = NULL;
|
||||
|
||||
if ((r = bus_parse_strv(message, &l)) < 0) {
|
||||
if (r == -ENOMEM)
|
||||
goto oom;
|
||||
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
}
|
||||
|
||||
e = strv_env_delete(m->environment, l, NULL);
|
||||
strv_free(l);
|
||||
|
||||
if (!e)
|
||||
goto oom;
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
strv_free(m->environment);
|
||||
m->environment = e;
|
||||
|
||||
} else
|
||||
return bus_default_message_handler(m, message, NULL, properties);
|
||||
|
||||
free(path);
|
||||
|
||||
if (reply) {
|
||||
if (!dbus_connection_send(connection, reply, NULL))
|
||||
goto oom;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
oom:
|
||||
free(path);
|
||||
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
dbus_error_free(&error);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
|
||||
const DBusObjectPathVTable bus_manager_vtable = {
|
||||
.message_function = bus_manager_message_handler
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusmanagerhfoo
|
||||
#define foodbusmanagerhfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
extern const DBusObjectPathVTable bus_manager_vtable;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-mount.h"
|
||||
#include "dbus-execute.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Mount\">"
|
||||
" <property name=\"Where\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"What\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"Options\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"Type\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>"
|
||||
BUS_EXEC_CONTEXT_INTERFACE
|
||||
" <property name=\"KillMode\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
static int bus_mount_append_what(Manager *n, DBusMessageIter *i, const char *property, void *data) {
|
||||
Mount *m = data;
|
||||
const char *d;
|
||||
|
||||
assert(n);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(m);
|
||||
|
||||
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what)
|
||||
d = m->parameters_proc_self_mountinfo.what;
|
||||
else if (m->from_fragment && m->parameters_fragment.what)
|
||||
d = m->parameters_fragment.what;
|
||||
else if (m->from_etc_fstab && m->parameters_etc_fstab.what)
|
||||
d = m->parameters_etc_fstab.what;
|
||||
else
|
||||
d = "";
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_mount_append_options(Manager *n, DBusMessageIter *i, const char *property, void *data) {
|
||||
Mount *m = data;
|
||||
const char *d;
|
||||
|
||||
assert(n);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(m);
|
||||
|
||||
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options)
|
||||
d = m->parameters_proc_self_mountinfo.options;
|
||||
else if (m->from_fragment && m->parameters_fragment.options)
|
||||
d = m->parameters_fragment.options;
|
||||
else if (m->from_etc_fstab && m->parameters_etc_fstab.options)
|
||||
d = m->parameters_etc_fstab.options;
|
||||
else
|
||||
d = "";
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bus_mount_append_type(Manager *n, DBusMessageIter *i, const char *property, void *data) {
|
||||
Mount *m = data;
|
||||
const char *d;
|
||||
|
||||
assert(n);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(m);
|
||||
|
||||
if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype)
|
||||
d = m->parameters_proc_self_mountinfo.fstype;
|
||||
else if (m->from_fragment && m->parameters_fragment.fstype)
|
||||
d = m->parameters_fragment.fstype;
|
||||
else if (m->from_etc_fstab && m->parameters_etc_fstab.fstype)
|
||||
d = m->parameters_etc_fstab.fstype;
|
||||
else
|
||||
d = "";
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBusHandlerResult bus_mount_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Mount", "Where", bus_property_append_string, "s", u->mount.where },
|
||||
{ "org.freedesktop.systemd1.Mount", "What", bus_mount_append_what, "s", u },
|
||||
{ "org.freedesktop.systemd1.Mount", "Options", bus_mount_append_options, "s", u },
|
||||
{ "org.freedesktop.systemd1.Mount", "Type", bus_mount_append_type, "s", u },
|
||||
{ "org.freedesktop.systemd1.Mount", "TimeoutUSec", bus_property_append_usec, "t", &u->mount.timeout_usec },
|
||||
/* ExecCommand */
|
||||
BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Mount", u->mount.exec_context),
|
||||
{ "org.freedesktop.systemd1.Mount", "KillMode", bus_unit_append_kill_mode, "s", &u->mount.kill_mode },
|
||||
{ "org.freedesktop.systemd1.Mount", "ControlPID", bus_property_append_pid, "u", &u->mount.control_pid },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusmounthfoo
|
||||
#define foodbusmounthfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_mount_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-execute.h"
|
||||
#include "dbus-service.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Service\">"
|
||||
" <property name=\"Type\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"Restart\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"PIDFile\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>"
|
||||
" <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>"
|
||||
BUS_EXEC_CONTEXT_INTERFACE
|
||||
" <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>"
|
||||
" <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>"
|
||||
" <property name=\"ValidNoProcess\" type=\"b\" access=\"read\"/>"
|
||||
" <property name=\"KillMode\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"MainPID\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"SysVPath\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"BusName\" type=\"s\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType);
|
||||
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart);
|
||||
|
||||
DBusHandlerResult bus_service_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Service", "Type", bus_service_append_type, "s", &u->service.type },
|
||||
{ "org.freedesktop.systemd1.Service", "Restart", bus_service_append_restart, "s", &u->service.restart },
|
||||
{ "org.freedesktop.systemd1.Service", "PIDFile", bus_property_append_string, "s", u->service.pid_file },
|
||||
{ "org.freedesktop.systemd1.Service", "RestartUSec", bus_property_append_usec, "t", &u->service.restart_usec },
|
||||
{ "org.freedesktop.systemd1.Service", "TimeoutUSec", bus_property_append_usec, "t", &u->service.timeout_usec },
|
||||
/* ExecCommand */
|
||||
BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Service", u->service.exec_context),
|
||||
{ "org.freedesktop.systemd1.Service", "PermissionsStartOnly", bus_property_append_bool, "b", &u->service.permissions_start_only },
|
||||
{ "org.freedesktop.systemd1.Service", "RootDirectoryStartOnly", bus_property_append_bool, "b", &u->service.root_directory_start_only },
|
||||
{ "org.freedesktop.systemd1.Service", "ValidNoProcess", bus_property_append_bool, "b", &u->service.valid_no_process },
|
||||
{ "org.freedesktop.systemd1.Service", "KillMode", bus_unit_append_kill_mode, "s", &u->service.kill_mode },
|
||||
/* MainExecStatus */
|
||||
{ "org.freedesktop.systemd1.Service", "MainPID", bus_property_append_pid, "u", &u->service.main_pid },
|
||||
{ "org.freedesktop.systemd1.Service", "ControlPID", bus_property_append_pid, "u", &u->service.control_pid },
|
||||
{ "org.freedesktop.systemd1.Service", "SysVPath", bus_property_append_string, "s", u->service.sysv_path },
|
||||
{ "org.freedesktop.systemd1.Service", "BusName", bus_property_append_string, "s", u->service.bus_name },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusservicehfoo
|
||||
#define foodbusservicehfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_service_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-snapshot.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Snapshot\">"
|
||||
" <method name=\"Remove\"/>"
|
||||
" <property name=\"Cleanup\" type=\"b\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Snapshot", "Cleanup", bus_property_append_bool, "b", &u->snapshot.cleanup },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
DBusMessage *reply = NULL;
|
||||
DBusError error;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) {
|
||||
|
||||
snapshot_remove(SNAPSHOT(u));
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
} else
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
|
||||
if (reply) {
|
||||
if (!dbus_connection_send(u->meta.manager->api_bus, reply, NULL))
|
||||
goto oom;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
oom:
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
dbus_error_free(&error);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbussnapshothfoo
|
||||
#define foodbussnapshothfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-socket.h"
|
||||
#include "dbus-execute.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Socket\">"
|
||||
" <property name=\"BindIPv6Only\" type=\"b\" access=\"read\"/>"
|
||||
" <property name=\"Backlog\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>"
|
||||
BUS_EXEC_CONTEXT_INTERFACE
|
||||
" <property name=\"KillMode\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"SocketMode\" type=\"u\" access=\"read\"/>"
|
||||
" <property name=\"Accept\" type=\"b\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
DBusHandlerResult bus_socket_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Socket", "BindIPv6Only", bus_property_append_bool, "b", &u->socket.bind_ipv6_only },
|
||||
{ "org.freedesktop.systemd1.Socket", "Backlog", bus_property_append_unsigned, "u", &u->socket.backlog },
|
||||
{ "org.freedesktop.systemd1.Socket", "TimeoutUSec", bus_property_append_usec, "t", &u->socket.timeout_usec },
|
||||
/* ExecCommand */
|
||||
BUS_EXEC_CONTEXT_PROPERTIES("org.freedesktop.systemd1.Socket", u->socket.exec_context),
|
||||
{ "org.freedesktop.systemd1.Socket", "KillMode", bus_unit_append_kill_mode, "s", &u->socket.kill_mode },
|
||||
{ "org.freedesktop.systemd1.Socket", "ControlPID", bus_property_append_pid, "u", &u->socket.control_pid },
|
||||
{ "org.freedesktop.systemd1.Socket", "BindToDevice", bus_property_append_string, "s", u->socket.bind_to_device },
|
||||
{ "org.freedesktop.systemd1.Socket", "DirectoryMode", bus_property_append_mode, "u", &u->socket.directory_mode },
|
||||
{ "org.freedesktop.systemd1.Socket", "SocketMode", bus_property_append_mode, "u", &u->socket.socket_mode },
|
||||
{ "org.freedesktop.systemd1.Socket", "Accept", bus_property_append_bool, "b", &u->socket.accept },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbussockethfoo
|
||||
#define foodbussockethfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_socket_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
Copyright 2010 Maarten Lankhorst
|
||||
|
||||
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 "dbus-unit.h"
|
||||
#include "dbus-swap.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Swap\">"
|
||||
" <property name=\"What\" type=\"s\" access=\"read\"/>"
|
||||
" <property name=\"Priority\" type=\"i\" access=\"read\"/>"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
static int bus_swap_append_priority(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Swap *s = data;
|
||||
dbus_int32_t j;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(s);
|
||||
|
||||
if (s->from_proc_swaps)
|
||||
j = s->parameters_proc_swaps.priority;
|
||||
else if (s->from_fragment)
|
||||
j = s->parameters_fragment.priority;
|
||||
else if (s->from_etc_fstab)
|
||||
j = s->parameters_etc_fstab.priority;
|
||||
else
|
||||
j = -1;
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &j))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBusHandlerResult bus_swap_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ "org.freedesktop.systemd1.Swap", "What", bus_property_append_string, "s", u->swap.what },
|
||||
{ "org.freedesktop.systemd1.Swap", "Priority", bus_swap_append_priority, "i", u },
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusswaphfoo
|
||||
#define foodbusswaphfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
Copyright 2010 Maarten Lankhorst
|
||||
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_swap_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/*-*- 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 "dbus-unit.h"
|
||||
#include "dbus-target.h"
|
||||
|
||||
static const char introspection[] =
|
||||
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
|
||||
"<node>"
|
||||
BUS_UNIT_INTERFACE
|
||||
BUS_PROPERTIES_INTERFACE
|
||||
" <interface name=\"org.freedesktop.systemd1.Target\">"
|
||||
" </interface>"
|
||||
BUS_INTROSPECTABLE_INTERFACE
|
||||
"</node>";
|
||||
|
||||
DBusHandlerResult bus_target_message_handler(Unit *u, DBusMessage *message) {
|
||||
const BusProperty properties[] = {
|
||||
BUS_UNIT_PROPERTIES,
|
||||
{ NULL, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
return bus_default_message_handler(u->meta.manager, message, introspection, properties);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbustargethfoo
|
||||
#define foodbustargethfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
DBusHandlerResult bus_target_message_handler(Unit *u, DBusMessage *message);
|
||||
|
||||
#endif
|
||||
+451
@@ -0,0 +1,451 @@
|
||||
/*-*- 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 "dbus.h"
|
||||
#include "log.h"
|
||||
#include "dbus-unit.h"
|
||||
|
||||
int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
char *t;
|
||||
Iterator j;
|
||||
DBusMessageIter sub;
|
||||
Unit *u = data;
|
||||
|
||||
if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
SET_FOREACH(t, u->meta.names, j)
|
||||
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dbus_message_iter_close_container(i, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_dependencies(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u;
|
||||
Iterator j;
|
||||
DBusMessageIter sub;
|
||||
Set *s = data;
|
||||
|
||||
if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
SET_FOREACH(u, s, j)
|
||||
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->meta.id))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dbus_message_iter_close_container(i, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
const char *d;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
d = unit_description(u);
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
|
||||
|
||||
int bus_unit_append_active_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
const char *state;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
state = unit_active_state_to_string(unit_active_state(u));
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_sub_state(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
const char *state;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
state = unit_sub_state_to_string(u);
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_can_start(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
dbus_bool_t b;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
b = unit_can_start(u);
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_can_reload(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
dbus_bool_t b;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
b = unit_can_reload(u);
|
||||
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
DBusMessageIter sub;
|
||||
char *p;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
if (u->meta.job) {
|
||||
|
||||
if (!(p = job_dbus_path(u->meta.job)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->meta.job->id) ||
|
||||
!dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
|
||||
free(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
uint32_t id = 0;
|
||||
|
||||
/* No job, so let's fill in some placeholder
|
||||
* data. Since we need to fill in a valid path we
|
||||
* simple point to ourselves. */
|
||||
|
||||
if (!(p = unit_dbus_path(u)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
|
||||
!dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
|
||||
free(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
if (!dbus_message_iter_close_container(i, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_unit_append_default_cgroup(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
char *t;
|
||||
CGroupBonding *cgb;
|
||||
bool success;
|
||||
|
||||
assert(m);
|
||||
assert(i);
|
||||
assert(property);
|
||||
assert(u);
|
||||
|
||||
if ((cgb = unit_get_default_cgroup(u))) {
|
||||
if (!(t = cgroup_bonding_to_string(cgb)))
|
||||
return -ENOMEM;
|
||||
} else
|
||||
t = (char*) "";
|
||||
|
||||
success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
|
||||
|
||||
if (cgb)
|
||||
free(t);
|
||||
|
||||
return success ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
int bus_unit_append_cgroups(Manager *m, DBusMessageIter *i, const char *property, void *data) {
|
||||
Unit *u = data;
|
||||
CGroupBonding *cgb;
|
||||
DBusMessageIter sub;
|
||||
|
||||
if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_FOREACH(by_unit, cgb, u->meta.cgroup_bondings) {
|
||||
char *t;
|
||||
bool success;
|
||||
|
||||
if (!(t = cgroup_bonding_to_string(cgb)))
|
||||
return -ENOMEM;
|
||||
|
||||
success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
|
||||
free(t);
|
||||
|
||||
if (!success)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!dbus_message_iter_close_container(i, &sub))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_kill_mode, kill_mode, KillMode);
|
||||
|
||||
static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusMessage *message) {
|
||||
DBusMessage *reply = NULL;
|
||||
Manager *m = u->meta.manager;
|
||||
DBusError error;
|
||||
JobType job_type = _JOB_TYPE_INVALID;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
|
||||
job_type = JOB_START;
|
||||
else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
|
||||
job_type = JOB_STOP;
|
||||
else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
|
||||
job_type = JOB_RELOAD;
|
||||
else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
|
||||
job_type = JOB_RESTART;
|
||||
else if (UNIT_VTABLE(u)->bus_message_handler)
|
||||
return UNIT_VTABLE(u)->bus_message_handler(u, message);
|
||||
else
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
if (job_type != _JOB_TYPE_INVALID) {
|
||||
const char *smode;
|
||||
JobMode mode;
|
||||
Job *j;
|
||||
int r;
|
||||
char *path;
|
||||
|
||||
if (!dbus_message_get_args(
|
||||
message,
|
||||
&error,
|
||||
DBUS_TYPE_STRING, &smode,
|
||||
DBUS_TYPE_INVALID))
|
||||
return bus_send_error_reply(m, message, &error, -EINVAL);
|
||||
|
||||
if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID)
|
||||
return bus_send_error_reply(m, message, NULL, -EINVAL);
|
||||
|
||||
if ((r = manager_add_job(m, job_type, u, mode, true, &j)) < 0)
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
|
||||
if (!(reply = dbus_message_new_method_return(message)))
|
||||
goto oom;
|
||||
|
||||
if (!(path = job_dbus_path(j)))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(
|
||||
reply,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
if (!dbus_connection_send(m->api_bus, reply, NULL))
|
||||
goto oom;
|
||||
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
oom:
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
dbus_error_free(&error);
|
||||
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
|
||||
static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
|
||||
Manager *m = data;
|
||||
Unit *u;
|
||||
int r;
|
||||
|
||||
assert(connection);
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
log_debug("Got D-Bus request: %s.%s() on %s",
|
||||
dbus_message_get_interface(message),
|
||||
dbus_message_get_member(message),
|
||||
dbus_message_get_path(message));
|
||||
|
||||
if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
|
||||
|
||||
if (r == -ENOMEM)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
if (r == -ENOENT)
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
return bus_send_error_reply(m, message, NULL, r);
|
||||
}
|
||||
|
||||
return bus_unit_message_dispatch(u, message);
|
||||
}
|
||||
|
||||
const DBusObjectPathVTable bus_unit_vtable = {
|
||||
.message_function = bus_unit_message_handler
|
||||
};
|
||||
|
||||
void bus_unit_send_change_signal(Unit *u) {
|
||||
char *p = NULL;
|
||||
DBusMessage *m = NULL;
|
||||
|
||||
assert(u);
|
||||
assert(u->meta.in_dbus_queue);
|
||||
|
||||
LIST_REMOVE(Meta, dbus_queue, u->meta.manager->dbus_unit_queue, &u->meta);
|
||||
u->meta.in_dbus_queue = false;
|
||||
|
||||
if (set_isempty(u->meta.manager->subscribed)) {
|
||||
u->meta.sent_dbus_new_signal = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(p = unit_dbus_path(u)))
|
||||
goto oom;
|
||||
|
||||
if (u->meta.sent_dbus_new_signal) {
|
||||
/* Send a change signal */
|
||||
|
||||
if (!(m = dbus_message_new_signal(p, "org.freedesktop.systemd1.Unit", "Changed")))
|
||||
goto oom;
|
||||
} else {
|
||||
/* Send a new signal */
|
||||
|
||||
if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_STRING, &u->meta.id,
|
||||
DBUS_TYPE_OBJECT_PATH, &p,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
}
|
||||
|
||||
if (!dbus_connection_send(u->meta.manager->api_bus, m, NULL))
|
||||
goto oom;
|
||||
|
||||
free(p);
|
||||
dbus_message_unref(m);
|
||||
|
||||
u->meta.sent_dbus_new_signal = true;
|
||||
|
||||
return;
|
||||
|
||||
oom:
|
||||
free(p);
|
||||
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
|
||||
log_error("Failed to allocate unit change/new signal.");
|
||||
}
|
||||
|
||||
void bus_unit_send_removed_signal(Unit *u) {
|
||||
char *p = NULL;
|
||||
DBusMessage *m = NULL;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (set_isempty(u->meta.manager->subscribed) || !u->meta.sent_dbus_new_signal)
|
||||
return;
|
||||
|
||||
if (!(p = unit_dbus_path(u)))
|
||||
goto oom;
|
||||
|
||||
if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_STRING, &u->meta.id,
|
||||
DBUS_TYPE_OBJECT_PATH, &p,
|
||||
DBUS_TYPE_INVALID))
|
||||
goto oom;
|
||||
|
||||
if (!dbus_connection_send(u->meta.manager->api_bus, m, NULL))
|
||||
goto oom;
|
||||
|
||||
free(p);
|
||||
dbus_message_unref(m);
|
||||
|
||||
return;
|
||||
|
||||
oom:
|
||||
free(p);
|
||||
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
|
||||
log_error("Failed to allocate unit remove signal.");
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbusunithfoo
|
||||
#define foodbusunithfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
#define BUS_UNIT_INTERFACE \
|
||||
" <interface name=\"org.freedesktop.systemd1.Unit\">" \
|
||||
" <method name=\"Start\">" \
|
||||
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg name=\"job\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"Stop\">" \
|
||||
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg name=\"job\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"Restart\">" \
|
||||
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg name=\"job\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"Reload\">" \
|
||||
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>" \
|
||||
" <arg name=\"job\" type=\"o\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" <signal name=\"Changed\"/>" \
|
||||
" <property name=\"Id\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"Names\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"Requires\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"RequiresOverridable\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"Requisite\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"Wants\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"WantedBy\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"Conflicts\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"Before\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"After\" type=\"as\" access=\"read\"/>" \
|
||||
" <property name=\"Description\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"LoadState\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"ActiveState\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"SubState\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"InactiveExitTimestamp\" type=\"t\" access=\"read\"/>" \
|
||||
" <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>" \
|
||||
" <property name=\"ActiveExitTimestamp\" type=\"t\" access=\"read\"/>" \
|
||||
" <property name=\"InactiveEnterTimestamp\" type=\"t\" access=\"read\"/>" \
|
||||
" <property name=\"CanReload\" type=\"b\" access=\"read\"/>" \
|
||||
" <property name=\"CanStart\" type=\"b\" access=\"read\"/>" \
|
||||
" <property name=\"Job\" type=\"(uo)\" access=\"read\"/>" \
|
||||
" <property name=\"RecursiveStop\" type=\"b\" access=\"read\"/>" \
|
||||
" <property name=\"StopWhenUneeded\" type=\"b\" access=\"read\"/>" \
|
||||
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>" \
|
||||
" <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>" \
|
||||
" </interface>"
|
||||
|
||||
#define BUS_UNIT_PROPERTIES \
|
||||
{ "org.freedesktop.systemd1.Unit", "Id", bus_property_append_string, "s", u->meta.id }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Names", bus_unit_append_names, "as", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Requires", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRES] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "RequiresOverridable", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Requisite", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "RequisiteOverridable", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Wants", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_WANTS] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "RequiredBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRED_BY] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "RequiredByOverridable",bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "WantedBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_WANTED_BY] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Conflicts", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_CONFLICTS] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Before", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BEFORE] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "After", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_AFTER] }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Description", bus_unit_append_description, "s", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "LoadState", bus_unit_append_load_state, "s", &u->meta.load_state }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "ActiveState", bus_unit_append_active_state, "s", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "SubState", bus_unit_append_sub_state, "s", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "FragmentPath", bus_property_append_string, "s", u->meta.fragment_path }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "InactiveExitTimestamp",bus_property_append_uint64, "t", &u->meta.inactive_exit_timestamp }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "ActiveEnterTimestamp", bus_property_append_uint64, "t", &u->meta.active_enter_timestamp }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "ActiveExitTimestamp", bus_property_append_uint64, "t", &u->meta.active_exit_timestamp }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "InactiveEnterTimestamp",bus_property_append_uint64, "t", &u->meta.inactive_enter_timestamp }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "CanStart", bus_unit_append_can_start, "b", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "CanReload", bus_unit_append_can_reload, "b", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "Job", bus_unit_append_job, "(uo)", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "RecursiveStop", bus_property_append_bool, "b", &u->meta.recursive_stop }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "StopWhenUneeded", bus_property_append_bool, "b", &u->meta.stop_when_unneeded }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "DefaultControlGroup", bus_unit_append_default_cgroup, "s", u }, \
|
||||
{ "org.freedesktop.systemd1.Unit", "ControlGroups", bus_unit_append_cgroups, "as", u }
|
||||
|
||||
int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_dependencies(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_description(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_load_state(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_active_state(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_sub_state(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_can_start(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_can_reload(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_job(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_default_cgroup(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_cgroups(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_unit_append_kill_mode(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
|
||||
void bus_unit_send_change_signal(Unit *u);
|
||||
void bus_unit_send_removed_signal(Unit *u);
|
||||
|
||||
extern const DBusObjectPathVTable bus_unit_vtable;
|
||||
|
||||
#endif
|
||||
+1136
File diff suppressed because it is too large
Load Diff
+107
@@ -0,0 +1,107 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodbushfoo
|
||||
#define foodbushfoo
|
||||
|
||||
/***
|
||||
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 <dbus/dbus.h>
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
typedef int (*BusPropertyCallback)(Manager *m, DBusMessageIter *iter, const char *property, void *data);
|
||||
|
||||
typedef struct BusProperty {
|
||||
const char *interface; /* interface of the property */
|
||||
const char *property; /* name of the property */
|
||||
BusPropertyCallback append; /* Function that is called to serialize this property */
|
||||
const char *signature;
|
||||
const void *data; /* The data of this property */
|
||||
} BusProperty;
|
||||
|
||||
#define BUS_PROPERTIES_INTERFACE \
|
||||
" <interface name=\"org.freedesktop.DBus.Properties\">" \
|
||||
" <method name=\"Get\">" \
|
||||
" <arg name=\"interface\" direction=\"in\" type=\"s\"/>" \
|
||||
" <arg name=\"property\" direction=\"in\" type=\"s\"/>" \
|
||||
" <arg name=\"value\" direction=\"out\" type=\"v\"/>" \
|
||||
" </method>" \
|
||||
" <method name=\"GetAll\">" \
|
||||
" <arg name=\"interface\" direction=\"in\" type=\"s\"/>" \
|
||||
" <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>" \
|
||||
" </method>" \
|
||||
" </interface>"
|
||||
|
||||
#define BUS_INTROSPECTABLE_INTERFACE \
|
||||
" <interface name=\"org.freedesktop.DBus.Introspectable\">" \
|
||||
" <method name=\"Introspect\">" \
|
||||
" <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
|
||||
" </method>" \
|
||||
" </interface>"
|
||||
|
||||
int bus_init_system(Manager *m);
|
||||
int bus_init_api(Manager *m);
|
||||
void bus_done_system(Manager *m);
|
||||
void bus_done_api(Manager *m);
|
||||
|
||||
unsigned bus_dispatch(Manager *m);
|
||||
|
||||
void bus_watch_event(Manager *m, Watch *w, int events);
|
||||
void bus_timeout_event(Manager *m, Watch *w, int events);
|
||||
|
||||
int bus_query_pid(Manager *m, const char *name);
|
||||
|
||||
DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, const char* introspection, const BusProperty *properties);
|
||||
|
||||
DBusHandlerResult bus_send_error_reply(Manager *m, DBusMessage *message, DBusError *bus_error, int error);
|
||||
|
||||
int bus_property_append_string(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_property_append_strv(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_property_append_bool(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_property_append_int32(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_property_append_uint32(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
int bus_property_append_uint64(Manager *m, DBusMessageIter *i, const char *property, void *data);
|
||||
|
||||
#define bus_property_append_int bus_property_append_int32
|
||||
#define bus_property_append_pid bus_property_append_uint32
|
||||
#define bus_property_append_mode bus_property_append_uint32
|
||||
#define bus_property_append_unsigned bus_property_append_uint32
|
||||
#define bus_property_append_usec bus_property_append_uint64
|
||||
|
||||
#define DEFINE_BUS_PROPERTY_APPEND_ENUM(function,name,type) \
|
||||
int function(Manager *m, DBusMessageIter *i, const char *property, void *data) { \
|
||||
const char *value; \
|
||||
type *field = data; \
|
||||
\
|
||||
assert(m); \
|
||||
assert(i); \
|
||||
assert(property); \
|
||||
\
|
||||
value = name##_to_string(*field); \
|
||||
\
|
||||
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &value)) \
|
||||
return -ENOMEM; \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
int bus_parse_strv(DBusMessage *m, char ***_l);
|
||||
|
||||
#endif
|
||||
+471
@@ -0,0 +1,471 @@
|
||||
/*-*- 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 <sys/epoll.h>
|
||||
#include <libudev.h>
|
||||
|
||||
#include "unit.h"
|
||||
#include "device.h"
|
||||
#include "strv.h"
|
||||
#include "log.h"
|
||||
#include "unit-name.h"
|
||||
#include "dbus-device.h"
|
||||
|
||||
static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
|
||||
[DEVICE_DEAD] = UNIT_INACTIVE,
|
||||
[DEVICE_AVAILABLE] = UNIT_ACTIVE
|
||||
};
|
||||
|
||||
static void device_done(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
|
||||
free(d->sysfs);
|
||||
d->sysfs = NULL;
|
||||
}
|
||||
|
||||
static void device_set_state(Device *d, DeviceState state) {
|
||||
DeviceState old_state;
|
||||
assert(d);
|
||||
|
||||
old_state = d->state;
|
||||
d->state = state;
|
||||
|
||||
if (state != old_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]);
|
||||
}
|
||||
|
||||
static int device_coldplug(Unit *u) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
assert(d->state == DEVICE_DEAD);
|
||||
|
||||
if (d->sysfs)
|
||||
device_set_state(d, DEVICE_AVAILABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Device *d = DEVICE(u);
|
||||
|
||||
assert(d);
|
||||
|
||||
fprintf(f,
|
||||
"%sDevice State: %s\n"
|
||||
"%sSysfs Path: %s\n",
|
||||
prefix, device_state_to_string(d->state),
|
||||
prefix, strna(d->sysfs));
|
||||
}
|
||||
|
||||
static UnitActiveState device_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_translation_table[DEVICE(u)->state];
|
||||
}
|
||||
|
||||
static const char *device_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return device_state_to_string(DEVICE(u)->state);
|
||||
}
|
||||
|
||||
static int device_add_escaped_name(Unit *u, const char *dn, bool make_id) {
|
||||
char *e;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(dn);
|
||||
assert(dn[0] == '/');
|
||||
|
||||
if (!(e = unit_name_from_path(dn, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
r = unit_add_name(u, e);
|
||||
|
||||
if (r >= 0 && make_id)
|
||||
unit_choose_id(u, e);
|
||||
|
||||
free(e);
|
||||
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
|
||||
char *e;
|
||||
Unit *u;
|
||||
|
||||
assert(m);
|
||||
assert(dn);
|
||||
assert(dn[0] == '/');
|
||||
assert(_u);
|
||||
|
||||
if (!(e = unit_name_from_path(dn, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
u = manager_get_unit(m, e);
|
||||
free(e);
|
||||
|
||||
if (u) {
|
||||
*_u = u;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
|
||||
const char *dn, *wants, *sysfs, *expose, *model, *alias;
|
||||
Unit *u = NULL;
|
||||
int r;
|
||||
char *w, *state;
|
||||
size_t l;
|
||||
bool delete;
|
||||
struct udev_list_entry *item = NULL, *first = NULL;
|
||||
int b;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!(sysfs = udev_device_get_syspath(dev)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(expose = udev_device_get_property_value(dev, "SYSTEMD_EXPOSE")))
|
||||
return 0;
|
||||
|
||||
if ((b = parse_boolean(expose)) < 0) {
|
||||
log_error("Failed to parse SYSTEMD_EXPOSE udev property for device %s: %s", sysfs, expose);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!b)
|
||||
return 0;
|
||||
|
||||
/* Check whether this entry is even relevant for us. */
|
||||
dn = udev_device_get_devnode(dev);
|
||||
wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
|
||||
alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");
|
||||
|
||||
/* We allow exactly one alias to be configured a this time and
|
||||
* it must be a path */
|
||||
|
||||
if (alias && !is_path(alias)) {
|
||||
log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias);
|
||||
alias = NULL;
|
||||
}
|
||||
|
||||
if ((r = device_find_escape_name(m, sysfs, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (r == 0 && dn)
|
||||
if ((r = device_find_escape_name(m, dn, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (r == 0) {
|
||||
first = udev_device_get_devlinks_list_entry(dev);
|
||||
udev_list_entry_foreach(item, first) {
|
||||
if ((r = device_find_escape_name(m, udev_list_entry_get_name(item), &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (r > 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r == 0 && alias)
|
||||
if ((r = device_find_escape_name(m, alias, &u)) < 0)
|
||||
return r;
|
||||
|
||||
/* FIXME: this needs proper merging */
|
||||
|
||||
assert((r > 0) == !!u);
|
||||
|
||||
/* If this is a different unit, then let's not merge things */
|
||||
if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
|
||||
u = NULL;
|
||||
|
||||
if (!u) {
|
||||
delete = true;
|
||||
|
||||
if (!(u = unit_new(m)))
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (alias)
|
||||
if ((r = device_add_escaped_name(u, alias, true)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (dn)
|
||||
if ((r = device_add_escaped_name(u, dn, true)) < 0)
|
||||
goto fail;
|
||||
|
||||
first = udev_device_get_devlinks_list_entry(dev);
|
||||
udev_list_entry_foreach(item, first)
|
||||
if ((r = device_add_escaped_name(u, udev_list_entry_get_name(item), false)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
|
||||
(model = udev_device_get_property_value(dev, "ID_MODEL"))) {
|
||||
if ((r = unit_set_description(u, model)) < 0)
|
||||
goto fail;
|
||||
} else if (dn) {
|
||||
if ((r = unit_set_description(u, dn)) < 0)
|
||||
goto fail;
|
||||
} else
|
||||
if ((r = unit_set_description(u, sysfs)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (wants) {
|
||||
FOREACH_WORD(w, l, wants, state) {
|
||||
char *e;
|
||||
|
||||
if (!(e = strndup(w, l))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = unit_add_dependency_by_name(u, UNIT_WANTS, NULL, e, true);
|
||||
free(e);
|
||||
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_state) {
|
||||
manager_dispatch_load_queue(u->meta.manager);
|
||||
device_set_state(DEVICE(u), DEVICE_AVAILABLE);
|
||||
}
|
||||
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (delete && u)
|
||||
unit_free(u);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int device_process_path(Manager *m, const char *path, bool update_state) {
|
||||
int r;
|
||||
struct udev_device *dev;
|
||||
|
||||
assert(m);
|
||||
assert(path);
|
||||
|
||||
if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
|
||||
log_warning("Failed to get udev device object from udev for path %s.", path);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = device_process_new_device(m, dev, update_state);
|
||||
udev_device_unref(dev);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int device_process_removed_device(Manager *m, struct udev_device *dev) {
|
||||
const char *sysfs;
|
||||
char *e;
|
||||
Unit *u;
|
||||
Device *d;
|
||||
|
||||
assert(m);
|
||||
assert(dev);
|
||||
|
||||
if (!(sysfs = udev_device_get_syspath(dev)))
|
||||
return -ENOMEM;
|
||||
|
||||
assert(sysfs[0] == '/');
|
||||
if (!(e = unit_name_from_path(sysfs, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
u = manager_get_unit(m, e);
|
||||
free(e);
|
||||
|
||||
if (!u)
|
||||
return 0;
|
||||
|
||||
d = DEVICE(u);
|
||||
free(d->sysfs);
|
||||
d->sysfs = NULL;
|
||||
|
||||
device_set_state(d, DEVICE_DEAD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_shutdown(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->udev_monitor) {
|
||||
udev_monitor_unref(m->udev_monitor);
|
||||
m->udev_monitor = NULL;
|
||||
}
|
||||
|
||||
if (m->udev) {
|
||||
udev_unref(m->udev);
|
||||
m->udev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int device_enumerate(Manager *m) {
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
struct udev_enumerate *e = NULL;
|
||||
struct udev_list_entry *item = NULL, *first = NULL;
|
||||
|
||||
assert(m);
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (udev_enumerate_scan_devices(e) < 0) {
|
||||
r = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
first = udev_enumerate_get_list_entry(e);
|
||||
udev_list_entry_foreach(item, first)
|
||||
device_process_path(m, udev_list_entry_get_name(item), false);
|
||||
|
||||
udev_enumerate_unref(e);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (e)
|
||||
udev_enumerate_unref(e);
|
||||
|
||||
device_shutdown(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
void device_fd_event(Manager *m, int events) {
|
||||
struct udev_device *dev;
|
||||
int r;
|
||||
const char *action;
|
||||
|
||||
assert(m);
|
||||
assert(events == EPOLLIN);
|
||||
|
||||
if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
|
||||
log_error("Failed to receive device.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(action = udev_device_get_action(dev))) {
|
||||
log_error("Failed to get udev action string.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (streq(action, "remove")) {
|
||||
if ((r = device_process_removed_device(m, dev)) < 0) {
|
||||
log_error("Failed to process udev device event: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if ((r = device_process_new_device(m, dev, true)) < 0) {
|
||||
log_error("Failed to process udev device event: %s", strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
|
||||
.no_requires = true,
|
||||
.no_instances = true,
|
||||
.no_snapshots = true,
|
||||
.no_isolate = true,
|
||||
|
||||
.load = unit_load_fragment_and_dropin_optional,
|
||||
.done = device_done,
|
||||
.coldplug = device_coldplug,
|
||||
|
||||
.dump = device_dump,
|
||||
|
||||
.active_state = device_active_state,
|
||||
.sub_state_to_string = device_sub_state_to_string,
|
||||
|
||||
.bus_message_handler = bus_device_message_handler,
|
||||
|
||||
.enumerate = device_enumerate,
|
||||
.shutdown = device_shutdown
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foodevicehfoo
|
||||
#define foodevicehfoo
|
||||
|
||||
/***
|
||||
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 Device Device;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
/* We simply watch devices, we cannot plug/unplug them. That
|
||||
* simplifies the state engine greatly */
|
||||
typedef enum DeviceState {
|
||||
DEVICE_DEAD,
|
||||
DEVICE_AVAILABLE,
|
||||
_DEVICE_STATE_MAX,
|
||||
_DEVICE_STATE_INVALID = -1
|
||||
} DeviceState;
|
||||
|
||||
struct Device {
|
||||
Meta meta;
|
||||
|
||||
DeviceState state;
|
||||
|
||||
char *sysfs;
|
||||
};
|
||||
|
||||
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
|
||||
+1619
File diff suppressed because it is too large
Load Diff
+221
@@ -0,0 +1,221 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooexecutehfoo
|
||||
#define fooexecutehfoo
|
||||
|
||||
/***
|
||||
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 ExecStatus ExecStatus;
|
||||
typedef struct ExecCommand ExecCommand;
|
||||
typedef struct ExecContext ExecContext;
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/capability.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sched.h>
|
||||
|
||||
struct CGroupBonding;
|
||||
|
||||
#include "list.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Abstract namespace! */
|
||||
#define LOGGER_SOCKET "/org/freedesktop/systemd1/logger"
|
||||
|
||||
typedef enum ExecInput {
|
||||
EXEC_INPUT_NULL,
|
||||
EXEC_INPUT_TTY,
|
||||
EXEC_INPUT_TTY_FORCE,
|
||||
EXEC_INPUT_TTY_FAIL,
|
||||
EXEC_INPUT_SOCKET,
|
||||
_EXEC_INPUT_MAX,
|
||||
_EXEC_INPUT_INVALID = -1
|
||||
} ExecInput;
|
||||
|
||||
typedef enum ExecOutput {
|
||||
EXEC_OUTPUT_INHERIT,
|
||||
EXEC_OUTPUT_NULL,
|
||||
EXEC_OUTPUT_TTY,
|
||||
EXEC_OUTPUT_SYSLOG,
|
||||
EXEC_OUTPUT_KERNEL,
|
||||
EXEC_OUTPUT_SOCKET,
|
||||
_EXEC_OUTPUT_MAX,
|
||||
_EXEC_OUTPUT_INVALID = -1
|
||||
} ExecOutput;
|
||||
|
||||
struct ExecStatus {
|
||||
usec_t start_timestamp;
|
||||
usec_t exit_timestamp;
|
||||
pid_t pid;
|
||||
int code; /* as in siginfo_t::si_code */
|
||||
int status; /* as in sigingo_t::si_status */
|
||||
};
|
||||
|
||||
struct ExecCommand {
|
||||
char *path;
|
||||
char **argv;
|
||||
ExecStatus exec_status;
|
||||
LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
|
||||
};
|
||||
|
||||
struct ExecContext {
|
||||
char **environment;
|
||||
struct rlimit *rlimit[RLIMIT_NLIMITS];
|
||||
char *working_directory, *root_directory;
|
||||
|
||||
mode_t umask;
|
||||
int oom_adjust;
|
||||
int nice;
|
||||
int ioprio;
|
||||
int cpu_sched_policy;
|
||||
int cpu_sched_priority;
|
||||
|
||||
cpu_set_t cpu_affinity;
|
||||
unsigned long timer_slack_ns;
|
||||
|
||||
ExecInput std_input;
|
||||
ExecOutput std_output;
|
||||
ExecOutput std_error;
|
||||
|
||||
int syslog_priority;
|
||||
char *syslog_identifier;
|
||||
bool syslog_no_prefix;
|
||||
|
||||
char *tty_path;
|
||||
|
||||
/* Since resolving these names might might involve socket
|
||||
* connections and we don't want to deadlock ourselves these
|
||||
* names are resolved on execution only and in the child
|
||||
* process. */
|
||||
char *user;
|
||||
char *group;
|
||||
char **supplementary_groups;
|
||||
|
||||
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
|
||||
unsigned long mount_flags;
|
||||
|
||||
uint64_t capability_bounding_set_drop;
|
||||
|
||||
cap_t capabilities;
|
||||
int secure_bits;
|
||||
|
||||
bool cpu_sched_reset_on_fork;
|
||||
bool non_blocking;
|
||||
bool private_tmp;
|
||||
|
||||
bool oom_adjust_set:1;
|
||||
bool nice_set:1;
|
||||
bool ioprio_set:1;
|
||||
bool cpu_sched_set:1;
|
||||
bool cpu_affinity_set:1;
|
||||
bool timer_slack_ns_set:1;
|
||||
|
||||
/* This is not exposed to the user but available
|
||||
* internally. We need it to make sure that whenever we spawn
|
||||
* /bin/mount it is run in the same process group as us so
|
||||
* that the autofs logic detects that it belongs to us and we
|
||||
* don't enter a trigger loop. */
|
||||
bool no_setsid:1;
|
||||
};
|
||||
|
||||
typedef enum ExitStatus {
|
||||
/* EXIT_SUCCESS defined by libc */
|
||||
/* EXIT_FAILURE defined by libc */
|
||||
EXIT_INVALIDARGUMENT = 2,
|
||||
EXIT_NOTIMPLEMENTED = 3,
|
||||
EXIT_NOPERMISSION = 4,
|
||||
EXIT_NOTINSTALLED = 5,
|
||||
EXIT_NOTCONFIGURED = 6,
|
||||
EXIT_NOTRUNNING = 7,
|
||||
|
||||
/* The LSB suggests that error codes >= 200 are "reserved". We
|
||||
* use them here under the assumption that they hence are
|
||||
* unused by init scripts.
|
||||
*
|
||||
* http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
|
||||
|
||||
EXIT_CHDIR = 200,
|
||||
EXIT_NICE,
|
||||
EXIT_FDS,
|
||||
EXIT_EXEC,
|
||||
EXIT_MEMORY,
|
||||
EXIT_LIMITS,
|
||||
EXIT_OOM_ADJUST,
|
||||
EXIT_SIGNAL_MASK,
|
||||
EXIT_STDIN,
|
||||
EXIT_STDOUT,
|
||||
EXIT_CHROOT, /* 210 */
|
||||
EXIT_IOPRIO,
|
||||
EXIT_TIMERSLACK,
|
||||
EXIT_SECUREBITS,
|
||||
EXIT_SETSCHEDULER,
|
||||
EXIT_CPUAFFINITY,
|
||||
EXIT_GROUP,
|
||||
EXIT_USER,
|
||||
EXIT_CAPABILITIES,
|
||||
EXIT_CGROUP,
|
||||
EXIT_SETSID, /* 220 */
|
||||
EXIT_CONFIRM,
|
||||
EXIT_STDERR
|
||||
|
||||
} ExitStatus;
|
||||
|
||||
int exec_spawn(ExecCommand *command,
|
||||
char **argv,
|
||||
const ExecContext *context,
|
||||
int fds[], unsigned n_fds,
|
||||
char **environment,
|
||||
bool apply_permissions,
|
||||
bool apply_chroot,
|
||||
bool confirm_spawn,
|
||||
struct CGroupBonding *cgroup_bondings,
|
||||
pid_t *ret);
|
||||
|
||||
void exec_command_done(ExecCommand *c);
|
||||
void exec_command_done_array(ExecCommand *c, unsigned n);
|
||||
|
||||
void exec_command_free_list(ExecCommand *c);
|
||||
void exec_command_free_array(ExecCommand **c, unsigned n);
|
||||
|
||||
char *exec_command_line(char **argv);
|
||||
|
||||
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix);
|
||||
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
|
||||
void exec_command_append_list(ExecCommand **l, ExecCommand *e);
|
||||
int exec_command_set(ExecCommand *c, const char *path, ...);
|
||||
|
||||
void exec_context_init(ExecContext *c);
|
||||
void exec_context_done(ExecContext *c);
|
||||
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
|
||||
|
||||
void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status);
|
||||
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
|
||||
|
||||
const char* exec_output_to_string(ExecOutput i);
|
||||
int exec_output_from_string(const char *s);
|
||||
|
||||
const char* exec_input_to_string(ExecInput i);
|
||||
int exec_input_from_string(const char *s);
|
||||
|
||||
const char* exit_status_to_string(ExitStatus status);
|
||||
|
||||
#endif
|
||||
+162
@@ -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
@@ -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
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
/*-*- 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
|
||||
#define NBUCKETS 127
|
||||
|
||||
struct hashmap_entry {
|
||||
const void *key;
|
||||
void *value;
|
||||
struct hashmap_entry *bucket_next, *bucket_previous;
|
||||
struct hashmap_entry *iterate_next, *iterate_previous;
|
||||
};
|
||||
|
||||
struct Hashmap {
|
||||
hash_func_t hash_func;
|
||||
compare_func_t compare_func;
|
||||
|
||||
struct hashmap_entry *iterate_list_head, *iterate_list_tail;
|
||||
unsigned n_entries;
|
||||
};
|
||||
|
||||
#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
|
||||
|
||||
unsigned string_hash_func(const void *p) {
|
||||
unsigned hash = 0;
|
||||
const char *c;
|
||||
|
||||
for (c = p; *c; c++)
|
||||
hash = 31 * hash + (unsigned) *c;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
int string_compare_func(const void *a, const void *b) {
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
unsigned trivial_hash_func(const void *p) {
|
||||
return PTR_TO_UINT(p);
|
||||
}
|
||||
|
||||
int trivial_compare_func(const void *a, const void *b) {
|
||||
return a < b ? -1 : (a > b ? 1 : 0);
|
||||
}
|
||||
|
||||
Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
|
||||
Hashmap *h;
|
||||
|
||||
if (!(h = malloc0(ALIGN(sizeof(Hashmap)) + NBUCKETS * ALIGN(sizeof(struct hashmap_entry*)))))
|
||||
return NULL;
|
||||
|
||||
h->hash_func = hash_func ? hash_func : trivial_hash_func;
|
||||
h->compare_func = compare_func ? compare_func : trivial_compare_func;
|
||||
|
||||
h->n_entries = 0;
|
||||
h->iterate_list_head = h->iterate_list_tail = NULL;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
|
||||
assert(h);
|
||||
|
||||
if (*h)
|
||||
return 0;
|
||||
|
||||
if (!(*h = hashmap_new(hash_func, compare_func)))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
|
||||
assert(h);
|
||||
assert(e);
|
||||
|
||||
/* Insert into hash table */
|
||||
e->bucket_next = BY_HASH(h)[hash];
|
||||
e->bucket_previous = NULL;
|
||||
if (BY_HASH(h)[hash])
|
||||
BY_HASH(h)[hash]->bucket_previous = e;
|
||||
BY_HASH(h)[hash] = e;
|
||||
|
||||
/* Insert into iteration list */
|
||||
e->iterate_previous = h->iterate_list_tail;
|
||||
e->iterate_next = NULL;
|
||||
if (h->iterate_list_tail) {
|
||||
assert(h->iterate_list_head);
|
||||
h->iterate_list_tail->iterate_next = e;
|
||||
} else {
|
||||
assert(!h->iterate_list_head);
|
||||
h->iterate_list_head = e;
|
||||
}
|
||||
h->iterate_list_tail = e;
|
||||
|
||||
h->n_entries++;
|
||||
assert(h->n_entries >= 1);
|
||||
}
|
||||
|
||||
static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
|
||||
assert(h);
|
||||
assert(e);
|
||||
|
||||
/* Remove from iteration list */
|
||||
if (e->iterate_next)
|
||||
e->iterate_next->iterate_previous = e->iterate_previous;
|
||||
else
|
||||
h->iterate_list_tail = e->iterate_previous;
|
||||
|
||||
if (e->iterate_previous)
|
||||
e->iterate_previous->iterate_next = e->iterate_next;
|
||||
else
|
||||
h->iterate_list_head = e->iterate_next;
|
||||
|
||||
/* Remove from hash table bucket list */
|
||||
if (e->bucket_next)
|
||||
e->bucket_next->bucket_previous = e->bucket_previous;
|
||||
|
||||
if (e->bucket_previous)
|
||||
e->bucket_previous->bucket_next = e->bucket_next;
|
||||
else
|
||||
BY_HASH(h)[hash] = e->bucket_next;
|
||||
|
||||
assert(h->n_entries >= 1);
|
||||
h->n_entries--;
|
||||
}
|
||||
|
||||
static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
|
||||
unsigned hash;
|
||||
|
||||
assert(h);
|
||||
assert(e);
|
||||
|
||||
hash = h->hash_func(e->key) % NBUCKETS;
|
||||
|
||||
unlink_entry(h, e, hash);
|
||||
free(e);
|
||||
}
|
||||
|
||||
void hashmap_free(Hashmap*h) {
|
||||
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
hashmap_clear(h);
|
||||
|
||||
free(h);
|
||||
}
|
||||
|
||||
void hashmap_clear(Hashmap *h) {
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
while (h->iterate_list_head)
|
||||
remove_entry(h, h->iterate_list_head);
|
||||
}
|
||||
|
||||
static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
|
||||
struct hashmap_entry *e;
|
||||
assert(h);
|
||||
assert(hash < NBUCKETS);
|
||||
|
||||
for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
|
||||
if (h->compare_func(e->key, key) == 0)
|
||||
return e;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hashmap_put(Hashmap *h, const void *key, void *value) {
|
||||
struct hashmap_entry *e;
|
||||
unsigned hash;
|
||||
|
||||
assert(h);
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if ((e = hash_scan(h, hash, key))) {
|
||||
|
||||
if (e->value == value)
|
||||
return 0;
|
||||
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (!(e = new(struct hashmap_entry, 1)))
|
||||
return -ENOMEM;
|
||||
|
||||
e->key = key;
|
||||
e->value = value;
|
||||
|
||||
link_entry(h, e, hash);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hashmap_replace(Hashmap *h, const void *key, void *value) {
|
||||
struct hashmap_entry *e;
|
||||
unsigned hash;
|
||||
|
||||
assert(h);
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if ((e = hash_scan(h, hash, key))) {
|
||||
e->key = key;
|
||||
e->value = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hashmap_put(h, key, value);
|
||||
}
|
||||
|
||||
void* hashmap_get(Hashmap *h, const void *key) {
|
||||
unsigned hash;
|
||||
struct hashmap_entry *e;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if (!(e = hash_scan(h, hash, key)))
|
||||
return NULL;
|
||||
|
||||
return e->value;
|
||||
}
|
||||
|
||||
void* hashmap_remove(Hashmap *h, const void *key) {
|
||||
struct hashmap_entry *e;
|
||||
unsigned hash;
|
||||
void *data;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if (!(e = hash_scan(h, hash, key)))
|
||||
return NULL;
|
||||
|
||||
data = e->value;
|
||||
remove_entry(h, e);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
|
||||
struct hashmap_entry *e;
|
||||
unsigned old_hash, new_hash;
|
||||
|
||||
if (!h)
|
||||
return -ENOENT;
|
||||
|
||||
old_hash = h->hash_func(old_key) % NBUCKETS;
|
||||
if (!(e = hash_scan(h, old_hash, old_key)))
|
||||
return -ENOENT;
|
||||
|
||||
new_hash = h->hash_func(new_key) % NBUCKETS;
|
||||
if (hash_scan(h, new_hash, new_key))
|
||||
return -EEXIST;
|
||||
|
||||
unlink_entry(h, e, old_hash);
|
||||
|
||||
e->key = new_key;
|
||||
e->value = value;
|
||||
|
||||
link_entry(h, e, new_hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
|
||||
struct hashmap_entry *e;
|
||||
unsigned hash;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if (!(e = hash_scan(h, hash, key)))
|
||||
return NULL;
|
||||
|
||||
if (e->value != value)
|
||||
return NULL;
|
||||
|
||||
remove_entry(h, e);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
|
||||
struct hashmap_entry *e;
|
||||
|
||||
assert(i);
|
||||
|
||||
if (!h)
|
||||
goto at_end;
|
||||
|
||||
if (*i == ITERATOR_LAST)
|
||||
goto at_end;
|
||||
|
||||
if (*i == ITERATOR_FIRST && !h->iterate_list_head)
|
||||
goto at_end;
|
||||
|
||||
e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
|
||||
|
||||
if (e->iterate_next)
|
||||
*i = (Iterator) e->iterate_next;
|
||||
else
|
||||
*i = ITERATOR_LAST;
|
||||
|
||||
if (key)
|
||||
*key = e->key;
|
||||
|
||||
return e->value;
|
||||
|
||||
at_end:
|
||||
*i = ITERATOR_LAST;
|
||||
|
||||
if (key)
|
||||
*key = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
|
||||
struct hashmap_entry *e;
|
||||
|
||||
assert(i);
|
||||
|
||||
if (!h)
|
||||
goto at_beginning;
|
||||
|
||||
if (*i == ITERATOR_FIRST)
|
||||
goto at_beginning;
|
||||
|
||||
if (*i == ITERATOR_LAST && !h->iterate_list_tail)
|
||||
goto at_beginning;
|
||||
|
||||
e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
|
||||
|
||||
if (e->iterate_previous)
|
||||
*i = (Iterator) e->iterate_previous;
|
||||
else
|
||||
*i = ITERATOR_FIRST;
|
||||
|
||||
if (key)
|
||||
*key = e->key;
|
||||
|
||||
return e->value;
|
||||
|
||||
at_beginning:
|
||||
*i = ITERATOR_FIRST;
|
||||
|
||||
if (key)
|
||||
*key = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
|
||||
unsigned hash;
|
||||
struct hashmap_entry *e;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
hash = h->hash_func(key) % NBUCKETS;
|
||||
|
||||
if (!(e = hash_scan(h, hash, key)))
|
||||
return NULL;
|
||||
|
||||
*i = (Iterator) e;
|
||||
|
||||
return e->value;
|
||||
}
|
||||
|
||||
void* hashmap_first(Hashmap *h) {
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
if (!h->iterate_list_head)
|
||||
return NULL;
|
||||
|
||||
return h->iterate_list_head->value;
|
||||
}
|
||||
|
||||
void* hashmap_last(Hashmap *h) {
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
if (!h->iterate_list_tail)
|
||||
return NULL;
|
||||
|
||||
return h->iterate_list_tail->value;
|
||||
}
|
||||
|
||||
void* hashmap_steal_first(Hashmap *h) {
|
||||
void *data;
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
if (!h->iterate_list_head)
|
||||
return NULL;
|
||||
|
||||
data = h->iterate_list_head->value;
|
||||
remove_entry(h, h->iterate_list_head);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
unsigned hashmap_size(Hashmap *h) {
|
||||
|
||||
if (!h)
|
||||
return 0;
|
||||
|
||||
return h->n_entries;
|
||||
}
|
||||
|
||||
bool hashmap_isempty(Hashmap *h) {
|
||||
|
||||
if (!h)
|
||||
return true;
|
||||
|
||||
return h->n_entries == 0;
|
||||
}
|
||||
|
||||
int hashmap_merge(Hashmap *h, Hashmap *other) {
|
||||
struct hashmap_entry *e;
|
||||
|
||||
assert(h);
|
||||
|
||||
if (!other)
|
||||
return 0;
|
||||
|
||||
for (e = other->iterate_list_head; e; e = e->iterate_next) {
|
||||
int r;
|
||||
|
||||
if ((r = hashmap_put(h, e->key, e->value)) < 0)
|
||||
if (r != -EEXIST)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hashmap_move(Hashmap *h, Hashmap *other) {
|
||||
struct hashmap_entry *e, *n;
|
||||
|
||||
assert(h);
|
||||
|
||||
/* The same as hashmap_merge(), but every new item from other
|
||||
* is moved to h. This function is guaranteed to succeed. */
|
||||
|
||||
if (!other)
|
||||
return;
|
||||
|
||||
for (e = other->iterate_list_head; e; e = n) {
|
||||
unsigned h_hash, other_hash;
|
||||
|
||||
n = e->iterate_next;
|
||||
|
||||
h_hash = h->hash_func(e->key) % NBUCKETS;
|
||||
|
||||
if (hash_scan(h, h_hash, e->key))
|
||||
continue;
|
||||
|
||||
other_hash = other->hash_func(e->key) % NBUCKETS;
|
||||
|
||||
unlink_entry(other, e, other_hash);
|
||||
link_entry(h, e, h_hash);
|
||||
}
|
||||
}
|
||||
|
||||
int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
|
||||
unsigned h_hash, other_hash;
|
||||
struct hashmap_entry *e;
|
||||
|
||||
if (!other)
|
||||
return 0;
|
||||
|
||||
assert(h);
|
||||
|
||||
h_hash = h->hash_func(key) % NBUCKETS;
|
||||
if (hash_scan(h, h_hash, key))
|
||||
return -EEXIST;
|
||||
|
||||
other_hash = other->hash_func(key) % NBUCKETS;
|
||||
if (!(e = hash_scan(other, other_hash, key)))
|
||||
return -ENOENT;
|
||||
|
||||
unlink_entry(other, e, other_hash);
|
||||
link_entry(h, e, h_hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Hashmap *hashmap_copy(Hashmap *h) {
|
||||
Hashmap *copy;
|
||||
|
||||
assert(h);
|
||||
|
||||
if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
|
||||
return NULL;
|
||||
|
||||
if (hashmap_merge(copy, h) < 0) {
|
||||
hashmap_free(copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foohashmaphfoo
|
||||
#define foohashmaphfoo
|
||||
|
||||
/***
|
||||
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 <stdbool.h>
|
||||
|
||||
/* Pretty straightforward hash table implementation. As a minor
|
||||
* optimization a NULL hashmap object will be treated as empty hashmap
|
||||
* for all read operations. That way it is not necessary to
|
||||
* instantiate an object for each Hashmap use. */
|
||||
|
||||
typedef struct Hashmap Hashmap;
|
||||
typedef struct _IteratorStruct _IteratorStruct;
|
||||
typedef _IteratorStruct* Iterator;
|
||||
|
||||
#define ITERATOR_FIRST ((Iterator) 0)
|
||||
#define ITERATOR_LAST ((Iterator) -1)
|
||||
|
||||
typedef unsigned (*hash_func_t)(const void *p);
|
||||
typedef int (*compare_func_t)(const void *a, const void *b);
|
||||
|
||||
unsigned string_hash_func(const void *p);
|
||||
int string_compare_func(const void *a, const void *b);
|
||||
|
||||
unsigned trivial_hash_func(const void *p);
|
||||
int trivial_compare_func(const void *a, const void *b);
|
||||
|
||||
Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
|
||||
void hashmap_free(Hashmap *h);
|
||||
Hashmap *hashmap_copy(Hashmap *h);
|
||||
int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
|
||||
|
||||
int hashmap_put(Hashmap *h, const void *key, void *value);
|
||||
int hashmap_replace(Hashmap *h, const void *key, void *value);
|
||||
void* hashmap_get(Hashmap *h, const void *key);
|
||||
void* hashmap_remove(Hashmap *h, const void *key);
|
||||
void* hashmap_remove_value(Hashmap *h, const void *key, void *value);
|
||||
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
|
||||
|
||||
int hashmap_merge(Hashmap *h, Hashmap *other);
|
||||
void hashmap_move(Hashmap *h, Hashmap *other);
|
||||
int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key);
|
||||
|
||||
unsigned hashmap_size(Hashmap *h);
|
||||
bool hashmap_isempty(Hashmap *h);
|
||||
|
||||
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
|
||||
void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
|
||||
void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
|
||||
|
||||
void hashmap_clear(Hashmap *h);
|
||||
void *hashmap_steal_first(Hashmap *h);
|
||||
void* hashmap_first(Hashmap *h);
|
||||
void* hashmap_last(Hashmap *h);
|
||||
|
||||
#define HASHMAP_FOREACH(e, h, i) \
|
||||
for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
|
||||
|
||||
#define HASHMAP_FOREACH_KEY(e, k, h, i) \
|
||||
for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
|
||||
|
||||
#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \
|
||||
for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
/*-*- 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 <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hostname-setup.h"
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
#define LINE_MAX 4096
|
||||
|
||||
#if defined(TARGET_FEDORA)
|
||||
#define FILENAME "/etc/sysconfig/network"
|
||||
#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE)
|
||||
#define FILENAME "/etc/HOSTNAME"
|
||||
#elif defined(TARGET_DEBIAN)
|
||||
#define FILENAME "/etc/hostname"
|
||||
#elif defined(TARGET_ARCH)
|
||||
#define FILENAME "/etc/rc.conf"
|
||||
#elif defined(TARGET_GENTOO)
|
||||
#define FILENAME "/etc/conf.d/hostname"
|
||||
#endif
|
||||
|
||||
static char* strip_bad_chars(char *s) {
|
||||
char *p, *d;
|
||||
|
||||
for (p = s, d = s; *p; p++)
|
||||
if ((*p >= 'a' && *p <= 'z') ||
|
||||
(*p >= 'A' && *p <= 'Z') ||
|
||||
(*p >= '0' && *p <= '9') ||
|
||||
*p == '-' ||
|
||||
*p == '_')
|
||||
*(d++) = *p;
|
||||
|
||||
*d = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int read_hostname(char **hn) {
|
||||
|
||||
#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO)
|
||||
int r;
|
||||
FILE *f;
|
||||
|
||||
assert(hn);
|
||||
|
||||
if (!(f = fopen(FILENAME, "re")))
|
||||
return -errno;
|
||||
|
||||
for (;;) {
|
||||
char line[LINE_MAX];
|
||||
char *s, *k;
|
||||
|
||||
if (!fgets(line, sizeof(line), f)) {
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
s = strstrip(line);
|
||||
|
||||
if (!startswith_no_case(s, "HOSTNAME="))
|
||||
continue;
|
||||
|
||||
if (!(k = strdup(s+9))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
strip_bad_chars(k);
|
||||
|
||||
if (k[0] == 0) {
|
||||
free(k);
|
||||
r = -ENOENT;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
*hn = k;
|
||||
break;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
fclose(f);
|
||||
return r;
|
||||
|
||||
#elif defined(TARGET_SUSE) || defined(TARGET_DEBIAN) || defined(TARGET_SLACKWARE)
|
||||
int r;
|
||||
char *s, *k;
|
||||
|
||||
assert(hn);
|
||||
|
||||
if ((r = read_one_line_file(FILENAME, &s)) < 0)
|
||||
return r;
|
||||
|
||||
k = strdup(strstrip(s));
|
||||
free(s);
|
||||
|
||||
if (!k)
|
||||
return -ENOMEM;
|
||||
|
||||
strip_bad_chars(k);
|
||||
|
||||
if (k[0] == 0) {
|
||||
free(k);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
*hn = k;
|
||||
|
||||
#else
|
||||
#warning "Don't know how to read the hostname"
|
||||
|
||||
return -ENOENT;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hostname_setup(void) {
|
||||
int r;
|
||||
char *hn;
|
||||
|
||||
if ((r = read_hostname(&hn)) < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_warning("Failed to read configured hostname: %s", strerror(-r));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sethostname(hn, strlen(hn)) < 0 ? -errno : 0;
|
||||
|
||||
if (r < 0)
|
||||
log_warning("Failed to set hostname to <%s>: %s", hn, strerror(-r));
|
||||
else
|
||||
log_info("Set hostname to <%s>.", hn);
|
||||
|
||||
free(hn);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foohostnamesetuphfoo
|
||||
#define foohostnamesetuphfoo
|
||||
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
int hostname_setup(void);
|
||||
|
||||
#endif
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
/*-*- 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 <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/un.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "list.h"
|
||||
#include "initreq.h"
|
||||
#include "manager.h"
|
||||
#include "sd-daemon.h"
|
||||
|
||||
#define SERVER_FD_MAX 16
|
||||
#define TIMEOUT ((int) (10*MSEC_PER_SEC))
|
||||
|
||||
typedef struct Fifo Fifo;
|
||||
|
||||
typedef struct Server {
|
||||
int epoll_fd;
|
||||
|
||||
LIST_HEAD(Fifo, fifos);
|
||||
unsigned n_fifos;
|
||||
|
||||
DBusConnection *bus;
|
||||
} Server;
|
||||
|
||||
struct Fifo {
|
||||
Server *server;
|
||||
|
||||
int fd;
|
||||
|
||||
struct init_request buffer;
|
||||
size_t bytes_read;
|
||||
|
||||
LIST_FIELDS(Fifo, fifo);
|
||||
};
|
||||
|
||||
static const char *translate_runlevel(int runlevel) {
|
||||
static const struct {
|
||||
const int runlevel;
|
||||
const char *special;
|
||||
} table[] = {
|
||||
{ '0', SPECIAL_RUNLEVEL0_TARGET },
|
||||
{ '1', SPECIAL_RUNLEVEL1_TARGET },
|
||||
{ 's', SPECIAL_RUNLEVEL1_TARGET },
|
||||
{ 'S', SPECIAL_RUNLEVEL1_TARGET },
|
||||
{ '2', SPECIAL_RUNLEVEL2_TARGET },
|
||||
{ '3', SPECIAL_RUNLEVEL3_TARGET },
|
||||
{ '4', SPECIAL_RUNLEVEL4_TARGET },
|
||||
{ '5', SPECIAL_RUNLEVEL5_TARGET },
|
||||
{ '6', SPECIAL_RUNLEVEL6_TARGET },
|
||||
};
|
||||
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(table); i++)
|
||||
if (table[i].runlevel == runlevel)
|
||||
return table[i].special;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void change_runlevel(Server *s, int runlevel) {
|
||||
const char *target;
|
||||
DBusMessage *m = NULL, *reply = NULL;
|
||||
DBusError error;
|
||||
const char *path, *replace = "isolate";
|
||||
|
||||
assert(s);
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (!(target = translate_runlevel(runlevel))) {
|
||||
log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
log_debug("Running request %s", target);
|
||||
|
||||
if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "LoadUnit"))) {
|
||||
log_error("Could not allocate message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_STRING, &target,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
log_error("Could not attach group information to signal message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
|
||||
log_error("Failed to get unit path: %s", error.message);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!dbus_message_get_args(reply, &error,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
log_error("Failed to parse unit path: %s", error.message);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
dbus_message_unref(m);
|
||||
if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", path, "org.freedesktop.systemd1.Unit", "Start"))) {
|
||||
log_error("Could not allocate message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!dbus_message_append_args(m,
|
||||
DBUS_TYPE_STRING, &replace,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
log_error("Could not attach group information to signal message.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
dbus_message_unref(reply);
|
||||
if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
|
||||
log_error("Failed to start unit: %s", error.message);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (m)
|
||||
dbus_message_unref(m);
|
||||
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
dbus_error_free(&error);
|
||||
}
|
||||
|
||||
static void request_process(Server *s, const struct init_request *req) {
|
||||
assert(s);
|
||||
assert(req);
|
||||
|
||||
if (req->magic != INIT_MAGIC) {
|
||||
log_error("Got initctl request with invalid magic. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (req->cmd) {
|
||||
|
||||
case INIT_CMD_RUNLVL:
|
||||
if (!isprint(req->runlevel))
|
||||
log_error("Got invalid runlevel. Ignoring.");
|
||||
else
|
||||
change_runlevel(s, req->runlevel);
|
||||
return;
|
||||
|
||||
case INIT_CMD_POWERFAIL:
|
||||
case INIT_CMD_POWERFAILNOW:
|
||||
case INIT_CMD_POWEROK:
|
||||
log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
|
||||
return;
|
||||
|
||||
case INIT_CMD_CHANGECONS:
|
||||
log_warning("Received console change initctl request. This is not implemented in systemd.");
|
||||
return;
|
||||
|
||||
case INIT_CMD_SETENV:
|
||||
case INIT_CMD_UNSETENV:
|
||||
log_warning("Received environment initctl request. This is not implemented in systemd.");
|
||||
return;
|
||||
|
||||
default:
|
||||
log_warning("Received unknown initctl request. Ignoring.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int fifo_process(Fifo *f) {
|
||||
ssize_t l;
|
||||
|
||||
assert(f);
|
||||
|
||||
errno = EIO;
|
||||
if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
log_warning("Failed to read from fifo: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->bytes_read += l;
|
||||
assert(f->bytes_read <= sizeof(f->buffer));
|
||||
|
||||
if (f->bytes_read == sizeof(f->buffer)) {
|
||||
request_process(f->server, &f->buffer);
|
||||
f->bytes_read = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fifo_free(Fifo *f) {
|
||||
assert(f);
|
||||
|
||||
if (f->server) {
|
||||
assert(f->server->n_fifos > 0);
|
||||
f->server->n_fifos--;
|
||||
LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
|
||||
}
|
||||
|
||||
if (f->fd >= 0) {
|
||||
if (f->server)
|
||||
epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
|
||||
|
||||
close_nointr_nofail(f->fd);
|
||||
}
|
||||
|
||||
free(f);
|
||||
}
|
||||
|
||||
static void server_done(Server *s) {
|
||||
assert(s);
|
||||
|
||||
while (s->fifos)
|
||||
fifo_free(s->fifos);
|
||||
|
||||
if (s->epoll_fd >= 0)
|
||||
close_nointr_nofail(s->epoll_fd);
|
||||
|
||||
if (s->bus)
|
||||
dbus_connection_unref(s->bus);
|
||||
}
|
||||
|
||||
static int server_init(Server *s, unsigned n_sockets) {
|
||||
int r;
|
||||
unsigned i;
|
||||
DBusError error;
|
||||
|
||||
assert(s);
|
||||
assert(n_sockets > 0);
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
zero(*s);
|
||||
|
||||
if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
|
||||
r = -errno;
|
||||
log_error("Failed to create epoll object: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_sockets; i++) {
|
||||
struct epoll_event ev;
|
||||
Fifo *f;
|
||||
|
||||
if (!(f = new0(Fifo, 1))) {
|
||||
r = -ENOMEM;
|
||||
log_error("Failed to create fifo object: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->fd = -1;
|
||||
|
||||
zero(ev);
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = f;
|
||||
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SD_LISTEN_FDS_START+i, &ev) < 0) {
|
||||
r = -errno;
|
||||
fifo_free(f);
|
||||
log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
f->fd = SD_LISTEN_FDS_START+i;
|
||||
LIST_PREPEND(Fifo, fifo, s->fifos, f);
|
||||
f->server = s;
|
||||
s->n_fifos ++;
|
||||
}
|
||||
|
||||
if (!(s->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
|
||||
log_error("Failed to get D-Bus connection: %s", error.message);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
server_done(s);
|
||||
|
||||
dbus_error_free(&error);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int process_event(Server *s, struct epoll_event *ev) {
|
||||
int r;
|
||||
Fifo *f;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!(ev->events & EPOLLIN)) {
|
||||
log_info("Got invalid event from epoll. (3)");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
f = (Fifo*) ev->data.ptr;
|
||||
|
||||
if ((r = fifo_process(f)) < 0) {
|
||||
log_info("Got error on fifo: %s", strerror(-r));
|
||||
fifo_free(f);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Server server;
|
||||
int r = 3, n;
|
||||
|
||||
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
|
||||
log_parse_environment();
|
||||
|
||||
log_info("systemd-initctl running as pid %llu", (unsigned long long) getpid());
|
||||
|
||||
if ((n = sd_listen_fds(true)) < 0) {
|
||||
log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (n <= 0 || n > SERVER_FD_MAX) {
|
||||
log_error("No or too many file descriptors passed.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (server_init(&server, (unsigned) n) < 0)
|
||||
return 2;
|
||||
|
||||
for (;;) {
|
||||
struct epoll_event event;
|
||||
int k;
|
||||
|
||||
if ((k = epoll_wait(server.epoll_fd,
|
||||
&event, 1,
|
||||
TIMEOUT)) < 0) {
|
||||
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
log_error("epoll_wait() failed: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (k <= 0)
|
||||
break;
|
||||
|
||||
if ((k = process_event(&server, &event)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
r = 0;
|
||||
|
||||
fail:
|
||||
server_done(&server);
|
||||
|
||||
log_info("systemd-initctl stopped as pid %llu", (unsigned long long) getpid());
|
||||
|
||||
dbus_shutdown();
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* initreq.h Interface to talk to init through /dev/initctl.
|
||||
*
|
||||
* Copyright (C) 1995-2004 Miquel van Smoorenburg
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
|
||||
*
|
||||
*/
|
||||
#ifndef _INITREQ_H
|
||||
#define _INITREQ_H
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#if defined(__FreeBSD_kernel__)
|
||||
# define INIT_FIFO "/etc/.initctl"
|
||||
#else
|
||||
# define INIT_FIFO "/dev/initctl"
|
||||
#endif
|
||||
|
||||
#define INIT_MAGIC 0x03091969
|
||||
#define INIT_CMD_START 0
|
||||
#define INIT_CMD_RUNLVL 1
|
||||
#define INIT_CMD_POWERFAIL 2
|
||||
#define INIT_CMD_POWERFAILNOW 3
|
||||
#define INIT_CMD_POWEROK 4
|
||||
#define INIT_CMD_BSD 5
|
||||
#define INIT_CMD_SETENV 6
|
||||
#define INIT_CMD_UNSETENV 7
|
||||
|
||||
#define INIT_CMD_CHANGECONS 12345
|
||||
|
||||
#ifdef MAXHOSTNAMELEN
|
||||
# define INITRQ_HLEN MAXHOSTNAMELEN
|
||||
#else
|
||||
# define INITRQ_HLEN 64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is what BSD 4.4 uses when talking to init.
|
||||
* Linux doesn't use this right now.
|
||||
*/
|
||||
struct init_request_bsd {
|
||||
char gen_id[8]; /* Beats me.. telnetd uses "fe" */
|
||||
char tty_id[16]; /* Tty name minus /dev/tty */
|
||||
char host[INITRQ_HLEN]; /* Hostname */
|
||||
char term_type[16]; /* Terminal type */
|
||||
int signal; /* Signal to send */
|
||||
int pid; /* Process to send to */
|
||||
char exec_name[128]; /* Program to execute */
|
||||
char reserved[128]; /* For future expansion. */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Because of legacy interfaces, "runlevel" and "sleeptime"
|
||||
* aren't in a seperate struct in the union.
|
||||
*
|
||||
* The weird sizes are because init expects the whole
|
||||
* struct to be 384 bytes.
|
||||
*/
|
||||
struct init_request {
|
||||
int magic; /* Magic number */
|
||||
int cmd; /* What kind of request */
|
||||
int runlevel; /* Runlevel to change to */
|
||||
int sleeptime; /* Time between TERM and KILL */
|
||||
union {
|
||||
struct init_request_bsd bsd;
|
||||
char data[368];
|
||||
} i;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef IOPRIO_H
|
||||
#define IOPRIO_H
|
||||
|
||||
/* This is minimal version of Linux' linux/ioprio.h header file, which
|
||||
* is licensed GPL2 */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
/*
|
||||
* Gives us 8 prio classes with 13-bits of data for each class
|
||||
*/
|
||||
#define IOPRIO_BITS (16)
|
||||
#define IOPRIO_CLASS_SHIFT (13)
|
||||
#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
|
||||
|
||||
#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
|
||||
#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
|
||||
#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
|
||||
|
||||
#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE)
|
||||
|
||||
/*
|
||||
* These are the io priority groups as implemented by CFQ. RT is the realtime
|
||||
* class, it always gets premium service. BE is the best-effort scheduling
|
||||
* class, the default for any process. IDLE is the idle scheduling class, it
|
||||
* is only served when no one else is using the disk.
|
||||
*/
|
||||
enum {
|
||||
IOPRIO_CLASS_NONE,
|
||||
IOPRIO_CLASS_RT,
|
||||
IOPRIO_CLASS_BE,
|
||||
IOPRIO_CLASS_IDLE,
|
||||
};
|
||||
|
||||
/*
|
||||
* 8 best effort priority levels are supported
|
||||
*/
|
||||
#define IOPRIO_BE_NR (8)
|
||||
|
||||
enum {
|
||||
IOPRIO_WHO_PROCESS = 1,
|
||||
IOPRIO_WHO_PGRP,
|
||||
IOPRIO_WHO_USER,
|
||||
};
|
||||
|
||||
static inline int ioprio_set(int which, int who, int ioprio)
|
||||
{
|
||||
return syscall(__NR_ioprio_set, which, who, ioprio);
|
||||
}
|
||||
|
||||
static inline int ioprio_get(int which, int who)
|
||||
{
|
||||
return syscall(__NR_ioprio_get, which, who);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,589 @@
|
||||
/*-*- 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 <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "set.h"
|
||||
#include "unit.h"
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
#include "load-fragment.h"
|
||||
#include "load-dropin.h"
|
||||
#include "log.h"
|
||||
#include "dbus-job.h"
|
||||
|
||||
Job* job_new(Manager *m, JobType type, Unit *unit) {
|
||||
Job *j;
|
||||
|
||||
assert(m);
|
||||
assert(type < _JOB_TYPE_MAX);
|
||||
assert(unit);
|
||||
|
||||
if (!(j = new0(Job, 1)))
|
||||
return NULL;
|
||||
|
||||
j->manager = m;
|
||||
j->id = m->current_job_id++;
|
||||
j->type = type;
|
||||
j->unit = unit;
|
||||
|
||||
/* We don't link it here, that's what job_dependency() is for */
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
void job_free(Job *j) {
|
||||
assert(j);
|
||||
|
||||
/* Detach from next 'bigger' objects */
|
||||
if (j->installed) {
|
||||
bus_job_send_removed_signal(j);
|
||||
|
||||
if (j->unit->meta.job == j) {
|
||||
j->unit->meta.job = NULL;
|
||||
unit_add_to_gc_queue(j->unit);
|
||||
}
|
||||
|
||||
hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
|
||||
j->installed = false;
|
||||
}
|
||||
|
||||
/* Detach from next 'smaller' objects */
|
||||
manager_transaction_unlink_job(j->manager, j, true);
|
||||
|
||||
if (j->in_run_queue)
|
||||
LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
|
||||
|
||||
if (j->in_dbus_queue)
|
||||
LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
|
||||
|
||||
free(j);
|
||||
}
|
||||
|
||||
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters) {
|
||||
JobDependency *l;
|
||||
|
||||
assert(object);
|
||||
|
||||
/* Adds a new job link, which encodes that the 'subject' job
|
||||
* needs the 'object' job in some way. If 'subject' is NULL
|
||||
* this means the 'anchor' job (i.e. the one the user
|
||||
* explcitily asked for) is the requester. */
|
||||
|
||||
if (!(l = new0(JobDependency, 1)))
|
||||
return NULL;
|
||||
|
||||
l->subject = subject;
|
||||
l->object = object;
|
||||
l->matters = matters;
|
||||
|
||||
if (subject)
|
||||
LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
|
||||
else
|
||||
LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
|
||||
|
||||
LIST_PREPEND(JobDependency, object, object->object_list, l);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void job_dependency_free(JobDependency *l) {
|
||||
assert(l);
|
||||
|
||||
if (l->subject)
|
||||
LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
|
||||
else
|
||||
LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
|
||||
|
||||
LIST_REMOVE(JobDependency, object, l->object->object_list, l);
|
||||
|
||||
free(l);
|
||||
}
|
||||
|
||||
void job_dependency_delete(Job *subject, Job *object, bool *matters) {
|
||||
JobDependency *l;
|
||||
|
||||
assert(object);
|
||||
|
||||
LIST_FOREACH(object, l, object->object_list) {
|
||||
assert(l->object == object);
|
||||
|
||||
if (l->subject == subject)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!l) {
|
||||
if (matters)
|
||||
*matters = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (matters)
|
||||
*matters = l->matters;
|
||||
|
||||
job_dependency_free(l);
|
||||
}
|
||||
|
||||
void job_dump(Job *j, FILE*f, const char *prefix) {
|
||||
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
fprintf(f,
|
||||
"%s-> Job %u:\n"
|
||||
"%s\tAction: %s -> %s\n"
|
||||
"%s\tState: %s\n"
|
||||
"%s\tForced: %s\n",
|
||||
prefix, j->id,
|
||||
prefix, j->unit->meta.id, job_type_to_string(j->type),
|
||||
prefix, job_state_to_string(j->state),
|
||||
prefix, yes_no(j->override));
|
||||
}
|
||||
|
||||
bool job_is_anchor(Job *j) {
|
||||
JobDependency *l;
|
||||
|
||||
assert(j);
|
||||
|
||||
LIST_FOREACH(object, l, j->object_list)
|
||||
if (!l->subject)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool types_match(JobType a, JobType b, JobType c, JobType d) {
|
||||
return
|
||||
(a == c && b == d) ||
|
||||
(a == d && b == c);
|
||||
}
|
||||
|
||||
int job_type_merge(JobType *a, JobType b) {
|
||||
if (*a == b)
|
||||
return 0;
|
||||
|
||||
/* Merging is associative! a merged with b merged with c is
|
||||
* the same as a merged with c merged with b. */
|
||||
|
||||
/* Mergeability is transitive! if a can be merged with b and b
|
||||
* with c then a also with c */
|
||||
|
||||
/* Also, if a merged with b cannot be merged with c, then
|
||||
* either a or b cannot be merged with c either */
|
||||
|
||||
if (types_match(*a, b, JOB_START, JOB_VERIFY_ACTIVE))
|
||||
*a = JOB_START;
|
||||
else if (types_match(*a, b, JOB_START, JOB_RELOAD) ||
|
||||
types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) ||
|
||||
types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RELOAD_OR_START) ||
|
||||
types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START))
|
||||
*a = JOB_RELOAD_OR_START;
|
||||
else if (types_match(*a, b, JOB_START, JOB_RESTART) ||
|
||||
types_match(*a, b, JOB_START, JOB_TRY_RESTART) ||
|
||||
types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RESTART) ||
|
||||
types_match(*a, b, JOB_RELOAD, JOB_RESTART) ||
|
||||
types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) ||
|
||||
types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) ||
|
||||
types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART))
|
||||
*a = JOB_RESTART;
|
||||
else if (types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RELOAD))
|
||||
*a = JOB_RELOAD;
|
||||
else if (types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_TRY_RESTART) ||
|
||||
types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART))
|
||||
*a = JOB_TRY_RESTART;
|
||||
else
|
||||
return -EEXIST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool job_type_is_mergeable(JobType a, JobType b) {
|
||||
return job_type_merge(&a, b) >= 0;
|
||||
}
|
||||
|
||||
bool job_type_is_superset(JobType a, JobType b) {
|
||||
|
||||
/* Checks whether operation a is a "superset" of b in its
|
||||
* actions */
|
||||
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
switch (a) {
|
||||
case JOB_START:
|
||||
return b == JOB_VERIFY_ACTIVE;
|
||||
|
||||
case JOB_RELOAD:
|
||||
return
|
||||
b == JOB_VERIFY_ACTIVE;
|
||||
|
||||
case JOB_RELOAD_OR_START:
|
||||
return
|
||||
b == JOB_RELOAD ||
|
||||
b == JOB_START ||
|
||||
b == JOB_VERIFY_ACTIVE;
|
||||
|
||||
case JOB_RESTART:
|
||||
return
|
||||
b == JOB_START ||
|
||||
b == JOB_VERIFY_ACTIVE ||
|
||||
b == JOB_RELOAD ||
|
||||
b == JOB_RELOAD_OR_START ||
|
||||
b == JOB_TRY_RESTART;
|
||||
|
||||
case JOB_TRY_RESTART:
|
||||
return
|
||||
b == JOB_VERIFY_ACTIVE ||
|
||||
b == JOB_RELOAD;
|
||||
default:
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool job_type_is_conflicting(JobType a, JobType b) {
|
||||
assert(a >= 0 && a < _JOB_TYPE_MAX);
|
||||
assert(b >= 0 && b < _JOB_TYPE_MAX);
|
||||
|
||||
return (a == JOB_STOP) != (b == JOB_STOP);
|
||||
}
|
||||
|
||||
bool job_type_is_redundant(JobType a, UnitActiveState b) {
|
||||
switch (a) {
|
||||
|
||||
case JOB_START:
|
||||
return
|
||||
b == UNIT_ACTIVE ||
|
||||
b == UNIT_ACTIVE_RELOADING;
|
||||
|
||||
case JOB_STOP:
|
||||
return
|
||||
b == UNIT_INACTIVE;
|
||||
|
||||
case JOB_VERIFY_ACTIVE:
|
||||
return
|
||||
b == UNIT_ACTIVE ||
|
||||
b == UNIT_ACTIVE_RELOADING;
|
||||
|
||||
case JOB_RELOAD:
|
||||
return
|
||||
b == UNIT_ACTIVE_RELOADING;
|
||||
|
||||
case JOB_RELOAD_OR_START:
|
||||
return
|
||||
b == UNIT_ACTIVATING ||
|
||||
b == UNIT_ACTIVE_RELOADING;
|
||||
|
||||
case JOB_RESTART:
|
||||
return
|
||||
b == UNIT_ACTIVATING;
|
||||
|
||||
case JOB_TRY_RESTART:
|
||||
return
|
||||
b == UNIT_ACTIVATING;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid job type");
|
||||
}
|
||||
}
|
||||
|
||||
bool job_is_runnable(Job *j) {
|
||||
Iterator i;
|
||||
Unit *other;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
|
||||
/* Checks whether there is any job running for the units this
|
||||
* job needs to be running after (in the case of a 'positive'
|
||||
* job type) or before (in the case of a 'negative' job type
|
||||
* . */
|
||||
|
||||
if (j->type == JOB_START ||
|
||||
j->type == JOB_VERIFY_ACTIVE ||
|
||||
j->type == JOB_RELOAD ||
|
||||
j->type == JOB_RELOAD_OR_START) {
|
||||
|
||||
/* Immediate result is that the job is or might be
|
||||
* started. In this case lets wait for the
|
||||
* dependencies, regardless whether they are
|
||||
* starting or stopping something. */
|
||||
|
||||
SET_FOREACH(other, j->unit->meta.dependencies[UNIT_AFTER], i)
|
||||
if (other->meta.job)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Also, if something else is being stopped and we should
|
||||
* change state after it, then lets wait. */
|
||||
|
||||
SET_FOREACH(other, j->unit->meta.dependencies[UNIT_BEFORE], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.job->type == JOB_STOP ||
|
||||
other->meta.job->type == JOB_RESTART ||
|
||||
other->meta.job->type == JOB_TRY_RESTART))
|
||||
return false;
|
||||
|
||||
/* This means that for a service a and a service b where b
|
||||
* shall be started after a:
|
||||
*
|
||||
* start a + start b → 1st step start a, 2nd step start b
|
||||
* start a + stop b → 1st step stop b, 2nd step start a
|
||||
* stop a + start b → 1st step stop a, 2nd step start b
|
||||
* stop a + stop b → 1st step stop b, 2nd step stop a
|
||||
*
|
||||
* This has the side effect that restarts are properly
|
||||
* synchronized too. */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int job_run_and_invalidate(Job *j) {
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
|
||||
if (j->in_run_queue) {
|
||||
LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
|
||||
j->in_run_queue = false;
|
||||
}
|
||||
|
||||
if (j->state != JOB_WAITING)
|
||||
return 0;
|
||||
|
||||
if (!job_is_runnable(j))
|
||||
return -EAGAIN;
|
||||
|
||||
j->state = JOB_RUNNING;
|
||||
job_add_to_dbus_queue(j);
|
||||
|
||||
switch (j->type) {
|
||||
|
||||
case JOB_START:
|
||||
r = unit_start(j->unit);
|
||||
if (r == -EBADR)
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case JOB_VERIFY_ACTIVE: {
|
||||
UnitActiveState t = unit_active_state(j->unit);
|
||||
if (UNIT_IS_ACTIVE_OR_RELOADING(t))
|
||||
r = -EALREADY;
|
||||
else if (t == UNIT_ACTIVATING)
|
||||
r = -EAGAIN;
|
||||
else
|
||||
r = -ENOEXEC;
|
||||
break;
|
||||
}
|
||||
|
||||
case JOB_STOP:
|
||||
r = unit_stop(j->unit);
|
||||
break;
|
||||
|
||||
case JOB_RELOAD:
|
||||
r = unit_reload(j->unit);
|
||||
break;
|
||||
|
||||
case JOB_RELOAD_OR_START:
|
||||
if (unit_active_state(j->unit) == UNIT_ACTIVE)
|
||||
r = unit_reload(j->unit);
|
||||
else
|
||||
r = unit_start(j->unit);
|
||||
break;
|
||||
|
||||
case JOB_RESTART: {
|
||||
UnitActiveState t = unit_active_state(j->unit);
|
||||
if (t == UNIT_INACTIVE || t == UNIT_ACTIVATING) {
|
||||
j->type = JOB_START;
|
||||
r = unit_start(j->unit);
|
||||
} else
|
||||
r = unit_stop(j->unit);
|
||||
break;
|
||||
}
|
||||
|
||||
case JOB_TRY_RESTART: {
|
||||
UnitActiveState t = unit_active_state(j->unit);
|
||||
if (t == UNIT_INACTIVE || t == UNIT_DEACTIVATING)
|
||||
r = -ENOEXEC;
|
||||
else if (t == UNIT_ACTIVATING) {
|
||||
j->type = JOB_START;
|
||||
r = unit_start(j->unit);
|
||||
} else
|
||||
r = unit_stop(j->unit);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown job type");
|
||||
}
|
||||
|
||||
if (r == -EALREADY)
|
||||
r = job_finish_and_invalidate(j, true);
|
||||
else if (r == -EAGAIN) {
|
||||
j->state = JOB_WAITING;
|
||||
return -EAGAIN;
|
||||
} else if (r < 0)
|
||||
r = job_finish_and_invalidate(j, false);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int job_finish_and_invalidate(Job *j, bool success) {
|
||||
Unit *u;
|
||||
Unit *other;
|
||||
JobType t;
|
||||
Iterator i;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
|
||||
log_debug("Job %s/%s finished, success=%s", j->unit->meta.id, job_type_to_string(j->type), yes_no(success));
|
||||
job_add_to_dbus_queue(j);
|
||||
|
||||
/* Patch restart jobs so that they become normal start jobs */
|
||||
if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) {
|
||||
|
||||
log_debug("Converting job %s/%s -> %s/%s",
|
||||
j->unit->meta.id, job_type_to_string(j->type),
|
||||
j->unit->meta.id, job_type_to_string(JOB_START));
|
||||
|
||||
j->state = JOB_RUNNING;
|
||||
j->type = JOB_START;
|
||||
|
||||
job_add_to_run_queue(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u = j->unit;
|
||||
t = j->type;
|
||||
job_free(j);
|
||||
|
||||
/* Fail depending jobs on failure */
|
||||
if (!success) {
|
||||
|
||||
if (t == JOB_START ||
|
||||
t == JOB_VERIFY_ACTIVE ||
|
||||
t == JOB_RELOAD_OR_START) {
|
||||
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.job->type == JOB_START ||
|
||||
other->meta.job->type == JOB_VERIFY_ACTIVE ||
|
||||
other->meta.job->type == JOB_RELOAD_OR_START))
|
||||
job_finish_and_invalidate(other->meta.job, false);
|
||||
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
|
||||
if (other->meta.job &&
|
||||
!other->meta.job->override &&
|
||||
(other->meta.job->type == JOB_START ||
|
||||
other->meta.job->type == JOB_VERIFY_ACTIVE ||
|
||||
other->meta.job->type == JOB_RELOAD_OR_START))
|
||||
job_finish_and_invalidate(other->meta.job, false);
|
||||
|
||||
} else if (t == JOB_STOP) {
|
||||
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i)
|
||||
if (other->meta.job &&
|
||||
(other->meta.job->type == JOB_START ||
|
||||
other->meta.job->type == JOB_VERIFY_ACTIVE ||
|
||||
other->meta.job->type == JOB_RELOAD_OR_START))
|
||||
job_finish_and_invalidate(other->meta.job, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to start the next jobs that can be started */
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i)
|
||||
if (other->meta.job)
|
||||
job_add_to_run_queue(other->meta.job);
|
||||
SET_FOREACH(other, u->meta.dependencies[UNIT_BEFORE], i)
|
||||
if (other->meta.job)
|
||||
job_add_to_run_queue(other->meta.job);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void job_add_to_run_queue(Job *j) {
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
|
||||
if (j->in_run_queue)
|
||||
return;
|
||||
|
||||
LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
|
||||
j->in_run_queue = true;
|
||||
}
|
||||
|
||||
void job_add_to_dbus_queue(Job *j) {
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
|
||||
if (j->in_dbus_queue)
|
||||
return;
|
||||
|
||||
if (set_isempty(j->manager->subscribed)) {
|
||||
j->sent_dbus_new_signal = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
|
||||
j->in_dbus_queue = true;
|
||||
}
|
||||
|
||||
char *job_dbus_path(Job *j) {
|
||||
char *p;
|
||||
|
||||
assert(j);
|
||||
|
||||
if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0)
|
||||
return NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char* const job_state_table[_JOB_STATE_MAX] = {
|
||||
[JOB_WAITING] = "waiting",
|
||||
[JOB_RUNNING] = "running"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
|
||||
|
||||
static const char* const job_type_table[_JOB_TYPE_MAX] = {
|
||||
[JOB_START] = "start",
|
||||
[JOB_VERIFY_ACTIVE] = "verify-active",
|
||||
[JOB_STOP] = "stop",
|
||||
[JOB_RELOAD] = "reload",
|
||||
[JOB_RELOAD_OR_START] = "reload-or-start",
|
||||
[JOB_RESTART] = "restart",
|
||||
[JOB_TRY_RESTART] = "try-restart",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
|
||||
|
||||
static const char* const job_mode_table[_JOB_MODE_MAX] = {
|
||||
[JOB_FAIL] = "fail",
|
||||
[JOB_REPLACE] = "replace",
|
||||
[JOB_ISOLATE] = "isolate"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
|
||||
@@ -0,0 +1,150 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foojobhfoo
|
||||
#define foojobhfoo
|
||||
|
||||
/***
|
||||
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 <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef struct Job Job;
|
||||
typedef struct JobDependency JobDependency;
|
||||
typedef enum JobType JobType;
|
||||
typedef enum JobState JobState;
|
||||
typedef enum JobMode JobMode;
|
||||
|
||||
#include "manager.h"
|
||||
#include "unit.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
|
||||
enum JobType {
|
||||
JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */
|
||||
JOB_VERIFY_ACTIVE,
|
||||
|
||||
JOB_STOP,
|
||||
|
||||
JOB_RELOAD, /* if running reload */
|
||||
JOB_RELOAD_OR_START, /* if running reload, if not running start */
|
||||
|
||||
/* Note that restarts are first treated like JOB_STOP, but
|
||||
* then instead of finishing are patched to become
|
||||
* JOB_START. */
|
||||
JOB_RESTART, /* if running stop, then start unconditionally */
|
||||
JOB_TRY_RESTART, /* if running stop and then start */
|
||||
|
||||
_JOB_TYPE_MAX,
|
||||
_JOB_TYPE_INVALID = -1
|
||||
};
|
||||
|
||||
enum JobState {
|
||||
JOB_WAITING,
|
||||
JOB_RUNNING,
|
||||
_JOB_STATE_MAX,
|
||||
_JOB_STATE_INVALID = -1
|
||||
};
|
||||
|
||||
enum JobMode {
|
||||
JOB_FAIL,
|
||||
JOB_REPLACE,
|
||||
JOB_ISOLATE,
|
||||
_JOB_MODE_MAX,
|
||||
_JOB_MODE_INVALID = -1
|
||||
};
|
||||
|
||||
struct JobDependency {
|
||||
/* Encodes that the 'subject' job needs the 'object' job in
|
||||
* some way. This structure is used only while building a transaction. */
|
||||
Job *subject;
|
||||
Job *object;
|
||||
|
||||
LIST_FIELDS(JobDependency, subject);
|
||||
LIST_FIELDS(JobDependency, object);
|
||||
|
||||
bool matters;
|
||||
};
|
||||
|
||||
struct Job {
|
||||
Manager *manager;
|
||||
Unit *unit;
|
||||
|
||||
LIST_FIELDS(Job, transaction);
|
||||
LIST_FIELDS(Job, run_queue);
|
||||
LIST_FIELDS(Job, dbus_queue);
|
||||
|
||||
LIST_HEAD(JobDependency, subject_list);
|
||||
LIST_HEAD(JobDependency, object_list);
|
||||
|
||||
/* Used for graph algs as a "I have been here" marker */
|
||||
Job* marker;
|
||||
unsigned generation;
|
||||
|
||||
uint32_t id;
|
||||
|
||||
JobType type;
|
||||
JobState state;
|
||||
|
||||
bool installed:1;
|
||||
bool in_run_queue:1;
|
||||
bool matters_to_anchor:1;
|
||||
bool override:1;
|
||||
bool in_dbus_queue:1;
|
||||
bool sent_dbus_new_signal:1;
|
||||
};
|
||||
|
||||
Job* job_new(Manager *m, JobType type, Unit *unit);
|
||||
void job_free(Job *job);
|
||||
void job_dump(Job *j, FILE*f, const char *prefix);
|
||||
|
||||
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters);
|
||||
void job_dependency_free(JobDependency *l);
|
||||
void job_dependency_delete(Job *subject, Job *object, bool *matters);
|
||||
|
||||
bool job_is_anchor(Job *j);
|
||||
|
||||
int job_merge(Job *j, Job *other);
|
||||
|
||||
int job_type_merge(JobType *a, JobType b);
|
||||
bool job_type_is_mergeable(JobType a, JobType b);
|
||||
bool job_type_is_superset(JobType a, JobType b);
|
||||
bool job_type_is_conflicting(JobType a, JobType b);
|
||||
bool job_type_is_redundant(JobType a, UnitActiveState b);
|
||||
|
||||
bool job_is_runnable(Job *j);
|
||||
|
||||
void job_add_to_run_queue(Job *j);
|
||||
void job_add_to_dbus_queue(Job *j);
|
||||
|
||||
int job_run_and_invalidate(Job *j);
|
||||
int job_finish_and_invalidate(Job *j, bool success);
|
||||
|
||||
const char* job_type_to_string(JobType t);
|
||||
JobType job_type_from_string(const char *s);
|
||||
|
||||
const char* job_state_to_string(JobState t);
|
||||
JobState job_state_from_string(const char *s);
|
||||
|
||||
const char* job_mode_to_string(JobMode t);
|
||||
JobMode job_mode_from_string(const char *s);
|
||||
|
||||
char *job_dbus_path(Job *j);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2008 Red Hat, Inc. All rights reserved.
|
||||
* Copyright 2008 Ian Kent <raven@themaw.net>
|
||||
*
|
||||
* This file is part of the Linux kernel and is made available under
|
||||
* the terms of the GNU General Public License, version 2, or at your
|
||||
* option, any later version, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AUTO_DEV_IOCTL_H
|
||||
#define _LINUX_AUTO_DEV_IOCTL_H
|
||||
|
||||
#include <linux/auto_fs.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/string.h>
|
||||
#else
|
||||
#include <string.h>
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#define AUTOFS_DEVICE_NAME "autofs"
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1
|
||||
#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0
|
||||
|
||||
#define AUTOFS_DEVID_LEN 16
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl)
|
||||
|
||||
/*
|
||||
* An ioctl interface for autofs mount point control.
|
||||
*/
|
||||
|
||||
struct args_protover {
|
||||
__u32 version;
|
||||
};
|
||||
|
||||
struct args_protosubver {
|
||||
__u32 sub_version;
|
||||
};
|
||||
|
||||
struct args_openmount {
|
||||
__u32 devid;
|
||||
};
|
||||
|
||||
struct args_ready {
|
||||
__u32 token;
|
||||
};
|
||||
|
||||
struct args_fail {
|
||||
__u32 token;
|
||||
__s32 status;
|
||||
};
|
||||
|
||||
struct args_setpipefd {
|
||||
__s32 pipefd;
|
||||
};
|
||||
|
||||
struct args_timeout {
|
||||
__u64 timeout;
|
||||
};
|
||||
|
||||
struct args_requester {
|
||||
__u32 uid;
|
||||
__u32 gid;
|
||||
};
|
||||
|
||||
struct args_expire {
|
||||
__u32 how;
|
||||
};
|
||||
|
||||
struct args_askumount {
|
||||
__u32 may_umount;
|
||||
};
|
||||
|
||||
struct args_ismountpoint {
|
||||
union {
|
||||
struct args_in {
|
||||
__u32 type;
|
||||
} in;
|
||||
struct args_out {
|
||||
__u32 devid;
|
||||
__u32 magic;
|
||||
} out;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* All the ioctls use this structure.
|
||||
* When sending a path size must account for the total length
|
||||
* of the chunk of memory otherwise is is the size of the
|
||||
* structure.
|
||||
*/
|
||||
|
||||
struct autofs_dev_ioctl {
|
||||
__u32 ver_major;
|
||||
__u32 ver_minor;
|
||||
__u32 size; /* total size of data passed in
|
||||
* including this struct */
|
||||
__s32 ioctlfd; /* automount command fd */
|
||||
|
||||
/* Command parameters */
|
||||
|
||||
union {
|
||||
struct args_protover protover;
|
||||
struct args_protosubver protosubver;
|
||||
struct args_openmount openmount;
|
||||
struct args_ready ready;
|
||||
struct args_fail fail;
|
||||
struct args_setpipefd setpipefd;
|
||||
struct args_timeout timeout;
|
||||
struct args_requester requester;
|
||||
struct args_expire expire;
|
||||
struct args_askumount askumount;
|
||||
struct args_ismountpoint ismountpoint;
|
||||
};
|
||||
|
||||
char path[0];
|
||||
};
|
||||
|
||||
static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in)
|
||||
{
|
||||
memset(in, 0, sizeof(struct autofs_dev_ioctl));
|
||||
in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
|
||||
in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
|
||||
in->size = sizeof(struct autofs_dev_ioctl);
|
||||
in->ioctlfd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If you change this make sure you make the corresponding change
|
||||
* to autofs-dev-ioctl.c:lookup_ioctl()
|
||||
*/
|
||||
enum {
|
||||
/* Get various version info */
|
||||
AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71,
|
||||
AUTOFS_DEV_IOCTL_PROTOVER_CMD,
|
||||
AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD,
|
||||
|
||||
/* Open mount ioctl fd */
|
||||
AUTOFS_DEV_IOCTL_OPENMOUNT_CMD,
|
||||
|
||||
/* Close mount ioctl fd */
|
||||
AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD,
|
||||
|
||||
/* Mount/expire status returns */
|
||||
AUTOFS_DEV_IOCTL_READY_CMD,
|
||||
AUTOFS_DEV_IOCTL_FAIL_CMD,
|
||||
|
||||
/* Activate/deactivate autofs mount */
|
||||
AUTOFS_DEV_IOCTL_SETPIPEFD_CMD,
|
||||
AUTOFS_DEV_IOCTL_CATATONIC_CMD,
|
||||
|
||||
/* Expiry timeout */
|
||||
AUTOFS_DEV_IOCTL_TIMEOUT_CMD,
|
||||
|
||||
/* Get mount last requesting uid and gid */
|
||||
AUTOFS_DEV_IOCTL_REQUESTER_CMD,
|
||||
|
||||
/* Check for eligible expire candidates */
|
||||
AUTOFS_DEV_IOCTL_EXPIRE_CMD,
|
||||
|
||||
/* Request busy status */
|
||||
AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD,
|
||||
|
||||
/* Check if path is a mountpoint */
|
||||
AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD,
|
||||
};
|
||||
|
||||
#define AUTOFS_IOCTL 0x93
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_VERSION \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_PROTOVER \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_PROTOSUBVER \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_OPENMOUNT \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_READY \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_FAIL \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_SETPIPEFD \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_CATATONIC \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_TIMEOUT \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_REQUESTER \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_EXPIRE \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_ASKUMOUNT \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \
|
||||
_IOWR(AUTOFS_IOCTL, \
|
||||
AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl)
|
||||
|
||||
#endif /* _LINUX_AUTO_DEV_IOCTL_H */
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foolisthfoo
|
||||
#define foolisthfoo
|
||||
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
/* The head of the linked list. Use this in the structure that shall
|
||||
* contain the head of the linked list */
|
||||
#define LIST_HEAD(t,name) \
|
||||
t *name
|
||||
|
||||
/* The pointers in the linked list's items. Use this in the item structure */
|
||||
#define LIST_FIELDS(t,name) \
|
||||
t *name##_next, *name##_prev
|
||||
|
||||
/* Initialize the list's head */
|
||||
#define LIST_HEAD_INIT(t,head) \
|
||||
do { \
|
||||
(head) = NULL; } \
|
||||
while(false)
|
||||
|
||||
/* Initialize a list item */
|
||||
#define LIST_INIT(t,name,item) \
|
||||
do { \
|
||||
t *_item = (item); \
|
||||
assert(_item); \
|
||||
_item->name##_prev = _item->name##_next = NULL; \
|
||||
} while(false)
|
||||
|
||||
/* Prepend an item to the list */
|
||||
#define LIST_PREPEND(t,name,head,item) \
|
||||
do { \
|
||||
t **_head = &(head), *_item = (item); \
|
||||
assert(_item); \
|
||||
if ((_item->name##_next = *_head)) \
|
||||
_item->name##_next->name##_prev = _item; \
|
||||
_item->name##_prev = NULL; \
|
||||
*_head = _item; \
|
||||
} while(false)
|
||||
|
||||
/* Remove an item from the list */
|
||||
#define LIST_REMOVE(t,name,head,item) \
|
||||
do { \
|
||||
t **_head = &(head), *_item = (item); \
|
||||
assert(_item); \
|
||||
if (_item->name##_next) \
|
||||
_item->name##_next->name##_prev = _item->name##_prev; \
|
||||
if (_item->name##_prev) \
|
||||
_item->name##_prev->name##_next = _item->name##_next; \
|
||||
else { \
|
||||
assert(*_head == _item); \
|
||||
*_head = _item->name##_next; \
|
||||
} \
|
||||
_item->name##_next = _item->name##_prev = NULL; \
|
||||
} while(false)
|
||||
|
||||
/* Find the head of the list */
|
||||
#define LIST_FIND_HEAD(t,name,item,head) \
|
||||
do { \
|
||||
t *_item = (item); \
|
||||
assert(_item); \
|
||||
while ((_item->name##_prev) \
|
||||
_item = _item->name##_prev; \
|
||||
(head) = _item; \
|
||||
} while (false)
|
||||
|
||||
/* Find the head of the list */
|
||||
#define LIST_FIND_TAIL(t,name,item,tail) \
|
||||
do { \
|
||||
t *_item = (item); \
|
||||
assert(_item); \
|
||||
while (_item->name##_next) \
|
||||
_item = _item->name##_next; \
|
||||
(tail) = _item; \
|
||||
} while (false)
|
||||
|
||||
/* Insert an item after another one (a = where, b = what) */
|
||||
#define LIST_INSERT_AFTER(t,name,head,a,b) \
|
||||
do { \
|
||||
t **_head = &(head), *_a = (a), *_b = (b); \
|
||||
assert(_b); \
|
||||
if (!_a) { \
|
||||
if ((_b->name##_next = *_head)) \
|
||||
_b->name##_next->name##_prev = _b; \
|
||||
_b->name##_prev = NULL; \
|
||||
*_head = _b; \
|
||||
} else { \
|
||||
if ((_b->name##_next = _a->name##_next)) \
|
||||
_b->name##_next->name##_prev = _b; \
|
||||
_b->name##_prev = _a; \
|
||||
_a->name##_next = _b; \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
#define LIST_FOREACH(name,i,head) \
|
||||
for ((i) = (head); (i); (i) = (i)->name##_next)
|
||||
|
||||
#define LIST_FOREACH_SAFE(name,i,n,head) \
|
||||
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*-*- 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 <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "unit.h"
|
||||
#include "load-dropin.h"
|
||||
#include "log.h"
|
||||
#include "strv.h"
|
||||
#include "unit-name.h"
|
||||
|
||||
static int iterate_dir(Unit *u, const char *path) {
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
int r;
|
||||
|
||||
if (!(d = opendir(path))) {
|
||||
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
char *f;
|
||||
|
||||
if (ignore_file(de->d_name))
|
||||
continue;
|
||||
|
||||
if (asprintf(&f, "%s/%s", path, de->d_name) < 0) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = unit_add_dependency_by_name(u, UNIT_WANTS, de->d_name, f, true);
|
||||
free(f);
|
||||
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
closedir(d);
|
||||
return r;
|
||||
}
|
||||
|
||||
int unit_load_dropin(Unit *u) {
|
||||
Iterator i;
|
||||
int r;
|
||||
char *t;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Load dependencies from supplementary drop-in directories */
|
||||
|
||||
SET_FOREACH(t, u->meta.names, i) {
|
||||
char *path;
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, u->meta.manager->unit_path) {
|
||||
|
||||
if (asprintf(&path, "%s/%s.wants", *p, t) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = iterate_dir(u, path);
|
||||
free(path);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u->meta.instance) {
|
||||
char *template;
|
||||
/* Also try the template dir */
|
||||
|
||||
if (!(template = unit_name_template(t)))
|
||||
return -ENOMEM;
|
||||
|
||||
r = asprintf(&path, "%s/%s.wants", *p, template);
|
||||
free(template);
|
||||
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = iterate_dir(u, path);
|
||||
free(path);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooloaddropinhfoo
|
||||
#define fooloaddropinhfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
/* Read service data supplementary drop-in directories */
|
||||
|
||||
int unit_load_dropin(Unit *u);
|
||||
|
||||
#endif
|
||||
+1483
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooloadfragmenthfoo
|
||||
#define fooloadfragmenthfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
/* Read service data from .desktop file style configuration fragments */
|
||||
|
||||
int unit_load_fragment(Unit *u);
|
||||
|
||||
void unit_dump_config_items(FILE *f);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,439 @@
|
||||
/*-*- 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 <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
|
||||
#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
|
||||
#define LOG_BUFFER_MAX 1024
|
||||
|
||||
static LogTarget log_target = LOG_TARGET_CONSOLE;
|
||||
static int log_max_level = LOG_DEBUG;
|
||||
|
||||
static int console_fd = STDERR_FILENO;
|
||||
static int syslog_fd = -1;
|
||||
static int kmsg_fd = -1;
|
||||
|
||||
/* Akin to glibc's __abort_msg; which is private and we hance cannot
|
||||
* use here. */
|
||||
static char *log_abort_msg = NULL;
|
||||
|
||||
void log_close_console(void) {
|
||||
|
||||
if (console_fd < 0)
|
||||
return;
|
||||
|
||||
if (getpid() == 1 || console_fd != STDERR_FILENO) {
|
||||
close_nointr_nofail(console_fd);
|
||||
console_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int log_open_console(void) {
|
||||
|
||||
if (console_fd >= 0)
|
||||
return 0;
|
||||
|
||||
if (getpid() == 1) {
|
||||
|
||||
if ((console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
|
||||
log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd));
|
||||
return console_fd;
|
||||
}
|
||||
|
||||
log_info("Succesfully opened /dev/console for logging.");
|
||||
} else
|
||||
console_fd = STDERR_FILENO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_close_kmsg(void) {
|
||||
|
||||
if (kmsg_fd < 0)
|
||||
return;
|
||||
|
||||
close_nointr_nofail(kmsg_fd);
|
||||
kmsg_fd = -1;
|
||||
}
|
||||
|
||||
static int log_open_kmsg(void) {
|
||||
|
||||
if (kmsg_fd >= 0)
|
||||
return 0;
|
||||
|
||||
if ((kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
|
||||
log_info("Failed to open /dev/kmsg for logging: %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
log_info("Succesfully opened /dev/kmsg for logging.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_close_syslog(void) {
|
||||
|
||||
if (syslog_fd < 0)
|
||||
return;
|
||||
|
||||
close_nointr_nofail(syslog_fd);
|
||||
syslog_fd = -1;
|
||||
}
|
||||
|
||||
static int log_open_syslog(void) {
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_un un;
|
||||
} sa;
|
||||
struct timeval tv;
|
||||
int r;
|
||||
|
||||
if (syslog_fd >= 0)
|
||||
return 0;
|
||||
|
||||
if ((syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Make sure we don't block for more than 5s when talking to
|
||||
* syslog */
|
||||
timeval_store(&tv, SYSLOG_TIMEOUT_USEC);
|
||||
if (setsockopt(syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
zero(sa);
|
||||
sa.un.sun_family = AF_UNIX;
|
||||
strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
|
||||
|
||||
if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_info("Succesfully opened syslog for logging.");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
log_close_syslog();
|
||||
log_info("Failed to open syslog for logging: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
int log_open(void) {
|
||||
int r;
|
||||
|
||||
/* If we don't use the console we close it here, to not get
|
||||
* killed by SAK. If we don't use syslog we close it here so
|
||||
* that we are not confused by somebody deleting the socket in
|
||||
* the fs. If we don't use /dev/kmsg we still keep it open,
|
||||
* because there is no reason to close it. */
|
||||
|
||||
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_SYSLOG)
|
||||
if ((r = log_open_syslog()) >= 0) {
|
||||
log_close_console();
|
||||
return r;
|
||||
}
|
||||
|
||||
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_KMSG)
|
||||
if ((r = log_open_kmsg()) >= 0) {
|
||||
log_close_syslog();
|
||||
log_close_console();
|
||||
return r;
|
||||
}
|
||||
|
||||
log_close_syslog();
|
||||
return log_open_console();
|
||||
}
|
||||
|
||||
void log_set_target(LogTarget target) {
|
||||
assert(target >= 0);
|
||||
assert(target < _LOG_TARGET_MAX);
|
||||
|
||||
log_target = target;
|
||||
}
|
||||
|
||||
void log_set_max_level(int level) {
|
||||
assert((level & LOG_PRIMASK) == level);
|
||||
|
||||
log_max_level = level;
|
||||
}
|
||||
|
||||
static int write_to_console(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
char location[64];
|
||||
struct iovec iovec[5];
|
||||
unsigned n = 0;
|
||||
bool highlight;
|
||||
|
||||
if (console_fd < 0)
|
||||
return 0;
|
||||
|
||||
snprintf(location, sizeof(location), "(%s:%u) ", file, line);
|
||||
char_array_0(location);
|
||||
|
||||
highlight = LOG_PRI(level) <= LOG_ERR;
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[n++], location);
|
||||
if (highlight)
|
||||
IOVEC_SET_STRING(iovec[n++], "\x1B[1;31m");
|
||||
IOVEC_SET_STRING(iovec[n++], buffer);
|
||||
if (highlight)
|
||||
IOVEC_SET_STRING(iovec[n++], "\x1B[0m");
|
||||
IOVEC_SET_STRING(iovec[n++], "\n");
|
||||
|
||||
if (writev(console_fd, iovec, n) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_to_syslog(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
char header_priority[16], header_time[64], header_pid[16];
|
||||
struct iovec iovec[5];
|
||||
struct msghdr msghdr;
|
||||
time_t t;
|
||||
struct tm *tm;
|
||||
|
||||
if (syslog_fd < 0)
|
||||
return 0;
|
||||
|
||||
snprintf(header_priority, sizeof(header_priority), "<%i>", LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(level)));
|
||||
char_array_0(header_priority);
|
||||
|
||||
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
|
||||
if (!(tm = localtime(&t)))
|
||||
return -EINVAL;
|
||||
|
||||
if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) getpid());
|
||||
char_array_0(header_pid);
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
IOVEC_SET_STRING(iovec[1], header_time);
|
||||
IOVEC_SET_STRING(iovec[2], __progname);
|
||||
IOVEC_SET_STRING(iovec[3], header_pid);
|
||||
IOVEC_SET_STRING(iovec[4], buffer);
|
||||
|
||||
zero(msghdr);
|
||||
msghdr.msg_iov = iovec;
|
||||
msghdr.msg_iovlen = ELEMENTSOF(iovec);
|
||||
|
||||
if (sendmsg(syslog_fd, &msghdr, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_to_kmsg(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
char header_priority[16], header_pid[16];
|
||||
struct iovec iovec[5];
|
||||
|
||||
if (kmsg_fd < 0)
|
||||
return 0;
|
||||
|
||||
snprintf(header_priority, sizeof(header_priority), "<%i>", LOG_PRI(level));
|
||||
char_array_0(header_priority);
|
||||
|
||||
snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) getpid());
|
||||
char_array_0(header_pid);
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
IOVEC_SET_STRING(iovec[1], __progname);
|
||||
IOVEC_SET_STRING(iovec[2], header_pid);
|
||||
IOVEC_SET_STRING(iovec[3], buffer);
|
||||
IOVEC_SET_STRING(iovec[4], "\n");
|
||||
|
||||
if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int log_dispatch(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
int r;
|
||||
|
||||
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_SYSLOG) {
|
||||
|
||||
if ((r = write_to_syslog(level, file, line, func, buffer)) < 0) {
|
||||
log_close_syslog();
|
||||
log_open_kmsg();
|
||||
} else if (r > 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_KMSG) {
|
||||
|
||||
if ((r = write_to_kmsg(level, file, line, func, buffer)) < 0) {
|
||||
log_close_kmsg();
|
||||
log_open_console();
|
||||
} else if (r > 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return write_to_console(level, file, line, func, buffer);
|
||||
}
|
||||
|
||||
int log_meta(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *format, ...) {
|
||||
|
||||
char buffer[LOG_BUFFER_MAX];
|
||||
int saved_errno, r;
|
||||
va_list ap;
|
||||
|
||||
if (_likely(LOG_PRI(level) > log_max_level))
|
||||
return 0;
|
||||
|
||||
saved_errno = errno;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(buffer, sizeof(buffer), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
char_array_0(buffer);
|
||||
|
||||
r = log_dispatch(level, file, line, func, buffer);
|
||||
errno = saved_errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void log_assert(
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *format, ...) {
|
||||
|
||||
static char buffer[LOG_BUFFER_MAX];
|
||||
int saved_errno = errno;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(buffer, sizeof(buffer), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
char_array_0(buffer);
|
||||
log_abort_msg = buffer;
|
||||
|
||||
log_dispatch(LOG_CRIT, file, line, func, buffer);
|
||||
abort();
|
||||
|
||||
/* If the user chose to ignore this SIGABRT, we are happy to go on, as if nothing happened. */
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
int log_set_target_from_string(const char *e) {
|
||||
LogTarget t;
|
||||
|
||||
if ((t = log_target_from_string(e)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
log_set_target(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_set_max_level_from_string(const char *e) {
|
||||
int t;
|
||||
|
||||
if ((t = log_level_from_string(e)) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
log_set_max_level(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_parse_environment(void) {
|
||||
const char *e;
|
||||
|
||||
if ((e = getenv("SYSTEMD_LOG_TARGET")))
|
||||
if (log_set_target_from_string(e) < 0)
|
||||
log_warning("Failed to parse log target %s. Ignoring.", e);
|
||||
|
||||
if ((e = getenv("SYSTEMD_LOG_LEVEL")))
|
||||
if (log_set_max_level_from_string(e) < 0)
|
||||
log_warning("Failed to parse log level %s. Ignoring.", e);
|
||||
}
|
||||
|
||||
LogTarget log_get_target(void) {
|
||||
return log_target;
|
||||
}
|
||||
|
||||
int log_get_max_level(void) {
|
||||
return log_max_level;
|
||||
}
|
||||
|
||||
static const char *const log_target_table[] = {
|
||||
[LOG_TARGET_CONSOLE] = "console",
|
||||
[LOG_TARGET_SYSLOG] = "syslog",
|
||||
[LOG_TARGET_KMSG] = "kmsg",
|
||||
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
|
||||
@@ -0,0 +1,79 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foologhfoo
|
||||
#define foologhfoo
|
||||
|
||||
/***
|
||||
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 <syslog.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* If set to SYSLOG and /dev/log can not be opened we fall back to
|
||||
* KSMG. If KMSG fails, we fall back to CONSOLE */
|
||||
typedef enum LogTarget{
|
||||
LOG_TARGET_CONSOLE,
|
||||
LOG_TARGET_KMSG,
|
||||
LOG_TARGET_SYSLOG,
|
||||
LOG_TARGET_SYSLOG_OR_KMSG,
|
||||
_LOG_TARGET_MAX,
|
||||
_LOG_TARGET_INVALID = -1
|
||||
} LogTarget;
|
||||
|
||||
void log_set_target(LogTarget target);
|
||||
void log_set_max_level(int level);
|
||||
|
||||
int log_set_target_from_string(const char *e);
|
||||
int log_set_max_level_from_string(const char *e);
|
||||
|
||||
LogTarget log_get_target(void);
|
||||
int log_get_max_level(void);
|
||||
|
||||
int log_open(void);
|
||||
|
||||
void log_close_syslog(void);
|
||||
void log_close_kmsg(void);
|
||||
void log_close_console(void);
|
||||
|
||||
void log_parse_environment(void);
|
||||
|
||||
int log_meta(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *format, ...) _printf_attr(5,6);
|
||||
|
||||
_noreturn void log_assert(
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *format, ...) _printf_attr(4,5);
|
||||
|
||||
#define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
#define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
#define log_notice(...) log_meta(LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
#define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
#define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
|
||||
const char *log_target_to_string(LogTarget target);
|
||||
LogTarget log_target_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
+565
@@ -0,0 +1,565 @@
|
||||
/*-*- 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 <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/un.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "list.h"
|
||||
#include "sd-daemon.h"
|
||||
|
||||
#define STREAM_BUFFER 2048
|
||||
#define STREAMS_MAX 256
|
||||
#define SERVER_FD_MAX 16
|
||||
#define TIMEOUT ((int) (10*MSEC_PER_SEC))
|
||||
|
||||
typedef struct Stream Stream;
|
||||
|
||||
typedef struct Server {
|
||||
int syslog_fd;
|
||||
int kmsg_fd;
|
||||
int epoll_fd;
|
||||
|
||||
unsigned n_server_fd;
|
||||
|
||||
LIST_HEAD(Stream, streams);
|
||||
unsigned n_streams;
|
||||
} Server;
|
||||
|
||||
typedef enum StreamTarget {
|
||||
STREAM_SYSLOG,
|
||||
STREAM_KMSG
|
||||
} StreamTarget;
|
||||
|
||||
typedef enum StreamState {
|
||||
STREAM_TARGET,
|
||||
STREAM_PRIORITY,
|
||||
STREAM_PROCESS,
|
||||
STREAM_PREFIX,
|
||||
STREAM_RUNNING
|
||||
} StreamState;
|
||||
|
||||
struct Stream {
|
||||
Server *server;
|
||||
|
||||
StreamState state;
|
||||
|
||||
int fd;
|
||||
|
||||
LogTarget target;
|
||||
int priority;
|
||||
char *process;
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
|
||||
bool prefix;
|
||||
|
||||
char buffer[STREAM_BUFFER];
|
||||
size_t length;
|
||||
|
||||
LIST_FIELDS(Stream, stream);
|
||||
};
|
||||
|
||||
static int stream_log(Stream *s, char *p, usec_t timestamp) {
|
||||
|
||||
char header_priority[16], header_time[64], header_pid[16];
|
||||
struct iovec iovec[5];
|
||||
int priority;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
||||
priority = s->priority;
|
||||
|
||||
if (s->prefix &&
|
||||
p[0] == '<' &&
|
||||
p[1] >= '0' && p[1] <= '7' &&
|
||||
p[2] == '>') {
|
||||
|
||||
/* Detected priority prefix */
|
||||
priority = LOG_MAKEPRI(LOG_FAC(priority), (p[1] - '0'));
|
||||
|
||||
p += 3;
|
||||
}
|
||||
|
||||
if (*p == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The format glibc uses to talk to the syslog daemon is:
|
||||
*
|
||||
* <priority>time process[pid]: msg
|
||||
*
|
||||
* The format the kernel uses is:
|
||||
*
|
||||
* <priority>msg\n
|
||||
*
|
||||
* We extend the latter to include the process name and pid.
|
||||
*/
|
||||
|
||||
snprintf(header_priority, sizeof(header_priority), "<%i>",
|
||||
s->target == STREAM_SYSLOG ? priority : LOG_PRI(priority));
|
||||
char_array_0(header_priority);
|
||||
|
||||
if (s->target == STREAM_SYSLOG) {
|
||||
time_t t;
|
||||
struct tm *tm;
|
||||
|
||||
t = (time_t) (timestamp / USEC_PER_SEC);
|
||||
if (!(tm = localtime(&t)))
|
||||
return -EINVAL;
|
||||
|
||||
if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) s->pid);
|
||||
char_array_0(header_pid);
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
|
||||
if (s->target == STREAM_SYSLOG) {
|
||||
struct msghdr msghdr;
|
||||
|
||||
IOVEC_SET_STRING(iovec[1], header_time);
|
||||
IOVEC_SET_STRING(iovec[2], s->process);
|
||||
IOVEC_SET_STRING(iovec[3], header_pid);
|
||||
IOVEC_SET_STRING(iovec[4], p);
|
||||
|
||||
zero(msghdr);
|
||||
msghdr.msg_iov = iovec;
|
||||
msghdr.msg_iovlen = ELEMENTSOF(iovec);
|
||||
|
||||
if (sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL) < 0)
|
||||
return -errno;
|
||||
|
||||
} else if (s->target == STREAM_KMSG) {
|
||||
IOVEC_SET_STRING(iovec[1], s->process);
|
||||
IOVEC_SET_STRING(iovec[2], header_pid);
|
||||
IOVEC_SET_STRING(iovec[3], p);
|
||||
IOVEC_SET_STRING(iovec[4], (char*) "\n");
|
||||
|
||||
if (writev(s->server->kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
|
||||
return -errno;
|
||||
} else
|
||||
assert_not_reached("Unknown log target");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stream_line(Stream *s, char *p, usec_t timestamp) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
||||
p = strstrip(p);
|
||||
|
||||
switch (s->state) {
|
||||
|
||||
case STREAM_TARGET:
|
||||
if (streq(p, "syslog"))
|
||||
s->target = STREAM_SYSLOG;
|
||||
else if (streq(p, "kmsg")) {
|
||||
|
||||
if (s->server->kmsg_fd >= 0 && s->uid == 0)
|
||||
s->target = STREAM_KMSG;
|
||||
else {
|
||||
log_warning("/dev/kmsg logging not available.");
|
||||
return -EPERM;
|
||||
}
|
||||
} else {
|
||||
log_warning("Failed to parse log target line.");
|
||||
return -EBADMSG;
|
||||
}
|
||||
s->state = STREAM_PRIORITY;
|
||||
return 0;
|
||||
|
||||
case STREAM_PRIORITY:
|
||||
if ((r = safe_atoi(p, &s->priority)) < 0) {
|
||||
log_warning("Failed to parse log priority line: %s", strerror(errno));
|
||||
return r;
|
||||
}
|
||||
|
||||
if (s->priority < 0) {
|
||||
log_warning("Log priority negative: %s", strerror(errno));
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
s->state = STREAM_PROCESS;
|
||||
return 0;
|
||||
|
||||
case STREAM_PROCESS:
|
||||
if (!(s->process = strdup(p)))
|
||||
return -ENOMEM;
|
||||
|
||||
s->state = STREAM_PREFIX;
|
||||
return 0;
|
||||
|
||||
case STREAM_PREFIX:
|
||||
|
||||
if ((r = parse_boolean(p)) < 0)
|
||||
return r;
|
||||
|
||||
s->prefix = r;
|
||||
s->state = STREAM_RUNNING;
|
||||
return 0;
|
||||
|
||||
case STREAM_RUNNING:
|
||||
return stream_log(s, p, timestamp);
|
||||
}
|
||||
|
||||
assert_not_reached("Unknown stream state");
|
||||
}
|
||||
|
||||
static int stream_scan(Stream *s, usec_t timestamp) {
|
||||
char *p;
|
||||
size_t remaining;
|
||||
int r = 0;
|
||||
|
||||
assert(s);
|
||||
|
||||
p = s->buffer;
|
||||
remaining = s->length;
|
||||
for (;;) {
|
||||
char *newline;
|
||||
|
||||
if (!(newline = memchr(p, '\n', remaining)))
|
||||
break;
|
||||
|
||||
*newline = 0;
|
||||
|
||||
if ((r = stream_line(s, p, timestamp)) >= 0) {
|
||||
remaining -= newline-p+1;
|
||||
p = newline+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (p > s->buffer) {
|
||||
memmove(s->buffer, p, remaining);
|
||||
s->length = remaining;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int stream_process(Stream *s, usec_t timestamp) {
|
||||
ssize_t l;
|
||||
int r;
|
||||
assert(s);
|
||||
|
||||
if ((l = read(s->fd, s->buffer+s->length, STREAM_BUFFER-s->length)) < 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
log_warning("Failed to read from stream: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (l == 0)
|
||||
return 0;
|
||||
|
||||
s->length += l;
|
||||
r = stream_scan(s, timestamp);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void stream_free(Stream *s) {
|
||||
assert(s);
|
||||
|
||||
if (s->server) {
|
||||
assert(s->server->n_streams > 0);
|
||||
s->server->n_streams--;
|
||||
LIST_REMOVE(Stream, stream, s->server->streams, s);
|
||||
|
||||
}
|
||||
|
||||
if (s->fd >= 0) {
|
||||
if (s->server)
|
||||
epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL);
|
||||
|
||||
close_nointr_nofail(s->fd);
|
||||
}
|
||||
|
||||
free(s->process);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int stream_new(Server *s, int server_fd) {
|
||||
Stream *stream;
|
||||
int fd;
|
||||
struct ucred ucred;
|
||||
socklen_t len = sizeof(ucred);
|
||||
struct epoll_event ev;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if ((fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (s->n_streams >= STREAMS_MAX) {
|
||||
log_warning("Too many connections, refusing connection.");
|
||||
close_nointr_nofail(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(stream = new0(Stream, 1))) {
|
||||
close_nointr_nofail(fd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
stream->fd = fd;
|
||||
|
||||
if (getsockopt(stream->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (shutdown(fd, SHUT_WR) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
zero(ev);
|
||||
ev.data.ptr = stream;
|
||||
ev.events = EPOLLIN;
|
||||
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
stream->pid = ucred.pid;
|
||||
stream->uid = ucred.uid;
|
||||
|
||||
stream->server = s;
|
||||
LIST_PREPEND(Stream, stream, s->streams, stream);
|
||||
s->n_streams ++;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
stream_free(stream);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void server_done(Server *s) {
|
||||
unsigned i;
|
||||
assert(s);
|
||||
|
||||
while (s->streams)
|
||||
stream_free(s->streams);
|
||||
|
||||
for (i = 0; i < s->n_server_fd; i++)
|
||||
close_nointr_nofail(SD_LISTEN_FDS_START+i);
|
||||
|
||||
if (s->syslog_fd >= 0)
|
||||
close_nointr_nofail(s->syslog_fd);
|
||||
|
||||
if (s->epoll_fd >= 0)
|
||||
close_nointr_nofail(s->epoll_fd);
|
||||
|
||||
if (s->kmsg_fd >= 0)
|
||||
close_nointr_nofail(s->kmsg_fd);
|
||||
}
|
||||
|
||||
static int server_init(Server *s, unsigned n_sockets) {
|
||||
int r;
|
||||
unsigned i;
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_un un;
|
||||
} sa;
|
||||
|
||||
assert(s);
|
||||
assert(n_sockets > 0);
|
||||
|
||||
zero(*s);
|
||||
|
||||
s->n_server_fd = n_sockets;
|
||||
s->syslog_fd = -1;
|
||||
s->kmsg_fd = -1;
|
||||
|
||||
if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
|
||||
r = -errno;
|
||||
log_error("Failed to create epoll object: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_sockets; i++) {
|
||||
struct epoll_event ev;
|
||||
|
||||
zero(ev);
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.ptr = UINT_TO_PTR(SD_LISTEN_FDS_START+i);
|
||||
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SD_LISTEN_FDS_START+i, &ev) < 0) {
|
||||
r = -errno;
|
||||
log_error("Failed to add server fd to epoll object: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
|
||||
r = -errno;
|
||||
log_error("Failed to create log fd: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
zero(sa);
|
||||
sa.un.sun_family = AF_UNIX;
|
||||
strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
|
||||
|
||||
if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) {
|
||||
r = -errno;
|
||||
log_error("Failed to connect log socket to /dev/log: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* /dev/kmsg logging is strictly optional */
|
||||
if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0)
|
||||
log_debug("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %s", strerror(errno));
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
server_done(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int process_event(Server *s, struct epoll_event *ev) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Yes, this is a bit ugly, we assume that that valid pointers
|
||||
* are > SD_LISTEN_FDS_START+SERVER_FD_MAX. Which is certainly
|
||||
* true on Linux (and probably most other OSes, too, since the
|
||||
* first 4k usually are part of a seperate null pointer
|
||||
* dereference page. */
|
||||
|
||||
if (PTR_TO_UINT(ev->data.ptr) >= SD_LISTEN_FDS_START &&
|
||||
PTR_TO_UINT(ev->data.ptr) < SD_LISTEN_FDS_START+s->n_server_fd) {
|
||||
|
||||
if (ev->events != EPOLLIN) {
|
||||
log_info("Got invalid event from epoll. (1)");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if ((r = stream_new(s, PTR_TO_UINT(ev->data.ptr))) < 0) {
|
||||
log_info("Failed to accept new connection: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
} else {
|
||||
usec_t timestamp;
|
||||
Stream *stream = ev->data.ptr;
|
||||
|
||||
timestamp = now(CLOCK_REALTIME);
|
||||
|
||||
if (!(ev->events & EPOLLIN)) {
|
||||
log_info("Got invalid event from epoll. (2)");
|
||||
stream_free(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((r = stream_process(stream, timestamp)) <= 0) {
|
||||
|
||||
if (r < 0)
|
||||
log_info("Got error on stream: %s", strerror(-r));
|
||||
|
||||
stream_free(stream);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Server server;
|
||||
int r = 3, n;
|
||||
|
||||
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
|
||||
log_parse_environment();
|
||||
|
||||
log_info("systemd-logger running as pid %llu", (unsigned long long) getpid());
|
||||
|
||||
if ((n = sd_listen_fds(true)) < 0) {
|
||||
log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (n <= 0 || n > SERVER_FD_MAX) {
|
||||
log_error("No or too many file descriptors passed.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (server_init(&server, (unsigned) n) < 0)
|
||||
return 3;
|
||||
|
||||
for (;;) {
|
||||
struct epoll_event event;
|
||||
int k;
|
||||
|
||||
if ((k = epoll_wait(server.epoll_fd,
|
||||
&event, 1,
|
||||
server.n_streams <= 0 ? TIMEOUT : -1)) < 0) {
|
||||
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
log_error("epoll_wait() failed: %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (k <= 0)
|
||||
break;
|
||||
|
||||
if ((k = process_event(&server, &event)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
r = 0;
|
||||
|
||||
fail:
|
||||
server_done(&server);
|
||||
|
||||
log_info("systemd-logger stopped as pid %llu", (unsigned long long) getpid());
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/*-*- 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 <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <asm/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
#include "loopback-setup.h"
|
||||
|
||||
enum {
|
||||
REQUEST_NONE = 0,
|
||||
REQUEST_ADDRESS_IPV4 = 1,
|
||||
REQUEST_ADDRESS_IPV6 = 2,
|
||||
REQUEST_FLAGS = 4,
|
||||
REQUEST_ALL = 7
|
||||
};
|
||||
|
||||
#define NLMSG_TAIL(nmsg) \
|
||||
((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
|
||||
|
||||
static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) {
|
||||
size_t length;
|
||||
struct rtattr *rta;
|
||||
|
||||
length = RTA_LENGTH(data_length);
|
||||
|
||||
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
|
||||
return -E2BIG;
|
||||
|
||||
rta = NLMSG_TAIL(n);
|
||||
rta->rta_type = type;
|
||||
rta->rta_len = length;
|
||||
memcpy(RTA_DATA(rta), data, data_length);
|
||||
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) {
|
||||
|
||||
for (;;) {
|
||||
ssize_t l;
|
||||
|
||||
if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
|
||||
return l;
|
||||
|
||||
if (errno != EINTR)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
|
||||
|
||||
for (;;) {
|
||||
ssize_t l;
|
||||
|
||||
if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
|
||||
return l;
|
||||
|
||||
if (errno != EINTR)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_adresses(int fd, int if_loopback) {
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_nl nl;
|
||||
} sa;
|
||||
union {
|
||||
struct nlmsghdr header;
|
||||
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
|
||||
RTA_LENGTH(sizeof(struct in6_addr))];
|
||||
} request;
|
||||
|
||||
struct ifaddrmsg *ifaddrmsg;
|
||||
uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
|
||||
int r;
|
||||
|
||||
zero(request);
|
||||
|
||||
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
request.header.nlmsg_type = RTM_NEWADDR;
|
||||
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
|
||||
request.header.nlmsg_seq = REQUEST_ADDRESS_IPV4;
|
||||
|
||||
ifaddrmsg = NLMSG_DATA(&request.header);
|
||||
ifaddrmsg->ifa_family = AF_INET;
|
||||
ifaddrmsg->ifa_prefixlen = 8;
|
||||
ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
|
||||
ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
|
||||
ifaddrmsg->ifa_index = if_loopback;
|
||||
|
||||
if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0)
|
||||
return r;
|
||||
|
||||
zero(sa);
|
||||
sa.nl.nl_family = AF_NETLINK;
|
||||
|
||||
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
|
||||
return -errno;
|
||||
|
||||
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
request.header.nlmsg_seq = REQUEST_ADDRESS_IPV6;
|
||||
|
||||
ifaddrmsg->ifa_family = AF_INET6;
|
||||
ifaddrmsg->ifa_prefixlen = 128;
|
||||
|
||||
if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0)
|
||||
return r;
|
||||
|
||||
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_interface(int fd, int if_loopback) {
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_nl nl;
|
||||
} sa;
|
||||
union {
|
||||
struct nlmsghdr header;
|
||||
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct ifinfomsg))];
|
||||
} request;
|
||||
|
||||
struct ifinfomsg *ifinfomsg;
|
||||
|
||||
zero(request);
|
||||
|
||||
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
request.header.nlmsg_type = RTM_NEWLINK;
|
||||
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
|
||||
request.header.nlmsg_seq = REQUEST_FLAGS;
|
||||
|
||||
ifinfomsg = NLMSG_DATA(&request.header);
|
||||
ifinfomsg->ifi_family = AF_UNSPEC;
|
||||
ifinfomsg->ifi_index = if_loopback;
|
||||
ifinfomsg->ifi_flags = IFF_UP;
|
||||
ifinfomsg->ifi_change = IFF_UP;
|
||||
|
||||
zero(sa);
|
||||
sa.nl.nl_family = AF_NETLINK;
|
||||
|
||||
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_response(int fd) {
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_nl nl;
|
||||
} sa;
|
||||
union {
|
||||
struct nlmsghdr header;
|
||||
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct nlmsgerr))];
|
||||
} response;
|
||||
|
||||
ssize_t l;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
struct nlmsgerr *nlmsgerr;
|
||||
|
||||
if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (sa_len != sizeof(sa.nl) ||
|
||||
sa.nl.nl_family != AF_NETLINK)
|
||||
return -EIO;
|
||||
|
||||
if (sa.nl.nl_pid != 0)
|
||||
return 0;
|
||||
|
||||
if ((size_t) l < sizeof(struct nlmsghdr))
|
||||
return -EIO;
|
||||
|
||||
if (response.header.nlmsg_type != NLMSG_ERROR ||
|
||||
(pid_t) response.header.nlmsg_pid != getpid() ||
|
||||
response.header.nlmsg_seq >= REQUEST_ALL)
|
||||
return 0;
|
||||
|
||||
if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
|
||||
response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
|
||||
return -EIO;
|
||||
|
||||
nlmsgerr = NLMSG_DATA(&response.header);
|
||||
|
||||
if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST)
|
||||
return nlmsgerr->error;
|
||||
|
||||
return response.header.nlmsg_seq;
|
||||
}
|
||||
|
||||
int loopback_setup(void) {
|
||||
int r, if_loopback;
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_nl nl;
|
||||
struct sockaddr_storage storage;
|
||||
} sa;
|
||||
int requests = REQUEST_NONE;
|
||||
|
||||
int fd;
|
||||
|
||||
errno = 0;
|
||||
if ((if_loopback = (int) if_nametoindex("lo")) <= 0)
|
||||
return errno ? -errno : -ENODEV;
|
||||
|
||||
if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
|
||||
return -errno;
|
||||
|
||||
zero(sa);
|
||||
sa.nl.nl_family = AF_NETLINK;
|
||||
|
||||
if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = add_adresses(fd, if_loopback)) < 0)
|
||||
goto finish;
|
||||
|
||||
if ((r = start_interface(fd, if_loopback)) < 0)
|
||||
goto finish;
|
||||
|
||||
do {
|
||||
if ((r = read_response(fd)) < 0)
|
||||
goto finish;
|
||||
|
||||
requests |= r;
|
||||
|
||||
} while (requests != REQUEST_ALL);
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (r < 0)
|
||||
log_error("Failed to configure loopback device: %s", strerror(-r));
|
||||
|
||||
if (fd >= 0)
|
||||
close_nointr_nofail(fd);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooloopbacksetuphfoo
|
||||
#define fooloopbacksetuphfoo
|
||||
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
int loopback_setup(void);
|
||||
|
||||
#endif
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foomacrohfoo
|
||||
#define foomacrohfoo
|
||||
|
||||
/***
|
||||
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 <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define _printf_attr(a,b) __attribute__ ((format (printf, a, b)))
|
||||
#define _sentinel __attribute__ ((sentinel))
|
||||
#define _noreturn __attribute__((noreturn))
|
||||
#define _unused __attribute__ ((unused))
|
||||
#define _destructor __attribute__ ((destructor))
|
||||
#define _pure __attribute__ ((pure))
|
||||
#define _const __attribute__ ((const))
|
||||
#define _deprecated __attribute__ ((deprecated))
|
||||
#define _packed __attribute__ ((packed))
|
||||
#define _malloc __attribute__ ((malloc))
|
||||
#define _weak __attribute__ ((weak))
|
||||
#define _likely(x) (__builtin_expect(!!(x),1))
|
||||
#define _unlikely(x) (__builtin_expect(!!(x),0))
|
||||
|
||||
/* Rounds up */
|
||||
static inline size_t ALIGN(size_t l) {
|
||||
return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
|
||||
}
|
||||
|
||||
#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
#define MAX(a,b) \
|
||||
__extension__ ({ \
|
||||
typeof(a) _a = (a); \
|
||||
typeof(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define MIN(a,b) \
|
||||
__extension__ ({ \
|
||||
typeof(a) _a = (a); \
|
||||
typeof(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define CLAMP(x, low, high) \
|
||||
__extension__ ({ \
|
||||
typeof(x) _x = (x); \
|
||||
typeof(low) _low = (low); \
|
||||
typeof(high) _high = (high); \
|
||||
((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
|
||||
})
|
||||
|
||||
#define assert_se(expr) \
|
||||
do { \
|
||||
if (_unlikely(!(expr))) \
|
||||
log_assert(__FILE__, __LINE__, __PRETTY_FUNCTION__, \
|
||||
"Assertion '%s' failed at %s:%u, function %s(). Aborting.", \
|
||||
#expr , __FILE__, __LINE__, __PRETTY_FUNCTION__); \
|
||||
} while (false) \
|
||||
|
||||
/* We override the glibc assert() here. */
|
||||
#undef assert
|
||||
#ifdef NDEBUG
|
||||
#define assert(expr) do {} while(false)
|
||||
#else
|
||||
#define assert(expr) assert_se(expr)
|
||||
#endif
|
||||
|
||||
#define assert_not_reached(t) \
|
||||
do { \
|
||||
log_assert(__FILE__, __LINE__, __PRETTY_FUNCTION__, \
|
||||
"Code should not be reached '%s' at %s:%u, function %s(). Aborting.", \
|
||||
t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
|
||||
} while (false)
|
||||
|
||||
#define assert_cc(expr) \
|
||||
do { \
|
||||
switch (0) { \
|
||||
case 0: \
|
||||
case !!(expr): \
|
||||
; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
|
||||
#define UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
|
||||
|
||||
#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
|
||||
#define UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
|
||||
|
||||
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
|
||||
#define INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
|
||||
|
||||
#define TO_INT32(p) ((int32_t) ((intptr_t) (p)))
|
||||
#define INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
|
||||
|
||||
#define memzero(x,l) (memset((x), 0, (l)))
|
||||
#define zero(x) (memzero(&(x), sizeof(x)))
|
||||
|
||||
#define char_array_0(x) x[sizeof(x)-1] = 0;
|
||||
|
||||
#define IOVEC_SET_STRING(i, s) \
|
||||
do { \
|
||||
struct iovec *_i = &(i); \
|
||||
char *_s = (char *)(s); \
|
||||
_i->iov_base = _s; \
|
||||
_i->iov_len = strlen(_s); \
|
||||
} while(false);
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#endif
|
||||
+787
@@ -0,0 +1,787 @@
|
||||
/*-*- 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 <dbus/dbus.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "manager.h"
|
||||
#include "log.h"
|
||||
#include "mount-setup.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "loopback-setup.h"
|
||||
#include "load-fragment.h"
|
||||
#include "fdset.h"
|
||||
|
||||
static enum {
|
||||
ACTION_RUN,
|
||||
ACTION_HELP,
|
||||
ACTION_TEST,
|
||||
ACTION_DUMP_CONFIGURATION_ITEMS
|
||||
} action = ACTION_RUN;
|
||||
|
||||
static char *default_unit = NULL;
|
||||
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 (;;)
|
||||
pause();
|
||||
}
|
||||
|
||||
static void nop_handler(int sig) {
|
||||
}
|
||||
|
||||
_noreturn static void crash(int sig) {
|
||||
|
||||
if (!dump_core)
|
||||
log_error("Caught <%s>, not dumping core.", strsignal(sig));
|
||||
else {
|
||||
struct sigaction sa;
|
||||
pid_t pid;
|
||||
|
||||
/* We want to wait for the core process, hence let's enable SIGCHLD */
|
||||
zero(sa);
|
||||
sa.sa_handler = nop_handler;
|
||||
sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
|
||||
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
log_error("Caught <%s>, cannot fork for core dump: %s", strsignal(sig), strerror(errno));
|
||||
|
||||
else if (pid == 0) {
|
||||
struct rlimit rl;
|
||||
|
||||
/* Enable default signal handler for core dump */
|
||||
zero(sa);
|
||||
sa.sa_handler = SIG_DFL;
|
||||
assert_se(sigaction(sig, &sa, NULL) == 0);
|
||||
|
||||
/* Don't limit the core dump size */
|
||||
zero(rl);
|
||||
rl.rlim_cur = RLIM_INFINITY;
|
||||
rl.rlim_max = RLIM_INFINITY;
|
||||
setrlimit(RLIMIT_CORE, &rl);
|
||||
|
||||
/* Just to be sure... */
|
||||
assert_se(chdir("/") == 0);
|
||||
|
||||
/* Raise the signal again */
|
||||
raise(sig);
|
||||
|
||||
assert_not_reached("We shouldn't be here...");
|
||||
_exit(1);
|
||||
|
||||
} else {
|
||||
int status, r;
|
||||
|
||||
/* Order things nicely. */
|
||||
if ((r = waitpid(pid, &status, 0)) < 0)
|
||||
log_error("Caught <%s>, waitpid() failed: %s", strsignal(sig), strerror(errno));
|
||||
else if (!WCOREDUMP(status))
|
||||
log_error("Caught <%s>, core dump failed.", strsignal(sig));
|
||||
else
|
||||
log_error("Caught <%s>, dumped core as pid %llu.", strsignal(sig), (unsigned long long) pid);
|
||||
}
|
||||
}
|
||||
|
||||
if (crash_chvt)
|
||||
chvt(crash_chvt);
|
||||
|
||||
if (crash_shell) {
|
||||
struct sigaction sa;
|
||||
pid_t pid;
|
||||
|
||||
log_info("Executing crash shell in 10s...");
|
||||
sleep(10);
|
||||
|
||||
/* Let the kernel reap children for us */
|
||||
zero(sa);
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART;
|
||||
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
log_error("Failed to fork off crash shell: %s", strerror(errno));
|
||||
else if (pid == 0) {
|
||||
int fd, r;
|
||||
|
||||
if ((fd = acquire_terminal("/dev/console", false, true)) < 0) {
|
||||
log_error("Failed to acquire terminal: %s", strerror(-fd));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if ((r = make_stdio(fd)) < 0) {
|
||||
log_error("Failed to duplicate terminal fd: %s", strerror(-r));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
execl("/bin/sh", "/bin/sh", NULL);
|
||||
|
||||
log_error("execl() failed: %s", strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
log_info("Successfully spawned crash shall as pid %llu.", (unsigned long long) pid);
|
||||
}
|
||||
|
||||
log_info("Freezing execution.");
|
||||
freeze();
|
||||
}
|
||||
|
||||
static void install_crash_handler(void) {
|
||||
struct sigaction sa;
|
||||
|
||||
zero(sa);
|
||||
|
||||
sa.sa_handler = crash;
|
||||
sa.sa_flags = SA_NODEFER;
|
||||
|
||||
assert_se(sigaction(SIGSEGV, &sa, NULL) == 0);
|
||||
assert_se(sigaction(SIGILL, &sa, NULL) == 0);
|
||||
assert_se(sigaction(SIGFPE, &sa, NULL) == 0);
|
||||
assert_se(sigaction(SIGBUS, &sa, NULL) == 0);
|
||||
assert_se(sigaction(SIGQUIT, &sa, NULL) == 0);
|
||||
assert_se(sigaction(SIGABRT, &sa, NULL) == 0);
|
||||
}
|
||||
|
||||
static int make_null_stdio(void) {
|
||||
int null_fd, r;
|
||||
|
||||
if ((null_fd = open("/dev/null", O_RDWR)) < 0) {
|
||||
log_error("Failed to open /dev/null: %m");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((r = make_stdio(null_fd)) < 0)
|
||||
log_warning("Failed to dup2() device: %s", strerror(-r));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int console_setup(bool do_reset) {
|
||||
int tty_fd, r;
|
||||
|
||||
/* If we are init, we connect stdin/stdout/stderr to /dev/null
|
||||
* and make sure we don't have a controlling tty. */
|
||||
|
||||
release_terminal();
|
||||
|
||||
if (!do_reset)
|
||||
return 0;
|
||||
|
||||
if ((tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
|
||||
log_error("Failed to open /dev/console: %s", strerror(-tty_fd));
|
||||
return -tty_fd;
|
||||
}
|
||||
|
||||
if ((r = reset_terminal(tty_fd)) < 0)
|
||||
log_error("Failed to reset /dev/console: %s", strerror(-r));
|
||||
|
||||
close_nointr_nofail(tty_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int set_default_unit(const char *u) {
|
||||
char *c;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!(c = strdup(u)))
|
||||
return -ENOMEM;
|
||||
|
||||
free(default_unit);
|
||||
default_unit = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_proc_cmdline_word(const char *word) {
|
||||
|
||||
static const char * const rlmap[] = {
|
||||
"single", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"-s", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"s", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"S", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"1", SPECIAL_RUNLEVEL1_TARGET,
|
||||
"2", SPECIAL_RUNLEVEL2_TARGET,
|
||||
"3", SPECIAL_RUNLEVEL3_TARGET,
|
||||
"4", SPECIAL_RUNLEVEL4_TARGET,
|
||||
"5", SPECIAL_RUNLEVEL5_TARGET
|
||||
};
|
||||
|
||||
if (startswith(word, "systemd.default="))
|
||||
return set_default_unit(word + 16);
|
||||
|
||||
else if (startswith(word, "systemd.log_target=")) {
|
||||
|
||||
if (log_set_target_from_string(word + 19) < 0)
|
||||
log_warning("Failed to parse log target %s. Ignoring.", word + 19);
|
||||
|
||||
} else if (startswith(word, "systemd.log_level=")) {
|
||||
|
||||
if (log_set_max_level_from_string(word + 18) < 0)
|
||||
log_warning("Failed to parse log level %s. Ignoring.", word + 18);
|
||||
|
||||
} else if (startswith(word, "systemd.dump_core=")) {
|
||||
int r;
|
||||
|
||||
if ((r = parse_boolean(word + 18)) < 0)
|
||||
log_warning("Failed to parse dump core switch %s, Ignoring.", word + 18);
|
||||
else
|
||||
dump_core = r;
|
||||
|
||||
} else if (startswith(word, "systemd.crash_shell=")) {
|
||||
int r;
|
||||
|
||||
if ((r = parse_boolean(word + 20)) < 0)
|
||||
log_warning("Failed to parse crash shell switch %s, Ignoring.", word + 20);
|
||||
else
|
||||
crash_shell = r;
|
||||
|
||||
|
||||
} else if (startswith(word, "systemd.confirm_spawn=")) {
|
||||
int r;
|
||||
|
||||
if ((r = parse_boolean(word + 22)) < 0)
|
||||
log_warning("Failed to parse confirm spawn switch %s, Ignoring.", word + 22);
|
||||
else
|
||||
confirm_spawn = r;
|
||||
|
||||
} else if (startswith(word, "systemd.crash_chvt=")) {
|
||||
int k;
|
||||
|
||||
if (safe_atoi(word + 19, &k) < 0)
|
||||
log_warning("Failed to parse crash chvt switch %s, Ignoring.", word + 19);
|
||||
else
|
||||
crash_chvt = k;
|
||||
|
||||
} else if (startswith(word, "systemd.")) {
|
||||
|
||||
log_warning("Unknown kernel switch %s. Ignoring.", word);
|
||||
|
||||
log_info("Supported kernel switches:");
|
||||
log_info("systemd.default=UNIT Default unit to start");
|
||||
log_info("systemd.log_target=console|kmsg|syslog Log target");
|
||||
log_info("systemd.log_level=LEVEL Log level");
|
||||
log_info("systemd.dump_core=0|1 Dump core on crash");
|
||||
log_info("systemd.crash_shell=0|1 On crash run shell");
|
||||
log_info("systemd.crash_chvt=N Change to VT #N on crash");
|
||||
log_info("systemd.confirm_spawn=0|1 Confirm every process spawn");
|
||||
|
||||
} else {
|
||||
unsigned i;
|
||||
|
||||
/* SysV compatibility */
|
||||
for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
|
||||
if (streq(word, rlmap[i]))
|
||||
return set_default_unit(rlmap[i+1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_proc_cmdline(void) {
|
||||
char *line;
|
||||
int r;
|
||||
char *w;
|
||||
size_t l;
|
||||
char *state;
|
||||
|
||||
if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
|
||||
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FOREACH_WORD_QUOTED(w, l, line, state) {
|
||||
char *word;
|
||||
|
||||
if (!(word = strndup(w, l))) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = parse_proc_cmdline_word(word);
|
||||
free(word);
|
||||
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
free(line);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_LOG_LEVEL = 0x100,
|
||||
ARG_LOG_TARGET,
|
||||
ARG_DEFAULT,
|
||||
ARG_RUNNING_AS,
|
||||
ARG_TEST,
|
||||
ARG_DUMP_CONFIGURATION_ITEMS,
|
||||
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 },
|
||||
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 1);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ARG_LOG_LEVEL:
|
||||
if ((r = log_set_max_level_from_string(optarg)) < 0) {
|
||||
log_error("Failed to parse log level %s.", optarg);
|
||||
return r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ARG_LOG_TARGET:
|
||||
|
||||
if ((r = log_set_target_from_string(optarg)) < 0) {
|
||||
log_error("Failed to parse log target %s.", optarg);
|
||||
return r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ARG_DEFAULT:
|
||||
|
||||
if ((r = set_default_unit(optarg)) < 0) {
|
||||
log_error("Failed to set default unit %s: %s", optarg, strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ARG_RUNNING_AS: {
|
||||
ManagerRunningAs as;
|
||||
|
||||
if ((as = manager_running_as_from_string(optarg)) < 0) {
|
||||
log_error("Failed to parse running as value %s", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
running_as = as;
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_TEST:
|
||||
action = ACTION_TEST;
|
||||
break;
|
||||
|
||||
case ARG_DUMP_CONFIGURATION_ITEMS:
|
||||
action = ACTION_DUMP_CONFIGURATION_ITEMS;
|
||||
break;
|
||||
|
||||
case ARG_CONFIRM_SPAWN:
|
||||
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;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
log_error("Unknown option code %c", c);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* PID 1 will get the kernel arguments as parameters, which we
|
||||
* ignore and unconditionally read from
|
||||
* /proc/cmdline. However, we need to ignore those arguments
|
||||
* here. */
|
||||
if (running_as != MANAGER_INIT && optind < argc) {
|
||||
log_error("Excess arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, syslog-or-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"
|
||||
" --confirm-spawn Ask for confirmation when spawning processes\n",
|
||||
__progname);
|
||||
|
||||
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;
|
||||
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
|
||||
} else
|
||||
running_as = MANAGER_SESSION;
|
||||
|
||||
if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0)
|
||||
goto finish;
|
||||
|
||||
/* Mount /proc, /sys and friends, so that /proc/cmdline and
|
||||
* /proc/$PID/fd is available. */
|
||||
if (mount_setup() < 0)
|
||||
goto finish;
|
||||
|
||||
/* Reset all signal handlers. */
|
||||
assert_se(reset_all_signal_handlers() == 0);
|
||||
|
||||
/* If we are init, we can block sigkill. Yay. */
|
||||
ignore_signal(SIGKILL);
|
||||
ignore_signal(SIGPIPE);
|
||||
|
||||
if (running_as != MANAGER_SESSION)
|
||||
if (parse_proc_cmdline() < 0)
|
||||
goto finish;
|
||||
|
||||
log_parse_environment();
|
||||
|
||||
if (parse_argv(argc, argv) < 0)
|
||||
goto finish;
|
||||
|
||||
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);
|
||||
|
||||
/* 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",
|
||||
running_as == MANAGER_INIT);
|
||||
|
||||
/* Move out of the way, so that we won't block unmounts */
|
||||
assert_se(chdir("/") == 0);
|
||||
|
||||
if (running_as != MANAGER_SESSION) {
|
||||
/* Become a session leader if we aren't one yet. */
|
||||
setsid();
|
||||
|
||||
/* Disable the umask logic */
|
||||
umask(0);
|
||||
}
|
||||
|
||||
/* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
|
||||
dbus_connection_set_change_sigpipe(FALSE);
|
||||
|
||||
/* Reset the console, but only if this is really init and we
|
||||
* are freshly booted */
|
||||
if (running_as != MANAGER_SESSION && action == ACTION_RUN) {
|
||||
console_setup(getpid() == 1 && !serialization);
|
||||
make_null_stdio();
|
||||
}
|
||||
|
||||
/* Open the logging devices, if possible and necessary */
|
||||
log_open();
|
||||
|
||||
/* Make sure we leave a core dump without panicing the
|
||||
* kernel. */
|
||||
if (getpid() == 1)
|
||||
install_crash_handler();
|
||||
|
||||
log_debug("systemd running in %s mode.", manager_running_as_to_string(running_as));
|
||||
|
||||
if (running_as == MANAGER_INIT) {
|
||||
hostname_setup();
|
||||
loopback_setup();
|
||||
}
|
||||
|
||||
if ((r = manager_new(running_as, confirm_spawn, &m)) < 0) {
|
||||
log_error("Failed to allocate manager object: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((r = manager_startup(m, serialization, fds)) < 0)
|
||||
log_error("Failed to fully start up 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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (m)
|
||||
manager_free(m);
|
||||
|
||||
free(default_unit);
|
||||
|
||||
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();
|
||||
|
||||
return retval;
|
||||
}
|
||||
+2291
File diff suppressed because it is too large
Load Diff
+284
@@ -0,0 +1,284 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foomanagerhfoo
|
||||
#define foomanagerhfoo
|
||||
|
||||
/***
|
||||
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 <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "fdset.h"
|
||||
|
||||
/* Enforce upper limit how many names we allow */
|
||||
#define MANAGER_MAX_NAMES 2048
|
||||
|
||||
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 */
|
||||
MANAGER_SESSION, /* non-root, for a session */
|
||||
_MANAGER_RUNNING_AS_MAX,
|
||||
_MANAGER_RUNNING_AS_INVALID = -1
|
||||
} ManagerRunningAs;
|
||||
|
||||
enum WatchType {
|
||||
WATCH_INVALID,
|
||||
WATCH_SIGNAL,
|
||||
WATCH_FD,
|
||||
WATCH_TIMER,
|
||||
WATCH_MOUNT,
|
||||
WATCH_UDEV,
|
||||
WATCH_DBUS_WATCH,
|
||||
WATCH_DBUS_TIMEOUT
|
||||
};
|
||||
|
||||
struct Watch {
|
||||
int fd;
|
||||
WatchType type;
|
||||
union {
|
||||
union Unit *unit;
|
||||
DBusWatch *bus_watch;
|
||||
DBusTimeout *bus_timeout;
|
||||
} data;
|
||||
bool fd_is_dupped:1;
|
||||
bool socket_accept:1;
|
||||
};
|
||||
|
||||
#include "unit.h"
|
||||
#include "job.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
#include "set.h"
|
||||
#include "dbus.h"
|
||||
|
||||
#define SPECIAL_DEFAULT_TARGET "default.target"
|
||||
|
||||
/* This is not really intended to be started by directly. This is
|
||||
* mostly so that other targets (reboot/halt/poweroff) can depend on
|
||||
* it to bring all services down that want to be brought down on
|
||||
* system shutdown. */
|
||||
#define SPECIAL_SHUTDOWN_TARGET "shutdown.target"
|
||||
|
||||
#define SPECIAL_LOGGER_SOCKET "systemd-logger.socket"
|
||||
|
||||
#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
|
||||
#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
|
||||
#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
|
||||
|
||||
#define SPECIAL_LOCAL_FS_TARGET "local-fs.target"
|
||||
#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target"
|
||||
#define SPECIAL_SWAP_TARGET "swap.target"
|
||||
#define SPECIAL_NETWORK_TARGET "network.target"
|
||||
#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
|
||||
#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
|
||||
#define SPECIAL_SYSLOG_TARGET "syslog.target" /* Should pull in syslog.socket or syslog.service */
|
||||
#define SPECIAL_RTC_SET_TARGET "rtc-set.target" /* LSB's $time */
|
||||
|
||||
#define SPECIAL_BASIC_TARGET "basic.target"
|
||||
#define SPECIAL_RESCUE_TARGET "rescue.target"
|
||||
|
||||
#ifndef SPECIAL_DBUS_SERVICE
|
||||
#define SPECIAL_DBUS_SERVICE "dbus.service"
|
||||
#endif
|
||||
|
||||
#ifndef SPECIAL_SYSLOG_SERVICE
|
||||
#define SPECIAL_SYSLOG_SERVICE "syslog.service"
|
||||
#endif
|
||||
|
||||
/* For SysV compatibility. Usually an alias for a saner target. On
|
||||
* SysV-free systems this doesn't exist. */
|
||||
#define SPECIAL_RUNLEVEL0_TARGET "runlevel0.target"
|
||||
#define SPECIAL_RUNLEVEL1_TARGET "runlevel1.target"
|
||||
#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target"
|
||||
#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target"
|
||||
#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
|
||||
#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
|
||||
#define SPECIAL_RUNLEVEL6_TARGET "runlevel6.target"
|
||||
|
||||
struct Manager {
|
||||
uint32_t current_job_id;
|
||||
|
||||
/* Note that the set of units we know of is allowed to be
|
||||
* incosistent. However the subset of it that is loaded may
|
||||
* not, and the list of jobs may neither. */
|
||||
|
||||
/* Active jobs and units */
|
||||
Hashmap *units; /* name string => Unit object n:1 */
|
||||
Hashmap *jobs; /* job id => Job object 1:1 */
|
||||
|
||||
/* To make it easy to iterate through the units of a specific
|
||||
* type we maintain a per type linked list */
|
||||
LIST_HEAD(Meta, units_per_type[_UNIT_TYPE_MAX]);
|
||||
|
||||
/* Units that need to be loaded */
|
||||
LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */
|
||||
|
||||
/* Jobs that need to be run */
|
||||
LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */
|
||||
|
||||
/* Units and jobs that have not yet been announced via
|
||||
* D-Bus. When something about a job changes it is added here
|
||||
* if it is not in there yet. This allows easy coalescing of
|
||||
* D-Bus change signals. */
|
||||
LIST_HEAD(Meta, dbus_unit_queue);
|
||||
LIST_HEAD(Job, dbus_job_queue);
|
||||
|
||||
/* Units to remove */
|
||||
LIST_HEAD(Meta, cleanup_queue);
|
||||
|
||||
/* Units to check when doing GC */
|
||||
LIST_HEAD(Meta, gc_queue);
|
||||
|
||||
/* Jobs to be added */
|
||||
Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
|
||||
JobDependency *transaction_anchor;
|
||||
|
||||
Hashmap *watch_pids; /* pid => Unit object n:1 */
|
||||
|
||||
Watch signal_watch;
|
||||
|
||||
int epoll_fd;
|
||||
|
||||
unsigned n_snapshots;
|
||||
|
||||
char **unit_path;
|
||||
char **sysvinit_path;
|
||||
char **sysvrcnd_path;
|
||||
|
||||
char **environment;
|
||||
|
||||
usec_t boot_timestamp;
|
||||
|
||||
/* Data specific to the device subsystem */
|
||||
struct udev* udev;
|
||||
struct udev_monitor* udev_monitor;
|
||||
Watch udev_watch;
|
||||
|
||||
/* Data specific to the mount subsystem */
|
||||
FILE *proc_self_mountinfo;
|
||||
Watch mount_watch;
|
||||
|
||||
/* Data specific to the swap filesystem */
|
||||
FILE *proc_swaps;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Data specific to the Automount subsystem */
|
||||
int dev_autofs_fd;
|
||||
|
||||
/* Data specific to the cgroup subsystem */
|
||||
Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
|
||||
char *cgroup_controller;
|
||||
char *cgroup_hierarchy;
|
||||
|
||||
usec_t gc_queue_timestamp;
|
||||
|
||||
int gc_marker;
|
||||
unsigned n_in_gc_queue;
|
||||
|
||||
/* Flags */
|
||||
ManagerRunningAs running_as;
|
||||
ManagerExitCode exit_code:4;
|
||||
|
||||
bool dispatching_load_queue:1;
|
||||
bool dispatching_run_queue:1;
|
||||
bool dispatching_dbus_queue:1;
|
||||
|
||||
bool request_api_bus_dispatch:1;
|
||||
bool request_system_bus_dispatch:1;
|
||||
|
||||
bool utmp_reboot_written:1;
|
||||
|
||||
bool confirm_spawn:1;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u);
|
||||
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
|
||||
|
||||
int manager_load_unit_prepare(Manager *m, const char *name, const char *path, Unit **_ret);
|
||||
int manager_load_unit(Manager *m, const char *name, const char *path, Unit **_ret);
|
||||
|
||||
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret);
|
||||
int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, Job **_ret);
|
||||
|
||||
void manager_dump_units(Manager *s, FILE *f, const char *prefix);
|
||||
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
|
||||
|
||||
void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies);
|
||||
|
||||
void manager_clear_jobs(Manager *m);
|
||||
|
||||
unsigned manager_dispatch_load_queue(Manager *m);
|
||||
unsigned manager_dispatch_run_queue(Manager *m);
|
||||
unsigned manager_dispatch_dbus_queue(Manager *m);
|
||||
|
||||
int manager_loop(Manager *m);
|
||||
|
||||
void manager_write_utmp_reboot(Manager *m);
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foomissinghfoo
|
||||
#define foomissinghfoo
|
||||
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
/* Missing glibc definitions to access certain kernel APIs */
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#ifndef RLIMIT_RTTIME
|
||||
#define RLIMIT_RTTIME 15
|
||||
#endif
|
||||
|
||||
static inline int pivot_root(const char *new_root, const char *put_old) {
|
||||
return syscall(SYS_pivot_root, new_root, put_old);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
/*-*- 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 <sys/mount.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mount-setup.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
|
||||
typedef struct MountPoint {
|
||||
const char *what;
|
||||
const char *where;
|
||||
const char *type;
|
||||
const char *options;
|
||||
unsigned long flags;
|
||||
bool fatal;
|
||||
} MountPoint;
|
||||
|
||||
static const MountPoint mount_table[] = {
|
||||
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
|
||||
{ "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
|
||||
{ "devtmps", "/dev", "devtmpfs", "mode=755", MS_NOSUID, true },
|
||||
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
|
||||
{ "devpts", "/dev/pts", "devpts", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
|
||||
{ "cgroup", "/cgroup/debug", "cgroup", "debug", MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
|
||||
{ "debugfs", "/sys/kernel/debug", "debugfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
|
||||
{ "binfmt_misc", "/proc/sys/fs/binfmt_misc", "binfmt_misc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
|
||||
{ "mqueue", "/dev/mqueue", "mqueue", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
|
||||
};
|
||||
|
||||
bool mount_point_is_api(const char *path) {
|
||||
unsigned i;
|
||||
|
||||
/* Checks if this mount point is considered "API", and hence
|
||||
* should be ignored */
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(mount_table); i ++)
|
||||
if (path_startswith(path, mount_table[i].where))
|
||||
return true;
|
||||
|
||||
return path_startswith(path, "/cgroup/");
|
||||
}
|
||||
|
||||
static int mount_one(const MountPoint *p) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
if ((r = path_is_mount_point(p->where)) < 0)
|
||||
return r;
|
||||
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
/* The access mode here doesn't really matter too much, since
|
||||
* the mounted file system will take precedence anyway. */
|
||||
mkdir_p(p->where, 0755);
|
||||
|
||||
log_debug("Mounting %s to %s of type %s with options %s.",
|
||||
p->what,
|
||||
p->where,
|
||||
p->type,
|
||||
strna(p->options));
|
||||
|
||||
if (mount(p->what,
|
||||
p->where,
|
||||
p->type,
|
||||
p->flags,
|
||||
p->options) < 0) {
|
||||
log_error("Failed to mount %s: %s", p->where, strerror(errno));
|
||||
return p->fatal ? -errno : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_cgroup_controllers(void) {
|
||||
int r;
|
||||
FILE *f;
|
||||
char buf [256];
|
||||
|
||||
/* Mount all available cgroup controllers. */
|
||||
|
||||
if (!(f = fopen("/proc/cgroups", "re")))
|
||||
return -ENOENT;
|
||||
|
||||
/* Ignore the header line */
|
||||
(void) fgets(buf, sizeof(buf), f);
|
||||
|
||||
for (;;) {
|
||||
MountPoint p;
|
||||
char *controller, *where;
|
||||
|
||||
if (fscanf(f, "%ms %*i %*i %*i", &controller) != 1) {
|
||||
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
log_error("Failed to parse /proc/cgroups.");
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (asprintf(&where, "/cgroup/%s", controller) < 0) {
|
||||
free(controller);
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
zero(p);
|
||||
p.what = "cgroup";
|
||||
p.where = where;
|
||||
p.type = "cgroup";
|
||||
p.options = controller;
|
||||
p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
|
||||
p.fatal = false;
|
||||
|
||||
r = mount_one(&p);
|
||||
free(controller);
|
||||
free(where);
|
||||
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int mount_setup(void) {
|
||||
int r;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(mount_table); i ++)
|
||||
if ((r = mount_one(mount_table+i)) < 0)
|
||||
return r;
|
||||
|
||||
return mount_cgroup_controllers();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foomountsetuphfoo
|
||||
#define foomountsetuphfoo
|
||||
|
||||
/***
|
||||
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 <stdbool.h>
|
||||
|
||||
int mount_setup(void);
|
||||
|
||||
bool mount_point_is_api(const char *path);
|
||||
|
||||
#endif
|
||||
+1539
File diff suppressed because it is too large
Load Diff
+110
@@ -0,0 +1,110 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foomounthfoo
|
||||
#define foomounthfoo
|
||||
|
||||
/***
|
||||
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 Mount Mount;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum MountState {
|
||||
MOUNT_DEAD,
|
||||
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_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;
|
||||
|
||||
char *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 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, deserialized_state;
|
||||
|
||||
KillMode kill_mode;
|
||||
|
||||
ExecCommand* control_command;
|
||||
MountExecCommand control_command_id;
|
||||
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);
|
||||
|
||||
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
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
/*-*- 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 <sys/mount.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <limits.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "strv.h"
|
||||
#include "util.h"
|
||||
#include "namespace.h"
|
||||
#include "missing.h"
|
||||
|
||||
typedef enum PathMode {
|
||||
/* This is ordered by priority! */
|
||||
INACCESSIBLE,
|
||||
READONLY,
|
||||
PRIVATE,
|
||||
READWRITE
|
||||
} PathMode;
|
||||
|
||||
typedef struct Path {
|
||||
const char *path;
|
||||
PathMode mode;
|
||||
} Path;
|
||||
|
||||
static int append_paths(Path **p, char **strv, PathMode mode) {
|
||||
char **i;
|
||||
|
||||
STRV_FOREACH(i, strv) {
|
||||
|
||||
if (!path_is_absolute(*i))
|
||||
return -EINVAL;
|
||||
|
||||
(*p)->path = *i;
|
||||
(*p)->mode = mode;
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int path_compare(const void *a, const void *b) {
|
||||
const Path *p = a, *q = b;
|
||||
|
||||
if (path_equal(p->path, q->path)) {
|
||||
|
||||
/* If the paths are equal, check the mode */
|
||||
if (p->mode < q->mode)
|
||||
return -1;
|
||||
|
||||
if (p->mode > q->mode)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the paths are not equal, then order prefixes first */
|
||||
if (path_startswith(p->path, q->path))
|
||||
return 1;
|
||||
|
||||
if (path_startswith(q->path, p->path))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) {
|
||||
Path *f, *t, *previous;
|
||||
|
||||
assert(p);
|
||||
assert(n);
|
||||
assert(need_inaccessible);
|
||||
assert(need_private);
|
||||
|
||||
for (f = p, t = p, previous = NULL; f < p+*n; f++) {
|
||||
|
||||
if (previous && path_equal(f->path, previous->path))
|
||||
continue;
|
||||
|
||||
t->path = f->path;
|
||||
t->mode = f->mode;
|
||||
|
||||
if (t->mode == PRIVATE)
|
||||
*need_private = true;
|
||||
|
||||
if (t->mode == INACCESSIBLE)
|
||||
*need_inaccessible = true;
|
||||
|
||||
previous = t;
|
||||
|
||||
t++;
|
||||
}
|
||||
|
||||
*n = t - p;
|
||||
}
|
||||
|
||||
static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) {
|
||||
const char *what;
|
||||
char *where;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(root_dir);
|
||||
assert(inaccessible_dir);
|
||||
assert(private_dir);
|
||||
|
||||
if (!(where = strappend(root_dir, p->path)))
|
||||
return -ENOMEM;
|
||||
|
||||
switch (p->mode) {
|
||||
|
||||
case INACCESSIBLE:
|
||||
what = inaccessible_dir;
|
||||
flags |= MS_RDONLY;
|
||||
break;
|
||||
|
||||
case READONLY:
|
||||
flags |= MS_RDONLY;
|
||||
/* Fall through */
|
||||
|
||||
case READWRITE:
|
||||
what = p->path;
|
||||
break;
|
||||
|
||||
case PRIVATE:
|
||||
what = private_dir;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((r = mount(what, where, NULL, MS_BIND|MS_REC, NULL)) >= 0) {
|
||||
log_debug("Successfully mounted %s to %s", what, where);
|
||||
|
||||
/* The bind mount will always inherit the original
|
||||
* flags. If we want to set any flag we need
|
||||
* to do so in a second indepdant step. */
|
||||
if (flags)
|
||||
r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL);
|
||||
|
||||
/* Avoid expontial growth of trees */
|
||||
if (r >= 0 && path_equal(p->path, "/"))
|
||||
r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_UNBINDABLE|flags, NULL);
|
||||
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
umount2(where, MNT_DETACH);
|
||||
}
|
||||
}
|
||||
|
||||
free(where);
|
||||
return r;
|
||||
}
|
||||
|
||||
int setup_namespace(
|
||||
char **writable,
|
||||
char **readable,
|
||||
char **inaccessible,
|
||||
bool private_tmp,
|
||||
unsigned long flags) {
|
||||
|
||||
char
|
||||
tmp_dir[] = "/tmp/systemd-namespace-XXXXXX",
|
||||
root_dir[] = "/tmp/systemd-namespace-XXXXXX/root",
|
||||
old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX",
|
||||
inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible",
|
||||
private_dir[] = "/tmp/systemd-namespace-XXXXXX/private";
|
||||
|
||||
Path *paths, *p;
|
||||
unsigned n;
|
||||
bool need_private = false, need_inaccessible = false;
|
||||
bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false;
|
||||
int r;
|
||||
const char *t;
|
||||
|
||||
n =
|
||||
strv_length(writable) +
|
||||
strv_length(readable) +
|
||||
strv_length(inaccessible) +
|
||||
(private_tmp ? 2 : 1);
|
||||
|
||||
if (!(paths = new(Path, n)))
|
||||
return -ENOMEM;
|
||||
|
||||
p = paths;
|
||||
if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
|
||||
(r = append_paths(&p, readable, READONLY)) < 0 ||
|
||||
(r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (private_tmp) {
|
||||
p->path = "/tmp";
|
||||
p->mode = PRIVATE;
|
||||
p++;
|
||||
}
|
||||
|
||||
p->path = "/";
|
||||
p->mode = READWRITE;
|
||||
p++;
|
||||
|
||||
assert(paths + n == p);
|
||||
|
||||
qsort(paths, n, sizeof(Path), path_compare);
|
||||
drop_duplicates(paths, &n, &need_inaccessible, &need_private);
|
||||
|
||||
if (!mkdtemp(tmp_dir)) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
remove_tmp = true;
|
||||
|
||||
memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1);
|
||||
if (mkdir(root_dir, 0777) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
remove_root = true;
|
||||
|
||||
if (need_inaccessible) {
|
||||
memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1);
|
||||
if (mkdir(inaccessible_dir, 0) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
remove_inaccessible = true;
|
||||
}
|
||||
|
||||
if (need_private) {
|
||||
memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1);
|
||||
if (mkdir(private_dir, 0777 + S_ISVTX) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
remove_private = true;
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNS) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* We assume that by default mount events from us won't be
|
||||
* propagated to the root namespace. */
|
||||
|
||||
for (p = paths; p < paths + n; p++)
|
||||
if ((r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags)) < 0)
|
||||
goto undo_mounts;
|
||||
|
||||
memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1);
|
||||
if (!mkdtemp(old_root_dir)) {
|
||||
r = -errno;
|
||||
goto undo_mounts;
|
||||
}
|
||||
remove_old_root = true;
|
||||
|
||||
if (chdir(root_dir) < 0) {
|
||||
r = -errno;
|
||||
goto undo_mounts;
|
||||
}
|
||||
|
||||
if (pivot_root(root_dir, old_root_dir) < 0) {
|
||||
r = -errno;
|
||||
goto undo_mounts;
|
||||
}
|
||||
|
||||
t = old_root_dir + sizeof(root_dir) - 1;
|
||||
if (umount2(t, MNT_DETACH) < 0)
|
||||
/* At this point it's too late to turn anything back,
|
||||
* since we are already in the new root. */
|
||||
return -errno;
|
||||
|
||||
if (rmdir(t) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
|
||||
undo_mounts:
|
||||
|
||||
for (p--; p >= paths; p--) {
|
||||
char full_path[PATH_MAX];
|
||||
|
||||
snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path);
|
||||
char_array_0(full_path);
|
||||
|
||||
umount2(full_path, MNT_DETACH);
|
||||
}
|
||||
|
||||
fail:
|
||||
if (remove_old_root)
|
||||
rmdir(old_root_dir);
|
||||
|
||||
if (remove_inaccessible)
|
||||
rmdir(inaccessible_dir);
|
||||
|
||||
if (remove_private)
|
||||
rmdir(private_dir);
|
||||
|
||||
if (remove_root)
|
||||
rmdir(root_dir);
|
||||
|
||||
if (remove_tmp)
|
||||
rmdir(tmp_dir);
|
||||
|
||||
free(paths);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foonamespacehfoo
|
||||
#define foonamespacehfoo
|
||||
|
||||
/***
|
||||
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 <stdbool.h>
|
||||
|
||||
int setup_namespace(
|
||||
char **writable,
|
||||
char **readable,
|
||||
char **inaccessible,
|
||||
bool private_tmp,
|
||||
unsigned long flags);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
/*-*- 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 <assert.h>
|
||||
|
||||
#include "ratelimit.h"
|
||||
#include "log.h"
|
||||
|
||||
/* Modelled after Linux' lib/ratelimit.c by Dave Young
|
||||
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
|
||||
|
||||
bool ratelimit_test(RateLimit *r) {
|
||||
usec_t timestamp;
|
||||
|
||||
timestamp = now(CLOCK_MONOTONIC);
|
||||
|
||||
assert(r);
|
||||
assert(r->interval > 0);
|
||||
assert(r->burst > 0);
|
||||
|
||||
if (r->begin <= 0 ||
|
||||
r->begin + r->interval < timestamp) {
|
||||
|
||||
if (r->n_missed > 0)
|
||||
log_warning("%u events suppressed", r->n_missed);
|
||||
|
||||
r->begin = timestamp;
|
||||
|
||||
/* Reset counters */
|
||||
r->n_printed = 0;
|
||||
r->n_missed = 0;
|
||||
goto good;
|
||||
}
|
||||
|
||||
if (r->n_printed <= r->burst)
|
||||
goto good;
|
||||
|
||||
r->n_missed++;
|
||||
return false;
|
||||
|
||||
good:
|
||||
r->n_printed++;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooratelimithfoo
|
||||
#define fooratelimithfoo
|
||||
|
||||
/***
|
||||
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"
|
||||
|
||||
typedef struct RateLimit {
|
||||
usec_t interval;
|
||||
usec_t begin;
|
||||
unsigned burst;
|
||||
unsigned n_printed, n_missed;
|
||||
} RateLimit;
|
||||
|
||||
#define RATELIMIT_DEFINE(_name, _interval, _burst) \
|
||||
RateLimit _name = { \
|
||||
.interval = (_interval), \
|
||||
.burst = (_burst), \
|
||||
.n_printed = 0, \
|
||||
.n_missed = 0, \
|
||||
.begin = 0 \
|
||||
}
|
||||
|
||||
#define RATELIMIT_INIT(v, _interval, _burst) \
|
||||
do { \
|
||||
RateLimit *_r = &(v); \
|
||||
_r->interval = (_interval); \
|
||||
_r->burst = (_burst); \
|
||||
_r->n_printed = 0; \
|
||||
_r->n_missed = 0; \
|
||||
_r->begin = 0; \
|
||||
} while (false);
|
||||
|
||||
bool ratelimit_test(RateLimit *r);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
|
||||
int sd_listen_fds(int unset_environment) {
|
||||
|
||||
#ifdef DISABLE_SYSTEMD
|
||||
return 0;
|
||||
#else
|
||||
int r;
|
||||
const char *e;
|
||||
char *p = NULL;
|
||||
unsigned long l;
|
||||
|
||||
if (!(e = getenv("LISTEN_PID"))) {
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
l = strtoul(e, &p, 10);
|
||||
|
||||
if (errno != 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!p || *p || l <= 0) {
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Is this for us? */
|
||||
if (getpid() != (pid_t) l) {
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!(e = getenv("LISTEN_FDS"))) {
|
||||
r = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
l = strtoul(e, &p, 10);
|
||||
|
||||
if (errno != 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!p || *p) {
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = (int) l;
|
||||
|
||||
finish:
|
||||
if (unset_environment) {
|
||||
unsetenv("LISTEN_PID");
|
||||
unsetenv("LISTEN_FDS");
|
||||
}
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foosddaemonhfoo
|
||||
#define foosddaemonhfoo
|
||||
|
||||
/***
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
***/
|
||||
|
||||
/* Reference implementation of a few systemd related interfaces for
|
||||
* writing daemons. These interfaces are trivial to implement, however
|
||||
* to simplify porting we provide this reference
|
||||
* implementation. Applications are free to reimplement the algorithms
|
||||
* described here. */
|
||||
|
||||
/*
|
||||
Log levels for usage on stderr:
|
||||
|
||||
fprintf(stderr, SD_NOTICE "Hello World!");
|
||||
|
||||
This is similar to printk() usage in the kernel.
|
||||
*/
|
||||
|
||||
#define SD_EMERG "<0>" /* system is unusable */
|
||||
#define SD_ALERT "<1>" /* action must be taken immediately */
|
||||
#define SD_CRIT "<2>" /* critical conditions */
|
||||
#define SD_ERR "<3>" /* error conditions */
|
||||
#define SD_WARNING "<4>" /* warning conditions */
|
||||
#define SD_NOTICE "<5>" /* normal but significant condition */
|
||||
#define SD_INFO "<6>" /* informational */
|
||||
#define SD_DEBUG "<7>" /* debug-level messages */
|
||||
|
||||
/* The first passed file descriptor is fd 3 */
|
||||
#define SD_LISTEN_FDS_START 3
|
||||
|
||||
/* Returns how many file descriptors have been passed, or a negative
|
||||
* errno code on failure. Optionally removes the $LISTEN_FDS and
|
||||
* $LISTEN_PID file descriptors from the environment (recommended). */
|
||||
int sd_listen_fds(int unset_environment);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef _LINUX_SECUREBITS_H
|
||||
#define _LINUX_SECUREBITS_H 1
|
||||
|
||||
/* This is minimal version of Linux' linux/securebits.h header file,
|
||||
* which is licensed GPL2 */
|
||||
|
||||
#define SECUREBITS_DEFAULT 0x00000000
|
||||
|
||||
/* When set UID 0 has no special privileges. When unset, we support
|
||||
inheritance of root-permissions and suid-root executable under
|
||||
compatibility mode. We raise the effective and inheritable bitmasks
|
||||
*of the executable file* if the effective uid of the new process is
|
||||
0. If the real uid is 0, we raise the effective (legacy) bit of the
|
||||
executable file. */
|
||||
#define SECURE_NOROOT 0
|
||||
#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */
|
||||
|
||||
/* When set, setuid to/from uid 0 does not trigger capability-"fixup".
|
||||
When unset, to provide compatiblility with old programs relying on
|
||||
set*uid to gain/lose privilege, transitions to/from uid 0 cause
|
||||
capabilities to be gained/lost. */
|
||||
#define SECURE_NO_SETUID_FIXUP 2
|
||||
#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */
|
||||
|
||||
/* When set, a process can retain its capabilities even after
|
||||
transitioning to a non-root user (the set-uid fixup suppressed by
|
||||
bit 2). Bit-4 is cleared when a process calls exec(); setting both
|
||||
bit 4 and 5 will create a barrier through exec that no exec()'d
|
||||
child can use this feature again. */
|
||||
#define SECURE_KEEP_CAPS 4
|
||||
#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */
|
||||
|
||||
/* Each securesetting is implemented using two bits. One bit specifies
|
||||
whether the setting is on or off. The other bit specify whether the
|
||||
setting is locked or not. A setting which is locked cannot be
|
||||
changed from user-level. */
|
||||
#define issecure_mask(X) (1 << (X))
|
||||
#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
|
||||
|
||||
#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
|
||||
issecure_mask(SECURE_NO_SETUID_FIXUP) | \
|
||||
issecure_mask(SECURE_KEEP_CAPS))
|
||||
#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
|
||||
|
||||
#endif /* !_LINUX_SECUREBITS_H */
|
||||
+2463
File diff suppressed because it is too large
Load Diff
+147
@@ -0,0 +1,147 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooservicehfoo
|
||||
#define fooservicehfoo
|
||||
|
||||
/***
|
||||
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 Service Service;
|
||||
|
||||
#include "unit.h"
|
||||
#include "ratelimit.h"
|
||||
|
||||
typedef enum ServiceState {
|
||||
SERVICE_DEAD,
|
||||
SERVICE_START_PRE,
|
||||
SERVICE_START,
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_EXITED, /* Nothing is running anymore, but ValidNoProcess is true, ehnce this is OK */
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
|
||||
SERVICE_STOP_SIGTERM,
|
||||
SERVICE_STOP_SIGKILL,
|
||||
SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
|
||||
SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_MAINTAINANCE,
|
||||
SERVICE_AUTO_RESTART,
|
||||
_SERVICE_STATE_MAX,
|
||||
_SERVICE_STATE_INVALID = -1
|
||||
} ServiceState;
|
||||
|
||||
typedef enum ServiceRestart {
|
||||
SERVICE_ONCE,
|
||||
SERVICE_RESTART_ON_SUCCESS,
|
||||
SERVICE_RESTART_ALWAYS,
|
||||
_SERVICE_RESTART_MAX,
|
||||
_SERVICE_RESTART_INVALID = -1
|
||||
} ServiceRestart;
|
||||
|
||||
typedef enum ServiceType {
|
||||
SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */
|
||||
SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */
|
||||
SERVICE_FINISH, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
|
||||
SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
|
||||
_SERVICE_TYPE_MAX,
|
||||
_SERVICE_TYPE_INVALID = -1
|
||||
} ServiceType;
|
||||
|
||||
typedef enum ServiceExecCommand {
|
||||
SERVICE_EXEC_START_PRE,
|
||||
SERVICE_EXEC_START,
|
||||
SERVICE_EXEC_START_POST,
|
||||
SERVICE_EXEC_RELOAD,
|
||||
SERVICE_EXEC_STOP,
|
||||
SERVICE_EXEC_STOP_POST,
|
||||
_SERVICE_EXEC_COMMAND_MAX,
|
||||
_SERVICE_EXEC_COMMAND_INVALID = -1
|
||||
} ServiceExecCommand;
|
||||
|
||||
struct Service {
|
||||
Meta meta;
|
||||
|
||||
ServiceType type;
|
||||
ServiceRestart restart;
|
||||
|
||||
/* If set we'll read the main daemon PID from this file */
|
||||
char *pid_file;
|
||||
|
||||
usec_t restart_usec;
|
||||
usec_t timeout_usec;
|
||||
|
||||
ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
bool permissions_start_only;
|
||||
bool root_directory_start_only;
|
||||
bool valid_no_process;
|
||||
|
||||
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;
|
||||
|
||||
/* If we shut down, remember why */
|
||||
bool failure:1;
|
||||
|
||||
bool bus_name_good:1;
|
||||
|
||||
bool allow_restart:1;
|
||||
|
||||
bool got_socket_fd:1;
|
||||
|
||||
bool sysv_has_lsb:1;
|
||||
char *sysv_path;
|
||||
int sysv_start_priority;
|
||||
char *sysv_runlevels;
|
||||
|
||||
char *bus_name;
|
||||
|
||||
RateLimit ratelimit;
|
||||
|
||||
int socket_fd;
|
||||
|
||||
Watch timer_watch;
|
||||
};
|
||||
|
||||
extern const UnitVTable service_vtable;
|
||||
|
||||
int service_set_socket_fd(Service *s, int fd);
|
||||
|
||||
const char* service_state_to_string(ServiceState i);
|
||||
ServiceState service_state_from_string(const char *s);
|
||||
|
||||
const char* service_restart_to_string(ServiceRestart i);
|
||||
ServiceRestart service_restart_from_string(const char *s);
|
||||
|
||||
const char* service_type_to_string(ServiceType i);
|
||||
ServiceType service_type_from_string(const char *s);
|
||||
|
||||
const char* service_exec_command_to_string(ServiceExecCommand i);
|
||||
ServiceExecCommand service_exec_command_from_string(const char *s);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
/*-*- 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 <stdlib.h>
|
||||
|
||||
#include "set.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
#define MAKE_SET(h) ((Set*) (h))
|
||||
#define MAKE_HASHMAP(s) ((Hashmap*) (s))
|
||||
|
||||
/* For now this is not much more than a wrapper around a hashmap */
|
||||
|
||||
Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
|
||||
return MAKE_SET(hashmap_new(hash_func, compare_func));
|
||||
}
|
||||
|
||||
void set_free(Set* s) {
|
||||
hashmap_free(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) {
|
||||
return hashmap_ensure_allocated((Hashmap**) s, hash_func, compare_func);
|
||||
}
|
||||
|
||||
int set_put(Set *s, void *value) {
|
||||
return hashmap_put(MAKE_HASHMAP(s), value, value);
|
||||
}
|
||||
|
||||
int set_replace(Set *s, void *value) {
|
||||
return hashmap_replace(MAKE_HASHMAP(s), value, value);
|
||||
}
|
||||
|
||||
void *set_get(Set *s, void *value) {
|
||||
return hashmap_get(MAKE_HASHMAP(s), value);
|
||||
}
|
||||
|
||||
void *set_remove(Set *s, void *value) {
|
||||
return hashmap_remove(MAKE_HASHMAP(s), value);
|
||||
}
|
||||
|
||||
int set_remove_and_put(Set *s, void *old_value, void *new_value) {
|
||||
return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value);
|
||||
}
|
||||
|
||||
unsigned set_size(Set *s) {
|
||||
return hashmap_size(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
bool set_isempty(Set *s) {
|
||||
return hashmap_isempty(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void *set_iterate(Set *s, Iterator *i) {
|
||||
return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
|
||||
}
|
||||
|
||||
void *set_iterate_backwards(Set *s, Iterator *i) {
|
||||
return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
|
||||
}
|
||||
|
||||
void *set_iterate_skip(Set *s, void *value, Iterator *i) {
|
||||
return hashmap_iterate_skip(MAKE_HASHMAP(s), value, i);
|
||||
}
|
||||
|
||||
void *set_steal_first(Set *s) {
|
||||
return hashmap_steal_first(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void* set_first(Set *s) {
|
||||
return hashmap_first(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void* set_last(Set *s) {
|
||||
return hashmap_last(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
int set_merge(Set *s, Set *other) {
|
||||
return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
|
||||
}
|
||||
|
||||
void set_move(Set *s, Set *other) {
|
||||
return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
|
||||
}
|
||||
|
||||
int set_move_one(Set *s, Set *other, void *value) {
|
||||
return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value);
|
||||
}
|
||||
|
||||
Set* set_copy(Set *s) {
|
||||
return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s)));
|
||||
}
|
||||
|
||||
void set_clear(Set *s) {
|
||||
hashmap_clear(MAKE_HASHMAP(s));
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foosethfoo
|
||||
#define foosethfoo
|
||||
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
/* Pretty straightforward set implementation. Internally based on the
|
||||
* hashmap. That means that as a minor optimization a NULL set
|
||||
* object will be treated as empty set for all read
|
||||
* operations. That way it is not necessary to instantiate an object
|
||||
* for each set use. */
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
typedef struct Set Set;
|
||||
|
||||
Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
|
||||
void set_free(Set* s);
|
||||
Set* set_copy(Set *s);
|
||||
int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
|
||||
|
||||
int set_put(Set *s, void *value);
|
||||
int set_replace(Set *s, void *value);
|
||||
void *set_get(Set *s, void *value);
|
||||
void *set_remove(Set *s, void *value);
|
||||
int set_remove_and_put(Set *s, void *old_value, void *new_value);
|
||||
|
||||
int set_merge(Set *s, Set *other);
|
||||
void set_move(Set *s, Set *other);
|
||||
int set_move_one(Set *s, Set *other, void *value);
|
||||
|
||||
unsigned set_size(Set *s);
|
||||
bool set_isempty(Set *s);
|
||||
|
||||
void *set_iterate(Set *s, Iterator *i);
|
||||
void *set_iterate_backwards(Set *s, Iterator *i);
|
||||
void *set_iterate_skip(Set *s, void *value, Iterator *i);
|
||||
|
||||
void set_clear(Set *s);
|
||||
void *set_steal_first(Set *s);
|
||||
void* set_first(Set *s);
|
||||
void* set_last(Set *s);
|
||||
|
||||
#define SET_FOREACH(e, s, i) \
|
||||
for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
|
||||
|
||||
#define SET_FOREACH_BACKWARDS(e, s, i) \
|
||||
for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
|
||||
|
||||
#endif
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
/*-*- 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 "unit.h"
|
||||
#include "snapshot.h"
|
||||
#include "unit-name.h"
|
||||
#include "dbus-snapshot.h"
|
||||
|
||||
static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
|
||||
[SNAPSHOT_DEAD] = UNIT_INACTIVE,
|
||||
[SNAPSHOT_ACTIVE] = UNIT_ACTIVE
|
||||
};
|
||||
|
||||
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,
|
||||
snapshot_state_to_string(old_state),
|
||||
snapshot_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int snapshot_coldplug(Unit *u) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SNAPSHOT_DEAD);
|
||||
|
||||
if (s->deserialized_state != s->state)
|
||||
snapshot_set_state(s, s->deserialized_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
|
||||
fprintf(f,
|
||||
"%sSnapshot State: %s\n"
|
||||
"%sClean Up: %s\n",
|
||||
prefix, snapshot_state_to_string(s->state),
|
||||
prefix, yes_no(s->cleanup));
|
||||
}
|
||||
|
||||
static int snapshot_start(Unit *u) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SNAPSHOT_DEAD);
|
||||
|
||||
snapshot_set_state(s, SNAPSHOT_ACTIVE);
|
||||
|
||||
if (s->cleanup)
|
||||
unit_add_to_cleanup_queue(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snapshot_stop(Unit *u) {
|
||||
Snapshot *s = SNAPSHOT(u);
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SNAPSHOT_ACTIVE);
|
||||
|
||||
snapshot_set_state(s, SNAPSHOT_DEAD);
|
||||
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, true)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency_by_name(u, UNIT_REQUIRES, value, NULL, true)) < 0)
|
||||
return r;
|
||||
} else
|
||||
log_debug("Unknown serialization key '%s'", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UnitActiveState snapshot_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_translation_table[SNAPSHOT(u)->state];
|
||||
}
|
||||
|
||||
static const char *snapshot_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return snapshot_state_to_string(SNAPSHOT(u)->state);
|
||||
}
|
||||
|
||||
int snapshot_create(Manager *m, const char *name, bool cleanup, Snapshot **_s) {
|
||||
Iterator i;
|
||||
Unit *other, *u = NULL;
|
||||
char *n = NULL;
|
||||
int r;
|
||||
const char *k;
|
||||
|
||||
assert(m);
|
||||
assert(_s);
|
||||
|
||||
if (name) {
|
||||
if (!unit_name_is_valid(name))
|
||||
return -EINVAL;
|
||||
|
||||
if (unit_name_to_type(name) != UNIT_SNAPSHOT)
|
||||
return -EINVAL;
|
||||
|
||||
if (manager_get_unit(m, name))
|
||||
return -EEXIST;
|
||||
|
||||
} else {
|
||||
|
||||
for (;;) {
|
||||
if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!manager_get_unit(m, n))
|
||||
break;
|
||||
|
||||
free(n);
|
||||
}
|
||||
|
||||
name = n;
|
||||
}
|
||||
|
||||
r = manager_load_unit(m, name, NULL, &u);
|
||||
free(n);
|
||||
|
||||
if (r < 0)
|
||||
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_VTABLE(other)->check_snapshot)
|
||||
if (!UNIT_VTABLE(other)->check_snapshot(other))
|
||||
continue;
|
||||
|
||||
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
|
||||
continue;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_REQUIRES, other, true)) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = unit_add_dependency(u, UNIT_AFTER, other, true)) < 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) {
|
||||
assert(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",
|
||||
|
||||
.no_alias = true,
|
||||
.no_instances = true,
|
||||
.no_snapshots = true,
|
||||
.no_gc = true,
|
||||
|
||||
.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,
|
||||
|
||||
.bus_message_handler = bus_snapshot_message_handler
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foosnapshothfoo
|
||||
#define foosnapshothfoo
|
||||
|
||||
/***
|
||||
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 Snapshot Snapshot;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum SnapshotState {
|
||||
SNAPSHOT_DEAD,
|
||||
SNAPSHOT_ACTIVE,
|
||||
_SNAPSHOT_STATE_MAX,
|
||||
_SNAPSHOT_STATE_INVALID = -1
|
||||
} SnapshotState;
|
||||
|
||||
struct Snapshot {
|
||||
Meta meta;
|
||||
|
||||
SnapshotState state, deserialized_state;
|
||||
|
||||
bool cleanup;
|
||||
};
|
||||
|
||||
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
|
||||
@@ -0,0 +1,468 @@
|
||||
/*-*- 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 <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
int r;
|
||||
char *e, *n;
|
||||
unsigned u;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
zero(*a);
|
||||
a->type = SOCK_STREAM;
|
||||
|
||||
if (*s == '[') {
|
||||
/* IPv6 in [x:.....:z]:p notation */
|
||||
|
||||
if (!(e = strchr(s+1, ']')))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(n = strndup(s+1, e-s-1)))
|
||||
return -ENOMEM;
|
||||
|
||||
errno = 0;
|
||||
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
|
||||
free(n);
|
||||
return errno != 0 ? -errno : -EINVAL;
|
||||
}
|
||||
|
||||
free(n);
|
||||
|
||||
e++;
|
||||
if (*e != ':')
|
||||
return -EINVAL;
|
||||
|
||||
e++;
|
||||
if ((r = safe_atou(e, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
|
||||
} else if (*s == '/') {
|
||||
/* AF_UNIX socket */
|
||||
|
||||
size_t l;
|
||||
|
||||
l = strlen(s);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path))
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path, s, l);
|
||||
a->size = sizeof(sa_family_t) + l + 1;
|
||||
|
||||
} else if (*s == '@') {
|
||||
/* Abstract AF_UNIX socket */
|
||||
size_t l;
|
||||
|
||||
l = strlen(s+1);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
|
||||
a->size = sizeof(struct sockaddr_un);
|
||||
|
||||
} else {
|
||||
|
||||
if ((e = strchr(s, ':'))) {
|
||||
|
||||
if ((r = safe_atou(e+1, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(n = strndup(s, e-s)))
|
||||
return -ENOMEM;
|
||||
|
||||
/* IPv4 in w.x.y.z:p notation? */
|
||||
if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
|
||||
free(n);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (r > 0) {
|
||||
/* Gotcha, it's a traditional IPv4 address */
|
||||
free(n);
|
||||
|
||||
a->sockaddr.in4.sin_family = AF_INET;
|
||||
a->sockaddr.in4.sin_port = htons((uint16_t) u);
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
unsigned idx;
|
||||
|
||||
if (strlen(n) > IF_NAMESIZE-1) {
|
||||
free(n);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Uh, our last resort, an interface name */
|
||||
idx = if_nametoindex(n);
|
||||
free(n);
|
||||
|
||||
if (idx == 0)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
|
||||
a->sockaddr.in6.sin6_scope_id = idx;
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Just a port */
|
||||
if ((r = safe_atou(s, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_verify(const SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
case AF_INET:
|
||||
if (a->size != sizeof(struct sockaddr_in))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->sockaddr.in4.sin_port == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
case AF_INET6:
|
||||
if (a->size != sizeof(struct sockaddr_in6))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->sockaddr.in6.sin6_port == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
case AF_UNIX:
|
||||
if (a->size < sizeof(sa_family_t))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->size > sizeof(sa_family_t)) {
|
||||
|
||||
if (a->sockaddr.un.sun_path[0] == 0) {
|
||||
/* abstract */
|
||||
if (a->size != sizeof(struct sockaddr_un))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
char *e;
|
||||
|
||||
/* path */
|
||||
if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->size != sizeof(sa_family_t) + (e - a->sockaddr.un.sun_path) + 1)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
int socket_address_print(const SocketAddress *a, char **p) {
|
||||
int r;
|
||||
assert(a);
|
||||
assert(p);
|
||||
|
||||
if ((r = socket_address_verify(a)) < 0)
|
||||
return r;
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
case AF_INET: {
|
||||
char *ret;
|
||||
|
||||
if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
|
||||
free(ret);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
|
||||
*p = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case AF_INET6: {
|
||||
char *ret;
|
||||
|
||||
if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
ret[0] = '[';
|
||||
if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
|
||||
free(ret);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
|
||||
*p = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case AF_UNIX: {
|
||||
char *ret;
|
||||
|
||||
if (a->size <= sizeof(sa_family_t)) {
|
||||
|
||||
if (!(ret = strdup("<unamed>")))
|
||||
return -ENOMEM;
|
||||
|
||||
} else if (a->sockaddr.un.sun_path[0] == 0) {
|
||||
/* abstract */
|
||||
|
||||
/* FIXME: We assume we can print the
|
||||
* socket path here and that it hasn't
|
||||
* more than one NUL byte. That is
|
||||
* actually an invalid assumption */
|
||||
|
||||
if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
ret[0] = '@';
|
||||
memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
|
||||
ret[sizeof(a->sockaddr.un.sun_path)] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
if (!(ret = strdup(a->sockaddr.un.sun_path)))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*p = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int socket_address_listen(
|
||||
const SocketAddress *a,
|
||||
int backlog,
|
||||
SocketAddressBindIPv6Only only,
|
||||
const char *bind_to_device,
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
int *ret) {
|
||||
|
||||
int r, fd, one;
|
||||
assert(a);
|
||||
assert(ret);
|
||||
|
||||
if ((r = socket_address_verify(a)) < 0)
|
||||
return r;
|
||||
|
||||
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) {
|
||||
int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
|
||||
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bind_to_device)
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
|
||||
goto fail;
|
||||
|
||||
one = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
|
||||
mode_t old_mask;
|
||||
|
||||
/* Create parents */
|
||||
mkdir_parents(a->sockaddr.un.sun_path, directory_mode);
|
||||
|
||||
/* Enforce the right access mode for the socket*/
|
||||
old_mask = umask(~ socket_mode);
|
||||
|
||||
/* Include the original umask in our mask */
|
||||
umask(~socket_mode | old_mask);
|
||||
|
||||
r = bind(fd, &a->sockaddr.sa, a->size);
|
||||
|
||||
if (r < 0 && errno == EADDRINUSE) {
|
||||
/* Unlink and try again */
|
||||
unlink(a->sockaddr.un.sun_path);
|
||||
r = bind(fd, &a->sockaddr.sa, a->size);
|
||||
}
|
||||
|
||||
umask(old_mask);
|
||||
} else
|
||||
r = bind(fd, &a->sockaddr.sa, a->size);
|
||||
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (a->type == SOCK_STREAM)
|
||||
if (listen(fd, backlog) < 0)
|
||||
goto fail;
|
||||
|
||||
*ret = fd;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
r = -errno;
|
||||
close_nointr_nofail(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool socket_address_can_accept(const SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
return
|
||||
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);
|
||||
}
|
||||
|
||||
bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
|
||||
assert(a);
|
||||
|
||||
if (socket_address_family(a) != AF_UNIX)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.un.sun_path[0] == 0)
|
||||
return false;
|
||||
|
||||
return path_startswith(a->sockaddr.un.sun_path, prefix);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foosocketutilhfoo
|
||||
#define foosocketutilhfoo
|
||||
|
||||
/***
|
||||
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 <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/un.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
|
||||
typedef struct SocketAddress {
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in in4;
|
||||
struct sockaddr_in6 in6;
|
||||
struct sockaddr_un un;
|
||||
struct sockaddr_storage storage;
|
||||
} sockaddr;
|
||||
|
||||
/* We store the size here explicitly due to the weird
|
||||
* sockaddr_un semantics for abstract sockets */
|
||||
socklen_t size;
|
||||
|
||||
/* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
|
||||
int type;
|
||||
} SocketAddress;
|
||||
|
||||
typedef enum SocketAddressBindIPv6Only {
|
||||
SOCKET_ADDRESS_DEFAULT,
|
||||
SOCKET_ADDRESS_BOTH,
|
||||
SOCKET_ADDRESS_IPV6_ONLY
|
||||
} SocketAddressBindIPv6Only;
|
||||
|
||||
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s);
|
||||
int socket_address_print(const SocketAddress *a, char **p);
|
||||
int socket_address_verify(const SocketAddress *a);
|
||||
|
||||
bool socket_address_can_accept(const SocketAddress *a);
|
||||
|
||||
int socket_address_listen(
|
||||
const SocketAddress *a,
|
||||
int backlog,
|
||||
SocketAddressBindIPv6Only only,
|
||||
const char *bind_to_device,
|
||||
mode_t directory_mode,
|
||||
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);
|
||||
|
||||
bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
|
||||
|
||||
#endif
|
||||
+1411
File diff suppressed because it is too large
Load Diff
+132
@@ -0,0 +1,132 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foosockethfoo
|
||||
#define foosockethfoo
|
||||
|
||||
/***
|
||||
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 Socket Socket;
|
||||
|
||||
#include "manager.h"
|
||||
#include "unit.h"
|
||||
#include "socket-util.h"
|
||||
#include "mount.h"
|
||||
|
||||
typedef enum SocketState {
|
||||
SOCKET_DEAD,
|
||||
SOCKET_START_PRE,
|
||||
SOCKET_START_POST,
|
||||
SOCKET_LISTENING,
|
||||
SOCKET_RUNNING,
|
||||
SOCKET_STOP_PRE,
|
||||
SOCKET_STOP_PRE_SIGTERM,
|
||||
SOCKET_STOP_PRE_SIGKILL,
|
||||
SOCKET_STOP_POST,
|
||||
SOCKET_FINAL_SIGTERM,
|
||||
SOCKET_FINAL_SIGKILL,
|
||||
SOCKET_MAINTAINANCE,
|
||||
_SOCKET_STATE_MAX,
|
||||
_SOCKET_STATE_INVALID = -1
|
||||
} SocketState;
|
||||
|
||||
typedef enum SocketExecCommand {
|
||||
SOCKET_EXEC_START_PRE,
|
||||
SOCKET_EXEC_START_POST,
|
||||
SOCKET_EXEC_STOP_PRE,
|
||||
SOCKET_EXEC_STOP_POST,
|
||||
_SOCKET_EXEC_COMMAND_MAX,
|
||||
_SOCKET_EXEC_COMMAND_INVALID = -1
|
||||
} SocketExecCommand;
|
||||
|
||||
typedef enum SocketType {
|
||||
SOCKET_SOCKET,
|
||||
SOCKET_FIFO,
|
||||
_SOCKET_FIFO_MAX,
|
||||
_SOCKET_FIFO_INVALID = -1
|
||||
} SocketType;
|
||||
|
||||
typedef struct SocketPort SocketPort;
|
||||
|
||||
struct SocketPort {
|
||||
SocketType type;
|
||||
int fd;
|
||||
|
||||
SocketAddress address;
|
||||
char *path;
|
||||
|
||||
Watch fd_watch;
|
||||
|
||||
LIST_FIELDS(SocketPort, port);
|
||||
};
|
||||
|
||||
struct Socket {
|
||||
Meta meta;
|
||||
|
||||
LIST_HEAD(SocketPort, ports);
|
||||
|
||||
/* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
|
||||
bool bind_ipv6_only;
|
||||
unsigned backlog;
|
||||
|
||||
usec_t timeout_usec;
|
||||
|
||||
ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
|
||||
ExecContext exec_context;
|
||||
|
||||
Service *service;
|
||||
|
||||
SocketState state, deserialized_state;
|
||||
|
||||
KillMode kill_mode;
|
||||
|
||||
ExecCommand* control_command;
|
||||
SocketExecCommand control_command_id;
|
||||
pid_t control_pid;
|
||||
|
||||
char *bind_to_device;
|
||||
mode_t directory_mode;
|
||||
mode_t socket_mode;
|
||||
|
||||
bool accept;
|
||||
unsigned n_accepted;
|
||||
|
||||
bool failure;
|
||||
Watch timer_watch;
|
||||
};
|
||||
|
||||
/* Called from the service code when collecting fds */
|
||||
int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
|
||||
|
||||
/* Called from the service when it shut down */
|
||||
void socket_notify_service_dead(Socket *s);
|
||||
|
||||
/* Called from the mount code figure out if a mount is a dependency of
|
||||
* any of the sockets of this socket */
|
||||
int socket_add_one_mount_link(Socket *s, Mount *m);
|
||||
|
||||
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
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
#include "specifier.h"
|
||||
|
||||
/*
|
||||
* Generic infrastructure for replacing %x style specifiers in
|
||||
* strings. Will call a callback for each replacement.
|
||||
*
|
||||
*/
|
||||
|
||||
char *specifier_printf(const char *text, const Specifier table[], void *userdata) {
|
||||
char *r, *t;
|
||||
const char *f;
|
||||
bool percent = false;
|
||||
size_t l;
|
||||
|
||||
assert(text);
|
||||
assert(table);
|
||||
|
||||
l = strlen(text);
|
||||
if (!(r = new(char, l+1)))
|
||||
return NULL;
|
||||
|
||||
t = r;
|
||||
|
||||
for (f = text; *f; f++, l--) {
|
||||
|
||||
if (percent) {
|
||||
if (*f == '%')
|
||||
*(t++) = '%';
|
||||
else {
|
||||
const Specifier *i;
|
||||
|
||||
for (i = table; i->specifier; i++)
|
||||
if (i->specifier == *f)
|
||||
break;
|
||||
|
||||
if (i->lookup) {
|
||||
char *n, *w;
|
||||
size_t k, j;
|
||||
|
||||
if (!(w = i->lookup(i->specifier, i->data, userdata))) {
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
j = t - r;
|
||||
k = strlen(w);
|
||||
|
||||
if (!(n = new(char, j + k + l + 1))) {
|
||||
free(r);
|
||||
free(w);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(n, r, j);
|
||||
memcpy(n + j, w, k);
|
||||
|
||||
free(r);
|
||||
free(w);
|
||||
|
||||
r = n;
|
||||
t = n + j + k;
|
||||
} else {
|
||||
*(t++) = '%';
|
||||
*(t++) = *f;
|
||||
}
|
||||
}
|
||||
|
||||
percent = false;
|
||||
} else if (*f == '%')
|
||||
percent = true;
|
||||
else
|
||||
*(t++) = *f;
|
||||
}
|
||||
|
||||
*t = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Generic handler for simple string replacements */
|
||||
|
||||
char* specifier_string(char specifier, void *data, void *userdata) {
|
||||
assert(data);
|
||||
|
||||
return strdup(strempty(data));
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foospecifierhfoo
|
||||
#define foospecifierhfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
typedef char* (*SpecifierCallback)(char specifier, void *data, void *userdata);
|
||||
|
||||
typedef struct Specifier {
|
||||
const char specifier;
|
||||
const SpecifierCallback lookup;
|
||||
void *data;
|
||||
} Specifier;
|
||||
|
||||
char *specifier_printf(const char *text, const Specifier table[], void *userdata);
|
||||
|
||||
char* specifier_string(char specifier, void *data, void *userdata);
|
||||
|
||||
#endif
|
||||
+503
@@ -0,0 +1,503 @@
|
||||
/*-*- 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
|
||||
char *strv_find(char **l, const char *name) {
|
||||
char **i;
|
||||
|
||||
assert(l);
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (streq(*i, name))
|
||||
return *i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void strv_free(char **l) {
|
||||
char **k;
|
||||
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
for (k = l; *k; k++)
|
||||
free(*k);
|
||||
|
||||
free(l);
|
||||
}
|
||||
|
||||
char **strv_copy(char **l) {
|
||||
char **r, **k;
|
||||
|
||||
if (!(r = new(char*, strv_length(l)+1)))
|
||||
return NULL;
|
||||
|
||||
for (k = r; *l; k++, l++)
|
||||
if (!(*k = strdup(*l)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--, l--; k >= r; k--, l--)
|
||||
free(*k);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned strv_length(char **l) {
|
||||
unsigned n = 0;
|
||||
|
||||
if (!l)
|
||||
return 0;
|
||||
|
||||
for (; *l; l++)
|
||||
n++;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
char **strv_new_ap(const char *x, va_list ap) {
|
||||
const char *s;
|
||||
char **a;
|
||||
unsigned n = 0, i = 0;
|
||||
va_list aq;
|
||||
|
||||
|
||||
if (x) {
|
||||
n = 1;
|
||||
|
||||
va_copy(aq, ap);
|
||||
while (va_arg(aq, const char*))
|
||||
n++;
|
||||
va_end(aq);
|
||||
}
|
||||
|
||||
if (!(a = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
if (x) {
|
||||
if (!(a[i] = strdup(x))) {
|
||||
free(a);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
while ((s = va_arg(ap, const char*))) {
|
||||
if (!(a[i] = strdup(s)))
|
||||
goto fail;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
a[i] = NULL;
|
||||
|
||||
return a;
|
||||
|
||||
fail:
|
||||
|
||||
for (; i > 0; i--)
|
||||
if (a[i-1])
|
||||
free(a[i-1]);
|
||||
|
||||
free(a);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **strv_new(const char *x, ...) {
|
||||
char **r;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, x);
|
||||
r = strv_new_ap(x, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_merge(char **a, char **b) {
|
||||
char **r, **k;
|
||||
|
||||
if (!a)
|
||||
return strv_copy(b);
|
||||
|
||||
if (!b)
|
||||
return strv_copy(a);
|
||||
|
||||
if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
|
||||
return NULL;
|
||||
|
||||
for (k = r; *a; k++, a++)
|
||||
if (!(*k = strdup(*a)))
|
||||
goto fail;
|
||||
for (; *b; k++, b++)
|
||||
if (!(*k = strdup(*b)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **strv_merge_concat(char **a, char **b, const char *suffix) {
|
||||
char **r, **k;
|
||||
|
||||
/* Like strv_merge(), but appends suffix to all strings in b, before adding */
|
||||
|
||||
if (!b)
|
||||
return strv_copy(a);
|
||||
|
||||
if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
|
||||
return NULL;
|
||||
|
||||
for (k = r; *a; k++, a++)
|
||||
if (!(*k = strdup(*a)))
|
||||
goto fail;
|
||||
for (; *b; k++, b++)
|
||||
if (!(*k = strappend(*b, suffix)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
char **strv_split(const char *s, const char *separator) {
|
||||
char *state;
|
||||
char *w;
|
||||
size_t l;
|
||||
unsigned n, i;
|
||||
char **r;
|
||||
|
||||
assert(s);
|
||||
|
||||
n = 0;
|
||||
FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
|
||||
n++;
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
|
||||
if (!(r[i++] = strndup(w, l))) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_split_quoted(const char *s) {
|
||||
char *state;
|
||||
char *w;
|
||||
size_t l;
|
||||
unsigned n, i;
|
||||
char **r;
|
||||
|
||||
assert(s);
|
||||
|
||||
n = 0;
|
||||
FOREACH_WORD_QUOTED(w, l, s, state)
|
||||
n++;
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
FOREACH_WORD_QUOTED(w, l, s, state)
|
||||
if (!(r[i++] = strndup(w, l))) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
char *strv_join(char **l, const char *separator) {
|
||||
char *r, *e;
|
||||
char **s;
|
||||
size_t n, k;
|
||||
|
||||
if (!separator)
|
||||
separator = " ";
|
||||
|
||||
k = strlen(separator);
|
||||
|
||||
n = 0;
|
||||
STRV_FOREACH(s, l) {
|
||||
if (n != 0)
|
||||
n += k;
|
||||
n += strlen(*s);
|
||||
}
|
||||
|
||||
if (!(r = new(char, n+1)))
|
||||
return NULL;
|
||||
|
||||
e = r;
|
||||
STRV_FOREACH(s, l) {
|
||||
if (e != r)
|
||||
e = stpcpy(e, separator);
|
||||
|
||||
e = stpcpy(e, *s);
|
||||
}
|
||||
|
||||
*e = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_append(char **l, const char *s) {
|
||||
char **r, **k;
|
||||
|
||||
if (!l)
|
||||
return strv_new(s, NULL);
|
||||
|
||||
if (!s)
|
||||
return strv_copy(l);
|
||||
|
||||
if (!(r = new(char*, strv_length(l)+2)))
|
||||
return NULL;
|
||||
|
||||
for (k = r; *l; k++, l++)
|
||||
if (!(*k = strdup(*l)))
|
||||
goto fail;
|
||||
|
||||
if (!(*(k++) = strdup(s)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **strv_uniq(char **l) {
|
||||
char **i;
|
||||
|
||||
/* Drops duplicate entries. The first identical string will be
|
||||
* kept, the others dropped */
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
strv_remove(i+1, *i);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
char **strv_remove(char **l, const char *s) {
|
||||
char **f, **t;
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
/* Drops every occurence of s in the string list */
|
||||
|
||||
for (f = t = l; *f; f++) {
|
||||
|
||||
if (streq(*f, s)) {
|
||||
free(*f);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(t++) = *f;
|
||||
}
|
||||
|
||||
*t = NULL;
|
||||
return l;
|
||||
}
|
||||
|
||||
static int env_append(char **r, char ***k, char **a) {
|
||||
assert(r);
|
||||
assert(k);
|
||||
assert(a);
|
||||
|
||||
/* Add the entries of a to *k unless they already exist in *r
|
||||
* in which case they are overriden instead. This assumes
|
||||
* there is enough space in the r */
|
||||
|
||||
for (; *a; a++) {
|
||||
char **j;
|
||||
size_t n = strcspn(*a, "=") + 1;
|
||||
|
||||
for (j = r; j < *k; j++)
|
||||
if (strncmp(*j, *a, n) == 0)
|
||||
break;
|
||||
|
||||
if (j >= *k)
|
||||
(*k)++;
|
||||
else
|
||||
free(*j);
|
||||
|
||||
if (!(*j = strdup(*a)))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **strv_env_merge(char **x, ...) {
|
||||
size_t n = 0;
|
||||
char **l, **k, **r;
|
||||
va_list ap;
|
||||
|
||||
/* Merges an arbitrary number of environment sets */
|
||||
|
||||
if (x) {
|
||||
n += strv_length(x);
|
||||
|
||||
va_start(ap, x);
|
||||
while ((l = va_arg(ap, char**)))
|
||||
n += strv_length(l);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
k = r;
|
||||
|
||||
if (x) {
|
||||
if (env_append(r, &k, x) < 0)
|
||||
goto fail;
|
||||
|
||||
va_start(ap, x);
|
||||
while ((l = va_arg(ap, char**)))
|
||||
if (env_append(r, &k, l) < 0)
|
||||
goto fail;
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
*k = NULL;
|
||||
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool env_match(const char *t, const char *pattern) {
|
||||
assert(t);
|
||||
assert(pattern);
|
||||
|
||||
/* pattern a matches string a
|
||||
* a matches a=
|
||||
* a matches a=b
|
||||
* a= matches a=
|
||||
* a=b matches a=b
|
||||
* a= does not match a
|
||||
* a=b does not match a=
|
||||
* a=b does not match a
|
||||
* a=b does not match a=c */
|
||||
|
||||
if (streq(t, pattern))
|
||||
return true;
|
||||
|
||||
if (!strchr(pattern, '=')) {
|
||||
size_t l = strlen(pattern);
|
||||
|
||||
return strncmp(t, pattern, l) == 0 && t[l] == '=';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char **strv_env_delete(char **x, ...) {
|
||||
size_t n = 0, i = 0;
|
||||
char **l, **k, **r, **j;
|
||||
va_list ap;
|
||||
|
||||
/* Deletes every entry fromx that is mentioned in the other
|
||||
* string lists */
|
||||
|
||||
n = strv_length(x);
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
STRV_FOREACH(k, x) {
|
||||
va_start(ap, x);
|
||||
|
||||
while ((l = va_arg(ap, char**)))
|
||||
STRV_FOREACH(j, l)
|
||||
if (env_match(*k, *j))
|
||||
goto delete;
|
||||
|
||||
va_end(ap);
|
||||
|
||||
if (!(r[i++] = strdup(*k))) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
delete:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
|
||||
assert(i <= n);
|
||||
|
||||
return r;
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef foostrvhfoo
|
||||
#define foostrvhfoo
|
||||
|
||||
/***
|
||||
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 <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
char *strv_find(char **l, const char *name);
|
||||
void strv_free(char **l);
|
||||
char **strv_copy(char **l) _malloc;
|
||||
unsigned strv_length(char **l);
|
||||
|
||||
char **strv_merge(char **a, char **b);
|
||||
char **strv_merge_concat(char **a, char **b, const char *suffix);
|
||||
char **strv_append(char **l, const char *s);
|
||||
|
||||
char **strv_remove(char **l, const char *s);
|
||||
char **strv_uniq(char **l);
|
||||
|
||||
#define strv_contains(l, s) (!!strv_find((l), (s)))
|
||||
|
||||
char **strv_new(const char *x, ...) _sentinel _malloc;
|
||||
char **strv_new_ap(const char *x, va_list ap) _malloc;
|
||||
|
||||
static inline bool strv_isempty(char **l) {
|
||||
return !l || !*l;
|
||||
}
|
||||
|
||||
char **strv_split(const char *s, const char *separator) _malloc;
|
||||
char **strv_split_quoted(const char *s) _malloc;
|
||||
|
||||
char *strv_join(char **l, const char *separator) _malloc;
|
||||
|
||||
char **strv_env_merge(char **x, ...) _sentinel;
|
||||
char **strv_env_delete(char **x, ...) _sentinel;
|
||||
|
||||
#define STRV_FOREACH(s, l) \
|
||||
for ((s) = (l); (s) && *(s); (s)++)
|
||||
|
||||
#define STRV_FOREACH_BACKWARDS(s, l) \
|
||||
for (; (l) && ((s) >= (l)); (s)--)
|
||||
|
||||
#endif
|
||||
+578
@@ -0,0 +1,578 @@
|
||||
/*-*- 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 <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/swap.h>
|
||||
|
||||
#include "unit.h"
|
||||
#include "swap.h"
|
||||
#include "load-fragment.h"
|
||||
#include "load-dropin.h"
|
||||
#include "unit-name.h"
|
||||
#include "dbus-swap.h"
|
||||
|
||||
static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
|
||||
[SWAP_DEAD] = UNIT_INACTIVE,
|
||||
[SWAP_ACTIVE] = UNIT_ACTIVE,
|
||||
[SWAP_MAINTAINANCE] = UNIT_INACTIVE
|
||||
};
|
||||
|
||||
static void swap_init(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
|
||||
assert(s);
|
||||
assert(s->meta.load_state == UNIT_STUB);
|
||||
|
||||
s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1;
|
||||
}
|
||||
|
||||
static void swap_done(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
free(s->what);
|
||||
free(s->parameters_etc_fstab.what);
|
||||
free(s->parameters_proc_swaps.what);
|
||||
free(s->parameters_fragment.what);
|
||||
}
|
||||
|
||||
int swap_add_one_mount_link(Swap *s, Mount *m) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(m);
|
||||
|
||||
if (s->meta.load_state != UNIT_LOADED ||
|
||||
m->meta.load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
if (is_device_path(s->what))
|
||||
return 0;
|
||||
|
||||
if (!path_startswith(s->what, m->where))
|
||||
return 0;
|
||||
|
||||
if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, UNIT(s), true)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_dependency(UNIT(s), UNIT_REQUIRES, UNIT(m), true)) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swap_add_mount_links(Swap *s) {
|
||||
Meta *other;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_MOUNT])
|
||||
if ((r = swap_add_one_mount_link(s, (Mount*) other)) < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swap_add_target_links(Swap *s) {
|
||||
Unit *tu;
|
||||
SwapParameters *p;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (s->from_fragment)
|
||||
p = &s->parameters_fragment;
|
||||
else if (s->from_etc_fstab)
|
||||
p = &s->parameters_etc_fstab;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if ((r = manager_load_unit(s->meta.manager, SPECIAL_SWAP_TARGET, NULL, &tu)) < 0)
|
||||
return r;
|
||||
|
||||
if (!p->noauto && p->handle)
|
||||
if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0)
|
||||
return r;
|
||||
|
||||
return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true);
|
||||
}
|
||||
|
||||
static int swap_verify(Swap *s) {
|
||||
bool b;
|
||||
char *e;
|
||||
|
||||
if (UNIT(s)->meta.load_state != UNIT_LOADED)
|
||||
return 0;
|
||||
|
||||
if (!(e = unit_name_from_path(s->what, ".swap")))
|
||||
return -ENOMEM;
|
||||
|
||||
b = unit_has_name(UNIT(s), e);
|
||||
free(e);
|
||||
|
||||
if (!b) {
|
||||
log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->meta.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swap_load(Unit *u) {
|
||||
int r;
|
||||
Swap *s = SWAP(u);
|
||||
|
||||
assert(s);
|
||||
assert(u->meta.load_state == UNIT_STUB);
|
||||
|
||||
/* Load a .swap file */
|
||||
if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u->meta.load_state == UNIT_LOADED) {
|
||||
|
||||
if (s->meta.fragment_path)
|
||||
s->from_fragment = true;
|
||||
|
||||
if (!s->what) {
|
||||
if (s->parameters_fragment.what)
|
||||
s->what = strdup(s->parameters_fragment.what);
|
||||
else if (s->parameters_etc_fstab.what)
|
||||
s->what = strdup(s->parameters_etc_fstab.what);
|
||||
else if (s->parameters_proc_swaps.what)
|
||||
s->what = strdup(s->parameters_proc_swaps.what);
|
||||
else
|
||||
s->what = unit_name_to_path(u->meta.id);
|
||||
|
||||
if (!s->what)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
path_kill_slashes(s->what);
|
||||
|
||||
if (!s->meta.description)
|
||||
if ((r = unit_set_description(u, s->what)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = unit_add_node_link(u, s->what,
|
||||
(u->meta.manager->running_as == MANAGER_INIT ||
|
||||
u->meta.manager->running_as == MANAGER_SYSTEM))) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = swap_add_mount_links(s)) < 0)
|
||||
return r;
|
||||
|
||||
if ((r = swap_add_target_links(s)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return swap_verify(s);
|
||||
}
|
||||
|
||||
static int swap_find(Manager *m, const char *what, Unit **_u) {
|
||||
Unit *u;
|
||||
char *e;
|
||||
|
||||
assert(m);
|
||||
assert(what);
|
||||
assert(_u);
|
||||
|
||||
/* /proc/swaps and /etc/fstab might refer to this device by
|
||||
* different names (e.g. one by uuid, the other by the kernel
|
||||
* name), we hence need to look for all aliases we are aware
|
||||
* of for this device */
|
||||
|
||||
if (!(e = unit_name_from_path(what, ".device")))
|
||||
return -ENOMEM;
|
||||
|
||||
u = manager_get_unit(m, e);
|
||||
free(e);
|
||||
|
||||
if (u) {
|
||||
Iterator i;
|
||||
const char *d;
|
||||
|
||||
SET_FOREACH(d, u->meta.names, i) {
|
||||
Unit *k;
|
||||
|
||||
if (!(e = unit_name_change_suffix(d, ".swap")))
|
||||
return -ENOMEM;
|
||||
|
||||
k = manager_get_unit(m, e);
|
||||
free(e);
|
||||
|
||||
if (k) {
|
||||
*_u = k;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*_u = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int swap_add_one(
|
||||
Manager *m,
|
||||
const char *what,
|
||||
int priority,
|
||||
bool noauto,
|
||||
bool handle,
|
||||
bool from_proc_swaps) {
|
||||
Unit *u = NULL;
|
||||
char *e = NULL, *w = NULL;
|
||||
bool delete;
|
||||
int r;
|
||||
SwapParameters *p;
|
||||
|
||||
assert(m);
|
||||
assert(what);
|
||||
|
||||
if (!(e = unit_name_from_path(what, ".swap")))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(u = manager_get_unit(m, e)))
|
||||
if ((r = swap_find(m, what, &u)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (!u) {
|
||||
delete = true;
|
||||
|
||||
if (!(u = unit_new(m))) {
|
||||
free(e);
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else
|
||||
delete = false;
|
||||
|
||||
if ((r = unit_add_name(u, e)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (!(w = strdup(what))) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (from_proc_swaps) {
|
||||
p = &SWAP(u)->parameters_proc_swaps;
|
||||
SWAP(u)->from_proc_swaps = true;
|
||||
} else {
|
||||
p = &SWAP(u)->parameters_etc_fstab;
|
||||
SWAP(u)->from_etc_fstab = true;
|
||||
}
|
||||
|
||||
free(p->what);
|
||||
p->what = w;
|
||||
|
||||
p->priority = priority;
|
||||
p->noauto = noauto;
|
||||
p->handle = handle;
|
||||
|
||||
if (delete)
|
||||
unit_add_to_load_queue(u);
|
||||
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
free(e);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(w);
|
||||
free(e);
|
||||
|
||||
if (delete && u)
|
||||
unit_free(u);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void swap_set_state(Swap *s, SwapState state) {
|
||||
SwapState 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,
|
||||
swap_state_to_string(old_state),
|
||||
swap_state_to_string(state));
|
||||
|
||||
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
|
||||
}
|
||||
|
||||
static int swap_coldplug(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
SwapState new_state = SWAP_DEAD;
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SWAP_DEAD);
|
||||
|
||||
if (s->deserialized_state != s->state)
|
||||
new_state = s->deserialized_state;
|
||||
else if (s->from_proc_swaps)
|
||||
new_state = SWAP_ACTIVE;
|
||||
|
||||
if (new_state != s->state)
|
||||
swap_set_state(s, new_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void swap_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
Swap *s = SWAP(u);
|
||||
SwapParameters *p;
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
|
||||
if (s->from_proc_swaps)
|
||||
p = &s->parameters_proc_swaps;
|
||||
else if (s->from_fragment)
|
||||
p = &s->parameters_fragment;
|
||||
else
|
||||
p = &s->parameters_etc_fstab;
|
||||
|
||||
fprintf(f,
|
||||
"%sSwap State: %s\n"
|
||||
"%sWhat: %s\n"
|
||||
"%sPriority: %i\n"
|
||||
"%sNoAuto: %s\n"
|
||||
"%sHandle: %s\n"
|
||||
"%sFrom /etc/fstab: %s\n"
|
||||
"%sFrom /proc/swaps: %s\n"
|
||||
"%sFrom fragment: %s\n",
|
||||
prefix, swap_state_to_string(s->state),
|
||||
prefix, s->what,
|
||||
prefix, p->priority,
|
||||
prefix, yes_no(p->noauto),
|
||||
prefix, yes_no(p->handle),
|
||||
prefix, yes_no(s->from_etc_fstab),
|
||||
prefix, yes_no(s->from_proc_swaps),
|
||||
prefix, yes_no(s->from_fragment));
|
||||
}
|
||||
|
||||
static void swap_enter_dead(Swap *s, bool success) {
|
||||
assert(s);
|
||||
|
||||
swap_set_state(s, success ? SWAP_MAINTAINANCE : SWAP_DEAD);
|
||||
}
|
||||
|
||||
static int swap_start(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
int priority = -1;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(s->state == SWAP_DEAD || s->state == SWAP_MAINTAINANCE);
|
||||
|
||||
if (s->from_fragment)
|
||||
priority = s->parameters_fragment.priority;
|
||||
else if (s->from_etc_fstab)
|
||||
priority = s->parameters_etc_fstab.priority;
|
||||
|
||||
r = swapon(s->what, (priority << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK);
|
||||
|
||||
if (r < 0 && errno != EBUSY) {
|
||||
r = -errno;
|
||||
swap_enter_dead(s, false);
|
||||
return r;
|
||||
}
|
||||
|
||||
swap_set_state(s, SWAP_ACTIVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swap_stop(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
assert(s->state == SWAP_ACTIVE);
|
||||
|
||||
r = swapoff(s->what);
|
||||
swap_enter_dead(s, r >= 0 || errno == EINVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
Swap *s = SWAP(u);
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(fds);
|
||||
|
||||
unit_serialize_item(u, f, "state", swap_state_to_string(s->state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||
Swap *s = SWAP(u);
|
||||
|
||||
assert(s);
|
||||
assert(fds);
|
||||
|
||||
if (streq(key, "state")) {
|
||||
SwapState state;
|
||||
|
||||
if ((state = swap_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 swap_active_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return state_translation_table[SWAP(u)->state];
|
||||
}
|
||||
|
||||
static const char *swap_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return swap_state_to_string(SWAP(u)->state);
|
||||
}
|
||||
|
||||
static bool swap_check_gc(Unit *u) {
|
||||
Swap *s = SWAP(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
return s->from_etc_fstab || s->from_proc_swaps;
|
||||
}
|
||||
|
||||
static int swap_load_proc_swaps(Manager *m) {
|
||||
rewind(m->proc_swaps);
|
||||
|
||||
(void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n");
|
||||
|
||||
for (;;) {
|
||||
char *dev = NULL, *d;
|
||||
int prio = 0, k;
|
||||
|
||||
if ((k = fscanf(m->proc_swaps,
|
||||
"%ms " /* device/file */
|
||||
"%*s " /* type of swap */
|
||||
"%*s " /* swap size */
|
||||
"%*s " /* used */
|
||||
"%i\n", /* priority */
|
||||
&dev, &prio)) != 2) {
|
||||
|
||||
if (k == EOF)
|
||||
break;
|
||||
|
||||
free(dev);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
d = cunescape(dev);
|
||||
free(dev);
|
||||
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
k = swap_add_one(m, d, prio, false, false, true);
|
||||
free(d);
|
||||
|
||||
if (k < 0)
|
||||
return k;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void swap_shutdown(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->proc_swaps) {
|
||||
fclose(m->proc_swaps);
|
||||
m->proc_swaps = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
|
||||
[SWAP_DEAD] = "dead",
|
||||
[SWAP_ACTIVE] = "active",
|
||||
[SWAP_MAINTAINANCE] = "maintainance"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
|
||||
|
||||
static int swap_enumerate(Manager *m) {
|
||||
int r;
|
||||
assert(m);
|
||||
|
||||
if (!m->proc_swaps)
|
||||
if (!(m->proc_swaps = fopen("/proc/swaps", "re")))
|
||||
return -errno;
|
||||
|
||||
if ((r = swap_load_proc_swaps(m)) < 0)
|
||||
swap_shutdown(m);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
const UnitVTable swap_vtable = {
|
||||
.suffix = ".swap",
|
||||
|
||||
.no_instances = true,
|
||||
.no_isolate = true,
|
||||
|
||||
.init = swap_init,
|
||||
.load = swap_load,
|
||||
.done = swap_done,
|
||||
|
||||
.coldplug = swap_coldplug,
|
||||
|
||||
.dump = swap_dump,
|
||||
|
||||
.start = swap_start,
|
||||
.stop = swap_stop,
|
||||
|
||||
.serialize = swap_serialize,
|
||||
.deserialize_item = swap_deserialize_item,
|
||||
|
||||
.active_state = swap_active_state,
|
||||
.sub_state_to_string = swap_sub_state_to_string,
|
||||
|
||||
.check_gc = swap_check_gc,
|
||||
|
||||
.bus_message_handler = bus_swap_message_handler,
|
||||
|
||||
.enumerate = swap_enumerate,
|
||||
.shutdown = swap_shutdown
|
||||
};
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef fooswaphfoo
|
||||
#define fooswaphfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
Copyright 2010 Maarten Lankhorst
|
||||
|
||||
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 Swap Swap;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum SwapState {
|
||||
SWAP_DEAD,
|
||||
SWAP_ACTIVE,
|
||||
SWAP_MAINTAINANCE,
|
||||
_SWAP_STATE_MAX,
|
||||
_SWAP_STATE_INVALID = -1
|
||||
} SwapState;
|
||||
|
||||
typedef struct SwapParameters {
|
||||
char *what;
|
||||
int priority;
|
||||
bool noauto:1;
|
||||
bool handle:1;
|
||||
} SwapParameters;
|
||||
|
||||
struct Swap {
|
||||
Meta meta;
|
||||
|
||||
SwapParameters parameters_etc_fstab;
|
||||
SwapParameters parameters_proc_swaps;
|
||||
SwapParameters parameters_fragment;
|
||||
|
||||
char *what;
|
||||
|
||||
bool from_etc_fstab:1;
|
||||
bool from_proc_swaps:1;
|
||||
bool from_fragment:1;
|
||||
|
||||
SwapState state, deserialized_state;
|
||||
};
|
||||
|
||||
extern const UnitVTable swap_vtable;
|
||||
|
||||
int swap_add_one(Manager *m, const char *what, int prio, bool no_auto, bool handle, bool from_proc_swap);
|
||||
|
||||
int swap_add_one_mount_link(Swap *s, Mount *m);
|
||||
|
||||
const char* swap_state_to_string(SwapState i);
|
||||
SwapState swap_state_from_string(const char *s);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,956 @@
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
using Gtk;
|
||||
using GLib;
|
||||
using DBus;
|
||||
using Pango;
|
||||
|
||||
static bool session = false;
|
||||
|
||||
public class LeftLabel : Label {
|
||||
public LeftLabel(string? text = null) {
|
||||
if (text != null)
|
||||
set_markup("<b>%s</b>".printf(text));
|
||||
set_alignment(0, 0);
|
||||
set_padding(6, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public class RightLabel : Label {
|
||||
public RightLabel(string? text = null) {
|
||||
set_text_or_na(text);
|
||||
set_alignment(0, 0);
|
||||
set_ellipsize(EllipsizeMode.START);
|
||||
set_selectable(true);
|
||||
}
|
||||
|
||||
public void set_text_or_na(string? text = null) {
|
||||
if (text == null || text == "")
|
||||
set_markup("<i>n/a</i>");
|
||||
else
|
||||
set_text(text);
|
||||
}
|
||||
|
||||
public void set_markup_or_na(string? text = null) {
|
||||
if (text == null || text == "")
|
||||
set_markup("<i>n/a</i>");
|
||||
else
|
||||
set_markup(text);
|
||||
}
|
||||
}
|
||||
|
||||
public class MainWindow : Window {
|
||||
|
||||
private string? current_unit_id;
|
||||
private uint32 current_job_id;
|
||||
|
||||
private TreeView unit_view;
|
||||
private TreeView job_view;
|
||||
|
||||
private ListStore unit_model;
|
||||
private ListStore job_model;
|
||||
|
||||
private Button start_button;
|
||||
private Button stop_button;
|
||||
private Button restart_button;
|
||||
private Button reload_button;
|
||||
private Button cancel_button;
|
||||
|
||||
private Entry unit_load_entry;
|
||||
private Button unit_load_button;
|
||||
|
||||
private Button server_snapshot_button;
|
||||
private Button server_reload_button;
|
||||
|
||||
private Connection bus;
|
||||
private Manager manager;
|
||||
|
||||
private RightLabel unit_id_label;
|
||||
private RightLabel unit_aliases_label;
|
||||
private RightLabel unit_dependency_label;
|
||||
private RightLabel unit_description_label;
|
||||
private RightLabel unit_load_state_label;
|
||||
private RightLabel unit_active_state_label;
|
||||
private RightLabel unit_sub_state_label;
|
||||
private RightLabel unit_fragment_path_label;
|
||||
private RightLabel unit_active_enter_timestamp_label;
|
||||
private RightLabel unit_active_exit_timestamp_label;
|
||||
private RightLabel unit_can_start_label;
|
||||
private RightLabel unit_can_reload_label;
|
||||
private RightLabel unit_cgroup_label;
|
||||
|
||||
private RightLabel job_id_label;
|
||||
private RightLabel job_state_label;
|
||||
private RightLabel job_type_label;
|
||||
|
||||
private ComboBox unit_type_combo_box;
|
||||
|
||||
public MainWindow() throws DBus.Error {
|
||||
title = session ? "systemd Session Manager" : "systemd System Manager";
|
||||
position = WindowPosition.CENTER;
|
||||
set_default_size(1000, 700);
|
||||
set_border_width(12);
|
||||
destroy += Gtk.main_quit;
|
||||
|
||||
Notebook notebook = new Notebook();
|
||||
add(notebook);
|
||||
|
||||
Box unit_vbox = new VBox(false, 12);
|
||||
notebook.append_page(unit_vbox, new Label("Units"));
|
||||
unit_vbox.set_border_width(12);
|
||||
|
||||
Box job_vbox = new VBox(false, 12);
|
||||
notebook.append_page(job_vbox, new Label("Jobs"));
|
||||
job_vbox.set_border_width(12);
|
||||
|
||||
unit_type_combo_box = new ComboBox.text();
|
||||
Box type_hbox = new HBox(false, 6);
|
||||
type_hbox.pack_start(unit_type_combo_box, false, false, 0);
|
||||
unit_vbox.pack_start(type_hbox, false, false, 0);
|
||||
|
||||
unit_type_combo_box.append_text("Show All Units");
|
||||
unit_type_combo_box.append_text("Show Only Live Units");
|
||||
unit_type_combo_box.append_text("Services");
|
||||
unit_type_combo_box.append_text("Sockets");
|
||||
unit_type_combo_box.append_text("Devices");
|
||||
unit_type_combo_box.append_text("Mounts");
|
||||
unit_type_combo_box.append_text("Automounts");
|
||||
unit_type_combo_box.append_text("Targets");
|
||||
unit_type_combo_box.append_text("Snapshots");
|
||||
unit_type_combo_box.set_active(1);
|
||||
unit_type_combo_box.changed += unit_type_changed;
|
||||
|
||||
unit_load_entry = new Entry();
|
||||
unit_load_button = new Button.with_mnemonic("_Load");
|
||||
unit_load_button.set_sensitive(false);
|
||||
|
||||
unit_load_entry.changed += on_unit_load_entry_changed;
|
||||
unit_load_entry.activate += on_unit_load;
|
||||
unit_load_button.clicked += on_unit_load;
|
||||
|
||||
Box unit_load_hbox = new HBox(false, 6);
|
||||
unit_load_hbox.pack_start(unit_load_entry, false, true, 0);
|
||||
unit_load_hbox.pack_start(unit_load_button, false, true, 0);
|
||||
|
||||
server_snapshot_button = new Button.with_mnemonic("Take S_napshot");
|
||||
server_reload_button = new Button.with_mnemonic("Reload _Configuration");
|
||||
|
||||
server_snapshot_button.clicked += on_server_snapshot;
|
||||
server_reload_button.clicked += on_server_reload;
|
||||
|
||||
type_hbox.pack_end(server_snapshot_button, false, true, 0);
|
||||
type_hbox.pack_end(server_reload_button, false, true, 0);
|
||||
type_hbox.pack_end(unit_load_hbox, false, true, 24);
|
||||
|
||||
unit_model = new ListStore(7, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Unit));
|
||||
job_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(Job), typeof(uint32));
|
||||
|
||||
TreeModelFilter unit_model_filter;
|
||||
unit_model_filter = new TreeModelFilter(unit_model, null);
|
||||
unit_model_filter.set_visible_func(unit_filter);
|
||||
|
||||
unit_view = new TreeView.with_model(unit_model_filter);
|
||||
job_view = new TreeView.with_model(job_model);
|
||||
|
||||
unit_view.cursor_changed += unit_changed;
|
||||
job_view.cursor_changed += job_changed;
|
||||
|
||||
unit_view.insert_column_with_attributes(-1, "Load State", new CellRendererText(), "text", 2);
|
||||
unit_view.insert_column_with_attributes(-1, "Active State", new CellRendererText(), "text", 3);
|
||||
unit_view.insert_column_with_attributes(-1, "Unit State", new CellRendererText(), "text", 4);
|
||||
unit_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 0);
|
||||
unit_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 5);
|
||||
|
||||
job_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 0);
|
||||
job_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 1);
|
||||
job_view.insert_column_with_attributes(-1, "Type", new CellRendererText(), "text", 2);
|
||||
job_view.insert_column_with_attributes(-1, "State", new CellRendererText(), "text", 3);
|
||||
|
||||
ScrolledWindow scroll = new ScrolledWindow(null, null);
|
||||
scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
|
||||
scroll.set_shadow_type(ShadowType.IN);
|
||||
scroll.add(unit_view);
|
||||
unit_vbox.pack_start(scroll, true, true, 0);
|
||||
|
||||
scroll = new ScrolledWindow(null, null);
|
||||
scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
|
||||
scroll.set_shadow_type(ShadowType.IN);
|
||||
scroll.add(job_view);
|
||||
job_vbox.pack_start(scroll, true, true, 0);
|
||||
|
||||
unit_id_label = new RightLabel();
|
||||
unit_aliases_label = new RightLabel();
|
||||
unit_dependency_label = new RightLabel();
|
||||
unit_description_label = new RightLabel();
|
||||
unit_load_state_label = new RightLabel();
|
||||
unit_active_state_label = new RightLabel();
|
||||
unit_sub_state_label = new RightLabel();
|
||||
unit_fragment_path_label = new RightLabel();
|
||||
unit_active_enter_timestamp_label = new RightLabel();
|
||||
unit_active_exit_timestamp_label = new RightLabel();
|
||||
unit_can_start_label = new RightLabel();
|
||||
unit_can_reload_label = new RightLabel();
|
||||
unit_cgroup_label = new RightLabel();
|
||||
|
||||
job_id_label = new RightLabel();
|
||||
job_state_label = new RightLabel();
|
||||
job_type_label = new RightLabel();
|
||||
|
||||
unit_dependency_label.set_track_visited_links(false);
|
||||
unit_dependency_label.set_selectable(false);
|
||||
unit_dependency_label.activate_link += on_activate_link;
|
||||
|
||||
Table unit_table = new Table(8, 6, false);
|
||||
unit_table.set_row_spacings(6);
|
||||
unit_table.set_border_width(0);
|
||||
unit_vbox.pack_start(unit_table, false, true, 0);
|
||||
|
||||
Table job_table = new Table(2, 2, false);
|
||||
job_table.set_row_spacings(6);
|
||||
job_table.set_border_width(0);
|
||||
job_vbox.pack_start(job_table, false, true, 0);
|
||||
|
||||
unit_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_id_label, 1, 6, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Aliases:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_aliases_label, 1, 6, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Description:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_description_label, 1, 6, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Dependencies:"), 0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_dependency_label, 1, 6, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Fragment Path:"), 0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_fragment_path_label, 1, 6, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Control Group:"), 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_cgroup_label, 1, 6, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
|
||||
unit_table.attach(new LeftLabel("Load State:"), 0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_load_state_label, 1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Active State:"), 0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_active_state_label, 1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Unit State:"), 0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_sub_state_label, 1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
|
||||
unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 2, 3, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_active_enter_timestamp_label, 3, 4, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Active Exit Timestamp:"), 2, 3, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_active_exit_timestamp_label, 3, 4, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
|
||||
unit_table.attach(new LeftLabel("Can Start/Stop:"), 4, 5, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_can_start_label, 5, 6, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(new LeftLabel("Can Reload:"), 4, 5, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
unit_table.attach(unit_can_reload_label, 5, 6, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
|
||||
job_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
job_table.attach(job_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
job_table.attach(new LeftLabel("State:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
job_table.attach(job_state_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
job_table.attach(new LeftLabel("Type:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
job_table.attach(job_type_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
|
||||
|
||||
ButtonBox bbox = new HButtonBox();
|
||||
bbox.set_layout(ButtonBoxStyle.START);
|
||||
bbox.set_spacing(6);
|
||||
unit_vbox.pack_start(bbox, false, true, 0);
|
||||
|
||||
start_button = new Button.with_mnemonic("_Start");
|
||||
stop_button = new Button.with_mnemonic("Sto_p");
|
||||
reload_button = new Button.with_mnemonic("_Reload");
|
||||
restart_button = new Button.with_mnemonic("Res_tart");
|
||||
|
||||
start_button.clicked += on_start;
|
||||
stop_button.clicked += on_stop;
|
||||
reload_button.clicked += on_reload;
|
||||
restart_button.clicked += on_restart;
|
||||
|
||||
bbox.pack_start(start_button, false, true, 0);
|
||||
bbox.pack_start(stop_button, false, true, 0);
|
||||
bbox.pack_start(restart_button, false, true, 0);
|
||||
bbox.pack_start(reload_button, false, true, 0);
|
||||
|
||||
bbox = new HButtonBox();
|
||||
bbox.set_layout(ButtonBoxStyle.START);
|
||||
bbox.set_spacing(6);
|
||||
job_vbox.pack_start(bbox, false, true, 0);
|
||||
|
||||
cancel_button = new Button.with_mnemonic("_Cancel");
|
||||
|
||||
cancel_button.clicked += on_cancel;
|
||||
|
||||
bbox.pack_start(cancel_button, false, true, 0);
|
||||
|
||||
bus = Bus.get(session ? BusType.SESSION : BusType.SYSTEM);
|
||||
|
||||
manager = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager") as Manager;
|
||||
|
||||
manager.unit_new += on_unit_new;
|
||||
manager.job_new += on_job_new;
|
||||
manager.unit_removed += on_unit_removed;
|
||||
manager.job_removed += on_job_removed;
|
||||
|
||||
manager.subscribe();
|
||||
|
||||
clear_unit();
|
||||
clear_job();
|
||||
populate_unit_model();
|
||||
populate_job_model();
|
||||
}
|
||||
|
||||
public void populate_unit_model() throws DBus.Error {
|
||||
unit_model.clear();
|
||||
|
||||
var list = manager.list_units();
|
||||
|
||||
foreach (var i in list) {
|
||||
TreeIter iter;
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
i.unit_path,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
u.changed += on_unit_changed;
|
||||
|
||||
unit_model.append(out iter);
|
||||
unit_model.set(iter,
|
||||
0, i.id,
|
||||
1, i.description,
|
||||
2, i.load_state,
|
||||
3, i.active_state,
|
||||
4, i.sub_state,
|
||||
5, i.job_type != "" ? "→ %s".printf(i.job_type) : "",
|
||||
6, u);
|
||||
}
|
||||
}
|
||||
|
||||
public void populate_job_model() throws DBus.Error {
|
||||
job_model.clear();
|
||||
|
||||
var list = manager.list_jobs();
|
||||
|
||||
foreach (var i in list) {
|
||||
TreeIter iter;
|
||||
|
||||
Job j = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
i.job_path,
|
||||
"org.freedesktop.systemd1.Job") as Job;
|
||||
|
||||
j.changed += on_job_changed;
|
||||
|
||||
job_model.append(out iter);
|
||||
job_model.set(iter,
|
||||
0, "%u".printf(i.id),
|
||||
1, i.name,
|
||||
2, "→ %s".printf(i.type),
|
||||
3, i.state,
|
||||
4, j,
|
||||
5, i.id);
|
||||
}
|
||||
}
|
||||
|
||||
public Unit? get_current_unit() {
|
||||
TreePath p;
|
||||
unit_view.get_cursor(out p, null);
|
||||
|
||||
if (p == null)
|
||||
return null;
|
||||
|
||||
TreeModel model = unit_view.get_model();
|
||||
TreeIter iter;
|
||||
Unit u;
|
||||
|
||||
model.get_iter(out iter, p);
|
||||
model.get(iter, 6, out u);
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
public void unit_changed() {
|
||||
Unit u = get_current_unit();
|
||||
|
||||
if (u == null)
|
||||
clear_unit();
|
||||
else
|
||||
show_unit(u);
|
||||
}
|
||||
|
||||
public void clear_unit() {
|
||||
current_unit_id = null;
|
||||
|
||||
start_button.set_sensitive(false);
|
||||
stop_button.set_sensitive(false);
|
||||
reload_button.set_sensitive(false);
|
||||
restart_button.set_sensitive(false);
|
||||
|
||||
unit_id_label.set_text_or_na();
|
||||
unit_aliases_label.set_text_or_na();
|
||||
unit_description_label.set_text_or_na();
|
||||
unit_description_label.set_text_or_na();
|
||||
unit_load_state_label.set_text_or_na();
|
||||
unit_active_state_label.set_text_or_na();
|
||||
unit_sub_state_label.set_text_or_na();
|
||||
unit_fragment_path_label.set_text_or_na();
|
||||
unit_active_enter_timestamp_label.set_text_or_na();
|
||||
unit_active_exit_timestamp_label.set_text_or_na();
|
||||
unit_can_reload_label.set_text_or_na();
|
||||
unit_can_start_label.set_text_or_na();
|
||||
unit_cgroup_label.set_text_or_na();
|
||||
}
|
||||
|
||||
public string make_dependency_string(string? prefix, string word, string[] dependencies) {
|
||||
bool first = true;
|
||||
string r;
|
||||
|
||||
if (prefix == null)
|
||||
r = "";
|
||||
else
|
||||
r = prefix;
|
||||
|
||||
foreach (string i in dependencies) {
|
||||
if (r != "")
|
||||
r += first ? "\n" : ",";
|
||||
|
||||
if (first) {
|
||||
r += word;
|
||||
first = false;
|
||||
}
|
||||
|
||||
r += " <a href=\"" + i + "\">" + i + "</a>";
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public void show_unit(Unit unit) {
|
||||
current_unit_id = unit.id;
|
||||
|
||||
unit_id_label.set_text_or_na(current_unit_id);
|
||||
|
||||
string a = "";
|
||||
foreach (string i in unit.names) {
|
||||
if (i == current_unit_id)
|
||||
continue;
|
||||
|
||||
if (a == "")
|
||||
a = i;
|
||||
else
|
||||
a += "\n" + i;
|
||||
}
|
||||
|
||||
unit_aliases_label.set_text_or_na(a);
|
||||
|
||||
string[]
|
||||
requires = unit.requires,
|
||||
requires_overridable = unit.requires_overridable,
|
||||
requisite = unit.requisite,
|
||||
requisite_overridable = unit.requisite_overridable,
|
||||
wants = unit.wants,
|
||||
required_by = unit.required_by,
|
||||
required_by_overridable = unit.required_by_overridable,
|
||||
wanted_by = unit.wanted_by,
|
||||
conflicts = unit.conflicts,
|
||||
before = unit.before,
|
||||
after = unit.after;
|
||||
|
||||
unit_dependency_label.set_markup_or_na(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(
|
||||
make_dependency_string(null,
|
||||
"requires", requires),
|
||||
"overridable requires", requires_overridable),
|
||||
"requisite", requisite),
|
||||
"overridable requisite", requisite_overridable),
|
||||
"wants", wants),
|
||||
"conflicts", conflicts),
|
||||
"required by", required_by),
|
||||
"overridable required by", required_by_overridable),
|
||||
"wanted by", wanted_by),
|
||||
"after", after),
|
||||
"before", before));
|
||||
|
||||
unit_description_label.set_text_or_na(unit.description);
|
||||
unit_load_state_label.set_text_or_na(unit.load_state);
|
||||
unit_active_state_label.set_text_or_na(unit.active_state);
|
||||
unit_sub_state_label.set_text_or_na(unit.sub_state);
|
||||
unit_fragment_path_label.set_text_or_na(unit.fragment_path);
|
||||
|
||||
uint64 t = unit.active_enter_timestamp;
|
||||
if (t > 0) {
|
||||
Time timestamp = Time.local((time_t) (t / 1000000));
|
||||
unit_active_enter_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
|
||||
} else
|
||||
unit_active_enter_timestamp_label.set_text_or_na();
|
||||
|
||||
t = unit.active_exit_timestamp;
|
||||
if (t > 0) {
|
||||
Time timestamp = Time.local((time_t) (t / 1000000));
|
||||
unit_active_exit_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
|
||||
} else
|
||||
unit_active_exit_timestamp_label.set_text_or_na();
|
||||
|
||||
bool b = unit.can_start;
|
||||
start_button.set_sensitive(b);
|
||||
stop_button.set_sensitive(b);
|
||||
restart_button.set_sensitive(b);
|
||||
unit_can_start_label.set_text_or_na(b ? "Yes" : "No");
|
||||
|
||||
b = unit.can_reload;
|
||||
reload_button.set_sensitive(b);
|
||||
unit_can_reload_label.set_text_or_na(b ? "Yes" : "No");
|
||||
|
||||
unit_cgroup_label.set_text_or_na(unit.default_control_group);
|
||||
}
|
||||
|
||||
public Job? get_current_job() {
|
||||
TreePath p;
|
||||
job_view.get_cursor(out p, null);
|
||||
|
||||
if (p == null)
|
||||
return null;
|
||||
|
||||
TreeIter iter;
|
||||
TreeModel model = job_view.get_model();
|
||||
Job *j;
|
||||
|
||||
model.get_iter(out iter, p);
|
||||
model.get(iter, 4, out j);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
public void job_changed() {
|
||||
Job j = get_current_job();
|
||||
|
||||
if (j == null)
|
||||
clear_job();
|
||||
else
|
||||
show_job(j);
|
||||
}
|
||||
|
||||
public void clear_job() {
|
||||
current_job_id = 0;
|
||||
|
||||
job_id_label.set_text_or_na();
|
||||
job_state_label.set_text_or_na();
|
||||
job_type_label.set_text_or_na();
|
||||
|
||||
cancel_button.set_sensitive(false);
|
||||
}
|
||||
|
||||
public void show_job(Job job) {
|
||||
current_job_id = job.id;
|
||||
|
||||
job_id_label.set_text_or_na("%u".printf(current_job_id));
|
||||
job_state_label.set_text_or_na(job.state);
|
||||
job_type_label.set_text_or_na(job.job_type);
|
||||
|
||||
cancel_button.set_sensitive(true);
|
||||
}
|
||||
|
||||
public void on_start() {
|
||||
Unit u = get_current_unit();
|
||||
|
||||
if (u == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
u.start("replace");
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_stop() {
|
||||
Unit u = get_current_unit();
|
||||
|
||||
if (u == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
u.stop("replace");
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_reload() {
|
||||
Unit u = get_current_unit();
|
||||
|
||||
if (u == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
u.reload("replace");
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_restart() {
|
||||
Unit u = get_current_unit();
|
||||
|
||||
if (u == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
u.restart("replace");
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_cancel() {
|
||||
Job j = get_current_job();
|
||||
|
||||
if (j == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
j.cancel();
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void update_unit_iter(TreeIter iter, string id, Unit u) {
|
||||
|
||||
string t = "";
|
||||
Unit.JobLink jl = u.job;
|
||||
|
||||
if (jl.id != 0) {
|
||||
Job j = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
jl.path,
|
||||
"org.freedesktop.systemd1.Job") as Job;
|
||||
|
||||
t = j.job_type;
|
||||
}
|
||||
|
||||
unit_model.set(iter,
|
||||
0, id,
|
||||
1, u.description,
|
||||
2, u.load_state,
|
||||
3, u.active_state,
|
||||
4, u.sub_state,
|
||||
5, t != "" ? "→ %s".printf(t) : "",
|
||||
6, u);
|
||||
}
|
||||
|
||||
public void on_unit_new(string id, ObjectPath path) {
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
u.changed += on_unit_changed;
|
||||
|
||||
TreeIter iter;
|
||||
unit_model.append(out iter);
|
||||
update_unit_iter(iter, id, u);
|
||||
}
|
||||
|
||||
public void update_job_iter(TreeIter iter, uint32 id, Job j) {
|
||||
job_model.set(iter,
|
||||
0, "%u".printf(id),
|
||||
1, j.unit.id,
|
||||
2, "→ %s".printf(j.job_type),
|
||||
3, j.state,
|
||||
4, j,
|
||||
5, id);
|
||||
}
|
||||
|
||||
public void on_job_new(uint32 id, ObjectPath path) {
|
||||
Job j = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Job") as Job;
|
||||
|
||||
j.changed += on_job_changed;
|
||||
|
||||
TreeIter iter;
|
||||
job_model.append(out iter);
|
||||
update_job_iter(iter, id, j);
|
||||
}
|
||||
|
||||
public void on_unit_removed(string id, ObjectPath path) {
|
||||
TreeIter iter;
|
||||
if (!(unit_model.get_iter_first(out iter)))
|
||||
return;
|
||||
|
||||
do {
|
||||
string name;
|
||||
|
||||
unit_model.get(iter, 0, out name);
|
||||
|
||||
if (id == name) {
|
||||
if (current_unit_id == name)
|
||||
clear_unit();
|
||||
|
||||
unit_model.remove(iter);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (unit_model.iter_next(ref iter));
|
||||
}
|
||||
|
||||
public void on_job_removed(uint32 id, ObjectPath path) {
|
||||
TreeIter iter;
|
||||
if (!(job_model.get_iter_first(out iter)))
|
||||
return;
|
||||
|
||||
do {
|
||||
uint32 j;
|
||||
|
||||
job_model.get(iter, 5, out j);
|
||||
|
||||
if (id == j) {
|
||||
if (current_job_id == j)
|
||||
clear_job();
|
||||
|
||||
job_model.remove(iter);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} while (job_model.iter_next(ref iter));
|
||||
}
|
||||
|
||||
public void on_unit_changed(Unit u) {
|
||||
TreeIter iter;
|
||||
string id;
|
||||
|
||||
if (!(unit_model.get_iter_first(out iter)))
|
||||
return;
|
||||
|
||||
id = u.id;
|
||||
|
||||
do {
|
||||
string name;
|
||||
|
||||
unit_model.get(iter, 0, out name);
|
||||
|
||||
if (id == name) {
|
||||
update_unit_iter(iter, id, u);
|
||||
|
||||
if (current_unit_id == id)
|
||||
show_unit(u);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} while (unit_model.iter_next(ref iter));
|
||||
}
|
||||
|
||||
public void on_job_changed(Job j) {
|
||||
TreeIter iter;
|
||||
uint32 id;
|
||||
|
||||
if (!(job_model.get_iter_first(out iter)))
|
||||
return;
|
||||
|
||||
id = j.id;
|
||||
|
||||
do {
|
||||
uint32 k;
|
||||
|
||||
job_model.get(iter, 5, out k);
|
||||
|
||||
if (id == k) {
|
||||
update_job_iter(iter, id, j);
|
||||
|
||||
if (current_job_id == id)
|
||||
show_job(j);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} while (job_model.iter_next(ref iter));
|
||||
}
|
||||
|
||||
public bool unit_filter(TreeModel model, TreeIter iter) {
|
||||
string id, active_state, job;
|
||||
|
||||
model.get(iter, 0, out id, 3, out active_state, 5, out job);
|
||||
|
||||
if (id == null)
|
||||
return false;
|
||||
|
||||
switch (unit_type_combo_box.get_active()) {
|
||||
|
||||
case 0:
|
||||
return true;
|
||||
|
||||
case 1:
|
||||
return active_state != "inactive" || job != "";
|
||||
|
||||
case 2:
|
||||
return id.has_suffix(".service");
|
||||
|
||||
case 3:
|
||||
return id.has_suffix(".socket");
|
||||
|
||||
case 4:
|
||||
return id.has_suffix(".device");
|
||||
|
||||
case 5:
|
||||
return id.has_suffix(".mount");
|
||||
|
||||
case 6:
|
||||
return id.has_suffix(".automount");
|
||||
|
||||
case 7:
|
||||
return id.has_suffix(".target");
|
||||
|
||||
case 8:
|
||||
return id.has_suffix(".snapshot");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void unit_type_changed() {
|
||||
TreeModelFilter model = (TreeModelFilter) unit_view.get_model();
|
||||
|
||||
model.refilter();
|
||||
}
|
||||
|
||||
public void on_server_reload() {
|
||||
try {
|
||||
manager.reload();
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_server_snapshot() {
|
||||
try {
|
||||
manager.create_snapshot();
|
||||
|
||||
if (unit_type_combo_box.get_active() != 0)
|
||||
unit_type_combo_box.set_active(8);
|
||||
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_unit_load() {
|
||||
string t = unit_load_entry.get_text();
|
||||
|
||||
if (t == "")
|
||||
return;
|
||||
|
||||
try {
|
||||
var path = manager.load_unit(t);
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
var m = new MessageDialog(this,
|
||||
DialogFlags.DESTROY_WITH_PARENT,
|
||||
MessageType.INFO,
|
||||
ButtonsType.CLOSE,
|
||||
"Unit available as id %s", u.id);
|
||||
m.title = "Unit";
|
||||
m.run();
|
||||
m.destroy();
|
||||
|
||||
show_unit(u);
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_unit_load_entry_changed() {
|
||||
unit_load_button.set_sensitive(unit_load_entry.get_text() != "");
|
||||
}
|
||||
|
||||
public bool on_activate_link(string uri) {
|
||||
|
||||
try {
|
||||
string path = manager.get_unit(uri);
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
show_unit(u);
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void show_error(string e) {
|
||||
var m = new MessageDialog(this,
|
||||
DialogFlags.DESTROY_WITH_PARENT,
|
||||
MessageType.ERROR,
|
||||
ButtonsType.CLOSE, "%s", e);
|
||||
m.title = "Error";
|
||||
m.run();
|
||||
m.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const OptionEntry entries[] = {
|
||||
{ "session", 0, 0, OptionArg.NONE, out session, "Connect to session bus", null },
|
||||
{ "system", 0, OptionFlags.REVERSE, OptionArg.NONE, out session, "Connect to system bus", null },
|
||||
{ null }
|
||||
};
|
||||
|
||||
void show_error(string e) {
|
||||
var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
|
||||
m.run();
|
||||
m.destroy();
|
||||
}
|
||||
|
||||
int main (string[] args) {
|
||||
|
||||
try {
|
||||
Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemadm");
|
||||
|
||||
MainWindow window = new MainWindow();
|
||||
window.show_all();
|
||||
|
||||
Gtk.main();
|
||||
} catch (DBus.Error e) {
|
||||
show_error(e.message);
|
||||
} catch (GLib.Error e) {
|
||||
show_error(e.message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
using DBus;
|
||||
using GLib;
|
||||
|
||||
static string type = null;
|
||||
static bool all = false;
|
||||
static bool replace = false;
|
||||
static bool session = false;
|
||||
static Connection bus = null;
|
||||
|
||||
public static int job_info_compare(void* key1, void* key2) {
|
||||
Manager.JobInfo *j1 = (Manager.JobInfo*) key1;
|
||||
Manager.JobInfo *j2 = (Manager.JobInfo*) key2;
|
||||
|
||||
return j1->id < j2->id ? -1 : (j1->id > j2->id ? 1 : 0);
|
||||
}
|
||||
|
||||
public static int unit_info_compare(void* key1, void* key2) {
|
||||
Manager.UnitInfo *u1 = (Manager.UnitInfo*) key1;
|
||||
Manager.UnitInfo *u2 = (Manager.UnitInfo*) key2;
|
||||
|
||||
int r = Posix.strcmp(Posix.strrchr(u1->id, '.'), Posix.strrchr(u2->id, '.'));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return Posix.strcmp(u1->id, u2->id);
|
||||
}
|
||||
|
||||
public void on_unit_changed(Unit u) {
|
||||
stdout.printf("Unit %s changed.\n", u.id);
|
||||
}
|
||||
|
||||
public void on_unit_new(string id, ObjectPath path) {
|
||||
stdout.printf("Unit %s added.\n", id);
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
u.changed += on_unit_changed;
|
||||
|
||||
/* FIXME: We leak memory here */
|
||||
u.ref();
|
||||
}
|
||||
|
||||
public void on_job_changed(Job j) {
|
||||
stdout.printf("Job %u changed.\n", j.id);
|
||||
}
|
||||
|
||||
public void on_job_new(uint32 id, ObjectPath path) {
|
||||
stdout.printf("Job %u added.\n", id);
|
||||
|
||||
Job j = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Job") as Job;
|
||||
|
||||
j.changed += on_job_changed;
|
||||
|
||||
/* FIXME: We leak memory here */
|
||||
j.ref();
|
||||
}
|
||||
|
||||
public void on_unit_removed(string id, ObjectPath path) {
|
||||
stdout.printf("Unit %s removed.\n", id);
|
||||
}
|
||||
|
||||
public void on_job_removed(uint32 id, ObjectPath path) {
|
||||
stdout.printf("Job %u removed.\n", id);
|
||||
}
|
||||
|
||||
static const OptionEntry entries[] = {
|
||||
{ "type", 't', 0, OptionArg.STRING, out type, "List only particular type of units", "TYPE" },
|
||||
{ "all", 'a', 0, OptionArg.NONE, out all, "Show all units, including dead ones", null },
|
||||
{ "replace", 0, 0, OptionArg.NONE, out replace, "When installing a new job, replace existing conflicting ones", null },
|
||||
{ "session", 0, 0, OptionArg.NONE, out session, "Connect to session bus", null },
|
||||
{ "system", 0, OptionFlags.REVERSE, OptionArg.NONE, out session, "Connect to system bus", null },
|
||||
{ null }
|
||||
};
|
||||
|
||||
int main (string[] args) {
|
||||
|
||||
OptionContext context = new OptionContext("[COMMAND [ARGUMENT...]]");
|
||||
context.add_main_entries(entries, null);
|
||||
context.set_description(
|
||||
"Commands:\n" +
|
||||
" list-units List units\n" +
|
||||
" list-jobs List jobs\n" +
|
||||
" clear-jobs Cancel all jobs\n" +
|
||||
" load [NAME...] Load one or more units\n" +
|
||||
" cancel [JOB...] Cancel one or more jobs\n" +
|
||||
" start [NAME...] Start on or more units\n" +
|
||||
" stop [NAME...] Stop on or more units\n" +
|
||||
" enter [NAME] Start one unit and stop all others\n" +
|
||||
" restart [NAME...] Restart on or more units\n" +
|
||||
" reload [NAME...] Reload on or more units\n" +
|
||||
" monitor Monitor unit/job changes\n" +
|
||||
" dump Dump server status\n" +
|
||||
" snapshot [NAME] Create a snapshot\n" +
|
||||
" daemon-reload Reload daemon configuration\n" +
|
||||
" daemon-reexecute Reexecute daemon\n" +
|
||||
" show-environment Dump environment\n" +
|
||||
" set-environment [NAME=VALUE...] Set one or more environment variables\n" +
|
||||
" unset-environment [NAME...] Unset one or more environment variables\n");
|
||||
|
||||
try {
|
||||
context.parse(ref args);
|
||||
} catch (GLib.OptionError e) {
|
||||
message("Failed to parse command line: %s".printf(e.message));
|
||||
}
|
||||
|
||||
try {
|
||||
bus = Bus.get(session ? BusType.SESSION : BusType.SYSTEM);
|
||||
|
||||
Manager manager = bus.get_object (
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager") as Manager;
|
||||
|
||||
if (args[1] == "list-units" || args.length <= 1) {
|
||||
var list = manager.list_units();
|
||||
uint n = 0;
|
||||
Posix.qsort(list, list.length, sizeof(Manager.UnitInfo), unit_info_compare);
|
||||
|
||||
stdout.printf("%-45s %-6s %-12s %-12s %-17s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB");
|
||||
|
||||
foreach (var i in list) {
|
||||
|
||||
if (type != null && !i.id.has_suffix(".%s".printf(type)))
|
||||
continue;
|
||||
|
||||
if (!all && i.active_state == "inactive")
|
||||
continue;
|
||||
|
||||
stdout.printf("%-45s %-6s %-12s %-12s", i.id, i.load_state, i.active_state, i.sub_state);
|
||||
|
||||
if (i.job_id != 0)
|
||||
stdout.printf(" -> %-15s", i.job_type);
|
||||
|
||||
stdout.puts("\n");
|
||||
n++;
|
||||
}
|
||||
|
||||
if (all)
|
||||
stdout.printf("\n%u units listed.\n", n);
|
||||
else
|
||||
stdout.printf("\n%u live units listed. Pass --all to see dead units, too.\n", n);
|
||||
|
||||
|
||||
} else if (args[1] == "list-jobs") {
|
||||
var list = manager.list_jobs();
|
||||
Posix.qsort(list, list.length, sizeof(Manager.JobInfo), job_info_compare);
|
||||
|
||||
stdout.printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
|
||||
|
||||
foreach (var i in list)
|
||||
stdout.printf("%4u %-45s → %-15s %-7s\n", i.id, i.name, i.type, i.state);
|
||||
|
||||
stdout.printf("\n%u jobs listed.\n", list.length);
|
||||
|
||||
} else if (args[1] == "clear-jobs") {
|
||||
|
||||
manager.clear_jobs();
|
||||
|
||||
} else if (args[1] == "load") {
|
||||
|
||||
if (args.length < 3) {
|
||||
stderr.printf("Missing argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 2; i < args.length; i++)
|
||||
manager.load_unit(args[i]);
|
||||
|
||||
} else if (args[1] == "cancel") {
|
||||
|
||||
if (args.length < 3) {
|
||||
stderr.printf("Missing argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 2; i < args.length; i++) {
|
||||
uint32 id;
|
||||
|
||||
if (args[i].scanf("%u", out id) != 1) {
|
||||
stderr.printf("Failed to parse argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ObjectPath p = manager.get_job(id);
|
||||
|
||||
Job j = bus.get_object (
|
||||
"org.freedesktop.systemd1",
|
||||
p,
|
||||
"org.freedesktop.systemd1.Job") as Job;
|
||||
|
||||
j.cancel();
|
||||
}
|
||||
|
||||
} else if (args[1] == "start" ||
|
||||
args[1] == "stop" ||
|
||||
args[1] == "reload" ||
|
||||
args[1] == "restart") {
|
||||
|
||||
if (args.length < 3) {
|
||||
stderr.printf("Missing argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 2; i < args.length; i++) {
|
||||
|
||||
ObjectPath p = manager.load_unit(args[i]);
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
p,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
string mode = replace ? "replace" : "fail";
|
||||
|
||||
if (args[1] == "start")
|
||||
u.start(mode);
|
||||
else if (args[1] == "stop")
|
||||
u.stop(mode);
|
||||
else if (args[1] == "restart")
|
||||
u.restart(mode);
|
||||
else if (args[1] == "reload")
|
||||
u.reload(mode);
|
||||
}
|
||||
|
||||
} else if (args[1] == "isolate") {
|
||||
|
||||
if (args.length != 3) {
|
||||
stderr.printf("Missing argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ObjectPath p = manager.load_unit(args[2]);
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
p,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
u.start("isolate");
|
||||
|
||||
} else if (args[1] == "monitor") {
|
||||
|
||||
manager.subscribe();
|
||||
|
||||
manager.unit_new += on_unit_new;
|
||||
manager.unit_removed += on_unit_removed;
|
||||
manager.job_new += on_job_new;
|
||||
manager.job_removed += on_job_removed;
|
||||
|
||||
MainLoop l = new MainLoop();
|
||||
l.run();
|
||||
|
||||
} else if (args[1] == "dump")
|
||||
stdout.puts(manager.dump());
|
||||
|
||||
else if (args[1] == "snapshot") {
|
||||
|
||||
ObjectPath p = manager.create_snapshot(args.length > 2 ? args[2] : "");
|
||||
|
||||
Unit u = bus.get_object(
|
||||
"org.freedesktop.systemd1",
|
||||
p,
|
||||
"org.freedesktop.systemd1.Unit") as Unit;
|
||||
|
||||
stdout.printf("%s\n", u.id);
|
||||
|
||||
} 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 if (args[1] == "show-environment") {
|
||||
foreach(var x in manager.environment)
|
||||
stderr.printf("%s\n", x);
|
||||
|
||||
} else if (args[1] == "set-environment")
|
||||
manager.set_environment(args[2:args.length]);
|
||||
|
||||
else if (args[1] == "unset-environment")
|
||||
manager.unset_environment(args[2:args.length]);
|
||||
|
||||
else {
|
||||
stderr.printf("Unknown command %s.\n", args[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
} catch (DBus.Error e) {
|
||||
stderr.printf("%s\n".printf(e.message));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/***
|
||||
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/>.
|
||||
***/
|
||||
|
||||
using DBus;
|
||||
|
||||
[DBus (name = "org.freedesktop.systemd1.Manager")]
|
||||
public interface Manager : DBus.Object {
|
||||
|
||||
public struct UnitInfo {
|
||||
string id;
|
||||
string description;
|
||||
string load_state;
|
||||
string active_state;
|
||||
string sub_state;
|
||||
ObjectPath unit_path;
|
||||
uint32 job_id;
|
||||
string job_type;
|
||||
ObjectPath job_path;
|
||||
}
|
||||
|
||||
public struct JobInfo {
|
||||
uint32 id;
|
||||
string name;
|
||||
string type;
|
||||
string state;
|
||||
ObjectPath job_path;
|
||||
ObjectPath unit_path;
|
||||
}
|
||||
|
||||
public abstract string[] environment { owned get; }
|
||||
|
||||
public abstract UnitInfo[] list_units() throws DBus.Error;
|
||||
public abstract JobInfo[] list_jobs() throws DBus.Error;
|
||||
|
||||
public abstract ObjectPath get_unit(string name) throws DBus.Error;
|
||||
public abstract ObjectPath load_unit(string name) throws DBus.Error;
|
||||
public abstract ObjectPath get_job(uint32 id) throws DBus.Error;
|
||||
|
||||
public abstract void clear_jobs() throws DBus.Error;
|
||||
|
||||
public abstract void subscribe() throws DBus.Error;
|
||||
public abstract void unsubscribe() throws DBus.Error;
|
||||
|
||||
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 void set_environment(string[] names) throws DBus.Error;
|
||||
public abstract void unset_environment(string[] names) throws DBus.Error;
|
||||
|
||||
public abstract signal void unit_new(string id, ObjectPath path);
|
||||
public abstract signal void unit_removed(string id, ObjectPath path);
|
||||
public abstract signal void job_new(uint32 id, ObjectPath path);
|
||||
public abstract signal void job_removed(uint32 id, ObjectPath path);
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.systemd1.Unit")]
|
||||
public interface Unit : DBus.Object {
|
||||
public struct JobLink {
|
||||
uint32 id;
|
||||
ObjectPath path;
|
||||
}
|
||||
|
||||
public abstract string id { owned get; }
|
||||
public abstract string[] names { owned get; }
|
||||
public abstract string[] requires { owned get; }
|
||||
public abstract string[] requires_overridable { owned get; }
|
||||
public abstract string[] requisite { owned get; }
|
||||
public abstract string[] requisite_overridable { owned get; }
|
||||
public abstract string[] wants { owned get; }
|
||||
public abstract string[] required_by { owned get; }
|
||||
public abstract string[] required_by_overridable { owned get; }
|
||||
public abstract string[] wanted_by { owned get; }
|
||||
public abstract string[] conflicts { owned get; }
|
||||
public abstract string[] before { owned get; }
|
||||
public abstract string[] after { owned get; }
|
||||
public abstract string description { owned get; }
|
||||
public abstract string load_state { owned get; }
|
||||
public abstract string active_state { owned get; }
|
||||
public abstract string sub_state { owned get; }
|
||||
public abstract string fragment_path { owned get; }
|
||||
public abstract uint64 inactive_exit_timestamp { owned get; }
|
||||
public abstract uint64 active_enter_timestamp { owned get; }
|
||||
public abstract uint64 active_exit_timestamp { owned get; }
|
||||
public abstract uint64 inactive_enter_timestamp { owned get; }
|
||||
public abstract bool can_start { owned get; }
|
||||
public abstract bool can_reload { owned get; }
|
||||
public abstract JobLink job { owned get; }
|
||||
public abstract bool recursive_stop { owned get; }
|
||||
public abstract bool stop_when_unneeded { owned get; }
|
||||
public abstract string default_control_group { owned get; }
|
||||
public abstract string[] control_groups { owned get; }
|
||||
|
||||
public abstract ObjectPath start(string mode) throws DBus.Error;
|
||||
public abstract ObjectPath stop(string mode) throws DBus.Error;
|
||||
public abstract ObjectPath restart(string mode) throws DBus.Error;
|
||||
public abstract ObjectPath reload(string mode) throws DBus.Error;
|
||||
|
||||
public abstract signal void changed();
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.systemd1.Job")]
|
||||
public interface Job : DBus.Object {
|
||||
public struct UnitLink {
|
||||
string id;
|
||||
ObjectPath path;
|
||||
}
|
||||
|
||||
public abstract uint32 id { owned get; }
|
||||
public abstract string state { owned get; }
|
||||
public abstract string job_type { owned get; }
|
||||
public abstract UnitLink unit { owned get; }
|
||||
|
||||
public abstract void cancel() throws DBus.Error;
|
||||
|
||||
public abstract signal void changed();
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*-*- 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 <signal.h>
|
||||
|
||||
#include "unit.h"
|
||||
#include "target.h"
|
||||
#include "load-fragment.h"
|
||||
#include "log.h"
|
||||
#include "dbus-target.h"
|
||||
|
||||
static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
|
||||
[TARGET_DEAD] = UNIT_INACTIVE,
|
||||
[TARGET_ACTIVE] = UNIT_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);
|
||||
|
||||
assert(t);
|
||||
assert(f);
|
||||
|
||||
fprintf(f,
|
||||
"%sTarget State: %s\n",
|
||||
prefix, target_state_to_string(t->state));
|
||||
}
|
||||
|
||||
static int target_start(Unit *u) {
|
||||
Target *t = TARGET(u);
|
||||
|
||||
assert(t);
|
||||
assert(t->state == TARGET_DEAD);
|
||||
|
||||
target_set_state(t, TARGET_ACTIVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int target_stop(Unit *u) {
|
||||
Target *t = TARGET(u);
|
||||
|
||||
assert(t);
|
||||
assert(t->state == TARGET_ACTIVE);
|
||||
|
||||
target_set_state(t, TARGET_DEAD);
|
||||
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);
|
||||
|
||||
return state_translation_table[TARGET(u)->state];
|
||||
}
|
||||
|
||||
static const char *target_sub_state_to_string(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
return target_state_to_string(TARGET(u)->state);
|
||||
}
|
||||
|
||||
int target_get_runlevel(Target *t) {
|
||||
|
||||
static const struct {
|
||||
const char *special;
|
||||
const int runlevel;
|
||||
} table[] = {
|
||||
{ SPECIAL_RUNLEVEL5_TARGET, '5' },
|
||||
{ SPECIAL_RUNLEVEL4_TARGET, '4' },
|
||||
{ SPECIAL_RUNLEVEL3_TARGET, '3' },
|
||||
{ SPECIAL_RUNLEVEL2_TARGET, '2' },
|
||||
{ SPECIAL_RUNLEVEL1_TARGET, '1' },
|
||||
{ SPECIAL_RUNLEVEL0_TARGET, '0' },
|
||||
{ SPECIAL_RUNLEVEL6_TARGET, '6' },
|
||||
};
|
||||
|
||||
unsigned i;
|
||||
|
||||
assert(t);
|
||||
|
||||
/* Tries to determine if this is a SysV runlevel and returns
|
||||
* it if that is so. */
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(table); i++)
|
||||
if (unit_has_name(UNIT(t), table[i].special))
|
||||
return table[i].runlevel;
|
||||
|
||||
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,
|
||||
|
||||
.bus_message_handler = bus_target_message_handler
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
#ifndef footargethfoo
|
||||
#define footargethfoo
|
||||
|
||||
/***
|
||||
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 Target Target;
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
typedef enum TargetState {
|
||||
TARGET_DEAD,
|
||||
TARGET_ACTIVE,
|
||||
_TARGET_STATE_MAX,
|
||||
_TARGET_STATE_INVALID = -1
|
||||
} TargetState;
|
||||
|
||||
struct Target {
|
||||
Meta meta;
|
||||
|
||||
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
|
||||
@@ -0,0 +1,99 @@
|
||||
/*-*- 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Manager *m = NULL;
|
||||
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
|
||||
Job *j;
|
||||
|
||||
assert_se(set_unit_path("test2") >= 0);
|
||||
|
||||
assert_se(manager_new(MANAGER_INIT, false, &m) >= 0);
|
||||
|
||||
printf("Load1:\n");
|
||||
assert_se(manager_load_unit(m, "a.service", NULL, &a) == 0);
|
||||
assert_se(manager_load_unit(m, "b.service", NULL, &b) == 0);
|
||||
assert_se(manager_load_unit(m, "c.service", NULL, &c) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test1: (Trivial)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, c, JOB_REPLACE, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Load2:\n");
|
||||
manager_clear_jobs(m);
|
||||
assert_se(manager_load_unit(m, "d.service", NULL, &d) == 0);
|
||||
assert_se(manager_load_unit(m, "e.service", NULL, &e) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test2: (Cyclic Order, Unfixable)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, &j) == -ENOEXEC);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Test4: (Identical transaction)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Load3:\n");
|
||||
assert_se(manager_load_unit(m, "g.service", NULL, &g) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test5: (Colliding transaction, fail)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, &j) == -EEXIST);
|
||||
|
||||
printf("Test6: (Colliding transaction, replace)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Test7: (Unmeargable job type, fail)\n");
|
||||
assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, &j) == -EEXIST);
|
||||
|
||||
printf("Test8: (Mergeable job type, fail)\n");
|
||||
assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Test9: (Unmeargable job type, replace)\n");
|
||||
assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
printf("Load4:\n");
|
||||
assert_se(manager_load_unit(m, "h.service", NULL, &h) == 0);
|
||||
manager_dump_units(m, stdout, "\t");
|
||||
|
||||
printf("Test10: (Unmeargable job type of auxiliary job, fail)\n");
|
||||
assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, false, &j) == 0);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
manager_free(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*-*- 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "job.h"
|
||||
|
||||
int main(int argc, char*argv[]) {
|
||||
JobType a, b, c, d, e, f, g;
|
||||
|
||||
for (a = 0; a < _JOB_TYPE_MAX; a++)
|
||||
for (b = 0; b < _JOB_TYPE_MAX; b++) {
|
||||
|
||||
if (!job_type_is_mergeable(a, b))
|
||||
printf("Not mergeable: %s + %s\n", job_type_to_string(a), job_type_to_string(b));
|
||||
|
||||
for (c = 0; c < _JOB_TYPE_MAX; c++) {
|
||||
|
||||
/* Verify transitivity of mergeability
|
||||
* of job types */
|
||||
assert(!job_type_is_mergeable(a, b) ||
|
||||
!job_type_is_mergeable(b, c) ||
|
||||
job_type_is_mergeable(a, c));
|
||||
|
||||
d = a;
|
||||
if (job_type_merge(&d, b) >= 0) {
|
||||
|
||||
printf("%s + %s = %s\n", job_type_to_string(a), job_type_to_string(b), job_type_to_string(d));
|
||||
|
||||
/* Verify that merged entries can be
|
||||
* merged with the same entries they
|
||||
* can be merged with seperately */
|
||||
assert(!job_type_is_mergeable(a, c) || job_type_is_mergeable(d, c));
|
||||
assert(!job_type_is_mergeable(b, c) || job_type_is_mergeable(d, c));
|
||||
|
||||
/* Verify that if a merged
|
||||
* with b is not mergable with
|
||||
* c then either a or b is not
|
||||
* mergeable with c either. */
|
||||
assert(job_type_is_mergeable(d, c) || !job_type_is_mergeable(a, c) || !job_type_is_mergeable(b, c));
|
||||
|
||||
e = b;
|
||||
if (job_type_merge(&e, c) >= 0) {
|
||||
|
||||
/* Verify associativity */
|
||||
|
||||
f = d;
|
||||
assert(job_type_merge(&f, c) == 0);
|
||||
|
||||
g = e;
|
||||
assert(job_type_merge(&g, a) == 0);
|
||||
|
||||
assert(f == g);
|
||||
|
||||
printf("%s + %s + %s = %s\n", job_type_to_string(a), job_type_to_string(b), job_type_to_string(c), job_type_to_string(d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8 -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "loopback-setup.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int r;
|
||||
|
||||
if ((r = loopback_setup()) < 0)
|
||||
fprintf(stderr, "loopback: %s\n", strerror(-r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*-*- 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "namespace.h"
|
||||
#include "log.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char * const writable[] = {
|
||||
"/home",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const readable[] = {
|
||||
"/",
|
||||
"/usr",
|
||||
"/boot",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const inaccessible[] = {
|
||||
"/home/lennart/projects",
|
||||
NULL
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
if ((r = setup_namespace((char**) writable, (char**) readable, (char**) inaccessible, true, MS_SHARED)) < 0) {
|
||||
log_error("Failed to setup namespace: %s", strerror(-r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
execl("/bin/sh", "/bin/sh", NULL);
|
||||
log_error("execl(): %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user