Systemd/src/systemctl/systemctl-set-environment.c
Zbigniew Jędrzejewski-Szmek 32854f7044 systemctl: deprecate blanket import-environment
Importing the full environment is convenient, but it doesn't work too well in
practice, because we get a metric ton of shell-specific crap that should never
end up in the global environment block:

$ systemctl --user show-environment
...
SHELL=/bin/zsh
AUTOJUMP_ERROR_PATH=/home/zbyszek/.local/share/autojump/errors.log
AUTOJUMP_SOURCED=1
CONDA_SHLVL=0
CVS_RSH=ssh
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
DESKTOP_SESSION=gnome
DISPLAY=:0
FPATH=/usr/share/Modules/init/zsh-functions:/usr/local/share/zsh/site-functions:/usr/share/zsh/site-functions:/usr/share/zsh/5.8/functions
GDMSESSION=gnome
GDM_LANG=en_US.UTF-8
GNOME_SETUP_DISPLAY=:1
GUESTFISH_INIT=$'\\e[1;34m'
GUESTFISH_OUTPUT=$'\\e[0m'
GUESTFISH_PS1=$'\\[\\e[1;32m\\]><fs>\\[\\e[0;31m\\] '
GUESTFISH_RESTORE=$'\\e[0m'
HISTCONTROL=ignoredups
HISTSIZE=1000
LOADEDMODULES=
OLDPWD=/home/zbyszek
PWD=/home/zbyszek
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
QTLIB=/usr/lib64/qt-3.3/lib
QT_IM_MODULE=ibus
SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0
SESSION_MANAGER=local/unix:@/tmp/.ICE-unix/2612,unix/unix:/tmp/.ICE-unix/2612
SHLVL=0
STEAM_FRAME_FORCE_CLOSE=1
TERM=xterm-256color
USERNAME=zbyszek
WISECONFIGDIR=/usr/share/wise2/
...

Plenty of shell-specific and terminal-specific stuff that have no global
significance.

Let's start warning when this is used to push people towards importing only
specific variables.

Putative NEWS entry:
  * systemctl import-environment will now emit a warning when called without
    any arguments (i.e. to import the full environment block of the called
    program). This command will usually be invoked from a shell, which means
    that it'll inherit a bunch of variables which are specific to that shell,
    and usually to the tty the shell is connected to, and don't have any
    meaning in the global context of the system or user service manager.
    Instead, only specific variables should be imported into the manager
    environment block.

    Similarly, programs which update the manager environment block by directly
    calling the D-Bus API of the manager, should also push specific variables,
    and not the full inherited environment.
2021-01-08 20:01:40 +01:00

170 lines
5.2 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-error.h"
#include "bus-locator.h"
#include "env-util.h"
#include "escape.h"
#include "systemctl-set-environment.h"
#include "systemctl-util.h"
#include "systemctl.h"
static int print_variable(const char *s) {
const char *sep;
_cleanup_free_ char *esc = NULL;
sep = strchr(s, '=');
if (!sep)
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
"Invalid environment block");
esc = shell_maybe_quote(sep + 1, ESCAPE_POSIX);
if (!esc)
return log_oom();
printf("%.*s=%s\n", (int)(sep-s), s, esc);
return 0;
}
int show_environment(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *text;
sd_bus *bus;
int r;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
(void) pager_open(arg_pager_flags);
r = bus_get_property(bus, bus_systemd_mgr, "Environment", &error, &reply, "as");
if (r < 0)
return log_error_errno(r, "Failed to get environment: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
if (r < 0)
return bus_log_parse_error(r);
while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) {
r = print_variable(text);
if (r < 0)
return r;
}
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
return 0;
}
static void invalid_callback(const char *p, void *userdata) {
_cleanup_free_ char *t = cescape(p);
log_debug("Ignoring invalid environment assignment \"%s\".", strnull(t));
}
int set_environment(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
const char *method;
sd_bus *bus;
int r;
assert(argc > 1);
assert(argv);
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
polkit_agent_open_maybe();
method = streq(argv[0], "set-environment")
? "SetEnvironment"
: "UnsetEnvironment";
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
if (r < 0)
return log_error_errno(r, "Failed to set environment: %s", bus_error_message(&error, r));
return 0;
}
int import_environment(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
sd_bus *bus;
int r;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
polkit_agent_open_maybe();
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetEnvironment");
if (r < 0)
return bus_log_create_error(r);
if (argc < 2) {
log_warning("Calling import-environment without a list of variable names is deprecated.");
_cleanup_strv_free_ char **copy = strv_copy(environ);
if (!copy)
return log_oom();
strv_env_clean_with_callback(copy, invalid_callback, NULL);
r = sd_bus_message_append_strv(m, copy);
} else {
char **a, **b;
r = sd_bus_message_open_container(m, 'a', "s");
if (r < 0)
return bus_log_create_error(r);
STRV_FOREACH(a, strv_skip(argv, 1)) {
if (!env_name_is_valid(*a))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid environment variable name: %s", *a);
STRV_FOREACH(b, environ) {
const char *eq;
eq = startswith(*b, *a);
if (eq && *eq == '=') {
r = sd_bus_message_append(m, "s", *b);
if (r < 0)
return bus_log_create_error(r);
break;
}
}
}
r = sd_bus_message_close_container(m);
}
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, NULL);
if (r < 0)
return log_error_errno(r, "Failed to import environment: %s", bus_error_message(&error, r));
return 0;
}