Compare commits

...

27 Commits

Author SHA1 Message Date
Lennart Poettering fb47ef6290
Merge c1875023b9 into 2e5b0412f9 2024-11-21 00:11:57 +00:00
Luca Boccassi 2e5b0412f9
network: update state files before replying bus method (#35255)
Follow-up for 2b07a3211b.

Fixes the failure found in
https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/noble/amd64/s/systemd-upstream/20241115_182040_92382@/log.gz
. Relevant logs:
```
Nov 16 02:48:36 systemd-networkd[2706]: veth99: Reconfiguring with /run/systemd/network/25-dhcp-client-ipv6-only.network.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Started IPv6 Router Solicitation client
Nov 16 02:48:36 systemd-networkd[2706]: veth99: IPv6 Router Discovery is configured and started.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Sent Router Solicitation, next solicitation in 3s
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Received Router Advertisement from fe80::1034:56ff:fe78:9abd: flags=0xc0(managed, other), preference=medium, lifetime=30min
Nov 16 02:48:36 systemd-networkd[2706]: veth99: NDISC: Invoking callback for 'router' event.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: link_check_ready(): dynamic addressing protocols are enabled but none of them finished yet.
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: Starting in Solicit mode
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: State changed: stopped -> solicitation
Nov 16 02:48:36 systemd-networkd[2706]: veth99: Acquiring DHCPv6 lease on NDisc request
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: Sent Solicit
Nov 16 02:48:36 systemd-networkd[2706]: veth99: DHCPv6 client: Next retransmission in 1s
Nov 16 02:48:37 systemd-networkd[2706]: veth99: DHCPv6 client: Sent Solicit
Nov 16 02:48:37 systemd-networkd[2706]: veth99: DHCPv6 client: Next retransmission in 1s
Nov 16 02:48:39 systemd-networkd[2706]: veth99: NDISC: Received Neighbor Advertisement from fe80::1034:56ff:fe78:9abd: Router=yes, Solicited=yes, Override=no
Nov 16 02:48:39 systemd-networkd[2706]: veth99: NDISC: Invoking callback for 'neighbor' event.
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: Processed Reply message
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: T1 expires in 50s
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: T2 expires in 55s
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: Valid lifetime expires in 2min
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 client: State changed: solicitation -> bound
Nov 16 02:48:39 systemd-networkd[2706]: veth99: DHCPv6 address 2600::15/128 (valid for 1min 59s, preferred for 1min 59s)
Nov 16 02:48:41 systemd-networkd[2706]: veth99: Received updated DHCPv6 address (configured): 2600::15/128 (valid for 1min 58s, preferred for 1min 58s), flags: no-prefixroute, scope: global
Nov 16 02:48:41 systemd-networkd[2706]: veth99: DHCPv6 addresses and routes set.
Nov 16 02:48:41 systemd-networkd[2706]: veth99: link_check_ready(): IPv4LL:no DHCPv4:no DHCPv6:yes DHCP-PD:no NDisc:no
Nov 16 02:48:41 systemd-networkd[2706]: veth99: State changed: configuring -> configured
```
The interface veth99 entered the configured state after 5 seconds, but
at the same time, the `wait_online()` in the test script considered the
test failed.
The function `wait_online()` first invokes
`systemd-networkd-wait-online` with `--timeout=20`, then check setup
states of interfaces with 5 seconds timeout. So, the failure suggests
that `systemd-networkd-wait-online` finishes immediately, as the state
file was not updated when it is invoked, and thus it handles the
interface veth99 already in the configured state.
2024-11-20 23:36:35 +00:00
Yu Watanabe 2b397d43ab test-network: actually check metric and preference
Otherwise, nexthop ID may contain e.g. 300, then
===
AssertionError: '300' unexpectedly found in
'default nhid 3860882700 via fe80::1034:56ff:fe78:9a99 proto ra metric 512 expires 1798sec pref high\n
 default nhid 2639230080 via fe80::1034:56ff:fe78:9a98 proto ra metric 2048 expires 1798sec pref low'
===
2024-11-21 03:43:35 +09:00
Yu Watanabe 9ad294efd0 network: update state files before replying bus method
Follow-up for 2b07a3211b.
2024-11-21 03:42:06 +09:00
Lennart Poettering c1875023b9 pam-systemd: talk to logind via varlink 2024-11-20 15:37:48 +01:00
Lennart Poettering ededa7ff7e logind: add basic Varlink API 2024-11-20 15:37:47 +01:00
Lennart Poettering b1c1397d2b logind: split create session reply handling in two 2024-11-20 15:36:13 +01:00
Lennart Poettering 7ceeaa4b06 logind: also potentially GC the session if we cannot send reply 2024-11-20 15:32:54 +01:00
Lennart Poettering e39ab557f8 logind: indicate that 'error' parameter is input by making it const 2024-11-20 15:32:54 +01:00
Lennart Poettering 4e80c32c1d logind: rework session creation logic, to be more reusable for varlink codepaths
This separates the preparatory checks that generate D-Bus errors from
the code that actually allocates the session. This make the logic easier
to follow and prepares ground so that we can reuse the 2nd part later
when exposing session creation via Varlink.
2024-11-20 15:32:54 +01:00
Lennart Poettering fb67432c13 logind: split out logic that finds free session ID into helper call
Just some refactoring to make an overly large function a bit smaller.
2024-11-20 15:32:54 +01:00
Lennart Poettering d1442d6f3d logind: normalize parameter to create_session()
We can pass a properly typed Manager object here, no reason to pass it
as void*.
2024-11-20 15:32:54 +01:00
Lennart Poettering fa1d2c93de sd-varlink: fix bug when enqueuing messages with fds asynchronously
When determining the poll events to wait for we need to take the queue
of pending messages that carry fds into account. Otherwise we might end
up not waking up if such an fd-carrying message is enqueued
asynchronously (i.e. not from a dispatch callback).
2024-11-20 15:25:12 +01:00
Lennart Poettering 7ce64bd604 sd-varlink: add flag for sd_varlink_server for creating connections with fd passing enabled
Let's add a simple flag that enables fd passing for all connections of a
server. It's much easier to use this than to install a connect handler
which manually enables this for each connection.
2024-11-20 15:25:12 +01:00
Lennart Poettering 7685f5674f terminal-util: modernize vtnr_from_tty() a bit 2024-11-20 15:25:12 +01:00
Lennart Poettering c022ee0a7e sd-login: make use of getpeerpidref() and cg_pidref_get_*() 2024-11-20 15:25:12 +01:00
Lennart Poettering ac41ac8e90 socket-util: introduce getpeerpidref()
This combines getpeercred() and getpeerpidfd() and returns a PidRef
2024-11-20 15:25:12 +01:00
Lennart Poettering 9b2abaa697 cgroup-util: add pidref counterparts for cg_pid_get_session() + cg_pid_get_owner_uid() 2024-11-20 15:25:12 +01:00
Lennart Poettering 42a67989a9 tree-wide: use pidref_is_self() at more places 2024-11-20 15:25:12 +01:00
Lennart Poettering 787fb6dd16 sd-json: add json_dispatch_const_path() helper
The new json_dispatch_const_path() is to json_dispatch_path() what
sd_json_dispatch_const_string() is to sd_json_dispatch_ string(), i.e.
doesn't implicitly strdup() the string, but gives you the pointer into
the JSON structure, and thus requires you to keep it pinned.
2024-11-20 15:25:12 +01:00
Lennart Poettering ed9a12846a pam_systemd: introduce pam_get_data_many() helper and make use of it
This is to pam_get_data() what pam_get_item() is to pam_get_item_many().
2024-11-20 15:25:12 +01:00
Lennart Poettering ef35900c93 pam_systemd: fix error code confusion when prepping D-Bus message
We got confused by the error codes here, and sometimes return PAM errors
where the caller propagated them unconverted as negative errno errors. Fix that.
2024-11-20 15:25:12 +01:00
Lennart Poettering be34afca0a pam_systemd: split pam_sm_open_session() into more digestable blocks
Let's separate four different parts of pam_sm_open_session():

1. Acquiring of our various parameters from pam env, pam data, pam items
2. Mangling of that data to clean it up
3. Registering of the service with logind
4. Importing shell credentials into environment variables
5. Enforcement of user record data

This makes the code a lot more readable, and gets rid of an ugly got
label.

It also corrects things: if step 3 doesnt work because logind is not
around, we'll now still do step 4, which we previously erroneously
skipped.

Besodes that no real code changes.
2024-11-20 15:25:12 +01:00
Lennart Poettering 9ae867125f pam_systemd: split out setting of shell env vars from credentials and move it later
Let's shorten the code of pam_sm_open_session() a bit, and also make
sure the importing of the env vars from the creds also happens if the
session registration with logind is skipped.
2024-11-20 15:25:12 +01:00
Lennart Poettering 0646fb547e pam_systemd: drop "uid" field from SessionContext
Let's instead just pass over the UserRecord, it's a much more useful
object with lots more information we'll sooner or later need
(preparation for later commits).
2024-11-20 15:25:12 +01:00
Lennart Poettering be40b40c3d pam_systemd: drop "pid" field from SessionContext
We never use the field and this is not going to change...

This addresses a weird asymmetry, as create_session_message() always
went to the process' own PID when doing pidfds but otherwise (i.e.
without pidfds) would honour the PID specified as function parameter.
2024-11-20 15:25:12 +01:00
Lennart Poettering 72e7ab610b pam-systemd: normalize parsing of XDG_VTNR
Let's make it more like the parsing of the "incomplete" boolean env var,
to streamline things.
2024-11-20 15:25:12 +01:00
34 changed files with 1513 additions and 584 deletions

View File

@ -1418,6 +1418,26 @@ int cg_pid_get_session(pid_t pid, char **ret_session) {
return cg_path_get_session(cgroup, ret_session); return cg_path_get_session(cgroup, ret_session);
} }
int cg_pidref_get_session(const PidRef *pidref, char **ret) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
_cleanup_free_ char *session = NULL;
r = cg_pid_get_session(pidref->pid, &session);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret)
*ret = TAKE_PTR(session);
return 0;
}
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid) { int cg_path_get_owner_uid(const char *path, uid_t *ret_uid) {
_cleanup_free_ char *slice = NULL; _cleanup_free_ char *slice = NULL;
char *start, *end; char *start, *end;
@ -1455,6 +1475,27 @@ int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid) {
return cg_path_get_owner_uid(cgroup, ret_uid); return cg_path_get_owner_uid(cgroup, ret_uid);
} }
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
uid_t uid;
r = cg_pid_get_owner_uid(pidref->pid, &uid);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret)
*ret = uid;
return 0;
}
int cg_path_get_slice(const char *p, char **ret_slice) { int cg_path_get_slice(const char *p, char **ret_slice) {
const char *e = NULL; const char *e = NULL;

View File

@ -280,7 +280,9 @@ int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup); int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
int cg_pid_get_session(pid_t pid, char **ret_session); int cg_pid_get_session(pid_t pid, char **ret_session);
int cg_pidref_get_session(const PidRef *pidref, char **ret_session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid); int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret_uid);
int cg_pid_get_unit(pid_t pid, char **ret_unit); int cg_pid_get_unit(pid_t pid, char **ret_unit);
int cg_pidref_get_unit(const PidRef *pidref, char **ret); int cg_pidref_get_unit(const PidRef *pidref, char **ret);
int cg_pid_get_user_unit(pid_t pid, char **ret_unit); int cg_pid_get_user_unit(pid_t pid, char **ret_unit);

View File

@ -429,7 +429,7 @@ int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
if (pidref_is_remote(pidref)) if (pidref_is_remote(pidref))
return -EREMOTE; return -EREMOTE;
if (pidref->pid == 1 || pidref->pid == getpid_cached()) if (pidref->pid == 1 || pidref_is_self(pidref))
return -ECHILD; return -ECHILD;
siginfo_t si = {}; siginfo_t si = {};

View File

@ -975,6 +975,28 @@ int getpeerpidfd(int fd) {
return pidfd; return pidfd;
} }
int getpeerpidref(int fd, PidRef *ret) {
int r;
assert(fd >= 0);
assert(ret);
int pidfd = getpeerpidfd(fd);
if (pidfd < 0) {
if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd))
return pidfd;
struct ucred ucred;
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
return pidref_set_pid(ret, ucred.pid);
}
return pidref_set_pidfd_consume(ret, pidfd);
}
ssize_t send_many_fds_iov_sa( ssize_t send_many_fds_iov_sa(
int transport_fd, int transport_fd,
int *fds_array, size_t n_fds_array, int *fds_array, size_t n_fds_array,

View File

@ -20,6 +20,7 @@
#include "macro.h" #include "macro.h"
#include "missing_network.h" #include "missing_network.h"
#include "missing_socket.h" #include "missing_socket.h"
#include "pidref.h"
#include "sparse-endian.h" #include "sparse-endian.h"
union sockaddr_union { union sockaddr_union {
@ -154,6 +155,7 @@ int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret); int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret); int getpeergroups(int fd, gid_t **ret);
int getpeerpidfd(int fd); int getpeerpidfd(int fd);
int getpeerpidref(int fd, PidRef *ret);
ssize_t send_many_fds_iov_sa( ssize_t send_many_fds_iov_sa(
int transport_fd, int transport_fd,

View File

@ -761,26 +761,24 @@ bool tty_is_console(const char *tty) {
} }
int vtnr_from_tty(const char *tty) { int vtnr_from_tty(const char *tty) {
int i, r; int r;
assert(tty); assert(tty);
tty = skip_dev_prefix(tty); tty = skip_dev_prefix(tty);
if (!startswith(tty, "tty") ) const char *e = startswith(tty, "tty");
if (!e)
return -EINVAL; return -EINVAL;
if (!ascii_isdigit(tty[3])) unsigned u;
return -EINVAL; r = safe_atou(e, &u);
r = safe_atoi(tty+3, &i);
if (r < 0) if (r < 0)
return r; return r;
if (!vtnr_is_valid(u))
return -ERANGE;
if (i < 0 || i > 63) return (int) u;
return -EINVAL;
return i;
} }
int resolve_dev_console(char **ret) { int resolve_dev_console(char **ret) {

View File

@ -157,3 +157,7 @@ int terminal_is_pty_fd(int fd);
int pty_open_peer_racefree(int fd, int mode); int pty_open_peer_racefree(int fd, int mode);
int pty_open_peer(int fd, int mode); int pty_open_peer(int fd, int mode);
static inline bool vtnr_is_valid(unsigned n) {
return n >= 1 && n <= 63;
}

View File

@ -81,7 +81,7 @@ static void *server(void *p) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
assert_se(pidref_set_pidfd_take(&pidref, pidfd) >= 0); assert_se(pidref_set_pidfd_take(&pidref, pidfd) >= 0);
assert_se(pidref.pid == getpid_cached()); assert_se(pidref_is_self(&pidref));
} }
const gid_t *gl = NULL; const gid_t *gl = NULL;

View File

@ -125,14 +125,13 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
return 0; return 0;
} }
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
char **p = ASSERT_PTR(userdata); const char **p = ASSERT_PTR(userdata), *path;
const char *path;
assert(variant); assert(variant);
if (sd_json_variant_is_null(variant)) { if (sd_json_variant_is_null(variant)) {
*p = mfree(*p); *p = NULL;
return 0; return 0;
} }
@ -145,8 +144,24 @@ int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispa
if (!path_is_absolute(path)) if (!path_is_absolute(path))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name)); return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
if (free_and_strdup(p, path) < 0) *p = path;
return json_log_oom(variant, flags); return 0;
}
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
char **s = ASSERT_PTR(userdata);
const char *p;
int r;
assert_return(variant, -EINVAL);
r = json_dispatch_const_path(name, variant, flags, &p);
if (r < 0)
return r;
r = free_and_strdup(s, p);
if (r < 0)
return json_log(variant, flags, r, "Failed to allocate path string: %m");
return 0; return 0;
} }

View File

@ -113,6 +113,7 @@ int json_dispatch_user_group_name(const char *name, sd_json_variant *variant, sd
int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);

View File

@ -334,46 +334,46 @@ _public_ int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup) {
return 0; return 0;
} }
_public_ int sd_peer_get_session(int fd, char **session) { _public_ int sd_peer_get_session(int fd, char **ret) {
struct ucred ucred = UCRED_INVALID;
int r; int r;
assert_return(fd >= 0, -EBADF); assert_return(fd >= 0, -EBADF);
assert_return(session, -EINVAL); assert_return(ret, -EINVAL);
r = getpeercred(fd, &ucred); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0) if (r < 0)
return r; return r;
return cg_pid_get_session(ucred.pid, session); return cg_pidref_get_session(&pidref, ret);
} }
_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) { _public_ int sd_peer_get_owner_uid(int fd, uid_t *ret) {
struct ucred ucred;
int r; int r;
assert_return(fd >= 0, -EBADF); assert_return(fd >= 0, -EBADF);
assert_return(uid, -EINVAL); assert_return(ret, -EINVAL);
r = getpeercred(fd, &ucred); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0) if (r < 0)
return r; return r;
return cg_pid_get_owner_uid(ucred.pid, uid); return cg_pidref_get_owner_uid(&pidref, ret);
} }
_public_ int sd_peer_get_unit(int fd, char **unit) { _public_ int sd_peer_get_unit(int fd, char **ret) {
struct ucred ucred;
int r; int r;
assert_return(fd >= 0, -EBADF); assert_return(fd >= 0, -EBADF);
assert_return(unit, -EINVAL); assert_return(ret, -EINVAL);
r = getpeercred(fd, &ucred); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0) if (r < 0)
return r; return r;
return cg_pid_get_unit(ucred.pid, unit); return cg_pidref_get_unit(&pidref, ret);
} }
_public_ int sd_peer_get_user_unit(int fd, char **unit) { _public_ int sd_peer_get_user_unit(int fd, char **unit) {

View File

@ -1698,7 +1698,8 @@ _public_ int sd_varlink_get_events(sd_varlink *v) {
ret |= EPOLLIN; ret |= EPOLLIN;
if (!v->write_disconnected && if (!v->write_disconnected &&
v->output_buffer_size > 0) (v->output_queue ||
v->output_buffer_size > 0))
ret |= EPOLLOUT; ret |= EPOLLOUT;
return ret; return ret;
@ -3218,7 +3219,13 @@ _public_ int sd_varlink_server_new(sd_varlink_server **ret, sd_varlink_server_fl
int r; int r;
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return((flags & ~(SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY|SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|SD_VARLINK_SERVER_INPUT_SENSITIVE)) == 0, -EINVAL); assert_return((flags & ~(SD_VARLINK_SERVER_ROOT_ONLY|
SD_VARLINK_SERVER_MYSELF_ONLY|
SD_VARLINK_SERVER_ACCOUNT_UID|
SD_VARLINK_SERVER_INHERIT_USERDATA|
SD_VARLINK_SERVER_INPUT_SENSITIVE|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT)) == 0, -EINVAL);
s = new(sd_varlink_server, 1); s = new(sd_varlink_server, 1);
if (!s) if (!s)
@ -3399,6 +3406,9 @@ _public_ int sd_varlink_server_add_connection_pair(
if (asprintf(&desc, "%s-%i-%i", varlink_server_description(server), input_fd, output_fd) >= 0) if (asprintf(&desc, "%s-%i-%i", varlink_server_description(server), input_fd, output_fd) >= 0)
v->description = TAKE_PTR(desc); v->description = TAKE_PTR(desc);
(void) sd_varlink_set_allow_fd_passing_input(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
(void) sd_varlink_set_allow_fd_passing_output(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT));
/* Link up the server and the connection, and take reference in both directions. Note that the /* Link up the server and the connection, and take reference in both directions. Note that the
* reference on the connection is left dangling. It will be dropped when the connection is closed, * reference on the connection is left dangling. It will be dropped when the connection is closed,
* which happens in varlink_close(), including in the event loop quit callback. */ * which happens in varlink_close(), including in the event loop quit callback. */

View File

@ -85,6 +85,8 @@ static int get_sender_session(
const char *name; const char *name;
int r; int r;
assert(m);
/* Acquire the sender's session. This first checks if the sending process is inside a session itself, /* Acquire the sender's session. This first checks if the sending process is inside a session itself,
* and returns that. If not and 'consult_display' is true, this returns the display session of the * and returns that. If not and 'consult_display' is true, this returns the display session of the
* owning user of the caller. */ * owning user of the caller. */
@ -827,186 +829,18 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_send(NULL, reply, NULL); return sd_bus_send(NULL, reply, NULL);
} }
static int create_session( static int manager_choose_session_id(
sd_bus_message *message, Manager *m,
void *userdata, PidRef *leader,
sd_bus_error *error, char **ret_id) {
uid_t uid,
pid_t leader_pid, assert(m);
int leader_pidfd, assert(pidref_is_set(leader));
const char *service, assert(ret_id);
const char *type,
const char *class,
const char *desktop,
const char *cseat,
uint32_t vtnr,
const char *tty,
const char *display,
int remote,
const char *remote_user,
const char *remote_host,
uint64_t flags) {
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
Manager *m = ASSERT_PTR(userdata);
_cleanup_free_ char *id = NULL; _cleanup_free_ char *id = NULL;
Session *session = NULL;
uint32_t audit_id = 0; uint32_t audit_id = 0;
User *user = NULL; (void) audit_session_from_pid(leader, &audit_id);
Seat *seat = NULL;
SessionType t;
SessionClass c;
int r;
assert(message);
if (!uid_is_valid(uid))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
if (flags != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
if (leader_pidfd >= 0)
r = pidref_set_pidfd(&leader, leader_pidfd);
else if (leader_pid == 0)
r = bus_query_sender_pidref(message, &leader);
else {
if (leader_pid < 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Leader PID is not valid");
r = pidref_set_pid(&leader, leader_pid);
}
if (r < 0)
return r;
if (leader.pid == 1 || leader.pid == getpid_cached())
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (isempty(type))
t = _SESSION_TYPE_INVALID;
else {
t = session_type_from_string(type);
if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session type %s", type);
}
if (isempty(class))
c = _SESSION_CLASS_INVALID;
else {
c = session_class_from_string(class);
if (c < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session class %s", class);
}
if (isempty(desktop))
desktop = NULL;
else {
if (!string_is_safe(desktop))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid desktop string %s", desktop);
}
if (isempty(cseat))
seat = NULL;
else {
seat = hashmap_get(m->seats, cseat);
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
"No seat '%s' known", cseat);
}
if (tty_is_vc(tty)) {
int v;
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
v = vtnr_from_tty(tty);
if (v <= 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Cannot determine VT number from virtual console TTY %s", tty);
if (vtnr == 0)
vtnr = (uint32_t) v;
else if (vtnr != (uint32_t) v)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Specified TTY and VT number do not match");
} else if (tty_is_console(tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but seat is not seat0");
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but VT number is not 0");
}
if (seat) {
if (seat_has_vts(seat)) {
if (vtnr <= 0 || vtnr > 63)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"VT number out of range");
} else {
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Seat has no VTs but VT number not 0");
}
}
if (t == _SESSION_TYPE_INVALID) {
if (!isempty(display))
t = SESSION_X11;
else if (!isempty(tty))
t = SESSION_TTY;
else
t = SESSION_UNSPECIFIED;
}
if (c == _SESSION_CLASS_INVALID) {
if (t == SESSION_UNSPECIFIED)
c = SESSION_BACKGROUND;
else
c = SESSION_USER;
}
/* Check if we are already in a logind session, and if so refuse. */
r = manager_get_session_by_pidref(m, &leader, /* ret_session= */ NULL);
if (r < 0)
return log_debug_errno(
r,
"Failed to check if process " PID_FMT " is already in a session: %m",
leader.pid);
if (r > 0)
return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY,
"Already running in a session or user slice");
/* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
* the greeter session after the user-session and want the user-session to take over the VT. We need
* to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
* greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
* used VT for the exact same reasons. */
if (c != SESSION_GREETER &&
vtnr > 0 &&
vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
m->seat0->positions[vtnr] &&
m->seat0->positions[vtnr]->class != SESSION_GREETER)
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
if (hashmap_size(m->sessions) >= m->sessions_max)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
"Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.",
m->sessions_max);
(void) audit_session_from_pid(&leader, &audit_id);
if (audit_session_is_valid(audit_id)) { if (audit_session_is_valid(audit_id)) {
/* Keep our session IDs and the audit session IDs in sync */ /* Keep our session IDs and the audit session IDs in sync */
@ -1022,7 +856,7 @@ static int create_session(
} }
} }
if (!id) { if (!id)
do { do {
id = mfree(id); id = mfree(id);
@ -1030,15 +864,97 @@ static int create_session(
return -ENOMEM; return -ENOMEM;
} while (hashmap_contains(m->sessions, id)); } while (hashmap_contains(m->sessions, id));
}
/* The generated names should not clash with 'auto' or 'self' */ /* The generated names should not clash with 'auto' or 'self' */
assert(!SESSION_IS_SELF(id)); assert(!SESSION_IS_SELF(id));
assert(!SESSION_IS_AUTO(id)); assert(!SESSION_IS_AUTO(id));
*ret_id = TAKE_PTR(id);
return 0;
}
int manager_create_session(
Manager *m,
uid_t uid,
PidRef *leader, /* consumed */
const char *service,
SessionType type,
SessionClass class,
const char *desktop,
Seat *seat,
unsigned vtnr,
const char *tty,
const char *display,
bool remote,
const char *remote_user,
const char *remote_host,
Session **ret_session) {
int r;
assert(m);
assert(uid_is_valid(uid));
assert(pidref_is_set(leader));
assert(ret_session);
/* Returns:
* -EBUSY client is already in a session
* -EADDRNOTAVAIL VT is already taken
* -EUSERS limit of sessions reached
*/
if (type == _SESSION_TYPE_INVALID) {
if (!isempty(display))
type = SESSION_X11;
else if (!isempty(tty))
type = SESSION_TTY;
else
type = SESSION_UNSPECIFIED;
}
if (class == _SESSION_CLASS_INVALID) {
if (type == SESSION_UNSPECIFIED)
class = SESSION_BACKGROUND;
else
class = SESSION_USER;
}
/* Check if we are already in a logind session, and if so refuse. */
r = manager_get_session_by_pidref(m, leader, /* ret_session= */ NULL);
if (r < 0)
return log_debug_errno(
r,
"Failed to check if process " PID_FMT " is already in a session: %m",
leader->pid);
if (r > 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Client is already in a session.");
/* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
* the greeter session after the user-session and want the user-session to take over the VT. We need
* to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
* greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
* used VT for the exact same reasons. */
if (class != SESSION_GREETER &&
vtnr > 0 &&
vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
m->seat0->positions[vtnr] &&
m->seat0->positions[vtnr]->class != SESSION_GREETER)
return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL), "VT already occupied by a session.");
if (hashmap_size(m->sessions) >= m->sessions_max)
return log_debug_errno(SYNTHETIC_ERRNO(EUSERS), "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
_cleanup_free_ char *id = NULL;
r = manager_choose_session_id(m, leader, &id);
if (r < 0)
return r;
/* If we are not watching utmp already, try again */ /* If we are not watching utmp already, try again */
manager_reconnect_utmp(m); manager_reconnect_utmp(m);
User *user = NULL;
Session *session = NULL;
r = manager_add_user_by_uid(m, uid, &user); r = manager_add_user_by_uid(m, uid, &user);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -1048,21 +964,21 @@ static int create_session(
goto fail; goto fail;
session_set_user(session, user); session_set_user(session, user);
r = session_set_leader_consume(session, TAKE_PIDREF(leader)); r = session_set_leader_consume(session, TAKE_PIDREF(*leader));
if (r < 0) if (r < 0)
goto fail; goto fail;
session->original_type = session->type = t; session->original_type = session->type = type;
session->remote = remote; session->remote = remote;
session->vtnr = vtnr; session->vtnr = vtnr;
session->class = c; session->class = class;
/* Once the first session that is of a pinning class shows up we'll change the GC mode for the user /* Once the first session that is of a pinning class shows up we'll change the GC mode for the user
* from USER_GC_BY_ANY to USER_GC_BY_PIN, so that the user goes away once the last pinning session * from USER_GC_BY_ANY to USER_GC_BY_PIN, so that the user goes away once the last pinning session
* goes away. Background: we want that user@.service when started manually remains around (which * goes away. Background: we want that user@.service when started manually remains around (which
* itself is a non-pinning session), but gets stopped when the last pinning session goes away. */ * itself is a non-pinning session), but gets stopped when the last pinning session goes away. */
if (SESSION_CLASS_PIN_USER(c)) if (SESSION_CLASS_PIN_USER(class))
user->gc_mode = USER_GC_BY_PIN; user->gc_mode = USER_GC_BY_PIN;
if (!isempty(tty)) { if (!isempty(tty)) {
@ -1109,14 +1025,184 @@ static int create_session(
goto fail; goto fail;
} }
*ret_session = session;
return 0;
fail:
if (session)
session_add_to_gc_queue(session);
if (user)
user_add_to_gc_queue(user);
return r;
}
static int manager_create_session_by_bus(
Manager *m,
sd_bus_message *message,
sd_bus_error *error,
uid_t uid,
pid_t leader_pid,
int leader_pidfd,
const char *service,
const char *type,
const char *class,
const char *desktop,
const char *cseat,
uint32_t vtnr,
const char *tty,
const char *display,
int remote,
const char *remote_user,
const char *remote_host,
uint64_t flags) {
int r;
assert(m);
assert(message);
if (!uid_is_valid(uid))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
if (flags != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
if (leader_pidfd >= 0)
r = pidref_set_pidfd(&leader, leader_pidfd);
else if (leader_pid == 0)
r = bus_query_sender_pidref(message, &leader);
else {
if (leader_pid < 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Leader PID is not valid");
r = pidref_set_pid(&leader, leader_pid);
}
if (r < 0)
return r;
if (leader.pid == 1 || pidref_is_self(&leader))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
SessionType t;
if (isempty(type))
t = _SESSION_TYPE_INVALID;
else {
t = session_type_from_string(type);
if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session type %s", type);
}
SessionClass c;
if (isempty(class))
c = _SESSION_CLASS_INVALID;
else {
c = session_class_from_string(class);
if (c < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session class %s", class);
}
if (isempty(desktop))
desktop = NULL;
else {
if (!string_is_safe(desktop))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid desktop string %s", desktop);
}
Seat *seat = NULL;
if (isempty(cseat))
seat = NULL;
else {
seat = hashmap_get(m->seats, cseat);
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
"No seat '%s' known", cseat);
}
if (isempty(tty))
tty = NULL;
else if (tty_is_vc(tty)) {
int v;
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
v = vtnr_from_tty(tty);
if (v <= 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Cannot determine VT number from virtual console TTY %s", tty);
if (vtnr == 0)
vtnr = (uint32_t) v;
else if (vtnr != (uint32_t) v)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Specified TTY and VT number do not match");
} else if (tty_is_console(tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but seat is not seat0");
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but VT number is not 0");
}
if (seat) {
if (seat_has_vts(seat)) {
if (!vtnr_is_valid(vtnr))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"VT number out of range");
} else {
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Seat has no VTs but VT number not 0");
}
}
Session *session;
r = manager_create_session(
m,
uid,
&leader,
service,
t,
c,
desktop,
seat,
vtnr,
tty,
display,
remote,
remote_user,
remote_host,
&session);
if (r == -EBUSY)
return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
if (r == -EADDRNOTAVAIL)
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Virtual terminal already occupied by a session");
if (r == -EUSERS)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
if (r < 0)
return r;
r = sd_bus_message_enter_container(message, 'a', "(sv)"); r = sd_bus_message_enter_container(message, 'a', "(sv)");
if (r < 0) if (r < 0)
goto fail; goto fail;
r = session_start(session, message, error); r = session_start(session, message, error);
if (r < 0) if (r < 0)
goto fail; goto fail;
r = sd_bus_message_exit_container(message); r = sd_bus_message_exit_container(message);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -1130,7 +1216,7 @@ static int create_session(
* all is complete - or wait again. */ * all is complete - or wait again. */
r = session_send_create_reply(session, /* error= */ NULL); r = session_send_create_reply(session, /* error= */ NULL);
if (r < 0) if (r < 0)
return r; goto fail;
return 1; return 1;
@ -1138,9 +1224,6 @@ fail:
if (session) if (session)
session_add_to_gc_queue(session); session_add_to_gc_queue(session);
if (user)
user_add_to_gc_queue(user);
return r; return r;
} }
@ -1175,9 +1258,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
if (r < 0) if (r < 0)
return r; return r;
return create_session( return manager_create_session_by_bus(
message,
userdata, userdata,
message,
error, error,
uid, uid,
leader_pid, leader_pid,
@ -1223,9 +1306,9 @@ static int method_create_session_pidfd(sd_bus_message *message, void *userdata,
if (r < 0) if (r < 0)
return r; return r;
return create_session( return manager_create_session_by_bus(
message,
userdata, userdata,
message,
error, error,
uid, uid,
/* leader_pid = */ 0, /* leader_pid = */ 0,

View File

@ -47,4 +47,21 @@ int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *erro
void manager_load_scheduled_shutdown(Manager *m); void manager_load_scheduled_shutdown(Manager *m);
int manager_create_session(
Manager *m,
uid_t uid,
PidRef *leader,
const char *service,
SessionType type,
SessionClass class,
const char *desktop,
Seat *seat,
unsigned vtnr,
const char *tty,
const char *display,
bool remote,
const char *remote_user,
const char *remote_host,
Session **ret_session);
extern const BusObjectImplementation manager_object; extern const BusObjectImplementation manager_object;

View File

@ -899,62 +899,40 @@ int session_send_lock_all(Manager *m, bool lock) {
return r; return r;
} }
static bool session_job_pending(Session *s) { int session_send_create_reply_bus(Session *s, const sd_bus_error *error) {
assert(s);
assert(s->user);
/* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
* relevant units, session_send_create_reply and hence us is called (see match_job_removed).
* Note that we don't care about job result here. */
return s->scope_job ||
s->user->runtime_dir_job ||
(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
}
int session_send_create_reply(Session *s, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_close_ int fifo_fd = -EBADF;
_cleanup_free_ char *p = NULL;
assert(s); assert(s);
/* This is called after the session scope and the user service were successfully created, and finishes where /* This is called after the session scope and the user service were successfully created, and
* bus_manager_create_session() left off. */ * finishes where manager_create_session() left off. */
if (!s->create_message) _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = TAKE_PTR(s->create_message);
if (!c)
return 0; return 0;
/* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before if (sd_bus_error_is_set(error))
* continuing. */
if (!sd_bus_error_is_set(error) && session_job_pending(s))
return 0;
c = TAKE_PTR(s->create_message);
if (error)
return sd_bus_reply_method_error(c, error); return sd_bus_reply_method_error(c, error);
fifo_fd = session_create_fifo(s); _cleanup_close_ int fifo_fd = session_create_fifo(s);
if (fifo_fd < 0) if (fifo_fd < 0)
return fifo_fd; return fifo_fd;
/* Update the session state file before we notify the client about the result. */ /* Update the session state file before we notify the client about the result. */
session_save(s); session_save(s);
p = session_bus_path(s); _cleanup_free_ char *p = session_bus_path(s);
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
log_debug("Sending reply about created session: " log_debug("Sending D-Bus reply about created session: "
"id=%s object_path=%s uid=%u runtime_path=%s " "id=%s object_path=%s uid=" UID_FMT " runtime_path=%s "
"session_fd=%d seat=%s vtnr=%u", "session_fd=%d seat=%s vtnr=%u",
s->id, s->id,
p, p,
(uint32_t) s->user->user_record->uid, s->user->user_record->uid,
s->user->runtime_path, s->user->runtime_path,
fifo_fd, fifo_fd,
s->seat ? s->seat->id : "", s->seat ? s->seat->id : "",
(uint32_t) s->vtnr); s->vtnr);
return sd_bus_reply_method_return( return sd_bus_reply_method_return(
c, "soshusub", c, "soshusub",
@ -968,7 +946,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
false); false);
} }
int session_send_upgrade_reply(Session *s, sd_bus_error *error) { int session_send_upgrade_reply(Session *s, const sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
assert(s); assert(s);

View File

@ -15,8 +15,8 @@ int session_send_changed(Session *s, const char *properties, ...) _sentinel_;
int session_send_lock(Session *s, bool lock); int session_send_lock(Session *s, bool lock);
int session_send_lock_all(Manager *m, bool lock); int session_send_lock_all(Manager *m, bool lock);
int session_send_create_reply(Session *s, sd_bus_error *error); int session_send_create_reply_bus(Session *s, const sd_bus_error *error);
int session_send_upgrade_reply(Session *s, sd_bus_error *error); int session_send_upgrade_reply(Session *s, const sd_bus_error *error);
int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);

View File

@ -29,6 +29,7 @@
#include "logind-session-dbus.h" #include "logind-session-dbus.h"
#include "logind-session.h" #include "logind-session.h"
#include "logind-user-dbus.h" #include "logind-user-dbus.h"
#include "logind-varlink.h"
#include "mkdir-label.h" #include "mkdir-label.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
@ -192,6 +193,8 @@ Session* session_free(Session *s) {
sd_bus_message_unref(s->create_message); sd_bus_message_unref(s->create_message);
sd_bus_message_unref(s->upgrade_message); sd_bus_message_unref(s->upgrade_message);
sd_varlink_unref(s->create_link);
free(s->tty); free(s->tty);
free(s->display); free(s->display);
free(s->remote_host); free(s->remote_host);
@ -1648,6 +1651,35 @@ void session_drop_controller(Session *s) {
session_restore_vt(s); session_restore_vt(s);
} }
bool session_job_pending(Session *s) {
assert(s);
assert(s->user);
/* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
* relevant units, session_send_create_reply and hence us is called (see match_job_removed).
* Note that we don't care about job result here. */
return s->scope_job ||
s->user->runtime_dir_job ||
(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
}
int session_send_create_reply(Session *s, const sd_bus_error *error) {
int r;
assert(s);
/* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before
* continuing. */
if (!sd_bus_error_is_set(error) && session_job_pending(s))
return 0;
r = 0;
RET_GATHER(r, session_send_create_reply_bus(s, error));
RET_GATHER(r, session_send_create_reply_varlink(s, error));
return r;
}
static const char* const session_state_table[_SESSION_STATE_MAX] = { static const char* const session_state_table[_SESSION_STATE_MAX] = {
[SESSION_OPENING] = "opening", [SESSION_OPENING] = "opening",
[SESSION_ONLINE] = "online", [SESSION_ONLINE] = "online",

View File

@ -150,6 +150,8 @@ struct Session {
sd_bus_message *create_message; /* The D-Bus message used to create the session, which we haven't responded to yet */ sd_bus_message *create_message; /* The D-Bus message used to create the session, which we haven't responded to yet */
sd_bus_message *upgrade_message; /* The D-Bus message used to upgrade the session class user-incomplete → user, which we haven't responded to yet */ sd_bus_message *upgrade_message; /* The D-Bus message used to upgrade the session class user-incomplete → user, which we haven't responded to yet */
sd_varlink *create_link; /* The Varlink connection used to create session, which we haven't responded to yet */
/* Set up when a client requested to release the session via the bus */ /* Set up when a client requested to release the session via the bus */
sd_event_source *timer_event_source; sd_event_source *timer_event_source;
@ -216,6 +218,10 @@ bool session_is_controller(Session *s, const char *sender);
int session_set_controller(Session *s, const char *sender, bool force, bool prepare); int session_set_controller(Session *s, const char *sender, bool force, bool prepare);
void session_drop_controller(Session *s); void session_drop_controller(Session *s);
bool session_job_pending(Session *s);
int session_send_create_reply(Session *s, const sd_bus_error *error);
static inline bool SESSION_IS_SELF(const char *name) { static inline bool SESSION_IS_SELF(const char *name) {
return isempty(name) || streq(name, "self"); return isempty(name) || streq(name, "self");
} }

373
src/login/logind-varlink.c Normal file
View File

@ -0,0 +1,373 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "cgroup-util.h"
#include "fd-util.h"
#include "json-util.h"
#include "logind.h"
#include "logind-dbus.h"
#include "logind-session-dbus.h"
#include "logind-varlink.h"
#include "terminal-util.h"
#include "user-util.h"
#include "varlink-io.systemd.Login.h"
#include "varlink-util.h"
static int manager_varlink_get_session_by_peer(
Manager *m,
sd_varlink *link,
bool consult_display,
Session **ret) {
int r;
assert(m);
assert(link);
assert(ret);
/* Determines the session of the peer. If the peer is not part of a session, but consult_display is
* true, then will return the display session of the peer's owning user */
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = varlink_get_peer_pidref(link, &pidref);
if (r < 0)
return log_error_errno(r, "Failed to acquire peer PID: %m");
Session *session = NULL;
_cleanup_free_ char *name = NULL;
r = cg_pidref_get_session(&pidref, &name);
if (r < 0) {
if (!consult_display)
log_debug_errno(r, "Failed to acquire session of peer, giving up: %m");
else {
log_debug_errno(r, "Failed to acquire session of peer, trying to find owner UID: %m");
uid_t uid;
r = cg_pidref_get_owner_uid(&pidref, &uid);
if (r < 0)
log_debug_errno(r, "Failed to acquire owning UID of peer, giving up: %m");
else {
User *user = hashmap_get(m->users, UID_TO_PTR(uid));
if (user)
session = user->display;
}
}
} else
session = hashmap_get(m->sessions, name);
if (!session)
return sd_varlink_error(link, "io.systemd.Login.NoSuchSession", /* parameters= */ NULL);
*ret = session;
return 0;
}
static int manager_varlink_get_session_by_name(
Manager *m,
sd_varlink *link,
const char *name,
Session **ret) {
assert(m);
assert(link);
assert(ret);
/* Resolves a session name to a session object. Supports resolving the special names "self" and "auto". */
if (SESSION_IS_SELF(name))
return manager_varlink_get_session_by_peer(m, link, /* consult_display= */ false, ret);
if (SESSION_IS_AUTO(name))
return manager_varlink_get_session_by_peer(m, link, /* consult_display= */ true, ret);
Session *session = hashmap_get(m->sessions, name);
if (!session)
return sd_varlink_error(link, "io.systemd.Login.NoSuchSession", /* parameters= */ NULL);
*ret = session;
return 0;
}
int session_send_create_reply_varlink(Session *s, const sd_bus_error *error) {
assert(s);
/* This is called after the session scope and the user service were successfully created, and
* finishes where manager_create_session() left off. */
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = TAKE_PTR(s->create_link);
if (!vl)
return 0;
if (sd_bus_error_is_set(error))
return sd_varlink_error(vl, "io.systemd.Login.UnitAllocationFailed", /* parameters= */ NULL);
_cleanup_close_ int fifo_fd = session_create_fifo(s);
if (fifo_fd < 0)
return fifo_fd;
/* Update the session state file before we notify the client about the result. */
session_save(s);
log_debug("Sending Varlink reply about created session: "
"id=%s uid=" UID_FMT " runtime_path=%s "
"session_fd=%d seat=%s vtnr=%u",
s->id,
s->user->user_record->uid,
s->user->runtime_path,
fifo_fd,
s->seat ? s->seat->id : "",
s->vtnr);
int fifo_fd_idx = sd_varlink_push_fd(vl, fifo_fd);
if (fifo_fd_idx < 0) {
log_error_errno(fifo_fd_idx, "Failed to push FIFO fd to Varlink: %m");
return sd_varlink_error_errno(vl, fifo_fd_idx);
}
TAKE_FD(fifo_fd);
return sd_varlink_replybo(
vl,
SD_JSON_BUILD_PAIR_STRING("Id", s->id),
SD_JSON_BUILD_PAIR_STRING("RuntimePath", s->user->runtime_path),
SD_JSON_BUILD_PAIR_UNSIGNED("SessionFileDescriptor", fifo_fd_idx),
SD_JSON_BUILD_PAIR_UNSIGNED("UID", s->user->user_record->uid),
SD_JSON_BUILD_PAIR_CONDITION(!!s->seat, "Seat", SD_JSON_BUILD_STRING(s->seat ? s->seat->id : NULL)),
SD_JSON_BUILD_PAIR_CONDITION(s->vtnr > 0, "VTNr", SD_JSON_BUILD_UNSIGNED(s->vtnr)));
}
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_session_class, SessionClass, session_class_from_string);
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_session_type, SessionType, session_type_from_string);
static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
int r;
struct {
uid_t uid;
const char *service;
SessionType type;
SessionClass class;
const char *desktop;
const char *seat;
unsigned vtnr;
const char *tty;
const char *display;
int remote;
const char *remote_user;
const char *remote_host;
} p = {
.uid = UID_INVALID,
.class = _SESSION_CLASS_INVALID,
.type = _SESSION_TYPE_INVALID,
.remote = -1,
};
static const sd_json_dispatch_field dispatch_table[] = {
{ "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, voffsetof(p, uid), SD_JSON_MANDATORY },
{ "Service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, service), 0 },
{ "Type", SD_JSON_VARIANT_STRING, json_dispatch_session_type, voffsetof(p, type), SD_JSON_MANDATORY },
{ "Class", SD_JSON_VARIANT_STRING, json_dispatch_session_class, voffsetof(p, class), SD_JSON_MANDATORY },
{ "Desktop", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, desktop), SD_JSON_STRICT },
{ "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, seat), 0 },
{ "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, vtnr), 0 },
{ "TTY", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, tty), 0 },
{ "Display", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, display), 0 },
{ "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, remote), 0 },
{ "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, remote_user), 0 },
{ "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, remote_host), 0 },
{}
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
Seat *seat = NULL;
if (p.seat) {
seat = hashmap_get(m->seats, p.seat);
if (!seat)
return sd_varlink_replyb(link, "io.systemd.Login.NoSuchSeat", /* parameters= */ NULL);
}
if (p.tty) {
if (tty_is_vc(p.tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_varlink_error_invalid_parameter_name(link, "Seat");
int v = vtnr_from_tty(p.tty);
if (v <= 0)
return sd_varlink_error_invalid_parameter_name(link, "TTY");
if (p.vtnr == 0)
p.vtnr = v;
else if (p.vtnr != (unsigned) v)
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
} else if (tty_is_console(p.tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_varlink_error_invalid_parameter_name(link, "Seat");
if (p.vtnr != 0)
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
}
}
if (seat) {
if (seat_has_vts(seat)) {
if (!vtnr_is_valid(p.vtnr))
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
} else {
if (p.vtnr != 0)
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
}
}
if (p.remote < 0)
p.remote = p.remote_user || p.remote_host;
/* Before we continue processing this, let's ensure the peer is privileged */
uid_t peer_uid;
r = sd_varlink_get_peer_uid(link, &peer_uid);
if (r < 0)
return log_debug_errno(r, "Failed to get peer UID: %m");
if (peer_uid != 0)
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, /* parameters= */ NULL);
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
r = varlink_get_peer_pidref(link, &leader);
if (r < 0)
return log_debug_errno(r, "Failed to get peer pidref: %m");
Session *session;
r = manager_create_session(
m,
p.uid,
&leader,
p.service,
p.type,
p.class,
p.desktop,
seat,
p.vtnr,
p.tty,
p.display,
p.remote,
p.remote_user,
p.remote_host,
&session);
if (r == -EBUSY)
return sd_varlink_error(link, "io.systemd.Login.AlreadySessionMember", /* parameters= */ NULL);
if (r == -EADDRNOTAVAIL)
return sd_varlink_error(link, "io.systemd.Login.VirtualTerminalAlreadyTaken", /* parameters= */ NULL);
if (r == -EUSERS)
return sd_varlink_error(link, "io.systemd.Login.TooManySessions", /* parameters= */ NULL);
if (r < 0)
return r;
r = session_start(session, /* properties= */ NULL, /* error= */ NULL);
if (r < 0)
goto fail;
session->create_link = sd_varlink_ref(link);
/* Let's check if this is complete now */
r = session_send_create_reply(session, /* error= */ NULL);
if (r < 0)
goto fail;
return 1;
fail:
if (session)
session_add_to_gc_queue(session);
return r;
}
static int vl_method_release_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
int r;
struct {
const char *id;
} p;
static const sd_json_dispatch_field dispatch_table[] = {
{ "Id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), SD_JSON_MANDATORY },
{}
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
Session *session;
r = manager_varlink_get_session_by_name(m, link, p.id, &session);
if (r != 0)
return r;
Session *peer_session;
r = manager_varlink_get_session_by_peer(m, link, /* consult_display= */ false, &peer_session);
if (r != 0)
return r;
if (session != peer_session)
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, /* parameters= */ NULL);
r = session_release(session);
if (r < 0)
return r;
return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT);
}
int manager_varlink_init(Manager *m) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
int r;
assert(m);
if (m->varlink_server)
return 0;
r = sd_varlink_server_new(
&s,
SD_VARLINK_SERVER_ACCOUNT_UID|
SD_VARLINK_SERVER_INHERIT_USERDATA|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT);
if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m");
sd_varlink_server_set_userdata(s, m);
r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Login);
if (r < 0)
return log_error_errno(r, "Failed to add Login interface to varlink server: %m");
r = sd_varlink_server_bind_method_many(
s,
"io.systemd.Login.CreateSession", vl_method_create_session,
"io.systemd.Login.ReleaseSession", vl_method_release_session);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");
r = sd_varlink_server_listen_address(s, "/run/systemd/io.systemd.Login", 0666);
if (r < 0)
return log_error_errno(r, "Failed to bind to varlink socket: %m");
r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
m->varlink_server = TAKE_PTR(s);
return 0;
}
void manager_varlink_done(Manager *m) {
assert(m);
m->varlink_server = sd_varlink_server_unref(m->varlink_server);
}

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-bus.h"
#include "logind.h"
#include "logind-session.h"
int manager_varlink_init(Manager *m);
void manager_varlink_done(Manager *m);
int session_send_create_reply_varlink(Session *s, const sd_bus_error *error);

View File

@ -24,11 +24,12 @@
#include "fd-util.h" #include "fd-util.h"
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "logind.h"
#include "logind-dbus.h" #include "logind-dbus.h"
#include "logind-seat-dbus.h" #include "logind-seat-dbus.h"
#include "logind-session-dbus.h" #include "logind-session-dbus.h"
#include "logind-user-dbus.h" #include "logind-user-dbus.h"
#include "logind.h" #include "logind-varlink.h"
#include "main-func.h" #include "main-func.h"
#include "mkdir-label.h" #include "mkdir-label.h"
#include "parse-util.h" #include "parse-util.h"
@ -153,6 +154,8 @@ static Manager* manager_free(Manager *m) {
hashmap_free(m->polkit_registry); hashmap_free(m->polkit_registry);
manager_varlink_done(m);
sd_bus_flush_close_unref(m->bus); sd_bus_flush_close_unref(m->bus);
sd_event_unref(m->event); sd_event_unref(m->event);
@ -1114,6 +1117,10 @@ static int manager_startup(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = manager_varlink_init(m);
if (r < 0)
return r;
/* Instantiate magic seat 0 */ /* Instantiate magic seat 0 */
r = manager_add_seat(m, "seat0", &m->seat0); r = manager_add_seat(m, "seat0", &m->seat0);
if (r < 0) if (r < 0)

View File

@ -7,6 +7,7 @@
#include "sd-bus.h" #include "sd-bus.h"
#include "sd-device.h" #include "sd-device.h"
#include "sd-event.h" #include "sd-event.h"
#include "sd-varlink.h"
#include "calendarspec.h" #include "calendarspec.h"
#include "conf-parser.h" #include "conf-parser.h"
@ -145,6 +146,8 @@ struct Manager {
struct stat efi_loader_entry_one_shot_stat; struct stat efi_loader_entry_one_shot_stat;
CalendarSpec *maintenance_time; CalendarSpec *maintenance_time;
sd_varlink_server *varlink_server;
}; };
void manager_reset_config(Manager *m); void manager_reset_config(Manager *m);

View File

@ -26,6 +26,7 @@ liblogind_core_sources = files(
'logind-session.c', 'logind-session.c',
'logind-user-dbus.c', 'logind-user-dbus.c',
'logind-user.c', 'logind-user.c',
'logind-varlink.c',
'logind-wall.c', 'logind-wall.c',
) )

View File

@ -18,6 +18,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "sd-bus.h"
#include "sd-varlink.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "audit-util.h" #include "audit-util.h"
#include "bus-common-errors.h" #include "bus-common-errors.h"
@ -35,6 +38,7 @@
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "json-util.h"
#include "locale-util.h" #include "locale-util.h"
#include "login-util.h" #include "login-util.h"
#include "macro.h" #include "macro.h"
@ -390,116 +394,108 @@ static int export_legacy_dbus_address(
} }
static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
assert(handle);
assert(m);
if (isempty(limit)) if (isempty(limit))
return PAM_SUCCESS; return 0;
if (streq(limit, "infinity")) { if (streq(limit, "infinity"))
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", UINT64_MAX); return sd_bus_message_append(m, "(sv)", "MemoryMax", "t", UINT64_MAX);
if (r < 0)
return pam_bus_log_create_error(handle, r);
return PAM_SUCCESS;
}
r = parse_permyriad(limit); r = parse_permyriad(limit);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r)); uint64_t val;
if (r < 0)
return pam_bus_log_create_error(handle, r);
return PAM_SUCCESS;
}
r = parse_size(limit, 1024, &val); r = parse_size(limit, 1024, &val);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
return PAM_SUCCESS;
}
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit);
return PAM_SUCCESS; return PAM_SUCCESS;
}
return sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
}
return sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r));
} }
static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
usec_t val;
int r; int r;
assert(handle);
assert(m);
/* No need to parse "infinity" here, it will be set by default later in scope_init() */ /* No need to parse "infinity" here, it will be set by default later in scope_init() */
if (isempty(limit) || streq(limit, "infinity")) if (isempty(limit) || streq(limit, "infinity"))
return PAM_SUCCESS; return 0;
usec_t val;
r = parse_sec(limit, &val); r = parse_sec(limit, &val);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} else
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit);
return 0;
}
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
} }
static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
assert(handle);
assert(m);
/* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */ /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
if (isempty(limit) || streq(limit, "infinity")) if (isempty(limit) || streq(limit, "infinity"))
return PAM_SUCCESS; return 0;
uint64_t val;
r = safe_atou64(limit, &val); r = safe_atou64(limit, &val);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} else
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit);
return 0;
}
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
} }
static int append_session_cpu_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_cpu_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
if (isempty(limit)) assert(handle);
return PAM_SUCCESS; assert(m);
if (isempty(limit))
return 0;
uint64_t val;
r = cg_cpu_weight_parse(limit, &val); r = cg_cpu_weight_parse(limit, &val);
if (r < 0) if (r < 0) {
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
else { return 0;
r = sd_bus_message_append(m, "(sv)", "CPUWeight", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} }
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "CPUWeight", "t", val);
} }
static int append_session_io_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_io_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
if (isempty(limit)) assert(handle);
return PAM_SUCCESS; assert(m);
if (isempty(limit))
return 0;
uint64_t val;
r = cg_weight_parse(limit, &val); r = cg_weight_parse(limit, &val);
if (r < 0) if (r < 0) {
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
else { return 0;
r = sd_bus_message_append(m, "(sv)", "IOWeight", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} }
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "IOWeight", "t", val);
} }
static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) { static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
@ -549,6 +545,26 @@ static bool getenv_harder_bool(pam_handle_t *handle, const char *key, bool fallb
return r; return r;
} }
static uint32_t getenv_harder_uint32(pam_handle_t *handle, const char *key, uint32_t fallback) {
int r;
assert(handle);
assert(key);
const char *v = getenv_harder(handle, key, NULL);
if (isempty(v))
return fallback;
uint32_t u;
r = safe_atou32(v, &u);
if (r < 0) {
pam_syslog(handle, LOG_ERR, "Unsigned integer environment variable value of '%s' is not valid: %s", key, v);
return fallback;
}
return u;
}
static int update_environment(pam_handle_t *handle, const char *key, const char *value) { static int update_environment(pam_handle_t *handle, const char *key, const char *value) {
int r; int r;
@ -826,17 +842,15 @@ static uint64_t pick_default_capability_ambient_set(
} }
typedef struct SessionContext { typedef struct SessionContext {
const uid_t uid;
const pid_t pid;
const char *service; const char *service;
const char *type; const char *type;
const char *class; const char *class;
const char *desktop; const char *desktop;
const char *seat; const char *seat;
const uint32_t vtnr; uint32_t vtnr;
const char *tty; const char *tty;
const char *display; const char *display;
const bool remote; bool remote;
const char *remote_user; const char *remote_user;
const char *remote_host; const char *remote_host;
const char *memory_max; const char *memory_max;
@ -844,11 +858,13 @@ typedef struct SessionContext {
const char *cpu_weight; const char *cpu_weight;
const char *io_weight; const char *io_weight;
const char *runtime_max_sec; const char *runtime_max_sec;
bool incomplete;
} SessionContext; } SessionContext;
static int create_session_message( static int create_session_message(
sd_bus *bus, sd_bus *bus,
pam_handle_t *handle, pam_handle_t *handle,
UserRecord *ur,
const SessionContext *context, const SessionContext *context,
bool avoid_pidfd, bool avoid_pidfd,
sd_bus_message **ret) { sd_bus_message **ret) {
@ -859,6 +875,7 @@ static int create_session_message(
assert(bus); assert(bus);
assert(handle); assert(handle);
assert(ur);
assert(context); assert(context);
assert(ret); assert(ret);
@ -872,10 +889,11 @@ static int create_session_message(
if (r < 0) if (r < 0)
return r; return r;
r = sd_bus_message_append(m, r = sd_bus_message_append(
m,
pidfd >= 0 ? "uhsssssussbss" : "uusssssussbss", pidfd >= 0 ? "uhsssssussbss" : "uusssssussbss",
(uint32_t) context->uid, (uint32_t) ur->uid,
pidfd >= 0 ? pidfd : context->pid, pidfd >= 0 ? pidfd : 0,
context->service, context->service,
context->type, context->type,
context->class, context->class,
@ -901,23 +919,23 @@ static int create_session_message(
return r; return r;
r = append_session_memory_max(handle, m, context->memory_max); r = append_session_memory_max(handle, m, context->memory_max);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_runtime_max_sec(handle, m, context->runtime_max_sec); r = append_session_runtime_max_sec(handle, m, context->runtime_max_sec);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_tasks_max(handle, m, context->tasks_max); r = append_session_tasks_max(handle, m, context->tasks_max);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_cpu_weight(handle, m, context->cpu_weight); r = append_session_cpu_weight(handle, m, context->cpu_weight);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_io_weight(handle, m, context->io_weight); r = append_session_io_weight(handle, m, context->io_weight);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = sd_bus_message_close_container(m); r = sd_bus_message_close_container(m);
@ -928,211 +946,251 @@ static int create_session_message(
return 0; return 0;
} }
_public_ PAM_EXTERN int pam_sm_open_session( static void session_context_mangle(
pam_handle_t *handle, pam_handle_t *handle,
int flags, SessionContext *c,
int argc, const char **argv) { UserRecord *ur,
bool debug) {
/* Let's release the D-Bus connection once this function exits, after all the session might live
* quite a long time, and we are not going to process the bus connection in that time, so let's
* better close before the daemon kicks us off because we are not processing anything. */
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
const char
*id, *object_path, *runtime_path,
*service = NULL,
*tty = NULL, *display = NULL,
*remote_user = NULL, *remote_host = NULL,
*seat = NULL,
*type = NULL, *class = NULL,
*class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
*memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL, *runtime_max_sec = NULL;
uint64_t default_capability_bounding_set = UINT64_MAX, default_capability_ambient_set = UINT64_MAX;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
int session_fd = -EBADF, existing, r;
bool debug = false, remote, incomplete;
uint32_t vtnr = 0;
uid_t original_uid;
assert(handle); assert(handle);
assert(c);
assert(ur);
pam_log_setup(); if (streq_ptr(c->service, "systemd-user")) {
if (parse_argv(handle,
argc, argv,
&class_pam,
&type_pam,
&desktop_pam,
&debug,
&default_capability_bounding_set,
&default_capability_ambient_set) < 0)
return PAM_SESSION_ERR;
pam_debug_syslog(handle, debug, "pam-systemd initializing");
r = acquire_user_record(handle, &ur);
if (r != PAM_SUCCESS)
return r;
/* Make most of this a NOP on non-logind systems */
if (!logind_running())
goto success;
r = pam_get_item_many(
handle,
PAM_SERVICE, &service,
PAM_XDISPLAY, &display,
PAM_TTY, &tty,
PAM_RUSER, &remote_user,
PAM_RHOST, &remote_host);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
seat = getenv_harder(handle, "XDG_SEAT", NULL);
cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
incomplete = getenv_harder_bool(handle, "XDG_SESSION_INCOMPLETE", false);
if (streq_ptr(service, "systemd-user")) {
/* If we detect that we are running in the "systemd-user" PAM stack, then let's patch the class to /* If we detect that we are running in the "systemd-user" PAM stack, then let's patch the class to
* 'manager' if not set, simply for robustness reasons. */ * 'manager' if not set, simply for robustness reasons. */
type = "unspecified"; c->type = "unspecified";
class = IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) ? c->class = IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) ?
"manager-early" : "manager"; "manager-early" : "manager";
tty = NULL; c->tty = NULL;
} else if (tty && strchr(tty, ':')) { } else if (c->tty && strchr(c->tty, ':')) {
/* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
* and don't pretend that an X display was a tty. */ * and don't pretend that an X display was a tty. */
if (isempty(display)) if (isempty(c->display))
display = tty; c->display = c->tty;
tty = NULL; c->tty = NULL;
} else if (streq_ptr(tty, "cron")) { } else if (streq_ptr(c->tty, "cron")) {
/* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
* probably because it wants to set it to something as pam_time/pam_access/ require PAM_TTY to be set * probably because it wants to set it to something as pam_time/pam_access/ require PAM_TTY to be set
* (as they otherwise even try to update it!) but cron doesn't actually allocate a TTY for its forked * (as they otherwise even try to update it!) but cron doesn't actually allocate a TTY for its forked
* off processes.) */ * off processes.) */
type = "unspecified"; c->type = "unspecified";
class = "background"; c->class = "background";
tty = NULL; c->tty = NULL;
} else if (streq_ptr(tty, "ssh")) { } else if (streq_ptr(c->tty, "ssh")) {
/* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
* details look for "PAM_TTY_KLUDGE" in the openssh sources). */ * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
type = "tty"; c->type = "tty";
class = "user"; c->class = "user";
tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually c->tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though
* associated with a pty won't be tracked by their tty in logind. This is because ssh * usually associated with a pty won't be tracked by their tty in
* does the PAM session registration early for new connections, and registers a pty only * logind. This is because ssh does the PAM session registration early for new
* much later (this is because it doesn't know yet if it needs one at all, as whether to * connections, and registers a pty only much later (this is because it doesn't
* register a pty or not is negotiated much later in the protocol). */ * know yet if it needs one at all, as whether to register a pty or not is
* negotiated much later in the protocol). */
} else if (tty) } else if (c->tty)
/* Chop off leading /dev prefix that some clients specify, but others do not. */ /* Chop off leading /dev prefix that some clients specify, but others do not. */
tty = skip_dev_prefix(tty); c->tty = skip_dev_prefix(c->tty);
/* If this fails vtnr will be 0, that's intended */ if (!isempty(c->display) && !c->vtnr) {
if (!isempty(cvtnr)) if (isempty(c->seat))
(void) safe_atou32(cvtnr, &vtnr); (void) get_seat_from_display(c->display, &c->seat, &c->vtnr);
else if (streq(c->seat, "seat0"))
if (!isempty(display) && !vtnr) { (void) get_seat_from_display(c->display, /* seat= */ NULL, &c->vtnr);
if (isempty(seat))
(void) get_seat_from_display(display, &seat, &vtnr);
else if (streq(seat, "seat0"))
(void) get_seat_from_display(display, NULL, &vtnr);
} }
if (seat && !streq(seat, "seat0") && vtnr != 0) { if (c->seat && !streq(c->seat, "seat0") && c->vtnr != 0) {
pam_debug_syslog(handle, debug, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat); pam_debug_syslog(handle, debug, "Ignoring vtnr %"PRIu32" for %s which is not seat0", c->vtnr, c->seat);
vtnr = 0; c->vtnr = 0;
} }
if (isempty(type)) if (isempty(c->type))
type = !isempty(display) ? "x11" : c->type = !isempty(c->display) ? "x11" :
!isempty(tty) ? "tty" : "unspecified"; !isempty(c->tty) ? "tty" : "unspecified";
if (isempty(class)) if (isempty(c->class))
class = streq(type, "unspecified") ? "background" : c->class = streq(c->type, "unspecified") ? "background" :
((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) && ((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) &&
streq(type, "tty")) ? "user-early" : "user"); streq(c->type, "tty")) ? "user-early" : "user");
if (incomplete) { if (c->incomplete) {
if (streq(class, "user")) if (streq(c->class, "user"))
class = "user-incomplete"; c->class = "user-incomplete";
else else
pam_syslog_pam_error(handle, LOG_WARNING, 0, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", class); pam_syslog_pam_error(handle, LOG_WARNING, 0, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", c->class);
} }
remote = !isempty(remote_host) && !is_localhost(remote_host); c->remote = !isempty(c->remote_host) && !is_localhost(c->remote_host);
}
r = pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max); static bool can_use_varlink(const SessionContext *c) {
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) /* Since PID 1 currently doesn't do Varlink right now, we cannot directly set properties for the
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.memory_max data: @PAMERR@"); * scope, for now. */
r = pam_get_data(handle, "systemd.tasks_max", (const void **)&tasks_max); return !c->memory_max &&
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) !c->runtime_max_sec &&
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.tasks_max data: @PAMERR@"); !c->tasks_max &&
r = pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight); !c->cpu_weight &&
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) !c->io_weight;
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.cpu_weight data: @PAMERR@"); }
r = pam_get_data(handle, "systemd.io_weight", (const void **)&io_weight);
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) static int register_session(
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.io_weight data: @PAMERR@"); pam_handle_t *handle,
r = pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec); SessionContext *c,
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) UserRecord *ur,
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.runtime_max_sec data: @PAMERR@"); bool debug,
char **ret_seat) {
int r;
assert(handle);
assert(c);
assert(ur);
assert(ret_seat);
/* Make most of this a NOP on non-logind systems */
if (!logind_running())
goto skip;
pam_debug_syslog(handle, debug,
"Asking logind to create session: "
"uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
ur->uid, getpid_cached(),
strempty(c->service),
c->type, c->class, strempty(c->desktop),
strempty(c->seat), c->vtnr, strempty(c->tty), strempty(c->display),
yes_no(c->remote), strempty(c->remote_user), strempty(c->remote_host));
pam_debug_syslog(handle, debug,
"Session limits: "
"memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
strna(c->memory_max), strna(c->tasks_max), strna(c->cpu_weight), strna(c->io_weight), strna(c->runtime_max_sec));
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; /* the following variables point into this message, hence pin it for longer */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vreply = NULL; /* similar */
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; /* similar */
const char *id = NULL, *object_path = NULL, *runtime_path = NULL, *real_seat = NULL;
int session_fd = -EBADF, existing = false;
uint32_t original_uid = UID_INVALID, real_vtnr = 0;
bool done = false;
if (can_use_varlink(c)) {
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.Login");
if (r < 0)
log_debug_errno(r, "Failed to connect to logind via Varlink, falling back to D-Bus: %m");
else {
r = sd_varlink_set_allow_fd_passing_input(vl, true);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to enable input fd passing on Varlink socket: %m");
r = sd_varlink_set_allow_fd_passing_output(vl, true);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to enable output fd passing on Varlink socket: %m");
r = sd_varlink_set_relative_timeout(vl, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to set relative timeout on Varlink socket: %m");
const char *error_id = NULL;
r = sd_varlink_callbo(
vl,
"io.systemd.Login.CreateSession",
&vreply,
&error_id,
SD_JSON_BUILD_PAIR_UNSIGNED("UID", ur->uid),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Service", c->service),
SD_JSON_BUILD_PAIR("Type", JSON_BUILD_STRING_UNDERSCORIFY(c->type)),
SD_JSON_BUILD_PAIR("Class", JSON_BUILD_STRING_UNDERSCORIFY(c->class)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Desktop", c->desktop),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Seat", c->seat),
SD_JSON_BUILD_PAIR_CONDITION(c->vtnr > 0, "VTNr", SD_JSON_BUILD_UNSIGNED(c->vtnr)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("TTY", c->tty),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Display", c->display),
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));
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r,
"Failed to register session: %s", error_id);
if (streq_ptr(error_id, "io.systemd.Login.AlreadySessionMember")) {
/* We are already in a session, don't do anything */
pam_debug_syslog(handle, debug, "Not creating session: %s", error_id);
goto skip;
}
if (error_id)
return pam_syslog_errno(handle, LOG_ERR, sd_varlink_error_to_errno(error_id, vreply),
"Failed to issue CreateSession() varlink call: %s", error_id);
struct {
const char *id;
const char *runtime_path;
unsigned session_fd_idx;
uid_t uid;
const char *seat;
unsigned vtnr;
bool existing;
} p = {
.session_fd_idx = UINT_MAX,
.uid = UID_INVALID,
};
static const sd_json_dispatch_field dispatch_table[] = {
{ "Id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), SD_JSON_MANDATORY },
{ "RuntimePath", SD_JSON_VARIANT_STRING, json_dispatch_const_path, voffsetof(p, runtime_path), SD_JSON_MANDATORY },
{ "SessionFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, session_fd_idx), SD_JSON_MANDATORY },
{ "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, voffsetof(p, uid), SD_JSON_MANDATORY },
{ "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, seat), 0 },
{ "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, vtnr), 0 },
{}
};
r = sd_json_dispatch(vreply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to parse CreateSession() reply: %m");
session_fd = sd_varlink_peek_fd(vl, p.session_fd_idx);
if (session_fd < 0)
return pam_syslog_errno(handle, LOG_ERR, session_fd, "Failed to extract session fd from CreateSession() reply: %m");
id = p.id;
runtime_path = p.runtime_path;
original_uid = p.uid;
real_seat = p.seat;
real_vtnr = p.vtnr;
existing = false; /* Even on D-Bus logind only returns false these days */
done = true;
}
}
if (!done) {
/* Let's release the D-Bus connection once we are done here, after all the session might live
* quite a long time, and we are not going to process the bus connection in that time, so
* let's better close before the daemon kicks us off because we are not processing
* anything. */
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
/* Talk to logind over the message bus */ /* Talk to logind over the message bus */
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d); r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
pam_debug_syslog(handle, debug, _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
"Asking logind to create session: " r = create_session_message(
"uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s", bus,
ur->uid, getpid_cached(),
strempty(service),
type, class, strempty(desktop),
strempty(seat), vtnr, strempty(tty), strempty(display),
yes_no(remote), strempty(remote_user), strempty(remote_host));
pam_debug_syslog(handle, debug,
"Session limits: "
"memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec));
const SessionContext context = {
.uid = ur->uid,
.pid = 0,
.service = service,
.type = type,
.class = class,
.desktop = desktop,
.seat = seat,
.vtnr = vtnr,
.tty = tty,
.display = display,
.remote = remote,
.remote_user = remote_user,
.remote_host = remote_host,
.memory_max = memory_max,
.tasks_max = tasks_max,
.cpu_weight = cpu_weight,
.io_weight = io_weight,
.runtime_max_sec = runtime_max_sec,
};
r = create_session_message(bus,
handle, handle,
&context, ur,
c,
/* avoid_pidfd = */ false, /* avoid_pidfd = */ false,
&m); &m);
if (r < 0) if (r < 0)
return pam_bus_log_create_error(handle, r); return pam_bus_log_create_error(handle, r);
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply); r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
sd_bus_error_free(&error); sd_bus_error_free(&error);
@ -1142,7 +1200,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
m = sd_bus_message_unref(m); m = sd_bus_message_unref(m);
r = create_session_message(bus, r = create_session_message(bus,
handle, handle,
&context, ur,
c,
/* avoid_pidfd = */ true, /* avoid_pidfd = */ true,
&m); &m);
if (r < 0) if (r < 0)
@ -1155,7 +1214,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
/* We are already in a session, don't do anything */ /* We are already in a session, don't do anything */
pam_debug_syslog(handle, debug, pam_debug_syslog(handle, debug,
"Not creating session: %s", bus_error_message(&error, r)); "Not creating session: %s", bus_error_message(&error, r));
goto success; goto skip;
} }
pam_syslog(handle, LOG_ERR, pam_syslog(handle, LOG_ERR,
@ -1163,23 +1222,25 @@ _public_ PAM_EXTERN int pam_sm_open_session(
return PAM_SESSION_ERR; return PAM_SESSION_ERR;
} }
r = sd_bus_message_read(reply, r = sd_bus_message_read(
reply,
"soshusub", "soshusub",
&id, &id,
&object_path, &object_path,
&runtime_path, &runtime_path,
&session_fd, &session_fd,
&original_uid, &original_uid,
&seat, &real_seat,
&vtnr, &real_vtnr,
&existing); &existing);
if (r < 0) if (r < 0)
return pam_bus_log_parse_error(handle, r); return pam_bus_log_parse_error(handle, r);
}
pam_debug_syslog(handle, debug, pam_debug_syslog(handle, debug,
"Reply from logind: " "Reply from logind: "
"id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u", "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
id, object_path, runtime_path, session_fd, seat, vtnr, original_uid); id, strna(object_path), runtime_path, session_fd, real_seat, real_vtnr, original_uid);
/* Please update manager_default_environment() in core/manager.c accordingly if more session envvars /* Please update manager_default_environment() in core/manager.c accordingly if more session envvars
* shall be added. */ * shall be added. */
@ -1202,38 +1263,25 @@ _public_ PAM_EXTERN int pam_sm_open_session(
* somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
* data is inherited into the session processes, and programs can rely on them to be initialized. */ * data is inherited into the session processes, and programs can rely on them to be initialized. */
r = update_environment(handle, "XDG_SESSION_TYPE", type); r = update_environment(handle, "XDG_SESSION_TYPE", c->type);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
r = update_environment(handle, "XDG_SESSION_CLASS", class); r = update_environment(handle, "XDG_SESSION_CLASS", c->class);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop); r = update_environment(handle, "XDG_SESSION_DESKTOP", c->desktop);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
r = update_environment(handle, "XDG_SEAT", seat); r = update_environment(handle, "XDG_SEAT", real_seat);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
static const char *const propagate[] = { if (real_vtnr > 0) {
"shell.prompt.prefix", "SHELL_PROMPT_PREFIX", char buf[DECIMAL_STR_MAX(real_vtnr)];
"shell.prompt.suffix", "SHELL_PROMPT_SUFFIX", sprintf(buf, "%u", real_vtnr);
"shell.welcome", "SHELL_WELCOME",
NULL
};
STRV_FOREACH_PAIR(k, v, propagate) {
r = propagate_credential_to_environment(handle, *k, *v);
if (r != PAM_SUCCESS)
return r;
}
if (vtnr > 0) {
char buf[DECIMAL_STR_MAX(vtnr)];
sprintf(buf, "%u", vtnr);
r = update_environment(handle, "XDG_VTNR", buf); r = update_environment(handle, "XDG_VTNR", buf);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
@ -1255,9 +1303,118 @@ _public_ PAM_EXTERN int pam_sm_open_session(
TAKE_FD(fd); TAKE_FD(fd);
} }
success: /* Everything worked, hence let's patch in the data we learned. Since 'real_set' points into the
* D-Bus message, let's copy it and return it as a buffer */
char *rs = NULL;
if (real_seat) {
rs = strdup(real_seat);
if (!rs)
return pam_log_oom(handle);
}
c->seat = *ret_seat = rs;
c->vtnr = real_vtnr;
return PAM_SUCCESS;
skip:
*ret_seat = NULL;
return PAM_SUCCESS;
}
static int import_shell_credentials(pam_handle_t *handle) {
static const char *const propagate[] = {
"shell.prompt.prefix", "SHELL_PROMPT_PREFIX",
"shell.prompt.suffix", "SHELL_PROMPT_SUFFIX",
"shell.welcome", "SHELL_WELCOME",
NULL
};
int r;
assert(handle);
STRV_FOREACH_PAIR(k, v, propagate) {
r = propagate_credential_to_environment(handle, *k, *v);
if (r != PAM_SUCCESS)
return r;
}
return PAM_SUCCESS;
}
_public_ PAM_EXTERN int pam_sm_open_session(
pam_handle_t *handle,
int flags,
int argc, const char **argv) {
int r;
assert(handle);
pam_log_setup();
uint64_t default_capability_bounding_set = UINT64_MAX, default_capability_ambient_set = UINT64_MAX;
const char *class_pam = NULL, *type_pam = NULL, *desktop_pam = NULL;
bool debug = false;
if (parse_argv(handle,
argc, argv,
&class_pam,
&type_pam,
&desktop_pam,
&debug,
&default_capability_bounding_set,
&default_capability_ambient_set) < 0)
return PAM_SESSION_ERR;
pam_debug_syslog(handle, debug, "pam-systemd initializing");
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
r = acquire_user_record(handle, &ur);
if (r != PAM_SUCCESS)
return r;
SessionContext c = {};
r = pam_get_item_many(
handle,
PAM_SERVICE, &c.service,
PAM_XDISPLAY, &c.display,
PAM_TTY, &c.tty,
PAM_RUSER, &c.remote_user,
PAM_RHOST, &c.remote_host);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
c.seat = getenv_harder(handle, "XDG_SEAT", NULL);
c.vtnr = getenv_harder_uint32(handle, "XDG_VTNR", 0);
c.type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
c.class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
c.desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
c.incomplete = getenv_harder_bool(handle, "XDG_SESSION_INCOMPLETE", false);
r = pam_get_data_many(
handle,
"systemd.memory_max", &c.memory_max,
"systemd.tasks_max", &c.tasks_max,
"systemd.cpu_weight", &c.cpu_weight,
"systemd.io_weight", &c.io_weight,
"systemd.runtime_max_sec", &c.runtime_max_sec);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM data: @PAMERR@");
session_context_mangle(handle, &c, ur, debug);
_cleanup_free_ char *seat_buffer = NULL;
r = register_session(handle, &c, ur, debug, &seat_buffer);
if (r != PAM_SUCCESS)
return r;
r = import_shell_credentials(handle);
if (r != PAM_SUCCESS)
return r;
if (default_capability_ambient_set == UINT64_MAX) if (default_capability_ambient_set == UINT64_MAX)
default_capability_ambient_set = pick_default_capability_ambient_set(ur, service, seat); default_capability_ambient_set = pick_default_capability_ambient_set(ur, c.service, c.seat);
return apply_user_record_settings(handle, ur, debug, default_capability_bounding_set, default_capability_ambient_set); return apply_user_record_settings(handle, ur, debug, default_capability_bounding_set, default_capability_ambient_set);
} }
@ -1297,13 +1454,39 @@ _public_ PAM_EXTERN int pam_sm_close_session(
id = pam_getenv(handle, "XDG_SESSION_ID"); id = pam_getenv(handle, "XDG_SESSION_ID");
if (id && !existing) { if (id && !existing) {
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
bool done = false;
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.Login");
if (r < 0)
log_debug_errno(r, "Failed to connect to logind via Varlink, falling back to D-Bus: %m");
else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vreply = NULL;
const char *error_id = NULL;
r = sd_varlink_callbo(
vl,
"io.systemd.Login.ReleaseSession",
/* ret_reply= */ NULL,
&error_id,
SD_JSON_BUILD_PAIR_STRING("Id", id));
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to register session: %s", error_id);
if (error_id)
return pam_syslog_errno(handle, LOG_ERR, sd_varlink_error_to_errno(error_id, vreply),
"Failed to issue ReleaseSession() varlink call: %s", error_id);
done = true;
}
if (!done) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
/* Before we go and close the FIFO we need to tell logind that this is a clean session /* Before we go and close the FIFO we need to tell logind that this is a clean session
* shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */ * shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, NULL); r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
@ -1312,6 +1495,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR, return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR,
"Failed to release session: %s", bus_error_message(&error, r)); "Failed to release session: %s", bus_error_message(&error, r));
} }
}
/* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we /* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
* closed it here it would not have any clue when that is completed. Given that one cannot really * closed it here it would not have any clue when that is completed. Given that one cannot really

View File

@ -1443,6 +1443,7 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
} }
typedef struct LinkReconfigurationData { typedef struct LinkReconfigurationData {
Manager *manager;
Link *link; Link *link;
LinkReconfigurationFlag flags; LinkReconfigurationFlag flags;
sd_bus_message *message; sd_bus_message *message;
@ -1473,6 +1474,12 @@ static void link_reconfiguration_data_destroy_callback(LinkReconfigurationData *
} }
if (!data->counter || *data->counter <= 0) { if (!data->counter || *data->counter <= 0) {
/* Update the state files before replying the bus method. Otherwise,
* systemd-networkd-wait-online following networkctl reload/reconfigure may read an
* outdated state file and wrongly handle an interface is already in the configured
* state. */
(void) manager_clean_all(data->manager);
r = sd_bus_reply_method_return(data->message, NULL); r = sd_bus_reply_method_return(data->message, NULL);
if (r < 0) if (r < 0)
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m"); log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
@ -1521,6 +1528,7 @@ int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_mess
} }
*data = (LinkReconfigurationData) { *data = (LinkReconfigurationData) {
.manager = link->manager,
.link = link_ref(link), .link = link_ref(link),
.flags = flags, .flags = flags,
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */ .message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */

View File

@ -182,6 +182,7 @@ shared_sources = files(
'varlink-io.systemd.Hostname.c', 'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Import.c', 'varlink-io.systemd.Import.c',
'varlink-io.systemd.Journal.c', 'varlink-io.systemd.Journal.c',
'varlink-io.systemd.Login.c',
'varlink-io.systemd.Machine.c', 'varlink-io.systemd.Machine.c',
'varlink-io.systemd.MachineImage.c', 'varlink-io.systemd.MachineImage.c',
'varlink-io.systemd.ManagedOOM.c', 'varlink-io.systemd.ManagedOOM.c',

View File

@ -253,17 +253,17 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
va_list ap; va_list ap;
int r; int r;
assert(handle);
va_start(ap, handle); va_start(ap, handle);
for (;;) { for (;;) {
int item_type = va_arg(ap, int); int item_type = va_arg(ap, int);
if (item_type <= 0) { if (item_type <= 0) {
r = PAM_SUCCESS; r = PAM_SUCCESS;
break; break;
} }
const void **value = ASSERT_PTR(va_arg(ap, const void **)); const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = pam_get_item(handle, item_type, value); r = pam_get_item(handle, item_type, value);
if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
break; break;
@ -273,6 +273,30 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
return r; return r;
} }
int pam_get_data_many_internal(pam_handle_t *handle, ...) {
va_list ap;
int r;
assert(handle);
va_start(ap, handle);
for (;;) {
const char *data_name = va_arg(ap, const char *);
if (!data_name) {
r = PAM_SUCCESS;
break;
}
const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = pam_get_data(handle, data_name, value);
if (!IN_SET(r, PAM_NO_MODULE_DATA, PAM_SUCCESS))
break;
}
va_end(ap);
return r;
}
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) { int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) {
va_list args; va_list args;
int r; int r;

View File

@ -44,7 +44,9 @@ int pam_get_bus_data(pam_handle_t *handle, const char *module_name, PamBusData *
void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status); void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
int pam_get_item_many_internal(pam_handle_t *handle, ...); int pam_get_item_many_internal(pam_handle_t *handle, ...);
#define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1) #define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1)
int pam_get_data_many_internal(pam_handle_t *handle, ...);
#define pam_get_data_many(handle, ...) pam_get_data_many_internal(handle, __VA_ARGS__, NULL)
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5); int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);

View File

@ -0,0 +1,92 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-polkit.h"
#include "varlink-io.systemd.Login.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
SessionType,
SD_VARLINK_DEFINE_ENUM_VALUE(unspecified),
SD_VARLINK_DEFINE_ENUM_VALUE(tty),
SD_VARLINK_DEFINE_ENUM_VALUE(x11),
SD_VARLINK_DEFINE_ENUM_VALUE(wayland),
SD_VARLINK_DEFINE_ENUM_VALUE(mir),
SD_VARLINK_DEFINE_ENUM_VALUE(web));
static SD_VARLINK_DEFINE_ENUM_TYPE(
SessionClass,
SD_VARLINK_DEFINE_ENUM_VALUE(user),
SD_VARLINK_DEFINE_ENUM_VALUE(user_early),
SD_VARLINK_DEFINE_ENUM_VALUE(user_incomplete),
SD_VARLINK_DEFINE_ENUM_VALUE(greeter),
SD_VARLINK_DEFINE_ENUM_VALUE(lock_screen),
SD_VARLINK_DEFINE_ENUM_VALUE(background),
SD_VARLINK_DEFINE_ENUM_VALUE(background_light),
SD_VARLINK_DEFINE_ENUM_VALUE(manager),
SD_VARLINK_DEFINE_ENUM_VALUE(manager_early));
static SD_VARLINK_DEFINE_METHOD(
CreateSession,
SD_VARLINK_FIELD_COMMENT("Numeric UNIX UID of the user this session shall be owned by"),
SD_VARLINK_DEFINE_INPUT(UID, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("PAM service name of the program requesting the session"),
SD_VARLINK_DEFINE_INPUT(Service, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The type of session"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(Type, SessionType, 0),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(Class, SessionClass, 0),
SD_VARLINK_DEFINE_INPUT(Desktop, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(Seat, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(VTNr, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(TTY, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(Display, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(Remote, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(RemoteUser, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(RemoteHost, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
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."),
SD_VARLINK_DEFINE_OUTPUT(RuntimePath, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Index into the file descriptor table of this reply with the session tracking fd for this session."),
SD_VARLINK_DEFINE_OUTPUT(SessionFileDescriptor, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The original UID of this session."),
SD_VARLINK_DEFINE_OUTPUT(UID, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The seat this session has been assigned to"),
SD_VARLINK_DEFINE_OUTPUT(Seat, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The virtual terminal number the session has been assigned to"),
SD_VARLINK_DEFINE_OUTPUT(VTNr, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
ReleaseSession,
SD_VARLINK_FIELD_COMMENT("The identifier string of the session to release. If unspecified or 'self', will return the callers session."),
SD_VARLINK_DEFINE_INPUT(Id, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(NoSuchSession);
static SD_VARLINK_DEFINE_ERROR(NoSuchSeat);
static SD_VARLINK_DEFINE_ERROR(AlreadySessionMember);
static SD_VARLINK_DEFINE_ERROR(VirtualTerminalAlreadyTaken);
static SD_VARLINK_DEFINE_ERROR(TooManySessions);
static SD_VARLINK_DEFINE_ERROR(UnitAllocationFailed);
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_Login,
"io.systemd.Login",
SD_VARLINK_INTERFACE_COMMENT("APIs for managing login sessions."),
SD_VARLINK_SYMBOL_COMMENT("Various types of sessions"),
&vl_type_SessionType,
SD_VARLINK_SYMBOL_COMMENT("Various classes of sessions"),
&vl_type_SessionClass,
SD_VARLINK_SYMBOL_COMMENT("Allocates a new session."),
&vl_method_CreateSession,
SD_VARLINK_SYMBOL_COMMENT("Releases an existing session. Currently, will be refuses unless originating from the session to release itself."),
&vl_method_ReleaseSession,
SD_VARLINK_SYMBOL_COMMENT("No session by this name found"),
&vl_error_NoSuchSession,
SD_VARLINK_SYMBOL_COMMENT("No seat by this name found"),
&vl_error_NoSuchSeat,
SD_VARLINK_SYMBOL_COMMENT("Process already member of a session"),
&vl_error_AlreadySessionMember,
SD_VARLINK_SYMBOL_COMMENT("The specified virtual terminal (VT) is already taken by another session"),
&vl_error_VirtualTerminalAlreadyTaken,
SD_VARLINK_SYMBOL_COMMENT("Maximum number of sessions reached"),
&vl_error_TooManySessions,
SD_VARLINK_SYMBOL_COMMENT("Failed to allocate a unit for the session"),
&vl_error_UnitAllocationFailed);

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_Login;

View File

@ -70,6 +70,8 @@ __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_server_flags_t) {
SD_VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */ SD_VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */
SD_VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from sd_varlink_server userdata */ SD_VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from sd_varlink_server userdata */
SD_VARLINK_SERVER_INPUT_SENSITIVE = 1 << 4, /* Automatically mark all connection input as sensitive */ SD_VARLINK_SERVER_INPUT_SENSITIVE = 1 << 4, /* Automatically mark all connection input as sensitive */
SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT = 1 << 5, /* Allow receiving fds over all connections */
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT = 1 << 6, /* Allow sending fds over all connections */
_SD_ENUM_FORCE_S64(SD_VARLINK_SERVER) _SD_ENUM_FORCE_S64(SD_VARLINK_SERVER)
} sd_varlink_server_flags_t; } sd_varlink_server_flags_t;

View File

@ -212,8 +212,8 @@ TEST(proc) {
cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid.pid, &path); cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid.pid, &path);
cg_pid_get_path_shifted(pid.pid, NULL, &path_shifted); cg_pid_get_path_shifted(pid.pid, NULL, &path_shifted);
cg_pid_get_owner_uid(pid.pid, &uid); cg_pidref_get_owner_uid(&pid, &uid);
cg_pid_get_session(pid.pid, &session); cg_pidref_get_session(&pid, &session);
cg_pid_get_unit(pid.pid, &unit); cg_pid_get_unit(pid.pid, &unit);
cg_pid_get_user_unit(pid.pid, &user_unit); cg_pid_get_user_unit(pid.pid, &user_unit);
cg_pid_get_machine_name(pid.pid, &machine); cg_pid_get_machine_name(pid.pid, &machine);

View File

@ -14,6 +14,7 @@
#include "varlink-io.systemd.Credentials.h" #include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Import.h" #include "varlink-io.systemd.Import.h"
#include "varlink-io.systemd.Journal.h" #include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.Login.h"
#include "varlink-io.systemd.Machine.h" #include "varlink-io.systemd.Machine.h"
#include "varlink-io.systemd.MachineImage.h" #include "varlink-io.systemd.MachineImage.h"
#include "varlink-io.systemd.ManagedOOM.h" #include "varlink-io.systemd.ManagedOOM.h"
@ -193,6 +194,8 @@ TEST(parse_format) {
print_separator(); print_separator();
test_parse_format_one(&vl_interface_io_systemd_MachineImage); test_parse_format_one(&vl_interface_io_systemd_MachineImage);
print_separator(); print_separator();
test_parse_format_one(&vl_interface_io_systemd_Login);
print_separator();
test_parse_format_one(&vl_interface_xyz_test); test_parse_format_one(&vl_interface_xyz_test);
} }

View File

@ -6406,11 +6406,11 @@ class NetworkdRATests(unittest.TestCase, Utilities):
for i in [100, 200, 300, 512, 1024, 2048]: for i in [100, 200, 300, 512, 1024, 2048]:
if i not in [metric_1, metric_2]: if i not in [metric_1, metric_2]:
self.assertNotIn(f'{i}', output) self.assertNotIn(f'metric {i} ', output)
for i in ['low', 'medium', 'high']: for i in ['low', 'medium', 'high']:
if i not in [preference_1, preference_2]: if i not in [preference_1, preference_2]:
self.assertNotIn(f'{i}', output) self.assertNotIn(f'pref {i}', output)
def test_router_preference(self): def test_router_preference(self):
copy_network_unit('25-veth-client.netdev', copy_network_unit('25-veth-client.netdev',