diff options
author | Martin Pitt <mpitt@debian.org> | 2016-06-08 14:14:20 +0100 |
---|---|---|
committer | Martin Pitt <mpitt@debian.org> | 2016-06-08 14:14:20 +0100 |
commit | a794d1ab788d419a1c49579cb7d2b411310e38f5 (patch) | |
tree | a3a4eadedc1352ad6508e30b247adb1e66dfdf46 /src |
Import systemd-shim_10.orig.tar.gz
[dgit import orig systemd-shim_10.orig.tar.gz]
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 2 | ||||
-rw-r--r-- | src/Makefile.am | 32 | ||||
-rw-r--r-- | src/cgmanager.c | 215 | ||||
-rw-r--r-- | src/cgmanager.h | 41 | ||||
-rw-r--r-- | src/cgroup-release-agent.c | 75 | ||||
-rw-r--r-- | src/cgroup-unit.c | 240 | ||||
-rw-r--r-- | src/macro.h | 181 | ||||
-rw-r--r-- | src/ntp-unit.c | 152 | ||||
-rw-r--r-- | src/power-unit.c | 161 | ||||
-rw-r--r-- | src/state.c | 89 | ||||
-rw-r--r-- | src/state.h | 36 | ||||
-rw-r--r-- | src/systemd-iface.h | 58 | ||||
-rw-r--r-- | src/systemd-shim.c | 463 | ||||
-rw-r--r-- | src/unit.c | 117 | ||||
-rw-r--r-- | src/unit.h | 64 | ||||
-rw-r--r-- | src/util.c | 110 | ||||
-rw-r--r-- | src/util.h | 544 | ||||
-rw-r--r-- | src/virt.c | 292 | ||||
-rw-r--r-- | src/virt.h | 38 |
19 files changed, 2910 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..5b9cc91 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +systemd-shim +systemd-shim-cgroup-release-agent diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..fc0742e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,32 @@ +AM_CFLAGS = $(gio_CFLAGS) + +systemd_imports = \ + macro.h \ + util.h \ + util.c \ + virt.h \ + virt.c + +libexec_PROGRAMS = systemd-shim systemd-shim-cgroup-release-agent +systemd_shim_LDADD = $(gio_LIBS) +systemd_shim_CPPFLAGS = \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + $(NULL) +systemd_shim_SOURCES = \ + $(systemd_imports) \ + cgmanager.h \ + cgmanager.c \ + unit.h \ + unit.c \ + ntp-unit.c \ + power-unit.c \ + cgroup-unit.c \ + state.h \ + state.c \ + systemd-iface.h \ + systemd-shim.c + +systemd_shim_cgroup_release_agent_LDADD = $(gio_LIBS) +systemd_shim_cgroup_release_agent_SOURCES = \ + cgroup-release-agent.c \ + $(NULL) diff --git a/src/cgmanager.c b/src/cgmanager.c new file mode 100644 index 0000000..39a9169 --- /dev/null +++ b/src/cgmanager.c @@ -0,0 +1,215 @@ +/* + * Copyright © 2014 Canonical Limited + * + * This program 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 licence, or (at + * your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * Authors: + * Serge Hallyn <serge.hallyn@canonical.com> + * Stéphane Graber <stephane.graber@canonical.com> + * Ryan Lortie <desrt@desrt.ca> + */ + +#include "cgmanager.h" + +#include <gio/gio.h> + +#define CGM_DBUS_ADDRESS "unix:path=/sys/fs/cgroup/cgmanager/sock" +#define CGM_REQUIRED_VERSION 8 + +static GDBusConnection * +cgmanager_connect (GError **error) +{ + GDBusConnection *connection; + GVariant *reply; + GVariant *version; + + connection = g_dbus_connection_new_for_address_sync (CGM_DBUS_ADDRESS, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, NULL, error); + + if (!connection) + return NULL; + + reply = g_dbus_connection_call_sync (connection, NULL, "/org/linuxcontainers/cgmanager", + "org.freedesktop.DBus.Properties", "Get", + g_variant_new ("(ss)", "org.linuxcontainers.cgmanager0_0", "api_version"), + G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + + if (!reply) + { + g_object_unref (connection); + return NULL; + } + + g_variant_get (reply, "(v)", &version); + g_variant_unref (reply); + + if (!g_variant_is_of_type (version, G_VARIANT_TYPE_INT32) || g_variant_get_int32 (version) < CGM_REQUIRED_VERSION) + { + g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Incorrect cgmanager API version"); + g_object_unref (connection); + g_variant_unref (version); + return NULL; + } + + g_variant_unref (version); + + return connection; +} + +static gboolean +cgmanager_call (const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GVariant **reply) +{ + static GDBusConnection *connection; + static gboolean initialised; + GVariant *my_reply = NULL; + GError *error = NULL; + + /* Use a separate bool to prevent repeated attempts to connect to a + * defunct cgmanager... + */ + if (!initialised) + { + GError *error = NULL; + + connection = cgmanager_connect (&error); + + if (!connection) + { + g_warning ("Could not connect to cgmanager: %s", error->message); + g_error_free (error); + } + + initialised = TRUE; + } + + if (!connection) + return FALSE; + + if (!reply) + reply = &my_reply; + + /* We do this sync because we need to ensure that the calls finish + * before we return to _our_ caller saying that this is done. + */ + *reply = g_dbus_connection_call_sync (connection, NULL, "/org/linuxcontainers/cgmanager", + "org.linuxcontainers.cgmanager0_0", method_name, + parameters, reply_type, G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + + if (!*reply) + { + if (reply_type) + g_warning ("cgmanager method call org.linuxcontainers.cgmanager0_0.%s failed: %s. " + "Use G_DBUS_DEBUG=message for more info.", method_name, error->message); + g_error_free (error); + + return FALSE; + } + + if (my_reply) + g_variant_unref (my_reply); + + return TRUE; +} + +void +cgmanager_create (const gchar *path, + gint uid, + const guint *pids, + guint n_pids) +{ + guint i; + + if (path[0] == '/') + path++; + + cgmanager_call ("Create", g_variant_new ("(ss)", "all", path), G_VARIANT_TYPE ("(i)"), NULL); + + if (uid != -1) + cgmanager_call ("Chown", g_variant_new ("(ssii)", "all", path, uid, -1), G_VARIANT_TYPE_UNIT, NULL); + + for (i = 0; i < n_pids; i++) + cgmanager_call ("MovePid", g_variant_new ("(ssi)", "all", path, pids[i]), G_VARIANT_TYPE_UNIT, NULL); + + cgmanager_call ("SetValue", g_variant_new ("(ssss)", "systemd", path, "notify_on_release", "1"), G_VARIANT_TYPE_UNIT, NULL); +} + +gboolean +cgmanager_remove (const gchar *path) +{ + if (path[0] == '/') + path++; + + return cgmanager_call ("Remove", g_variant_new ("(ssi)", "all", path, 1), G_VARIANT_TYPE ("(i)"), NULL); +} + +void +cgmanager_move_self (void) +{ + GVariant *reply; + gchar *str; + + cgmanager_call ("MovePidAbs", g_variant_new ("(ssi)", "all", "/", getpid ()), G_VARIANT_TYPE_UNIT, NULL); + + int need_agent = 1; + if (cgmanager_call ("GetValue", g_variant_new ("(sss)", "systemd", "/", "release_agent"), G_VARIANT_TYPE ("(s)"), &reply)) + { + g_variant_get(reply, "(s)", &str); + g_variant_unref(reply); + need_agent = strlen(str) < 1; + g_free(str); + } + + if (!need_agent) + return; + + /* install our systemd cgroup release handler */ + g_debug ("Installing cgroup release handler " LIBEXECDIR "/systemd-shim-cgroup-release-agent"); + cgmanager_call ("SetValue", + g_variant_new ("(ssss)", "systemd", "/", "release_agent", LIBEXECDIR "/systemd-shim-cgroup-release-agent"), + G_VARIANT_TYPE_UNIT, + NULL); +} + +void +cgmanager_prune (const gchar *path) +{ + cgmanager_call ("Prune", g_variant_new ("(ss)", "all", path), G_VARIANT_TYPE_UNIT, NULL); +} + +void +cgmanager_kill (const gchar *path) +{ + GVariant *reply; + + if (cgmanager_call ("GetTasksRecursive", g_variant_new ("(ss)", "all", path), G_VARIANT_TYPE ("(ai)"), &reply)) + { + GVariantIter *iter; + guint32 pid; + + g_variant_get (reply, "(ai)", &iter); + + while (g_variant_iter_next (iter, "i", &pid)) + kill (pid, SIGKILL); + + g_variant_iter_free (iter); + g_variant_unref (reply); + } +} diff --git a/src/cgmanager.h b/src/cgmanager.h new file mode 100644 index 0000000..1b73ef4 --- /dev/null +++ b/src/cgmanager.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2014 Canonical Limited + * + * This program 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 licence, or (at + * your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * Authors: + * Ryan Lortie <desrt@desrt.ca> + */ + +#ifndef _cgmanager_h_ +#define _cgmanager_h_ + +#include <glib.h> + +void cgmanager_create (const gchar *path, + gint uid, + const guint *pids, + guint n_pids); + +void cgmanager_prune (const gchar *path); + +gboolean cgmanager_remove (const gchar *path); + +void cgmanager_move_self (void); + +void cgmanager_kill (const gchar *scope); + +#endif /* _cgmanager_h_ */ diff --git a/src/cgroup-release-agent.c b/src/cgroup-release-agent.c new file mode 100644 index 0000000..e83c01e --- /dev/null +++ b/src/cgroup-release-agent.c @@ -0,0 +1,75 @@ +/* + * Copyright © 2014 Canonical Limited + * + * This program 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 licence, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * Authors: + * Martin Pitt <martin.pitt@ubuntu.com> + */ + +#include <glib.h> +#include <gio/gio.h> + +#include <unistd.h> +#include <stdio.h> + +#define CGMANAGER_AGENT "/run/cgmanager/agents/cgm-release-agent.systemd" + +int +main (int argc, char** argv) +{ + gchar* unit_name; + GDBusConnection *connection; + GVariant *reply = NULL; + GError *error = NULL; + + g_assert(argc == 2); + unit_name = g_path_get_basename (argv[1]); + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (connection == NULL) + g_error ("Cannot connect to system D-BUS: %s", error->message); + g_debug ("sending StopUnit(%s) to systemd-shim", unit_name); + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + g_variant_new ("(ss)", unit_name, "/"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + if (reply != NULL) + g_debug ("Got reply: %s", g_variant_print (reply, TRUE)); + else + g_warning ("StopUnit call failed: %s", error->message); + + g_object_unref (connection); + + /* Now chain-call cgmanager's agent */ + if (access (CGMANAGER_AGENT, X_OK) == 0) + { + g_debug ("calling " CGMANAGER_AGENT); + execl (CGMANAGER_AGENT, CGMANAGER_AGENT, argv[1], NULL); + perror ("failed to run " CGMANAGER_AGENT); + return 1; + } + else + { + g_debug (CGMANAGER_AGENT " does not exist"); + } + + return 0; +} diff --git a/src/cgroup-unit.c b/src/cgroup-unit.c new file mode 100644 index 0000000..e3c322d --- /dev/null +++ b/src/cgroup-unit.c @@ -0,0 +1,240 @@ +/* + * Copyright © 2014 Canonical Limited + * + * This program 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 licence, or (at + * your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#define _GNU_SOURCE + +#include "cgmanager.h" +#include "state.h" +#include "unit.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <glib/gstdio.h> + +typedef UnitClass CGroupUnitClass; +static GType cgroup_unit_get_type (void); + +typedef struct +{ + Unit parent_instance; + gchar *name; +} CGroupUnit; + +G_DEFINE_TYPE (CGroupUnit, cgroup_unit, UNIT_TYPE) + +static gchar * +cgroup_unit_get_path_and_uid (const gchar *slice, + const gchar *scope, + gint *uid) +{ + GString *path; + gint i; + + path = g_string_new (NULL); + for (i = 0; slice[i]; i++) + if (slice[i] == '-') + { + g_string_append_len (path, slice, i); + g_string_append (path, ".slice/"); + } + + g_string_append_len (path, slice, i); + + *uid = -1; + if (g_str_has_prefix (slice, "user-")) + { + guint64 value; + gchar *end; + + errno = 0; + value = g_ascii_strtoull (slice + 5, &end, 10); + if (errno == 0 && g_str_equal (end, ".slice") && value < G_MAXINT) + *uid = (gint) value; + } + + if (scope) + { + g_string_append_c (path, '/'); + g_string_append (path, scope); + } + + return g_string_free (path, FALSE); +} + +static void +cgroup_unit_start_transient (Unit *unit, + GVariant *properties) +{ + CGroupUnit *cg_unit = (CGroupUnit *) unit; + GVariantIter iter; + const gchar *key; + GVariant *value; + gchar *slice; + GArray *pids; + + if (!g_str_has_suffix (cg_unit->name, ".scope")) + { + g_warning ("%s: Can only StartTransient for scopes", cg_unit->name); + return; + } + + pids = g_array_new (TRUE, FALSE, sizeof (guint)); + slice = NULL; + + g_variant_iter_init (&iter, properties); + while (g_variant_iter_loop (&iter, "(&sv)", &key, &value)) + { + if (g_str_equal (key, "Slice") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) + { + g_free (slice); + slice = g_variant_dup_string (value, NULL); + } + + else if (g_str_equal (key, "PIDs") && g_variant_is_of_type (value, G_VARIANT_TYPE ("au"))) + { + const guint *vals; + gsize n_vals; + + vals = g_variant_get_fixed_array (value, &n_vals, sizeof (guint)); + g_array_append_vals (pids, vals, n_vals); + } + } + + if (slice && g_str_has_suffix (slice, ".slice")) + { + gchar *path; + gint uid; + + path = cgroup_unit_get_path_and_uid (slice, cg_unit->name, &uid); + cgmanager_create (path, uid, (const guint *) pids->data, pids->len); + state_set_string (cg_unit->name, "path", path); + g_free (path); + } + else + g_warning ("%s: StartTransient failed: requires 'Slice' property ending with '.slice'", cg_unit->name); + + g_array_free (pids, TRUE); +} + +static void +cgroup_unit_start (Unit *unit) +{ + CGroupUnit *cg_unit = (CGroupUnit *) unit; + gchar *path; + gint uid; + + if (!g_str_has_suffix (cg_unit->name, ".slice")) + { + g_warning ("%s: Can only Start for slices", cg_unit->name); + return; + } + + path = cgroup_unit_get_path_and_uid (cg_unit->name, NULL, &uid); + cgmanager_create (path, uid, NULL, 0); + state_set_string (cg_unit->name, "path", path); + g_free (path); +} + +static void +cgroup_unit_stop (Unit *unit) +{ + CGroupUnit *cg_unit = (CGroupUnit *) unit; + gboolean successful; + gint tries; + gchar *path; + + path = state_get_string (cg_unit->name, "path"); + + if (!path) + { + g_warning ("can't Stop: cgroup unit not previously started"); + return; + } + + tries = 5; + + do + { + cgmanager_kill (path); + successful = cgmanager_remove (path); + } + while (tries-- && !successful); + + state_remove_unit (cg_unit->name); + + g_free (path); +} + +static void +cgroup_unit_abandon (Unit *unit) +{ + CGroupUnit *cg_unit = (CGroupUnit *) unit; + gchar *path; + + path = state_get_string (cg_unit->name, "path"); + + if (!path) + { + g_warning ("can't Abandon: cgroup unit not previously started"); + return; + } + + cgmanager_prune (path); + + state_remove_unit (cg_unit->name); + + g_free (path); +} + +static const gchar * +cgroup_unit_get_state (Unit *unit) +{ + CGroupUnit *gc = (CGroupUnit *)unit; + return gc->name; +} + +Unit * +cgroup_unit_new (const gchar *name) +{ + CGroupUnit *unit; + + unit = g_object_new (cgroup_unit_get_type (), NULL); + unit->name = g_strdup (name); + + return (Unit *) unit; +} + +static void +cgroup_unit_init (CGroupUnit *unit) +{ +} + +static void +cgroup_unit_class_init (UnitClass *class) +{ + class->start_transient = cgroup_unit_start_transient; + class->start = cgroup_unit_start; + class->stop = cgroup_unit_stop; + class->abandon = cgroup_unit_abandon; + class->get_state = cgroup_unit_get_state; +} diff --git a/src/macro.h b/src/macro.h new file mode 100644 index 0000000..7cd8d81 --- /dev/null +++ b/src/macro.h @@ -0,0 +1,181 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#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/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <inttypes.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)) +#define _public_ __attribute__ ((visibility("default"))) +#define _hidden_ __attribute__ ((visibility("hidden"))) +#define _weakref_(x) __attribute__((weakref(#x))) +#define _introspect_(x) __attribute__((section("introspect." x))) + +#define XSTRINGIFY(x) #x +#define STRINGIFY(x) XSTRINGIFY(x) + +/* Rounds up */ +#define ALIGN(l) ALIGN_TO((l), sizeof(void*)) +static inline size_t ALIGN_TO(size_t l, size_t ali) { + return ((l + ali - 1) & ~(ali - 1)); +} + +#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +#ifndef MAX +#define MAX(a,b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +#define MAX3(a,b,c) \ + MAX(MAX(a,b),c) + +#ifndef MIN +#define MIN(a,b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif + +#define MIN3(a,b,c) \ + MIN(MIN(a,b),c) + +#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_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (false) \ + +/* We override the glibc assert() here. */ +#if 0 +#undef assert +#ifdef NDEBUG +#define assert(expr) do {} while(false) +#else +#define assert(expr) assert_se(expr) +#endif +#endif + +#define assert_not_reached(t) \ + do { \ + log_assert_failed_unreachable(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_ULONG(p) ((unsigned long) ((uintptr_t) (p))) +#define ULONG_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 PTR_TO_LONG(p) ((long) ((intptr_t) (p))) +#define LONG_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) + +static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { + unsigned j; + size_t r = 0; + + for (j = 0; j < n; j++) + r += i[j].iov_len; + + return r; +} + +static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { + unsigned j; + + for (j = 0; j < n; j++) { + size_t sub; + + if (_unlikely_(k <= 0)) + break; + + sub = MIN(i[j].iov_len, k); + i[j].iov_len -= sub; + i[j].iov_base = (uint8_t*) i[j].iov_base + sub; + k -= sub; + } + + return k; +} + +#endif diff --git a/src/ntp-unit.c b/src/ntp-unit.c new file mode 100644 index 0000000..f47097c --- /dev/null +++ b/src/ntp-unit.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2007 David Zeuthen <david@fubar.dk> + * Copyright (C) 2011 Bastien Nocera <hadess@hadess.net> + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or (at + * your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "unit.h" + +#include <stdio.h> + +#define NTPDATE_ENABLED "/etc/network/if-up.d/ntpdate" +#define NTPDATE_DISABLED "/etc/network/if-up.d/ntpdate.disabled" +#define NTPDATE_AVAILABLE "/usr/sbin/ntpdate-debian" +#define NTPD_AVAILABLE "/usr/sbin/ntpd" + +static gboolean +ntp_unit_get_can_use_ntpdate (void) +{ + return g_file_test (NTPDATE_AVAILABLE, G_FILE_TEST_EXISTS); +} + +static gboolean +ntp_unit_get_using_ntpdate (void) +{ + if (!ntp_unit_get_can_use_ntpdate ()) + return FALSE; + + return g_file_test (NTPDATE_ENABLED, G_FILE_TEST_EXISTS); +} + +static gboolean +ntp_unit_get_can_use_ntpd (void) +{ + return g_file_test (NTPD_AVAILABLE, G_FILE_TEST_EXISTS); +} + +static gboolean +ntp_unit_get_using_ntpd (void) +{ + int exit_status; + + if (!ntp_unit_get_can_use_ntpd ()) + return FALSE; + + if (!g_spawn_command_line_sync ("/usr/sbin/service ntp status", NULL, NULL, &exit_status, NULL)) + return FALSE; + + return exit_status == 0; +} + +static void +ntp_unit_set_using_ntpdate (gboolean using_ntp) +{ + if (using_ntp == ntp_unit_get_using_ntpdate ()) + return; + + if (using_ntp) + { + rename (NTPDATE_DISABLED, NTPDATE_ENABLED); + + /* Kick start ntpdate to sync time immediately */ + g_spawn_command_line_sync ("/etc/network/if-up.d/ntpdate", NULL, NULL, NULL, NULL); + } + else + rename (NTPDATE_ENABLED, NTPDATE_DISABLED); +} + +static void +ntp_unit_set_using_ntpd (gboolean using_ntp) +{ + char *cmd; + + cmd = g_strconcat ("/usr/sbin/update-rc.d ntp ", using_ntp ? "enable" : "disable", NULL); + g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL); + g_free (cmd); + + cmd = g_strconcat ("/usr/sbin/service ntp ", using_ntp ? "restart" : "stop", NULL);; + g_spawn_command_line_sync (cmd, NULL, NULL, NULL, NULL); + g_free (cmd); +} + +typedef Unit NtpUnit; +typedef UnitClass NtpUnitClass; +static GType ntp_unit_get_type (void); + +G_DEFINE_TYPE (NtpUnit, ntp_unit, UNIT_TYPE) + +static void +ntp_unit_start (Unit *unit) +{ + if (ntp_unit_get_can_use_ntpdate ()) + ntp_unit_set_using_ntpdate (TRUE); + + if (ntp_unit_get_can_use_ntpd ()) + ntp_unit_set_using_ntpd (TRUE); +} + +static void +ntp_unit_stop (Unit *unit) +{ + if (ntp_unit_get_can_use_ntpdate ()) + ntp_unit_set_using_ntpdate (FALSE); + + if (ntp_unit_get_can_use_ntpd ()) + ntp_unit_set_using_ntpd (FALSE); +} + +static const gchar * +ntp_unit_get_state (Unit *unit) +{ + if (ntp_unit_get_using_ntpdate () || ntp_unit_get_using_ntpd ()) + return "enabled"; + else + return "disabled"; +} + +Unit * +ntp_unit_get (void) +{ + if (ntp_unit_get_can_use_ntpdate () || ntp_unit_get_can_use_ntpd ()) + return g_object_new (ntp_unit_get_type (), NULL); + + return NULL; +} + +static void +ntp_unit_init (Unit *unit) +{ +} + +static void +ntp_unit_class_init (UnitClass *class) +{ + class->start = ntp_unit_start; + class->stop = ntp_unit_stop; + class->get_state = ntp_unit_get_state; +} diff --git a/src/power-unit.c b/src/power-unit.c new file mode 100644 index 0000000..45dc70c --- /dev/null +++ b/src/power-unit.c @@ -0,0 +1,161 @@ +/* + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or (at + * your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "unit.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +typedef UnitClass PowerUnitClass; +static GType power_unit_get_type (void); + +const gchar *power_cmds[] = { + [POWER_OFF] = "/sbin/shutdown -h now", + [POWER_REBOOT] = "/sbin/reboot", + [POWER_SUSPEND] = "/usr/sbin/pm-suspend", + [POWER_HIBERNATE] = "/usr/sbin/pm-hibernate" +}; + +typedef struct +{ + Unit parent_instance; + PowerAction action; +} PowerUnit; + +G_DEFINE_TYPE (PowerUnit, power_unit, UNIT_TYPE) + +gboolean in_shutdown; + +static void +power_unit_start (Unit *unit) +{ + PowerUnit *pu = (PowerUnit *) unit; + static gint64 last_suspend_time; + + /* If we request power off or reboot actions then we should ignore any + * suspend or hibernate actions that come after this. + */ + if (pu->action == POWER_OFF || pu->action == POWER_REBOOT) + { + GError *error = NULL; + gchar *pid_str; + gboolean success; + + in_shutdown = TRUE; + + /* avoid being killed during shutdown, so that we can keep our + * in_shutdown state */ + pid_str = g_strdup_printf ("%u", (unsigned) getpid ()); + success = g_file_set_contents ("/run/sendsigs.omit.d/systemd-shim.pid", pid_str, -1, &error); + g_free (pid_str); + if (!success) + { + g_warning ("Unable to write sendsigs.omit.d pid file: %s", error->message); + g_error_free (error); + } + + if (system (power_cmds[pu->action]) != 0) + g_warning ("Error while running '%s'", power_cmds[pu->action]); + } + else + { + if (in_shutdown) + return; + + /* This is pretty ugly: if we are being asked to perform a suspend + * or hibernate action within 1 second of the previous one, don't + * do it. + * + * We will be able to do this properly once we forward the + * timestamp of the event that caused the suspend all the way + * down. + */ + if (pu->action == POWER_SUSPEND && last_suspend_time + G_TIME_SPAN_SECOND > g_get_monotonic_time ()) + return; + + /* pm-utils might not have been installed, so go the direct route + * if we find that we don't have it... + */ + if (g_file_test (power_cmds[pu->action], G_FILE_TEST_IS_EXECUTABLE)) + { + if (system (power_cmds[pu->action]) != 0) + g_warning ("Error while running '%s'", power_cmds[pu->action]); + } + else + { + const gchar *kind; + gint fd; + + fd = open ("/sys/power/state", O_WRONLY); + if (fd == -1) + { + g_warning ("Could not open /sys/power/state"); + return; + } + + kind = (pu->action == POWER_SUSPEND) ? "mem" : "disk"; + if (write (fd, kind, strlen (kind)) != strlen (kind)) + g_warning ("Failed to write() to /sys/power/state?!?"); + close (fd); + } + + if (pu->action == POWER_SUSPEND) + last_suspend_time = g_get_monotonic_time (); + } +} + +static void +power_unit_stop (Unit *unit) +{ +} + +static const gchar * +power_unit_get_state (Unit *unit) +{ + return "static"; +} + +Unit * +power_unit_new (PowerAction action) +{ + PowerUnit *unit; + + g_return_val_if_fail (action < N_POWER_ACTIONS, NULL); + + unit = g_object_new (power_unit_get_type (), NULL); + unit->action = action; + + return (Unit *) unit; +} + +static void +power_unit_init (PowerUnit *unit) +{ +} + +static void +power_unit_class_init (UnitClass *class) +{ + class->start = power_unit_start; + class->stop = power_unit_stop; + class->get_state = power_unit_get_state; +} diff --git a/src/state.c b/src/state.c new file mode 100644 index 0000000..9bf499a --- /dev/null +++ b/src/state.c @@ -0,0 +1,89 @@ +/* + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "state.h" + +#define STATE_FILENAME "/run/systemd-shim-state" + +static GKeyFile * +state_get_key_file (void) +{ + static GKeyFile *key_file; + + if (!key_file) + { + key_file = g_key_file_new (); + g_key_file_load_from_file (key_file, STATE_FILENAME, G_KEY_FILE_NONE, NULL); + } + + return key_file; +} + +static void +state_sync (void) +{ + GError *error = NULL; + GKeyFile *key_file; + + key_file = state_get_key_file (); + + /* This will be world-readable but that's OK */ + if (!g_key_file_save_to_file (key_file, STATE_FILENAME, &error)) + { + g_warning ("cannot save systemd-shim state: %s", error->message); + g_error_free (error); + } +} + +gchar ** +state_list_units (void) +{ + GKeyFile *key_file = state_get_key_file (); + + return g_key_file_get_groups (key_file, NULL); +} + +gchar * +state_get_string (const gchar *unit, + const gchar *key) +{ + GKeyFile *key_file = state_get_key_file (); + + return g_key_file_get_string (key_file, unit, key, NULL); +} + +void +state_set_string (const gchar *unit, + const gchar *key, + const gchar *value) +{ + GKeyFile *key_file = state_get_key_file (); + + g_key_file_set_string (key_file, unit, key, value); + state_sync (); +} + +void +state_remove_unit (const gchar *unit) +{ + GKeyFile *key_file = state_get_key_file (); + + g_key_file_remove_group (key_file, unit, NULL); + state_sync (); +} diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..e90641d --- /dev/null +++ b/src/state.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef _state_h_ +#define _state_h_ + +#include <glib.h> + +gchar ** state_list_units (void); + +gchar * state_get_string (const gchar *unit, + const gchar *key); + +void state_set_string (const gchar *unit, + const gchar *key, + const gchar *value); + +void state_remove_unit (const gchar *unit); + +#endif /* _state_h_ */ diff --git a/src/systemd-iface.h b/src/systemd-iface.h new file mode 100644 index 0000000..b8d7156 --- /dev/null +++ b/src/systemd-iface.h @@ -0,0 +1,58 @@ +#ifndef _systemd_iface_h_ +#define _systemd_iface_h_ + +#include "config.h" + +static const gchar *systemd_iface = + "<node>" + "<interface name='org.freedesktop.systemd1.Manager'>" + "<method name='GetUnitFileState'>" + "<arg name='file' type='s' direction='in'/>" + "<arg name='state' type='s' direction='out'/>" + "</method>" + "<method name='DisableUnitFiles'>" + "<arg name='files' type='as' direction='in'/>" + "<arg name='runtime' type='b' direction='in'/>" + "<arg name='changes' type='a(sss)' direction='out'/>" + "</method>" + "<method name='EnableUnitFiles'>" + "<arg name='files' type='as' direction='in'/>" + "<arg name='runtime' type='b' direction='in'/>" + "<arg name='force' type='b' direction='in'/>" + "<arg name='carries_install_info' type='b' direction='out'/>" + "<arg name='changes' type='a(sss)' direction='out'/>" + "</method>" + "<method name='Reload'/>" + "<method name='StartUnit'>" + "<arg name='name' type='s' direction='in'/>" + "<arg name='mode' type='s' direction='in'/>" + "<arg name='job' type='o' direction='out'/>" + "</method>" + "<method name='StopUnit'>" + "<arg name='name' type='s' direction='in'/>" + "<arg name='mode' type='s' direction='in'/>" + "<arg name='job' type='o' direction='out'/>" + "</method>" + "<method name='StartTransientUnit'>" + "<arg name='name' type='s' direction='in'/>" + "<arg name='mode' type='s' direction='in'/>" + "<arg name='properties' type='a(sv)' direction='in'/>" +#if SYSTEMD_VERSION >= 209 + "<arg name='aux' type='a(sa(sv))' direction='in'/>" +#endif + "<arg name='job' type='o' direction='out'/>" + "</method>" + "<method name='Subscribe'/>" + "<method name='Unsubscribe'/>" + "<property name='Version' type='s' access='read'/>" + "<property name='Virtualization' type='s' access='read'/>" + "</interface>" + "<interface name='org.freedesktop.systemd1.Scope'>" + "<method name='Abandon'/>" + "</interface>" + "<interface name='org.freedesktop.systemd1.Unit'>" + "<property name='ActiveState' type='s' access='read'/>" + "</interface>" + "</node>"; + +#endif diff --git a/src/systemd-shim.c b/src/systemd-shim.c new file mode 100644 index 0000000..50d2120 --- /dev/null +++ b/src/systemd-shim.c @@ -0,0 +1,463 @@ +/* + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <gio/gio.h> + +#include "cgmanager.h" +#include "state.h" +#include "unit.h" +#include "virt.h" + +#include "systemd-iface.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +static gboolean +exit_on_inactivity (gpointer user_data) +{ + extern gboolean in_shutdown; + + if (!in_shutdown) + { + GDBusConnection *system_bus; + + system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + g_dbus_connection_flush_sync (system_bus, NULL, NULL); + g_object_unref (system_bus); + + exit (0); + } + + return FALSE; +} + +static void +had_activity (void) +{ + static gint inactivity_timeout; + + if (inactivity_timeout) + g_source_remove (inactivity_timeout); + + inactivity_timeout = g_timeout_add (10000, exit_on_inactivity, NULL); +} + +static void +shim_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GError *error = NULL; + + if (g_str_equal (method_name, "GetUnitFileState")) + { + const gchar *unit_name; + Unit *unit; + + g_variant_get_child (parameters, 0, "&s", &unit_name); + unit = lookup_unit (unit_name, &error); + + if (unit) + { + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", unit_get_state (unit))); + g_object_unref (unit); + goto success; + } + } + + else if (g_str_equal (method_name, "DisableUnitFiles")) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(sss))", NULL)); + goto success; + } + + else if (g_str_equal (method_name, "EnableUnitFiles")) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ba(sss))", TRUE, NULL)); + goto success; + } + + else if (g_str_equal (method_name, "Reload")) + { + g_dbus_method_invocation_return_value (invocation, NULL); + goto success; + } + + else if (g_str_equal (method_name, "Subscribe")) + { + g_dbus_method_invocation_return_value (invocation, NULL); + goto success; + } + + else if (g_str_equal (method_name, "Unsubscribe")) + { + g_dbus_method_invocation_return_value (invocation, NULL); + goto success; + } + + else if (g_str_equal (method_name, "StopUnit")) + { + const gchar *unit_name; + Unit *unit; + + g_variant_get_child (parameters, 0, "&s", &unit_name); + g_debug ("StopUnit(%s)", unit_name); + unit = lookup_unit (unit_name, &error); + + if (unit) + { + unit_stop (unit); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", "/")); + g_dbus_connection_emit_signal (connection, NULL, "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", "UnitRemoved", + g_variant_new ("(so)", unit_name, "/"), NULL); + g_object_unref (unit); + goto success; + } + } + + else if (g_str_equal (method_name, "StartUnit")) + { + const gchar *unit_name; + Unit *unit; + + g_variant_get_child (parameters, 0, "&s", &unit_name); + g_debug ("StartUnit(%s)", unit_name); + unit = lookup_unit (unit_name, &error); + + if (unit) + { + unit_start (unit); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", "/")); + g_dbus_connection_emit_signal (connection, sender, "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", "JobRemoved", + g_variant_new ("(uoss)", 0, "/", "", ""), NULL); + g_object_unref (unit); + goto success; + } + } + else if (g_str_equal (method_name, "StartTransientUnit")) + { + const gchar *unit_name; + Unit *unit; + + g_variant_get_child (parameters, 0, "&s", &unit_name); + g_debug ("StartTransientUnit(%s)", unit_name); + unit = lookup_unit (unit_name, &error); + + if (unit) + { + GVariant *properties; + + properties = g_variant_get_child_value (parameters, 2); + unit_start_transient (unit, properties); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", "/")); + g_dbus_connection_emit_signal (connection, sender, "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", "JobRemoved", + g_variant_new ("(uoss)", 0, "/", unit_get_state(unit), "done"), NULL); + g_variant_unref (properties); + g_object_unref (unit); + goto success; + } + } + + else + g_assert_not_reached (); + + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + +success: + had_activity (); +} + +static GVariant * +shim_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + const gchar *id = ""; + + had_activity (); + + if (g_strcmp0(property_name, "Virtualization") == 0) { + detect_virtualization (&id); + return g_variant_new ("s", id); + } + + if (g_strcmp0(property_name, "Version") == 0) { + return g_variant_new_take_string (g_strdup_printf("%d", SYSTEMD_VERSION)); + } + + return NULL; +} + +static gchar * +unescape_object_path (const gchar *path) +{ + gchar *result; + gint i, j; + + result = g_malloc (strlen (path) + 1); + for (i = 0, j = 0; path[i]; i++) + { + if (path[i] == '_') + { + if (g_ascii_isxdigit (path[i + 1]) && g_ascii_isxdigit (path[i + 2])) + { + gint val = g_ascii_xdigit_value(path[i + 1]) * 16 + g_ascii_xdigit_value (path[i + 2]); + + if (g_ascii_isgraph (val)) + result[j++] = val; + + i += 2; + } + } + + else + result[j++] = path[i]; + } + + result[j] = '\0'; + + return result; +} + +static gchar * +escape_object_path (const gchar *path) +{ + gchar *result; + gint i, j; + + result = g_malloc (3 * strlen (path) + 1); + for (i = 0, j = 0; path[i]; i++) + { + if (g_ascii_isalnum (path[i])) + result[j++] = path[i]; + else + { + snprintf (&result[j], 4, "_%02x", (guint) path[i]); + j += 3; + } + } + + result[j] = '\0'; + + return result; +} + +static void +shim_unit_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + const gchar *node = user_data; + + had_activity (); + + if (g_str_equal (method_name, "Abandon")) + { + GError *error = NULL; + gchar *unit_name; + Unit *unit; + + unit_name = unescape_object_path (node); + unit = lookup_unit (unit_name, &error); + + if (unit) + { + unit_abandon (unit); + + g_dbus_method_invocation_return_value (invocation, NULL); + g_object_unref (unit); + } + else + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + } + + g_free (unit_name); + } + + else + g_assert_not_reached (); +} + +static GVariant * +shim_unit_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + had_activity (); + + /* FIXME: This is probably wrong for many cases, but the right value for + * logind cleaning up sessions */ + if (g_strcmp0(property_name, "ActiveState") == 0) { + return g_variant_new_string ("inactive"); + } + + return NULL; +} + +static gchar ** +shim_units_enumerate (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data) +{ + gchar **cgroups; + guint i; + + had_activity (); + + cgroups = state_list_units (); + + for (i = 0; cgroups[i]; i++) + { + gchar *unescaped; + + unescaped = cgroups[i]; + cgroups[i] = escape_object_path (unescaped); + g_free (unescaped); + } + + return cgroups; +} + +static GDBusInterfaceInfo* shim_units_iface; +static GDBusInterfaceInfo* shim_scope_iface; + +static GDBusInterfaceInfo ** +shim_units_introspect (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data) +{ + GDBusInterfaceInfo *result[] = { g_dbus_interface_info_ref (shim_units_iface), + g_dbus_interface_info_ref (shim_scope_iface), + NULL }; + + had_activity (); + + return g_memdup (result, sizeof result); +} + +static const GDBusInterfaceVTable * +shim_units_dispatch (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data) +{ + static const GDBusInterfaceVTable vtable = { + shim_unit_method_call, + shim_unit_get_property + }; + + had_activity (); + + *out_user_data = (gpointer) node; + + return &vtable; +} + +static void +shim_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GDBusInterfaceVTable vtable = { + shim_method_call, + shim_get_property, + }; + GDBusSubtreeVTable sub_vtable = { + shim_units_enumerate, + shim_units_introspect, + shim_units_dispatch + }; + GDBusInterfaceInfo *iface; + GDBusNodeInfo *node; + + node = g_dbus_node_info_new_for_xml (systemd_iface, NULL); + shim_scope_iface = g_dbus_node_info_lookup_interface (node, "org.freedesktop.systemd1.Scope"); + g_assert (shim_scope_iface); + g_dbus_interface_info_ref (shim_scope_iface); + shim_units_iface = g_dbus_node_info_lookup_interface (node, "org.freedesktop.systemd1.Unit"); + g_assert (shim_units_iface); + g_dbus_interface_info_ref (shim_units_iface); + + iface = g_dbus_node_info_lookup_interface (node, "org.freedesktop.systemd1.Manager"); + + g_dbus_connection_register_object (connection, "/org/freedesktop/systemd1", iface, &vtable, NULL, NULL, NULL); + g_dbus_connection_register_subtree (connection, "/org/freedesktop/systemd1/unit", &sub_vtable, + G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES, NULL, NULL, NULL); + + g_dbus_node_info_unref (node); +} + +static void +shim_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_critical ("Unable to acquire bus name '%s'. Quitting.", name); + exit (1); +} + +int +main (void) +{ + g_bus_own_name (G_BUS_TYPE_SYSTEM, + "org.freedesktop.systemd1", + G_BUS_NAME_OWNER_FLAGS_NONE, + shim_bus_acquired, + NULL, /* name_acquired */ + shim_name_lost, + NULL, NULL); + + cgmanager_move_self (); + + while (1) + g_main_context_iteration (NULL, TRUE); +} diff --git a/src/unit.c b/src/unit.c new file mode 100644 index 0000000..7caf264 --- /dev/null +++ b/src/unit.c @@ -0,0 +1,117 @@ +/* + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "unit.h" + +G_DEFINE_TYPE (Unit, unit, G_TYPE_OBJECT) + +static void +unit_init (Unit *unit) +{ +} + +static void +unit_class_init (UnitClass *class) +{ +} + +Unit * +lookup_unit (const gchar *unit_name, + GError **error) +{ + Unit *unit = NULL; + + if (g_str_equal (unit_name, "ntpd.service") || + g_str_equal (unit_name, "systemd-timesyncd.service")) + unit = ntp_unit_get (); + + if (g_str_equal (unit_name, "suspend.target")) + unit = power_unit_new (POWER_SUSPEND); + + else if (g_str_equal (unit_name, "hibernate.target")) + unit = power_unit_new (POWER_HIBERNATE); + + else if (g_str_equal (unit_name, "reboot.target")) + unit = power_unit_new (POWER_REBOOT); + + else if (g_str_equal (unit_name, "shutdown.target") || g_str_equal (unit_name, "poweroff.target")) + unit = power_unit_new (POWER_OFF); + + else if (g_str_has_suffix (unit_name, ".slice") || g_str_has_suffix (unit_name, ".scope")) + unit = cgroup_unit_new (unit_name); + + if (unit == NULL) + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND, + "Unknown unit: %s", unit_name); + + return unit; +} + +const gchar * +unit_get_state (Unit *unit) +{ + g_return_val_if_fail (unit != NULL, NULL); + + return UNIT_GET_CLASS (unit)->get_state (unit); +} + +void +unit_start (Unit *unit) +{ + g_return_if_fail (unit != NULL); + + return UNIT_GET_CLASS (unit)->start (unit); +} + +void +unit_start_transient (Unit *unit, + GVariant *properties) +{ + g_return_if_fail (unit != NULL); + + if (!UNIT_GET_CLASS (unit)->start_transient) + { + g_warning ("%s does not implement StartTransient", G_OBJECT_TYPE_NAME (unit)); + return; + } + + return UNIT_GET_CLASS (unit)->start_transient (unit, properties); +} + +void +unit_stop (Unit *unit) +{ + g_return_if_fail (unit != NULL); + + return UNIT_GET_CLASS (unit)->stop (unit); +} + +void +unit_abandon (Unit *unit) +{ + g_return_if_fail (unit != NULL); + + if (!UNIT_GET_CLASS (unit)->start_transient) + { + g_warning ("%s does not implement StartTransient", G_OBJECT_TYPE_NAME (unit)); + return; + } + + return UNIT_GET_CLASS (unit)->abandon (unit); +} diff --git a/src/unit.h b/src/unit.h new file mode 100644 index 0000000..8e83925 --- /dev/null +++ b/src/unit.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2013 Canonical Limited + * + * This program 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 licence, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef _unit_h_ +#define _unit_h_ + +#include <gio/gio.h> + +#define UNIT_TYPE (unit_get_type ()) +#define UNIT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), UNIT_TYPE, UnitClass)) + +typedef GObject Unit; + +typedef struct +{ + GObjectClass parent_class; + + const gchar * (* get_state) (Unit *unit); + void (* start) (Unit *unit); + void (* start_transient) (Unit *unit, GVariant *properties); + void (* stop) (Unit *unit); + void (* abandon) (Unit *unit); +} UnitClass; + +GType unit_get_type (void); +Unit *lookup_unit (const gchar *name, GError **error); +const gchar *unit_get_state (Unit *unit); +void unit_start_transient (Unit *unit, GVariant *properties); +void unit_start (Unit *unit); +void unit_stop (Unit *unit); +void unit_abandon (Unit *unit); + +Unit *ntp_unit_get (void); + +typedef enum +{ + POWER_OFF, + POWER_REBOOT, + POWER_SUSPEND, + POWER_HIBERNATE, + N_POWER_ACTIONS +} PowerAction; + +Unit *power_unit_new (PowerAction action); + +Unit *cgroup_unit_new (const gchar *name); + +#endif /* _unit_h_ */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..20212b0 --- /dev/null +++ b/src/util.c @@ -0,0 +1,110 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + 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" + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +int read_one_line_file(const char *fn, char **line) { + FILE *f; + int r; + char t[LINE_MAX], *c; + + assert(fn); + assert(line); + + f = fopen(fn, "re"); + if (!f) + return -errno; + + if (!fgets(t, sizeof(t), f)) { + + if (ferror(f)) { + r = -errno; + goto finish; + } + + t[0] = 0; + } + + c = strdup(t); + if (!c) { + r = -ENOMEM; + goto finish; + } + + truncate_nl(c); + + *line = c; + r = 0; + +finish: + fclose(f); + return r; +} + +int running_in_chroot(void) { + struct stat a, b; + + zero(a); + zero(b); + + /* Only works as root */ + + if (stat("/proc/1/root", &a) < 0) + return -errno; + + if (stat("/", &b) < 0) + return -errno; + + return + a.st_dev != b.st_dev || + a.st_ino != b.st_ino; +} + +bool startswith(const char *s, const char *prefix) { + size_t sl, pl; + + assert(s); + assert(prefix); + + sl = strlen(s); + pl = strlen(prefix); + + if (pl == 0) + return true; + + if (sl < pl) + return false; + + return memcmp(s, prefix, pl) == 0; +} + +char *truncate_nl(char *s) { + assert(s); + + s[strcspn(s, NEWLINE)] = 0; + return s; +} + + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..b1af6db --- /dev/null +++ b/src/util.h @@ -0,0 +1,544 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooutilhfoo +#define fooutilhfoo + +/*** + 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 <inttypes.h> +#include <time.h> +#include <sys/time.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <sched.h> +#include <limits.h> +#include <sys/stat.h> +#include <dirent.h> + +#include "macro.h" + +typedef uint64_t usec_t; + +typedef struct dual_timestamp { + usec_t realtime; + usec_t monotonic; +} dual_timestamp; + +#define MSEC_PER_SEC 1000ULL +#define USEC_PER_SEC 1000000ULL +#define USEC_PER_MSEC 1000ULL +#define NSEC_PER_SEC 1000000000ULL +#define NSEC_PER_MSEC 1000000ULL +#define NSEC_PER_USEC 1000ULL + +#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC) +#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE) +#define USEC_PER_DAY (24ULL*USEC_PER_HOUR) +#define USEC_PER_WEEK (7ULL*USEC_PER_DAY) +#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC) +#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC) + +/* What is interpreted as whitespace? */ +#define WHITESPACE " \t\n\r" +#define NEWLINE "\n\r" +#define QUOTES "\"\'" +#define COMMENTS "#;\n" + +#define FORMAT_TIMESTAMP_MAX 64 +#define FORMAT_TIMESTAMP_PRETTY_MAX 256 +#define FORMAT_TIMESPAN_MAX 64 +#define FORMAT_BYTES_MAX 8 + +#define ANSI_HIGHLIGHT_ON "\x1B[1;39m" +#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m" +#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m" +#define ANSI_HIGHLIGHT_OFF "\x1B[0m" + +usec_t now(clockid_t clock); + +dual_timestamp* dual_timestamp_get(dual_timestamp *ts); +dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); + +#define dual_timestamp_is_set(ts) ((ts)->realtime > 0) + +usec_t timespec_load(const struct timespec *ts); +struct timespec *timespec_store(struct timespec *ts, usec_t u); + +usec_t timeval_load(const struct timeval *tv); +struct timeval *timeval_store(struct timeval *tv, usec_t u); + +size_t page_size(void); +#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) + +#define streq(a,b) (strcmp((a),(b)) == 0) +#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) + +bool streq_ptr(const char *a, const char *b); + +#define new(t, n) ((t*) malloc(sizeof(t)*(n))) + +#define new0(t, n) ((t*) calloc((n), sizeof(t))) + +#define malloc0(n) (calloc((n), 1)) + +static inline const char* yes_no(bool b) { + return b ? "yes" : "no"; +} + +static inline const char* strempty(const char *s) { + return s ? s : ""; +} + +static inline const char* strnull(const char *s) { + return s ? s : "(null)"; +} + +static inline const char *strna(const char *s) { + return s ? s : "n/a"; +} + +static inline bool is_path_absolute(const char *p) { + return *p == '/'; +} + +static inline bool isempty(const char *p) { + return !p || !p[0]; +} + +bool endswith(const char *s, const char *postfix); +bool startswith(const char *s, const char *prefix); +bool startswith_no_case(const char *s, const char *prefix); + +bool first_word(const char *s, const char *word); + +int close_nointr(int fd); +void close_nointr_nofail(int fd); +void close_many(const int fds[], unsigned n_fd); + +int parse_boolean(const char *v); +int parse_usec(const char *t, usec_t *usec); +int parse_bytes(const char *t, off_t *bytes); +int parse_pid(const char *s, pid_t* ret_pid); +int parse_uid(const char *s, uid_t* ret_uid); +#define parse_gid(s, ret_uid) parse_uid(s, ret_uid) + +int safe_atou(const char *s, unsigned *ret_u); +int safe_atoi(const char *s, int *ret_i); + +int safe_atollu(const char *s, unsigned long long *ret_u); +int safe_atolli(const char *s, long long int *ret_i); + +#if __WORDSIZE == 32 +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned)); + return safe_atou(s, (unsigned*) ret_u); +} +static inline int safe_atoli(const char *s, long int *ret_u) { + assert_cc(sizeof(long int) == sizeof(int)); + return safe_atoi(s, (int*) ret_u); +} +#else +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +} +static inline int safe_atoli(const char *s, long int *ret_u) { + assert_cc(sizeof(long int) == sizeof(long long int)); + return safe_atolli(s, (long long int*) ret_u); +} +#endif + +static inline int safe_atou32(const char *s, uint32_t *ret_u) { + assert_cc(sizeof(uint32_t) == sizeof(unsigned)); + return safe_atou(s, (unsigned*) ret_u); +} + +static inline int safe_atoi32(const char *s, int32_t *ret_i) { + assert_cc(sizeof(int32_t) == sizeof(int)); + return safe_atoi(s, (int*) ret_i); +} + +static inline int safe_atou64(const char *s, uint64_t *ret_u) { + assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +} + +static inline int safe_atoi64(const char *s, int64_t *ret_i) { + assert_cc(sizeof(int64_t) == sizeof(long long int)); + return safe_atolli(s, (long long int*) ret_i); +} + +char *split(const char *c, size_t *l, const char *separator, char **state); +char *split_quoted(const char *c, size_t *l, char **state); + +#define FOREACH_WORD(word, length, s, state) \ + for ((state) = NULL, (word) = split((s), &(length), WHITESPACE, &(state)); (word); (word) = split((s), &(length), WHITESPACE, &(state))) + +#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ + for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state))) + +#define FOREACH_WORD_QUOTED(word, length, s, state) \ + for ((state) = NULL, (word) = split_quoted((s), &(length), &(state)); (word); (word) = split_quoted((s), &(length), &(state))) + +char **split_path_and_make_absolute(const char *p); + +pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); +int get_starttime_of_pid(pid_t pid, unsigned long long *st); + +int write_one_line_file(const char *fn, const char *line); +int write_one_line_file_atomic(const char *fn, const char *line); +int read_one_line_file(const char *fn, char **line); +int read_full_file(const char *fn, char **contents, size_t *size); + +int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; +int load_env_file(const char *fname, char ***l); +int write_env_file(const char *fname, char **l); + +char *strappend(const char *s, const char *suffix); +char *strnappend(const char *s, const char *suffix, size_t length); + +char *replace_env(const char *format, char **env); +char **replace_env_argv(char **argv, char **env); + +int readlink_malloc(const char *p, char **r); +int readlink_and_make_absolute(const char *p, char **r); +int readlink_and_canonicalize(const char *p, char **r); + +char *file_name_from_path(const char *p); +bool is_path(const char *p); + +bool path_is_absolute(const char *p); +char *path_make_absolute(const char *p, const char *prefix); +char *path_make_absolute_cwd(const char *p); + +char **strv_path_make_absolute_cwd(char **l); +char **strv_path_canonicalize(char **l); +char **strv_path_remove_empty(char **l); + +int reset_all_signal_handlers(void); + +char *strstrip(char *s); +char *delete_chars(char *s, const char *bad); +char *truncate_nl(char *s); + +char *file_in_same_dir(const char *path, const char *filename); +int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid); +int mkdir_parents(const char *path, mode_t mode); +int mkdir_p(const char *path, mode_t mode); + +int parent_of_path(const char *path, char **parent); + +int rmdir_parents(const char *path, const char *stop); + +int get_process_comm(pid_t pid, char **name); +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); +int get_process_exe(pid_t pid, char **name); +int get_process_uid(pid_t pid, uid_t *uid); + +char hexchar(int x); +int unhexchar(char c); +char octchar(int x); +int unoctchar(char c); +char decchar(int x); +int undecchar(char c); + +char *cescape(const char *s); +char *cunescape(const char *s); +char *cunescape_length(const char *s, size_t length); + +char *xescape(const char *s, const char *bad); + +char *bus_path_escape(const char *s); +char *bus_path_unescape(const char *s); + +char *path_kill_slashes(char *path); + +bool path_startswith(const char *path, const char *prefix); +bool path_equal(const char *a, const char *b); + +char *ascii_strlower(char *path); + +bool dirent_is_file(const struct dirent *de); +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix); + +bool ignore_file(const char *filename); + +bool chars_intersect(const char *a, const char *b); + +char *format_timestamp(char *buf, size_t l, usec_t t); +char *format_timestamp_pretty(char *buf, size_t l, usec_t t); +char *format_timespan(char *buf, size_t l, usec_t t); + +int make_stdio(int fd); +int make_null_stdio(void); + +unsigned long long random_ull(void); + +#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ + scope const char *name##_to_string(type i) { \ + if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ + return NULL; \ + return name##_table[i]; \ + } \ + scope type name##_from_string(const char *s) { \ + type i; \ + unsigned u = 0; \ + assert(s); \ + for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \ + if (name##_table[i] && \ + streq(name##_table[i], s)) \ + return i; \ + if (safe_atou(s, &u) >= 0 && \ + u < ELEMENTSOF(name##_table)) \ + return (type) u; \ + return (type) -1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static) + +int fd_nonblock(int fd, bool nonblock); +int fd_cloexec(int fd, bool cloexec); + +int close_all_fds(const int except[], unsigned n_except); + +bool fstype_is_network(const char *fstype); + +int chvt(int vt); + +int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); +int ask(char *ret, const char *replies, const char *text, ...); + +int reset_terminal_fd(int fd, bool switch_to_text); +int reset_terminal(const char *name); + +int open_terminal(const char *name, int mode); +int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm); +int release_terminal(void); + +int flush_fd(int fd); + +int ignore_signals(int sig, ...); +int default_signals(int sig, ...); +int sigaction_many(const struct sigaction *sa, ...); + +int close_pipe(int p[]); +int fopen_temporary(const char *path, FILE **_f, char **_temp_path); + +ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); +ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); + +int path_is_mount_point(const char *path, bool allow_symlink); + +bool is_device_path(const char *path); + +int dir_is_empty(const char *path); + +void rename_process(const char name[8]); + +void sigset_add_many(sigset_t *ss, ...); + +char* gethostname_malloc(void); +char* getlogname_malloc(void); + +int getttyname_malloc(int fd, char **r); +int getttyname_harder(int fd, char **r); + +int get_ctty_devnr(pid_t pid, dev_t *d); +int get_ctty(pid_t, dev_t *_devnr, char **r); + +int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); + +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); + +int pipe_eof(int fd); + +cpu_set_t* cpu_set_malloc(unsigned *ncpus); + +void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap); +void status_printf(const char *status, bool ellipse, const char *format, ...); +void status_welcome(void); + +int fd_columns(int fd); +unsigned columns(void); + +int fd_lines(int fd); +unsigned lines(void); + +int running_in_chroot(void); + +char *ellipsize(const char *s, size_t length, unsigned percent); +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent); + +int touch(const char *path); + +char *unquote(const char *s, const char *quotes); +char *normalize_env_assignment(const char *s); + +int wait_for_terminate(pid_t pid, siginfo_t *status); +int wait_for_terminate_and_warn(const char *name, pid_t pid); + +_noreturn_ void freeze(void); + +bool null_or_empty(struct stat *st); +int null_or_empty_path(const char *fn); + +DIR *xopendirat(int dirfd, const char *name, int flags); + +void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); +void dual_timestamp_deserialize(const char *value, dual_timestamp *t); + +char *fstab_node_to_udev_node(const char *p); + +void filter_environ(const char *prefix); + +bool tty_is_vc(const char *tty); +bool tty_is_vc_resolve(const char *tty); +int vtnr_from_tty(const char *tty); +const char *default_term_for_tty(const char *tty); + +void execute_directory(const char *directory, DIR *_d, char *argv[]); + +int kill_and_sigcont(pid_t pid, int sig); + +bool nulstr_contains(const char*nulstr, const char *needle); + +bool plymouth_running(void); + +void parse_syslog_priority(char **p, int *priority); +void skip_syslog_pid(char **buf); +void skip_syslog_date(char **buf); + +int have_effective_cap(int value); + +bool hostname_is_valid(const char *s); +char* hostname_cleanup(char *s); + +char* strshorten(char *s, size_t l); + +int terminal_vhangup_fd(int fd); +int terminal_vhangup(const char *name); + +int vt_disallocate(const char *name); + +int copy_file(const char *from, const char *to); +int symlink_or_copy(const char *from, const char *to); +int symlink_or_copy_atomic(const char *from, const char *to); + +int fchmod_umask(int fd, mode_t mode); + +int conf_files_list(char ***strv, const char *suffix, const char *dir, ...); + +int hwclock_is_localtime(void); +int hwclock_apply_localtime_delta(int *min); +int hwclock_reset_localtime_delta(void); +int hwclock_get_time(struct tm *tm); +int hwclock_set_time(const struct tm *tm); + +int audit_session_from_pid(pid_t pid, uint32_t *id); +int audit_loginuid_from_pid(pid_t pid, uid_t *uid); + +bool display_is_local(const char *display); +int socket_from_display(const char *display, char **path); + +int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home); +int get_group_creds(const char **groupname, gid_t *gid); + +int in_group(const char *name); + +int glob_exists(const char *path); + +int dirent_ensure_type(DIR *d, struct dirent *de); + +int in_search_path(const char *path, char **search); +int get_files_in_directory(const char *path, char ***list); + +char *join(const char *x, ...) _sentinel_; + +bool is_main_thread(void); + +bool in_charset(const char *s, const char* charset); + +int block_get_whole_disk(dev_t d, dev_t *ret); + +int file_is_priv_sticky(const char *p); + +int strdup_or_null(const char *a, char **b); + +#define NULSTR_FOREACH(i, l) \ + for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) + +#define NULSTR_FOREACH_PAIR(i, j, l) \ + for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) + +const char *ioprio_class_to_string(int i); +int ioprio_class_from_string(const char *s); + +const char *sigchld_code_to_string(int i); +int sigchld_code_from_string(const char *s); + +const char *log_facility_unshifted_to_string(int i); +int log_facility_unshifted_from_string(const char *s); + +const char *log_level_to_string(int i); +int log_level_from_string(const char *s); + +const char *sched_policy_to_string(int i); +int sched_policy_from_string(const char *s); + +const char *rlimit_to_string(int i); +int rlimit_from_string(const char *s); + +const char *ip_tos_to_string(int i); +int ip_tos_from_string(const char *s); + +const char *signal_to_string(int i); +int signal_from_string(const char *s); + +int signal_from_string_try_harder(const char *s); + +extern int saved_argc; +extern char **saved_argv; + +bool kexec_loaded(void); + +int prot_from_flags(int flags); + +unsigned long cap_last_cap(void); + +char *format_bytes(char *buf, size_t l, off_t t); + +int fd_wait_for_event(int fd, int event, usec_t timeout); + +void* memdup(const void *p, size_t l); + +int rtc_open(int flags); + +int is_kernel_thread(pid_t pid); + +int fd_inc_sndbuf(int fd, size_t n); +int fd_inc_rcvbuf(int fd, size_t n); + +#endif diff --git a/src/virt.c b/src/virt.c new file mode 100644 index 0000000..4c526ff --- /dev/null +++ b/src/virt.c @@ -0,0 +1,292 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 <errno.h> +#include <unistd.h> + +#include "util.h" +#include "virt.h" + +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { + +#if defined(__i386__) || defined(__x86_64__) + + /* Both CPUID and DMI are x86 specific interfaces... */ + + static const char *const dmi_vendors[] = { + "/sys/class/dmi/id/sys_vendor", + "/sys/class/dmi/id/board_vendor", + "/sys/class/dmi/id/bios_vendor" + }; + + static const char dmi_vendor_table[] = + "QEMU\0" "qemu\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMware\0" "vmware\0" + "VMW\0" "vmware\0" + "Microsoft Corporation\0" "microsoft\0" + "innotek GmbH\0" "oracle\0" + "Xen\0" "xen\0" + "Bochs\0" "bochs\0"; + + static const char cpuid_vendor_table[] = + "XenVMMXenVMM\0" "xen\0" + "KVMKVMKVM\0" "kvm\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMwareVMware\0" "vmware\0" + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + "Microsoft Hv\0" "microsoft\0"; + + uint32_t eax, ecx; + union { + uint32_t sig32[3]; + char text[13]; + } sig; + unsigned i; + const char *j, *k; + bool hypervisor; + + /* http://lwn.net/Articles/301888/ */ + zero(sig); + +#if defined (__i386__) +#define REG_a "eax" +#define REG_b "ebx" +#elif defined (__amd64__) +#define REG_a "rax" +#define REG_b "rbx" +#endif + + /* First detect whether there is a hypervisor */ + eax = 1; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=c" (ecx) + : "0" (eax) + ); + + hypervisor = !!(ecx & 0x80000000U); + + if (hypervisor) { + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) + if (streq(sig.text, j)) { + + if (id) + *id = k; + + return 1; + } + } + + for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { + char *s; + int r; + const char *found = NULL; + + if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { + if (r != -ENOENT) + return r; + + continue; + } + + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) + found = k; + free(s); + + if (found) { + if (id) + *id = found; + + return 1; + } + } + + if (hypervisor) { + if (id) + *id = "other"; + + return 1; + } + +#endif + return 0; +} + +int detect_container(const char **id) { + FILE *f; + + /* Unfortunately many of these operations require root access + * in one way or another */ + + if (geteuid() != 0) + return -EPERM; + + if (running_in_chroot() > 0) { + + if (id) + *id = "chroot"; + + return 1; + } + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + + if (id) + *id = "openvz"; + + return 1; + } + + f = fopen("/proc/1/environ", "re"); + if (f) { + bool done = false; + + do { + char line[LINE_MAX]; + unsigned i; + + for (i = 0; i < sizeof(line)-1; i++) { + int c; + + c = getc(f); + if (_unlikely_(c == EOF)) { + done = true; + break; + } else if (c == 0) + break; + + line[i] = c; + } + line[i] = 0; + + if (streq(line, "container=lxc")) { + fclose(f); + + if (id) + *id = "lxc"; + return 1; + + } else if (streq(line, "container=lxc-libvirt")) { + fclose(f); + + if (id) + *id = "lxc-libvirt"; + return 1; + + } else if (streq(line, "container=systemd-nspawn")) { + fclose(f); + + if (id) + *id = "systemd-nspawn"; + return 1; + + } else if (startswith(line, "container=")) { + fclose(f); + + if (id) + *id = "other"; + return 1; + } + + } while (!done); + + fclose(f); + } + + return 0; +} + +/* Returns a short identifier for the various VM/container implementations */ +Virtualization detect_virtualization(const char **id) { + + static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID; + static __thread const char *cached_id = NULL; + + const char *_id; + int r; + Virtualization v; + + if (_likely_(cached_virt >= 0)) { + + if (id && cached_virt > 0) + *id = cached_id; + + return cached_virt; + } + + r = detect_container(&_id); + if (r < 0) { + v = r; + goto finish; + } else if (r > 0) { + v = VIRTUALIZATION_CONTAINER; + goto finish; + } + + r = detect_vm(&_id); + if (r < 0) { + v = r; + goto finish; + } else if (r > 0) { + v = VIRTUALIZATION_VM; + goto finish; + } + + v = VIRTUALIZATION_NONE; + +finish: + if (v > 0) { + cached_id = _id; + + if (id) + *id = _id; + } + + if (v >= 0) + cached_virt = v; + + return v; +} diff --git a/src/virt.h b/src/virt.h new file mode 100644 index 0000000..f55c9a6 --- /dev/null +++ b/src/virt.h @@ -0,0 +1,38 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foovirthfoo +#define foovirthfoo + +/*** + This file is part of systemd. + + Copyright 2011 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 detect_vm(const char **id); +int detect_container(const char **id); + +typedef enum Virtualization { + VIRTUALIZATION_NONE = 0, + VIRTUALIZATION_VM, + VIRTUALIZATION_CONTAINER, + _VIRTUALIZATION_MAX, + _VIRTUALIZATION_INVALID = -1 +} Virtualization; + +Virtualization detect_virtualization(const char **id); + +#endif |