summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am8
-rw-r--r--tools/dbus-update-activation-environment.c417
2 files changed, 425 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 80025b82..68a59707 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -16,6 +16,7 @@ bin_PROGRAMS = \
dbus-monitor \
dbus-send \
dbus-test-tool \
+ dbus-update-activation-environment \
$(NULL)
if DBUS_UNIX
@@ -98,6 +99,13 @@ dbus_test_tool_SOURCES = \
$(NULL)
dbus_test_tool_LDADD = $(top_builddir)/dbus/libdbus-1.la
+dbus_update_activation_environment_SOURCES = \
+ dbus-update-activation-environment.c \
+ tool-common.c \
+ tool-common.h \
+ $(NULL)
+dbus_update_activation_environment_LDADD = $(top_builddir)/dbus/libdbus-1.la
+
EXTRA_DIST = run-with-tmp-session-bus.sh strtoll.c strtoull.c
CLEANFILES = \
run-with-tmp-session-bus.conf
diff --git a/tools/dbus-update-activation-environment.c b/tools/dbus-update-activation-environment.c
new file mode 100644
index 00000000..56d3b20d
--- /dev/null
+++ b/tools/dbus-update-activation-environment.c
@@ -0,0 +1,417 @@
+/*
+ * dbus-update-activation-environment - update D-Bus, and optionally
+ * systemd, activation environment
+ *
+ * Copyright © 2014-2015 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYSEXITS_H
+# include <sysexits.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#ifdef DBUS_UNIX
+# include <unistd.h>
+# include <sys/stat.h>
+# include <sys/types.h>
+#endif
+
+#include "tool-common.h"
+
+#define PROGNAME "dbus-update-activation-environment"
+
+#ifndef EX_USAGE
+# define EX_USAGE 64
+#endif
+
+#ifndef EX_UNAVAILABLE
+# define EX_UNAVAILABLE 69
+#endif
+
+#ifndef EX_OSERR
+# define EX_OSERR 71
+#endif
+
+/* apparently this is the portable way to get the entire environment... */
+extern char **environ;
+
+/* we don't really have anything useful to say about the stage at which we
+ * failed */
+#define oom() tool_oom ("updating environment")
+
+static dbus_bool_t verbose = FALSE;
+
+static void say (const char *format, ...) _DBUS_GNUC_PRINTF (1, 2);
+
+static void
+say (const char *format,
+ ...)
+{
+ va_list ap;
+
+ if (!verbose)
+ return;
+
+ fprintf (stderr, "%s: ", PROGNAME);
+ va_start (ap, format);
+ vfprintf (stderr, format, ap);
+ fputc ('\n', stderr);
+ va_end (ap);
+}
+
+#ifdef __linux__
+static dbus_bool_t
+systemd_user_running (void)
+{
+ char *xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
+ char *path;
+ struct stat buf;
+ dbus_bool_t ret = FALSE;
+
+ if (xdg_runtime_dir == NULL)
+ return FALSE;
+
+ /* Assume that XDG_RUNTIME_DIR/systemd exists if and only if
+ * "systemd --user" is running. It's OK to use asprintf() here
+ * because we know we're on Linux. */
+ if (asprintf (&path, "%s/systemd", xdg_runtime_dir) < 0)
+ oom ();
+
+ if (stat (path, &buf) == 0)
+ ret = TRUE;
+
+ free (path);
+ return ret;
+}
+#endif
+
+int
+main (int argc, char **argv)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessage *reply;
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessageIter msg_iter;
+ DBusMessageIter array_iter;
+ int i;
+ int first_non_option = argc;
+ dbus_bool_t all = FALSE;
+#ifdef __linux__
+ DBusMessage *sd_msg = NULL;
+ DBusMessageIter sd_msg_iter;
+ DBusMessageIter sd_array_iter;
+ dbus_bool_t systemd = FALSE;
+#endif
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] != '-')
+ {
+ first_non_option = i;
+ break;
+ }
+ else if (strcmp (argv[i], "--") == 0)
+ {
+ first_non_option = i + 1;
+ break;
+ }
+ else if (strcmp (argv[i], "--all") == 0)
+ {
+ all = TRUE;
+ }
+ else if (strcmp (argv[i], "--systemd") == 0)
+ {
+#ifdef __linux__
+ systemd = TRUE;
+#else
+ say ("not on Linux, ignoring --systemd argument");
+#endif
+ }
+ else if (strcmp (argv[i], "--verbose") == 0)
+ {
+ verbose = TRUE;
+ }
+ else
+ {
+ fprintf (stderr,
+ "%1$s: update environment variables that will be set for D-Bus\n"
+ " session services\n"
+ "\n"
+ "%1$s [options] VAR[=VAL] [VAR2[=VAL2] ...]\n"
+ " Add specified variables to D-Bus activation environment.\n"
+ " If omitted, values are taken from current environment;\n"
+ " variables not found in the environment are ignored.\n"
+ "%1$s --all\n"
+ " Add entire current environment to D-Bus activation\n"
+ " environment.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ "--all\n"
+ " Upload all environment variables.\n"
+ "--systemd\n"
+ " Also update the 'systemd --user' environment\n"
+ " if possible.\n"
+ "--verbose\n"
+ " Talk about it.\n"
+ ,
+ PROGNAME);
+ exit (EX_USAGE);
+ }
+ }
+
+ if (all && first_non_option < argc)
+ {
+ fprintf (stderr, "%s: error: --all cannot be used with VAR or "
+ "VAR=VAL arguments\n", PROGNAME);
+ exit (EX_USAGE);
+ }
+
+ conn = dbus_bus_get (DBUS_BUS_SESSION, &error);
+
+ if (conn == NULL)
+ {
+ fprintf (stderr,
+ "%s: error: unable to connect to D-Bus: %s\n", PROGNAME,
+ error.message);
+ exit (EX_OSERR);
+ }
+
+ msg = dbus_message_new_method_call (DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "UpdateActivationEnvironment");
+
+ if (msg == NULL)
+ oom ();
+
+ dbus_message_iter_init_append (msg, &msg_iter);
+
+ if (!dbus_message_iter_open_container (&msg_iter, DBUS_TYPE_ARRAY,
+ "{ss}", &array_iter))
+ oom ();
+
+#ifdef __linux__
+ if (systemd)
+ {
+ if (!systemd_user_running ())
+ {
+ /* This is only best-effort. */
+ say ("systemd --user not found, ignoring --systemd argument");
+ systemd = FALSE;
+ }
+ }
+
+ if (systemd)
+ {
+ sd_msg = dbus_message_new_method_call ("org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
+ "SetEnvironment");
+
+ if (sd_msg == NULL)
+ oom ();
+
+ dbus_message_iter_init_append (sd_msg, &sd_msg_iter);
+
+ if (!dbus_message_iter_open_container (&sd_msg_iter, DBUS_TYPE_ARRAY,
+ "s", &sd_array_iter))
+ oom ();
+ }
+#endif
+
+ for (i = all ? 0 : first_non_option;
+ all ? environ[i] != NULL : i < argc;
+ i++)
+ {
+ const char *var;
+ char *copy;
+ char *eq;
+ const char *val;
+ DBusMessageIter pair_iter;
+
+ if (all)
+ var = environ[i];
+ else
+ var = argv[i];
+
+ copy = strdup (var);
+
+ if (copy == NULL)
+ oom ();
+
+ if (!dbus_validate_utf8 (var, NULL))
+ {
+ /* var is either of the form VAR or VAR=VAL */
+ fprintf (stderr,
+ "%s: warning: environment variable not UTF-8: %s\n",
+ PROGNAME, var);
+ goto next;
+ }
+
+ eq = strchr (copy, '=');
+
+ if (eq == NULL)
+ {
+ if (all)
+ {
+ /* items in the environment block should be of the form
+ * VAR=VAL */
+ fprintf (stderr,
+ "%s: warning: environment variable without '=': %s\n",
+ PROGNAME, var);
+ goto next;
+ }
+ else
+ {
+ /* items on the command-line may be of the form VAR
+ * in which case we infer the value from the environment */
+ val = getenv (var);
+
+ if (val == NULL)
+ {
+ /* nothing to be done here */
+ goto next;
+ }
+
+ if (!dbus_validate_utf8 (val, NULL))
+ {
+ fprintf (stderr,
+ "%s: warning: environment variable not UTF-8: %s=%s\n",
+ PROGNAME, var, val);
+ goto next;
+ }
+ }
+ }
+ else
+ {
+ /* split VAR=VAL into VAR and VAL */
+ *eq = '\0';
+ val = eq + 1;
+ }
+
+#ifdef __linux__
+ if (systemd)
+ {
+ char *combined;
+
+ /* recombine if necessary */
+ if (asprintf (&combined, "%s=%s", copy, val) < 0)
+ oom ();
+
+ if (!dbus_message_iter_append_basic (&sd_array_iter,
+ DBUS_TYPE_STRING, &combined))
+ oom ();
+
+ free (combined);
+ }
+#endif
+
+ if (!dbus_message_iter_open_container (&array_iter,
+ DBUS_TYPE_DICT_ENTRY, NULL, &pair_iter))
+ oom ();
+
+ say ("setting %s=%s", copy, val);
+
+ if (!dbus_message_iter_append_basic (&pair_iter, DBUS_TYPE_STRING,
+ &copy))
+ oom ();
+
+ if (!dbus_message_iter_append_basic (&pair_iter, DBUS_TYPE_STRING,
+ &val))
+ oom ();
+
+ if (!dbus_message_iter_close_container (&array_iter, &pair_iter))
+ oom ();
+
+next:
+ free (copy);
+ }
+
+ if (!dbus_message_iter_close_container (&msg_iter, &array_iter))
+ oom ();
+
+#ifdef __linux__
+ if (systemd &&
+ !dbus_message_iter_close_container (&sd_msg_iter, &sd_array_iter))
+ oom ();
+#endif
+
+ reply = dbus_connection_send_with_reply_and_block (conn, msg, -1, &error);
+
+ if (reply == NULL)
+ {
+ fprintf (stderr,
+ "%s: error sending to dbus-daemon: %s: %s\n",
+ PROGNAME, error.name, error.message);
+ exit (EX_UNAVAILABLE);
+ }
+
+ if (!dbus_message_get_args (msg, &error, DBUS_TYPE_INVALID))
+ {
+ fprintf (stderr,
+ "%s: error from dbus-daemon: %s: %s\n",
+ PROGNAME, error.name, error.message);
+ exit (EX_UNAVAILABLE);
+ }
+
+ dbus_message_unref (reply);
+
+#ifdef __linux__
+ if (systemd)
+ {
+ reply = dbus_connection_send_with_reply_and_block (conn, sd_msg, -1,
+ &error);
+
+ /* non-fatal, the main purpose of this thing is to communicate
+ * with dbus-daemon */
+ if (reply == NULL)
+ {
+ fprintf (stderr,
+ "%s: warning: error sending to systemd: %s: %s\n",
+ PROGNAME, error.name, error.message);
+ }
+ else if (!dbus_message_get_args (msg, &error, DBUS_TYPE_INVALID))
+ {
+ fprintf (stderr,
+ "%s: warning: error from systemd: %s: %s\n",
+ PROGNAME, error.name, error.message);
+ }
+
+ if (reply != NULL)
+ dbus_message_unref (reply);
+
+ dbus_message_unref (sd_msg);
+ dbus_error_free (&error);
+ }
+#endif
+
+ dbus_message_unref (msg);
+ dbus_connection_unref (conn);
+ return 0;
+}