build-sys: move source files to subdirectory

This commit is contained in:
Lennart Poettering
2010-05-16 18:45:24 +02:00
parent e9da3678fc
commit e99e38bbdc
112 changed files with 90 additions and 80 deletions
+784
View File
@@ -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(&param);
if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, &param) < 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(&param);
param.ioctlfd = ioctl_fd;
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, &param) < 0)
return -errno;
major = param.protover.version;
init_autofs_dev_ioctl(&param);
param.ioctlfd = ioctl_fd;
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, &param) < 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(&param);
param.ioctlfd = ioctl_fd;
param.timeout.timeout = sec;
if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, &param) < 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(&param);
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, &param) < 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
};
+65
View File
@@ -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
View File
@@ -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;
}
+82
View File
@@ -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
+72
View File
@@ -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;
}
+460
View File
@@ -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, &section, 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;
}
+55
View File
@@ -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
+44
View File
@@ -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);
}
+31
View File
@@ -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
+44
View File
@@ -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);
}
+31
View File
@@ -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
+28
View File
@@ -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);
+79
View File
@@ -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
View File
@@ -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.");
}
+32
View File
@@ -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
+657
View File
@@ -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
};
+29
View File
@@ -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
+134
View File
@@ -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);
}
+31
View File
@@ -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
+78
View File
@@ -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);
}
+31
View File
@@ -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
+75
View File
@@ -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;
}
+31
View File
@@ -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
+64
View File
@@ -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);
}
+31
View File
@@ -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
+73
View File
@@ -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);
}
+32
View File
@@ -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
+42
View File
@@ -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);
}
+31
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+107
View File
@@ -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
View File
@@ -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
};
+53
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+221
View File
@@ -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
View File
@@ -0,0 +1,162 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include "set.h"
#include "util.h"
#include "macro.h"
#include "fdset.h"
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
/* Make sure we can distuingish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
FDSet *fdset_new(void) {
return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func));
}
void fdset_free(FDSet *s) {
void *p;
while ((p = set_steal_first(MAKE_SET(s)))) {
/* Valgrind's fd might have ended up in this set here,
* due to fdset_new_fill(). We'll ignore all failures
* here, so that the EBADFD that valgrind will return
* us on close() doesn't influence us */
log_warning("Closing left-over fd %i", PTR_TO_FD(p));
close_nointr(PTR_TO_FD(p));
}
set_free(MAKE_SET(s));
}
int fdset_put(FDSet *s, int fd) {
assert(s);
assert(fd >= 0);
return set_put(MAKE_SET(s), FD_TO_PTR(fd));
}
int fdset_put_dup(FDSet *s, int fd) {
int copy, r;
assert(s);
assert(fd >= 0);
if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0)
return -errno;
if ((r = fdset_put(s, copy)) < 0) {
close_nointr_nofail(copy);
return r;
}
return copy;
}
bool fdset_contains(FDSet *s, int fd) {
assert(s);
assert(fd >= 0);
return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
}
int fdset_remove(FDSet *s, int fd) {
assert(s);
assert(fd >= 0);
return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
}
int fdset_new_fill(FDSet **_s) {
DIR *d;
struct dirent *de;
int r = 0;
FDSet *s;
assert(_s);
/* Creates an fdsets and fills in all currently open file
* descriptors. */
if (!(d = opendir("/proc/self/fd")))
return -errno;
if (!(s = fdset_new())) {
r = -ENOMEM;
goto finish;
}
while ((de = readdir(d))) {
int fd = -1;
if (ignore_file(de->d_name))
continue;
if ((r = safe_atoi(de->d_name, &fd)) < 0)
goto finish;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if ((r = fdset_put(s, fd)) < 0)
goto finish;
}
r = 0;
*_s = s;
s = NULL;
finish:
closedir(d);
/* We won't close the fds here! */
if (s)
set_free(MAKE_SET(s));
return r;
}
int fdset_cloexec(FDSet *fds, bool b) {
Iterator i;
void *p;
int r;
assert(fds);
SET_FOREACH(p, MAKE_SET(fds), i)
if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
return r;
return 0;
}
+40
View File
@@ -0,0 +1,40 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foofdsethfoo
#define foofdsethfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef struct FDSet FDSet;
FDSet* fdset_new(void);
void fdset_free(FDSet *s);
int fdset_put(FDSet *s, int fd);
int fdset_put_dup(FDSet *s, int fd);
bool fdset_contains(FDSet *s, int fd);
int fdset_remove(FDSet *s, int fd);
int fdset_new_fill(FDSet **_s);
int fdset_cloexec(FDSet *fds, bool b);
#endif
+543
View File
@@ -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;
}
+85
View File
@@ -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
+168
View File
@@ -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;
}
+27
View File
@@ -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
View File
@@ -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;
}
+77
View File
@@ -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
+57
View File
@@ -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
+589
View File
@@ -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);
+150
View File
@@ -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
+229
View File
@@ -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
View File
@@ -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
+117
View File
@@ -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;
}
+31
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -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
+439
View File
@@ -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);
+79
View File
@@ -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
View File
@@ -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;
}
+276
View File
@@ -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;
}
+27
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+284
View File
@@ -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
+38
View File
@@ -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
+168
View File
@@ -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();
}
+31
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+110
View File
@@ -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
View File
@@ -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;
}
+34
View File
@@ -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
+62
View File
@@ -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;
}
+55
View File
@@ -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
+96
View File
@@ -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
}
+61
View File
@@ -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
+45
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+147
View File
@@ -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
+114
View File
@@ -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));
}
+68
View File
@@ -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
View File
@@ -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
};
+52
View File
@@ -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
+468
View File
@@ -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);
}
+79
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+132
View File
@@ -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
View File
@@ -0,0 +1,110 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <string.h>
#include "macro.h"
#include "util.h"
#include "specifier.h"
/*
* Generic infrastructure for replacing %x style specifiers in
* strings. Will call a callback for each replacement.
*
*/
char *specifier_printf(const char *text, const Specifier table[], void *userdata) {
char *r, *t;
const char *f;
bool percent = false;
size_t l;
assert(text);
assert(table);
l = strlen(text);
if (!(r = new(char, l+1)))
return NULL;
t = r;
for (f = text; *f; f++, l--) {
if (percent) {
if (*f == '%')
*(t++) = '%';
else {
const Specifier *i;
for (i = table; i->specifier; i++)
if (i->specifier == *f)
break;
if (i->lookup) {
char *n, *w;
size_t k, j;
if (!(w = i->lookup(i->specifier, i->data, userdata))) {
free(r);
return NULL;
}
j = t - r;
k = strlen(w);
if (!(n = new(char, j + k + l + 1))) {
free(r);
free(w);
return NULL;
}
memcpy(n, r, j);
memcpy(n + j, w, k);
free(r);
free(w);
r = n;
t = n + j + k;
} else {
*(t++) = '%';
*(t++) = *f;
}
}
percent = false;
} else if (*f == '%')
percent = true;
else
*(t++) = *f;
}
*t = 0;
return r;
}
/* Generic handler for simple string replacements */
char* specifier_string(char specifier, void *data, void *userdata) {
assert(data);
return strdup(strempty(data));
}
+37
View File
@@ -0,0 +1,37 @@
/*-*- Mode: C; c-basic-offset: 8 -*-*/
#ifndef foospecifierhfoo
#define foospecifierhfoo
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef char* (*SpecifierCallback)(char specifier, void *data, void *userdata);
typedef struct Specifier {
const char specifier;
const SpecifierCallback lookup;
void *data;
} Specifier;
char *specifier_printf(const char *text, const Specifier table[], void *userdata);
char* specifier_string(char specifier, void *data, void *userdata);
#endif
+503
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+956
View File
@@ -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;
}
+321
View File
@@ -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;
}
+137
View File
@@ -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
View File
@@ -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
};
+49
View File
@@ -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
+99
View File
@@ -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;
}
+84
View File
@@ -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;
}
+35
View File
@@ -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;
}
+60
View File
@@ -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