summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2015-02-11 15:47:53 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2015-02-24 11:15:52 +0000
commit2a6cefbc3bd3ed9392603da6a76b49c0dcba7e0d (patch)
treedf99c280c45338a93752ba68c8cf90d3126e823a
parent263aca37ecf5f977f68d87b54f2fb30584725781 (diff)
downloaddbus-2a6cefbc3bd3ed9392603da6a76b49c0dcba7e0d.tar.gz
Add dbus-update-activation-environment tool
If OS builders (distributions) have chosen to use the per-user bus, this provides two possible modes of operation for compatibility with existing X session startup hooks. A legacy-free system can just upload DISPLAY, XAUTHORITY and possibly DBUS_SESSION_BUS_ADDRESS into dbus-daemon's and systemd's activation environments, similar to http://cgit.freedesktop.org/systemd/systemd/tree/xorg/50-systemd-user.sh installed by systemd (but unlike systemctl, dbus-update-activation-environment works for traditional D-Bus-activated services, not just for systemd services). A system where compatibility is required for environment variables exported by snippets in /etc/X11/xinit/xinitrc.d (in Red Hat derivatives, Gentoo, etc.) or /etc/X11/Xsession.d (Debian derivatives) can upload the entire environment of the X session, minus some selected environment variables which are specific to a login session (notably XDG_SESSION_ID). In Debian, I plan to put the former in a new dbus-user-session package that enables a user-session-centric mode of operation for D-Bus, and the latter in the existing dbus-x11 package, with the intention that dbus-x11 eventually becomes a tool for change-averse setups or goes away entirely. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=61301 Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
-rw-r--r--cmake/doc/CMakeLists.txt3
-rw-r--r--cmake/tools/CMakeLists.txt10
-rw-r--r--configure.ac1
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/dbus-update-activation-environment.1.xml.in213
-rw-r--r--tools/Makefile.am8
-rw-r--r--tools/dbus-update-activation-environment.c417
7 files changed, 653 insertions, 0 deletions
diff --git a/cmake/doc/CMakeLists.txt b/cmake/doc/CMakeLists.txt
index 2bb720bd..f21e84e7 100644
--- a/cmake/doc/CMakeLists.txt
+++ b/cmake/doc/CMakeLists.txt
@@ -138,6 +138,7 @@ configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-launch.1.xml.in ${CMAKE_BINARY_DI
configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-monitor.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-monitor.1.xml)
configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-send.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-send.1.xml)
configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-test-tool.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-test-tool.1.xml)
+configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-update-activation-environment.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-update-activation-environment.1.xml)
configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-uuidgen.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-uuidgen.1.xml)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-cleanup-sockets.1.xml html-nochunks)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-daemon.1.xml html-nochunks)
@@ -146,6 +147,7 @@ DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-monitor.1.xml html-nochunks)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-send.1.xml html-nochunks)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-test-tool.1.xml html-nochunks)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-uuidgen.1.xml html-nochunks)
+DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-update-activation-environment.1.xml html-nochunks)
if (UNIX)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-daemon.1.xml man)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-monitor.1.xml man)
@@ -154,6 +156,7 @@ if (UNIX)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-launch.1.xml man)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-uuidgen.1.xml man)
DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-cleanup-sockets.1.xml man)
+ DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-update-activation-environment.1.xml man)
endif()
#
# handle html index file
diff --git a/cmake/tools/CMakeLists.txt b/cmake/tools/CMakeLists.txt
index 525dc057..fd0617d1 100644
--- a/cmake/tools/CMakeLists.txt
+++ b/cmake/tools/CMakeLists.txt
@@ -25,6 +25,12 @@ set (dbus_test_tool_SOURCES
../../tools/test-tool.h
)
+set (dbus_update_activation_environment_SOURCES
+ ../../tools/dbus-update-activation-environment.c
+ ../../tools/tool-common.c
+ ../../tools/tool-common.h
+)
+
if (WIN32)
set (dbus_launch_SOURCES
../../tools/dbus-launch-win.c
@@ -54,6 +60,10 @@ add_executable(dbus-test-tool ${dbus_test_tool_SOURCES})
target_link_libraries(dbus-test-tool ${DBUS_LIBRARIES})
install_targets(/bin dbus-test-tool )
+add_executable(dbus-update-activation-environment ${dbus_update_activation_environment_SOURCES})
+target_link_libraries(dbus-update-activation-environment ${DBUS_LIBRARIES})
+install_targets(/bin dbus-update-activation-environment )
+
add_executable(dbus-launch ${dbus_launch_SOURCES})
target_link_libraries(dbus-launch )
if (DBUS_BUILD_X11)
diff --git a/configure.ac b/configure.ac
index 303bc344..bea630a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1861,6 +1861,7 @@ doc/dbus-monitor.1.xml
doc/dbus-run-session.1.xml
doc/dbus-send.1.xml
doc/dbus-test-tool.1.xml
+doc/dbus-update-activation-environment.1.xml
doc/dbus-uuidgen.1.xml
dbus-1.pc
dbus-1-uninstalled.pc
diff --git a/doc/Makefile.am b/doc/Makefile.am
index f875dc25..8bc85c53 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -8,6 +8,7 @@ man_pages = \
dbus-run-session.1 \
dbus-send.1 \
dbus-test-tool.1 \
+ dbus-update-activation-environment.1 \
dbus-uuidgen.1 \
$(NULL)
diff --git a/doc/dbus-update-activation-environment.1.xml.in b/doc/dbus-update-activation-environment.1.xml.in
new file mode 100644
index 00000000..8a495df6
--- /dev/null
+++ b/doc/dbus-update-activation-environment.1.xml.in
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<refentry id="dbus-update-activation-environment.1">
+ <refentryinfo>
+ <copyright>
+ <year>2015</year>
+ <holder>Collabora Ltd.</holder>
+ </copyright>
+ <legalnotice>
+ <para>This man page is distributed under the same terms as
+ dbus-update-activation-environment (MIT/X11). There is NO WARRANTY,
+ to the extent permitted by law.</para>
+ </legalnotice>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>dbus-update-activation-environment</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="manual">User Commands</refmiscinfo>
+ <refmiscinfo class="source">D-Bus</refmiscinfo>
+ <refmiscinfo class="version">@DBUS_VERSION@</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+ <refname>dbus-update-activation-environment</refname>
+ <refpurpose>update environment used for D-Bus session services</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv id="synopsis">
+ <cmdsynopsis>
+ <command>dbus-update-activation-environment</command>
+ <arg choice="opt">--systemd</arg>
+ <arg choice="opt">--verbose</arg>
+ <group choice="plain">
+ <arg choice="plain">--all</arg>
+ <arg choice="plain" rep="repeat"><replaceable>VAR</replaceable></arg>
+ <arg choice="plain" rep="repeat"><replaceable>VAR</replaceable>=<replaceable>VAL</replaceable></arg>
+ </group>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="description">
+ <title>DESCRIPTION</title>
+ <para><command>dbus-update-activation-environment</command>
+ updates the list of environment variables used by
+ <command>dbus-daemon --session</command>
+ when it activates session services without using
+ <command>systemd</command>.</para>
+
+ <para>With the <option>--systemd</option> option,
+ if an instance of <command>systemd --user</command> is
+ available on D-Bus, it also updates the list of environment variables
+ used by <command>systemd --user</command>
+ when it activates user services, including D-Bus session services
+ for which <command>dbus-daemon</command> has been configured to
+ delegate activation to <command>systemd</command>.
+ This is very similar to the <option>import-environment</option>
+ command provided by
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para>
+
+ <para>Variables that are special to <command>dbus-daemon</command>
+ or <command>systemd</command> may be set, but their values will
+ be overridden when a service is started. For instance, it is
+ not useful to add <envar>DBUS_SESSION_BUS_ADDRESS</envar> to
+ <command>dbus-daemon</command>'s activation environment,
+ although it might still be useful to add it to
+ <command>systemd</command>'s activation environment.</para>
+ </refsect1>
+
+ <refsect1 id="options">
+ <title>OPTIONS</title>
+ <variablelist remap="TP">
+
+ <varlistentry>
+ <term><option>--all</option></term>
+ <listitem>
+ <para>Set all environment variables present in
+ the environment used by
+ <command>dbus-update-activation-environment</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--systemd</option></term>
+ <listitem>
+ <para>Set environment variables for systemd user services as well as
+ for traditional D-Bus session services.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--verbose</option></term>
+ <listitem>
+ <para>Output messages to standard error explaining what
+ dbus-update-activation-environment is doing.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable>VAR</replaceable></term>
+ <listitem>
+ <para>If <replaceable>VAR</replaceable> is present in the
+ environment of <command>dbus-update-activation-environment</command>,
+ set it to the same value for D-Bus services. Its value must be
+ UTF-8 (if not, it is skipped with a warning). If
+ <replaceable>VAR</replaceable> is not present
+ in the environment, this argument is silently ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable>VAR</replaceable>=<replaceable>VAL</replaceable></term>
+ <listitem>
+ <para>Set <replaceable>VAR</replaceable> to <replaceable>VAL</replaceable>,
+ which must be UTF-8.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="examples">
+ <title>EXAMPLES</title>
+ <para>
+ <command>dbus-update-activation-environment</command> is
+ primarily designed to be used in Linux distributions' X11 session
+ startup scripts, in conjunction with the "user bus" design.
+ </para>
+
+ <para>To propagate <envar>DISPLAY</envar> and <envar>XAUTHORITY</envar>
+ to <command>dbus-daemon</command>
+ and, if present, <command>systemd</command>,
+ and propagate <envar>DBUS_SESSION_BUS_ADDRESS</envar> to
+ <command>systemd</command>:
+ <programlisting language="sh">
+ dbus-update-activation-environment --systemd \
+ DBUS_SESSION_BUS_ADDRESS DISPLAY XAUTHORITY
+ </programlisting>
+ </para>
+
+ <para>To propagate all environment variables except
+ <envar>XDG_SEAT</envar>, <envar>XDG_SESSION_ID</envar>
+ and <envar>XDG_VTNR</envar> to <command>dbus-daemon</command>
+ (and, if present, <command>systemd</command>) for compatibility
+ with legacy X11 session startup scripts:
+ <programlisting language="sh">
+ # in a subshell so the variables remain set in the
+ # parent script
+ (
+ unset XDG_SEAT
+ unset XDG_SESSION_ID
+ unset XDG_VTNR
+
+ dbus-update-activation-environment --systemd --all
+ )
+ </programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1 id="exit_status">
+ <title>EXIT STATUS</title>
+ <para>
+ <command>dbus-update-activation-environment</command>
+ exits with status 0 on success, EX_USAGE (64) on invalid
+ command-line options, EX_OSERR (71) if unable to connect
+ to the session bus, or EX_UNAVAILABLE (69) if unable to
+ set the environment variables. Other nonzero exit codes might be
+ added in future versions.</para>
+ </refsect1>
+
+ <refsect1 id="environment">
+ <title>ENVIRONMENT</title>
+ <para><envar>DBUS_SESSION_BUS_ADDRESS</envar>,
+ <envar>XDG_RUNTIME_DIR</envar> and/or <envar>DISPLAY</envar>
+ are used to find the address of the session bus.</para>
+ </refsect1>
+
+ <refsect1 id="limitations">
+ <title>LIMITATIONS</title>
+ <para>
+ <command>dbus-daemon</command> does not provide a way to unset
+ environment variables after they have been set (although
+ <command>systemd</command> does), so
+ <command>dbus-update-activation-environment</command> does not
+ offer this functionality either.
+ </para>
+
+ <para>
+ POSIX does not specify the encoding of non-ASCII environment variable
+ names or values and allows them to contain any non-zero byte, but
+ neither <command>dbus-daemon</command> nor <command>systemd</command>
+ supports environment variables with non-UTF-8 names or values.
+ Accordingly, <command>dbus-update-activation-environment</command>
+ assumes that any name or value that appears to be valid UTF-8 is
+ intended to be UTF-8, and ignores other names or values with a warning.
+ </para>
+ </refsect1>
+
+ <refsect1 id="bugs">
+ <title>BUGS</title>
+ <para>Please send bug reports to the D-Bus bug tracker or mailing list.
+ See <ulink url="http://www.freedesktop.org/software/dbus/">http://www.freedesktop.org/software/dbus/</ulink>.</para>
+ </refsect1>
+
+ <refsect1 id="see_also">
+ <title>SEE ALSO</title>
+ <para><citerefentry><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ the <option>import-environment</option> command of
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></para>
+ </refsect1>
+</refentry>
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;
+}