summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Pitt <mpitt@debian.org>2016-06-08 14:14:20 +0100
committerMartin Pitt <mpitt@debian.org>2016-06-08 14:14:20 +0100
commita794d1ab788d419a1c49579cb7d2b411310e38f5 (patch)
treea3a4eadedc1352ad6508e30b247adb1e66dfdf46 /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/.gitignore2
-rw-r--r--src/Makefile.am32
-rw-r--r--src/cgmanager.c215
-rw-r--r--src/cgmanager.h41
-rw-r--r--src/cgroup-release-agent.c75
-rw-r--r--src/cgroup-unit.c240
-rw-r--r--src/macro.h181
-rw-r--r--src/ntp-unit.c152
-rw-r--r--src/power-unit.c161
-rw-r--r--src/state.c89
-rw-r--r--src/state.h36
-rw-r--r--src/systemd-iface.h58
-rw-r--r--src/systemd-shim.c463
-rw-r--r--src/unit.c117
-rw-r--r--src/unit.h64
-rw-r--r--src/util.c110
-rw-r--r--src/util.h544
-rw-r--r--src/virt.c292
-rw-r--r--src/virt.h38
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