mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-20 04:30:10 +01:00
429 lines
11 KiB
C
429 lines
11 KiB
C
/*
|
|
* dbus-update-activation-environment - update D-Bus, and optionally
|
|
* systemd, activation environment
|
|
*
|
|
* Copyright © 2014-2015 Collabora Ltd.
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* 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
|
|
|
|
#ifdef DBUS_WIN
|
|
/* The Windows C runtime uses a different name */
|
|
#define environ _environ
|
|
#elif defined(__APPLE__)
|
|
# include <crt_externs.h>
|
|
# define environ (*_NSGetEnviron ())
|
|
#elif HAVE_DECL_ENVIRON && defined(HAVE_UNISTD_H)
|
|
# include <unistd.h>
|
|
#else
|
|
extern char **environ;
|
|
#endif
|
|
|
|
/* 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,
|
|
©))
|
|
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_set_error_from_message (&error, msg) ||
|
|
!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_set_error_from_message (&error, msg) ||
|
|
!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;
|
|
}
|