@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s RemoteUser = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly b ExtraDeviceAccess = ...;
+ readonly as ExtraDeviceAccess = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Service = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<para><varname>RemoteHost</varname> and <varname>RemoteUser</varname> encode the remote host and user
if this is a remote session, or an empty string otherwise.</para>
- <para><varname>ExtraDeviceAccess</varname> encodes whether the session is granted access to additional
- hardware devices, typically useful for for graphical, remote session. If true, the session is granted
- access to all devices tagged with <literal>xaccess</literal> in udev.</para>
+ <para><varname>ExtraDeviceAccess</varname> encodes the set of additional hardware devices that the session
+ is granted access to. For every <literal><replaceable>ID</replaceable></literal> in the list, the session
+ is granted access to all devices tagged with <literal>xaccess-<replaceable>ID</replaceable></literal> in udev.</para>
<para><varname>Service</varname> encodes the PAM service name that registered the session.</para>
<varlistentry>
<term><varname>$XDG_SESSION_EXTRA_DEVICE_ACCESS</varname></term>
- <listitem><para>Whether or not the session shall be granted additional hardware device access,
- typically useful for graphical, remote session. If true, the session is granted access to all
- devices tagged with <literal>xaccess</literal> in udev. Typically, rendering device nodes of
- the GPU are tagged like this.</para>
+ <listitem><para>The set of additional hardware devices that the session shall be granted access to.
+ For every <literal><replaceable>ID</replaceable></literal> in the list, the session is granted
+ access to all devices tagged with <literal>xaccess-<replaceable>ID</replaceable></literal> in udev.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
'sd_session_get_uid',
'sd_session_get_username',
'sd_session_get_vt',
- 'sd_session_has_extra_device_access',
+ 'sd_session_get_extra_device_access',
'sd_session_is_remote'],
'HAVE_PAM'],
['sd_uid_get_state',
</varlistentry>
<varlistentry>
- <term>Tag <literal>xaccess</literal></term>
+ <term>Tag <literal>xaccess-*</literal></term>
- <listitem><para>When set, access to this device is granted to sessions
- created with <literal>ExtraDeviceAccess</literal>. This is typically useful
- for graphical, remote sessions. As the <literal>ExtraDeviceAccess</literal>
- sessions open and close, access to the device is updated accordingly.</para>
+ <listitem><para>When <literal>xaccess-<replaceable>ID</replaceable></literal>
+ is set, access to this device is granted to sessions created with
+ <literal><replaceable>ID</replaceable></literal> listed in <literal>ExtraDeviceAccess</literal>.
+ As the sessions with some <literal>ExtraDeviceAccess</literal> open and close,
+ access to the device is updated accordingly.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<refname>sd_session_get_remote_host</refname>
<refname>sd_session_get_remote_user</refname>
<refname>sd_session_get_leader</refname>
- <refname>sd_session_has_extra_device_access</refname>
+ <refname>sd_session_get_extra_device_access</refname>
<refpurpose>Determine state of a specific session</refpurpose>
</refnamediv>
</funcprototype>
<funcprototype>
- <funcdef>int <function>sd_session_has_extra_device_access</function></funcdef>
+ <funcdef>int <function>sd_session_get_extra_device_access</function></funcdef>
<paramdef>const char *<parameter>session</parameter></paramdef>
+ <paramdef>char ***<parameter>ret_ids</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
session identifier. This function will return an error if the seat
does not support VTs.</para>
- <para><function>sd_session_has_extra_device_access()</function> may
- be used to determine whether the session is granted access to
- additional hardware devices, typically useful for for graphical,
- remote session. If true, the session is granted access to all
- devices tagged with <literal>xaccess</literal> in udev.</para>
+ <para><function>sd_session_get_extra_device_access()</function> may
+ be used to determine which additional hardware devices the session
+ is granted access to. For every <literal><replaceable>ID</replaceable></literal>
+ in the list, the session is granted access to all devices tagged with
+ <literal>xaccess-<replaceable>ID</replaceable></literal> in udev.</para>
<para>If the <varname>session</varname> parameter of any of these
functions is passed as <constant>NULL</constant>, the operation is
<title>Return Value</title>
<para>If the test succeeds,
- <function>sd_session_is_active()</function>,
- <function>sd_session_is_remote()</function>, and
- <function>sd_session_has_extra_device_access()</function>
+ <function>sd_session_is_active()</function> and
+ <function>sd_session_is_remote()</function>
return a positive integer; if it fails, 0. On success,
<function>sd_session_get_state()</function>,
<function>sd_session_get_uid()</function>,
<function>sd_session_get_display()</function>,
<function>sd_session_get_leader()</function>,
<function>sd_session_get_remote_user()</function>,
- <function>sd_session_get_remote_host()</function> and
- <function>sd_session_get_tty()</function> return 0 or
- a positive integer. On failure, these calls return a
+ <function>sd_session_get_remote_host()</function>,
+ <function>sd_session_get_tty()</function>, and
+ <function>sd_session_get_extra_device_access()</function>
+ return 0 or a positive integer. On failure, these calls return a
negative errno-style error code.</para>
<refsect2>
<para><function>sd_session_get_username()</function>,
<function>sd_session_get_start_time()</function>, and
<function>sd_session_get_leader()</function> were added in version 254.</para>
- <para><function>sd_session_has_extra_device_access()</function> was added in version 260.</para>
+ <para><function>sd_session_get_extra_device_access()</function> was added in version 260.</para>
</refsect1>
<refsect1>
LIBSYSTEMD_260 {
global:
- sd_session_has_extra_device_access;
+ sd_session_get_extra_device_access;
} LIBSYSTEMD_259;
SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_UNSIGNED(value))
#define JSON_BUILD_PAIR_CONDITION_BOOLEAN(condition, name, value) \
SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_BOOLEAN(value))
+#define JSON_BUILD_PAIR_CONDITION_STRV(condition, name, value) \
+ SD_JSON_BUILD_PAIR_CONDITION(condition, name, SD_JSON_BUILD_STRV(value))
int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref);
int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum);
return parse_boolean(s);
}
-_public_ int sd_session_has_extra_device_access(const char *session) {
+_public_ int sd_session_get_extra_device_access(const char *session, char ***ret_ids) {
_cleanup_free_ char *p = NULL, *s = NULL;
int r;
return -ENXIO;
if (r < 0)
return r;
- if (isempty(s))
- return -ENODATA;
- return parse_boolean(s);
+ _cleanup_strv_free_ char **ids = NULL;
+ size_t n_ids = 0;
+ if (!isempty(s)) {
+ ids = strv_split(s, /* separators= */ NULL);
+ if (!ids)
+ return -ENOMEM;
+
+ n_ids = strv_length(ids);
+ }
+
+ if (ret_ids)
+ *ret_ids = TAKE_PTR(ids);
+
+ return n_ids;
}
_public_ int sd_session_get_state(const char *session, char **ret_state) {
bool remote,
const char *remote_user,
const char *remote_host,
- bool extra_device_access,
+ char * const *extra_device_access,
Session **ret_session) {
bool mangle_class = false;
session->original_type = session->type = type;
session->remote = remote;
- session->extra_device_access = extra_device_access;
session->vtnr = vtnr;
session->class = class;
goto fail;
}
+ r = strv_copy_unless_empty(extra_device_access, &session->extra_device_access);
+ if (r < 0)
+ goto fail;
+
if (seat) {
r = seat_attach_session(seat, session);
if (r < 0)
remote,
remote_user,
remote_host,
- /* extra_device_access= */ false,
+ /* extra_device_access= */ NULL,
&session);
if (r == -EBUSY)
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
bool remote,
const char *remote_user,
const char *remote_host,
- bool extra_device_access,
+ char * const *extra_device_access,
Session **ret_session);
extern const BusObjectImplementation manager_object;
SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("ExtraDeviceAccess", "b", bus_property_get_bool, offsetof(Session, extra_device_access), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ExtraDeviceAccess", "as", NULL, offsetof(Session, extra_device_access), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
#include "process-util.h"
#include "serialize.h"
#include "string-table.h"
+#include "strv.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
#include "user-record.h"
free(s->remote_user);
free(s->service);
free(s->desktop);
+ strv_free(s->extra_device_access);
hashmap_remove(s->manager->sessions, s->id);
}
}
-static int trigger_xaccess(void) {
+static int trigger_xaccess(char * const *extra_devices) {
int r;
- _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- r = sd_device_enumerator_new(&e);
+ if (strv_isempty(extra_devices))
+ return 0;
+
+ _cleanup_strv_free_ char **tags = NULL;
+ r = strv_extend_strv_biconcat(&tags, "xaccess-", (const char * const *)extra_devices, /* suffix= */ NULL);
if (r < 0)
return r;
- r = sd_device_enumerator_add_match_tag(e, "xaccess");
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
- FOREACH_DEVICE(e, d) {
- /* Verify that the tag is still in place. */
- r = sd_device_has_current_tag(d, "xaccess");
+ STRV_FOREACH(tag, tags) {
+ r = sd_device_enumerator_add_match_tag(e, *tag);
if (r < 0)
return r;
- if (r == 0)
+ }
+
+ FOREACH_DEVICE(e, d) {
+ /* Verify that the tag is still in place. */
+ bool has_xaccess = false;
+ STRV_FOREACH(tag, tags)
+ if (sd_device_has_current_tag(d, *tag)) {
+ has_xaccess = true;
+ break;
+ }
+ if (!has_xaccess)
continue;
/* In case people mistag devices without nodes, we need to ignore this. */
"IS_DISPLAY=%s\n"
"STATE=%s\n"
"REMOTE=%s\n"
- "EXTRA_DEVICE_ACCESS=%s\n"
"LEADER_FD_SAVED=%s\n",
s->user->user_record->uid,
one_zero(session_is_active(s)),
one_zero(s->user->display == s),
session_state_to_string(session_get_state(s)),
one_zero(s->remote),
- one_zero(s->extra_device_access),
one_zero(s->leader_fd_saved));
env_file_fputs_assignment(f, "USER=", s->user->user_record->user_name);
session_save_devices(s, f);
}
+ if (s->extra_device_access) {
+ _cleanup_free_ char *extra_devices = strv_join(s->extra_device_access, " ");
+ if (!extra_devices)
+ return log_oom();
+ fprintf(f, "EXTRA_DEVICE_ACCESS=%s\n", extra_devices);
+ }
+
r = flink_tmpfile(f, temp_path, s->state_file, LINK_TMPFILE_REPLACE);
if (r < 0)
return log_error_errno(r, "Failed to move '%s' into place: %m", s->state_file);
}
if (extra_device_access) {
- k = parse_boolean(extra_device_access);
- if (k >= 0)
- s->extra_device_access = k;
+ s->extra_device_access = strv_split(extra_device_access, /* separators= */ NULL);
+ if (!s->extra_device_access)
+ return log_oom();
}
if (vtnr)
if (s->seat)
(void) seat_save(s->seat);
- if (s->extra_device_access)
- (void) trigger_xaccess();
+ (void) trigger_xaccess(s->extra_device_access);
/* Send signals */
(void) session_send_signal(s, true);
(void) session_save(s);
(void) user_save(s->user);
- if (s->extra_device_access)
- (void) trigger_xaccess();
+ (void) trigger_xaccess(s->extra_device_access);
return r;
}
char *remote_host;
char *service;
char *desktop;
- bool extra_device_access;
+ char **extra_device_access;
char *scope;
char *scope_job;
#include "logind-seat.h"
#include "logind-user.h"
#include "logind-varlink.h"
+#include "strv.h"
#include "terminal-util.h"
#include "user-record.h"
#include "user-util.h"
int remote;
const char *remote_user;
const char *remote_host;
- bool extra_device_access;
+ char **extra_device_access;
} CreateSessionParameters;
static void create_session_parameters_done(CreateSessionParameters *p) {
pidref_done(&p->pid);
+ strv_free(p->extra_device_access);
}
static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
{ "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(CreateSessionParameters, remote), 0 },
{ "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_user), 0 },
{ "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_host), 0 },
- { "ExtraDeviceAccess", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(CreateSessionParameters, extra_device_access), 0 },
+ { "ExtraDeviceAccess", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(CreateSessionParameters, extra_device_access), 0 },
{}
};
.class = _SESSION_CLASS_INVALID,
.type = _SESSION_TYPE_INVALID,
.remote = -1,
- .extra_device_access = false,
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
uint32_t vtnr;
const char *tty;
const char *display;
- bool extra_device_access;
+ char **extra_device_access;
bool remote;
const char *remote_user;
const char *remote_host;
bool incomplete;
} SessionContext;
+static void session_context_done(SessionContext *c) {
+ strv_free(c->extra_device_access);
+}
+
static int create_session_message(
sd_bus *bus,
pam_handle_t *pamh,
SD_JSON_BUILD_PAIR_BOOLEAN("Remote", c->remote),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteUser", c->remote_user),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteHost", c->remote_host),
- JSON_BUILD_PAIR_CONDITION_BOOLEAN(c->extra_device_access, "ExtraDeviceAccess", c->extra_device_access));
+ JSON_BUILD_PAIR_CONDITION_STRV(!strv_isempty(c->extra_device_access), "ExtraDeviceAccess", c->extra_device_access));
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r,
"Failed to issue io.systemd.Login.CreateSession varlink call: %m");
if (r != PAM_SUCCESS)
return r;
- r = update_environment(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", one_zero(c->extra_device_access));
+ _cleanup_free_ char *extra_devices = strv_join(c->extra_device_access, ":");
+ if (!extra_devices)
+ return pam_log_oom(pamh);
+
+ r = update_environment(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", extra_devices);
if (r != PAM_SUCCESS)
return r;
if (r != PAM_SUCCESS)
return r;
- SessionContext c = {};
+ _cleanup_(session_context_done) SessionContext c = {};
r = pam_get_item_many(
pamh,
PAM_SERVICE, &c.service,
c.desktop = getenv_harder(pamh, "XDG_SESSION_DESKTOP", desktop_pam);
c.area = getenv_harder(pamh, "XDG_AREA", area_pam);
c.incomplete = getenv_harder_bool(pamh, "XDG_SESSION_INCOMPLETE", false);
- c.extra_device_access = getenv_harder_bool(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", false);
+
+ const char *extra_device_access = getenv_harder(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", NULL);
+ if (extra_device_access) {
+ c.extra_device_access = strv_split(extra_device_access, ":");
+ if (!c.extra_device_access)
+ return pam_log_oom(pamh);
+ }
r = pam_get_data_many(
pamh,
SD_VARLINK_DEFINE_INPUT(RemoteUser, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Host name of the remote host"),
SD_VARLINK_DEFINE_INPUT(RemoteHost, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
- SD_VARLINK_FIELD_COMMENT("If true this session is granted access to additional hardware devices, "
- "typically useful for remote, graphical sessions. "
- "This adds access for all devices tagged with \"xaccess\" in udev."),
- SD_VARLINK_DEFINE_INPUT(ExtraDeviceAccess, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("List of additional hardware devices that this session is granted access to."
+ "For every $ID in the list, this adds access for all devices tagged with \"xaccess-$ID\" in udev."),
+ SD_VARLINK_DEFINE_INPUT(ExtraDeviceAccess, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("The identifier string of the session of the user."),
SD_VARLINK_DEFINE_OUTPUT(Id, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("The runtime path ($XDG_RUNTIME_DIR) of the user."),
/* Return 1 if the session is remote. */
int sd_session_is_remote(const char *session);
-/* Return 1 if the session is granted extra device access. */
-int sd_session_has_extra_device_access(const char *session);
+/* Return extra hardware devices that the session is granted access to.
+ * For every $ID in the list, this adds access for all devices tagged with
+ * "xaccess-$ID" in udev. */
+int sd_session_get_extra_device_access(const char *session, char ***ret_ids);
/* Get state from session. Possible states: online, active, closing.
* This function is a more generic version of sd_session_is_active(). */
}
}
- r = sd_device_has_tag(dev, "xaccess");
- if (r < 0)
- return log_device_error_errno(dev, r, "Failed to query device xaccess tag: %m");
+ bool has_xaccess = false;
+ FOREACH_DEVICE_CURRENT_TAG(dev, tag)
+ if (startswith(tag, "xaccess-")) {
+ has_xaccess = true;
+ break;
+ }
- if (r > 0) {
+ if (has_xaccess) {
r = sd_get_sessions(&sessions);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to list sessions: %m");
STRV_FOREACH(s, sessions) {
_cleanup_free_ char *state = NULL;
- if (sd_session_get_state(*s, &state) < 0) {
+ r = sd_session_get_state(*s, &state);
+ if (r < 0) {
log_device_debug_errno(dev, r, "Failed to query state for session %s, ignoring: %m", *s);
continue;
}
if (streq(state, "closing"))
continue;
- r = sd_session_has_extra_device_access(*s);
+
+ r = sd_session_get_uid(*s, &uid);
if (r < 0) {
- log_device_debug_errno(dev, r, "Failed to query extra device access for session %s, ignoring: %m", *s);
+ log_device_debug_errno(dev, r, "Failed to query uid for session %s, ignoring: %m", *s);
continue;
}
- if (r == 0)
- continue;
- if (sd_session_get_uid(*s, &uid) < 0) {
- log_device_debug_errno(dev, r, "Failed to query uid for session %s, ignoring: %m", *s);
+
+ _cleanup_strv_free_ char **extra_devices = NULL;
+ r = sd_session_get_extra_device_access(*s, &extra_devices);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to query extra device access for session %s, ignoring: %m", *s);
continue;
}
+
+ bool match = false;
+ STRV_FOREACH(id, extra_devices) {
+ _cleanup_free_ char *tag = strjoin("xaccess-", *id);
+ if (!tag)
+ return log_oom();
+
+ r = sd_device_has_current_tag(dev, tag);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to query %s tag: %m", tag);
+ if (r > 0) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ continue;
+
if (set_ensure_put(&uids, NULL, UID_TO_PTR(uid)) < 0)
return log_oom();
}