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',
) )

File diff suppressed because it is too large Load Diff

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

@ -65,11 +65,13 @@ __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_method_flags_t) {
} sd_varlink_method_flags_t; } sd_varlink_method_flags_t;
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_server_flags_t) { __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_server_flags_t) {
SD_VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */ SD_VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */
SD_VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */ SD_VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */
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',