diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 2bdf1135778..433c59500a9 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -29,6 +29,7 @@ #include "logind-session-dbus.h" #include "logind-session.h" #include "logind-user-dbus.h" +#include "logind-varlink.h" #include "mkdir-label.h" #include "parse-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->upgrade_message); + sd_varlink_unref(s->create_link); + free(s->tty); free(s->display); free(s->remote_host); @@ -1662,6 +1665,8 @@ bool session_job_pending(Session *s) { } 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 @@ -1669,7 +1674,10 @@ int session_send_create_reply(Session *s, const sd_bus_error *error) { if (!sd_bus_error_is_set(error) && session_job_pending(s)) return 0; - return session_send_create_reply_bus(s, error); + 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] = { diff --git a/src/login/logind-session.h b/src/login/logind-session.h index fc6a66e2175..9ec19ba53ec 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -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 *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 */ sd_event_source *timer_event_source; diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c new file mode 100644 index 00000000000..8f060f67af4 --- /dev/null +++ b/src/login/logind-varlink.c @@ -0,0 +1,383 @@ +/* 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); + +typedef struct CreateSessionParameters { + uid_t uid; + PidRef pid; + 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; +} CreateSessionParameters; + +static void create_session_parameters_done(CreateSessionParameters *p) { + pidref_done(&p->pid); +} + +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; + + static const sd_json_dispatch_field dispatch_table[] = { + { "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(CreateSessionParameters, uid), SD_JSON_MANDATORY }, + { "PID", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, offsetof(CreateSessionParameters, pid), SD_JSON_RELAX }, + { "Service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, service), 0 }, + { "Type", SD_JSON_VARIANT_STRING, json_dispatch_session_type, offsetof(CreateSessionParameters, type), SD_JSON_MANDATORY }, + { "Class", SD_JSON_VARIANT_STRING, json_dispatch_session_class, offsetof(CreateSessionParameters, class), SD_JSON_MANDATORY }, + { "Desktop", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, desktop), SD_JSON_STRICT }, + { "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, seat), 0 }, + { "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(CreateSessionParameters, vtnr), 0 }, + { "TTY", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, tty), 0 }, + { "Display", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, display), 0 }, + { "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(CreateSessionParameters, remote), 0 }, + { "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_user), 0 }, + { "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_host), 0 }, + {} + }; + + _cleanup_(create_session_parameters_done) CreateSessionParameters p = { + .uid = UID_INVALID, + .pid = PIDREF_NULL, + .class = _SESSION_CLASS_INVALID, + .type = _SESSION_TYPE_INVALID, + .remote = -1, + }; + + 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); + + if (!pidref_is_set(&p.pid)) { + r = varlink_get_peer_pidref(link, &p.pid); + if (r < 0) + return log_debug_errno(r, "Failed to get peer pidref: %m"); + } + + Session *session; + r = manager_create_session( + m, + p.uid, + &p.pid, + 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, NULL); +} + +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); +} diff --git a/src/login/logind-varlink.h b/src/login/logind-varlink.h new file mode 100644 index 00000000000..bcf2af4fedd --- /dev/null +++ b/src/login/logind-varlink.h @@ -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); diff --git a/src/login/logind.c b/src/login/logind.c index 186778677b6..ef1952b0cca 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -24,11 +24,12 @@ #include "fd-util.h" #include "format-util.h" #include "fs-util.h" +#include "logind.h" #include "logind-dbus.h" #include "logind-seat-dbus.h" #include "logind-session-dbus.h" #include "logind-user-dbus.h" -#include "logind.h" +#include "logind-varlink.h" #include "main-func.h" #include "mkdir-label.h" #include "parse-util.h" @@ -154,6 +155,8 @@ static Manager* manager_free(Manager *m) { hashmap_free(m->polkit_registry); + manager_varlink_done(m); + sd_bus_flush_close_unref(m->bus); sd_event_unref(m->event); @@ -1115,6 +1118,10 @@ static int manager_startup(Manager *m) { if (r < 0) return r; + r = manager_varlink_init(m); + if (r < 0) + return r; + /* Instantiate magic seat 0 */ r = manager_add_seat(m, "seat0", &m->seat0); if (r < 0) diff --git a/src/login/logind.h b/src/login/logind.h index ce7e76e761b..b19fbb7f3e0 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -7,6 +7,7 @@ #include "sd-bus.h" #include "sd-device.h" #include "sd-event.h" +#include "sd-varlink.h" #include "calendarspec.h" #include "conf-parser.h" @@ -147,6 +148,8 @@ struct Manager { CalendarSpec *maintenance_time; dual_timestamp init_ts; + + sd_varlink_server *varlink_server; }; void manager_reset_config(Manager *m); diff --git a/src/login/meson.build b/src/login/meson.build index 43db03184c5..86879b47237 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -26,6 +26,7 @@ liblogind_core_sources = files( 'logind-session.c', 'logind-user-dbus.c', 'logind-user.c', + 'logind-varlink.c', 'logind-wall.c', ) diff --git a/src/shared/meson.build b/src/shared/meson.build index af9ef74b329..7a79d26b0f3 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -182,6 +182,7 @@ shared_sources = files( 'varlink-io.systemd.Hostname.c', 'varlink-io.systemd.Import.c', 'varlink-io.systemd.Journal.c', + 'varlink-io.systemd.Login.c', 'varlink-io.systemd.Machine.c', 'varlink-io.systemd.MachineImage.c', 'varlink-io.systemd.ManagedOOM.c', diff --git a/src/shared/varlink-io.systemd.Login.c b/src/shared/varlink-io.systemd.Login.c new file mode 100644 index 00000000000..722a2b55b48 --- /dev/null +++ b/src/shared/varlink-io.systemd.Login.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bus-polkit.h" +#include "varlink-idl-common.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_FIELD_COMMENT("Regular user sessions"), + SD_VARLINK_DEFINE_ENUM_VALUE(user), + SD_VARLINK_FIELD_COMMENT("Session of the root user that shall be open for login from earliest moment on, and not be delayed for /run/nologin"), + SD_VARLINK_DEFINE_ENUM_VALUE(user_early), + SD_VARLINK_FIELD_COMMENT("Regular user session whose home directory is not available right now, but will be later, at which point the session class can be upgraded to 'user'"), + SD_VARLINK_DEFINE_ENUM_VALUE(user_incomplete), + SD_VARLINK_FIELD_COMMENT("Display manager greeter screen used for login"), + SD_VARLINK_DEFINE_ENUM_VALUE(greeter), + SD_VARLINK_FIELD_COMMENT("Similar, but a a lock screen"), + SD_VARLINK_DEFINE_ENUM_VALUE(lock_screen), + SD_VARLINK_FIELD_COMMENT("Background session (that has no TTY, VT, Seat)"), + SD_VARLINK_DEFINE_ENUM_VALUE(background), + SD_VARLINK_FIELD_COMMENT("Similar, but for which no service manager is invoked"), + SD_VARLINK_DEFINE_ENUM_VALUE(background_light), + SD_VARLINK_FIELD_COMMENT("The special session of the service manager"), + SD_VARLINK_DEFINE_ENUM_VALUE(manager), + SD_VARLINK_FIELD_COMMENT("The special session of the service manager for the root user"), + 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("Process that shall become the leader of the session. If null defaults to the IPC client."), + SD_VARLINK_DEFINE_INPUT_BY_TYPE(PID, ProcessId, SD_VARLINK_NULLABLE), + 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 the session"), + SD_VARLINK_DEFINE_INPUT_BY_TYPE(Type, SessionType, 0), + SD_VARLINK_FIELD_COMMENT("The class of the session"), + SD_VARLINK_DEFINE_INPUT_BY_TYPE(Class, SessionClass, 0), + SD_VARLINK_FIELD_COMMENT("An identifier for the chosen desktop"), + SD_VARLINK_DEFINE_INPUT(Desktop, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The name of the seat to assign this session to"), + SD_VARLINK_DEFINE_INPUT(Seat, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The virtual terminal number to assign this session to"), + SD_VARLINK_DEFINE_INPUT(VTNr, SD_VARLINK_INT, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The TTY device to assign this session to, if applicable"), + SD_VARLINK_DEFINE_INPUT(TTY, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("The X11 display for this session"), + SD_VARLINK_DEFINE_INPUT(Display, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("If true this is a remote session"), + SD_VARLINK_DEFINE_INPUT(Remote, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("User name on the remote site, if known"), + SD_VARLINK_DEFINE_INPUT(RemoteUser, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Host name of the remote host"), + SD_VARLINK_DEFINE_INPUT(RemoteHost, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("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("Process identifier"), + &vl_type_ProcessId, + 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); diff --git a/src/shared/varlink-io.systemd.Login.h b/src/shared/varlink-io.systemd.Login.h new file mode 100644 index 00000000000..5453cdfb25f --- /dev/null +++ b/src/shared/varlink-io.systemd.Login.h @@ -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; diff --git a/src/test/test-varlink-idl.c b/src/test/test-varlink-idl.c index 182d59bd206..fc319dc2cce 100644 --- a/src/test/test-varlink-idl.c +++ b/src/test/test-varlink-idl.c @@ -14,6 +14,7 @@ #include "varlink-io.systemd.Credentials.h" #include "varlink-io.systemd.Import.h" #include "varlink-io.systemd.Journal.h" +#include "varlink-io.systemd.Login.h" #include "varlink-io.systemd.Machine.h" #include "varlink-io.systemd.MachineImage.h" #include "varlink-io.systemd.ManagedOOM.h" @@ -194,6 +195,8 @@ TEST(parse_format) { print_separator(); test_parse_format_one(&vl_interface_io_systemd_MachineImage); print_separator(); + test_parse_format_one(&vl_interface_io_systemd_Login); + print_separator(); test_parse_format_one(&vl_interface_xyz_test); } diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh index 80320e32e17..2ecb3acca68 100755 --- a/test/units/TEST-35-LOGIN.sh +++ b/test/units/TEST-35-LOGIN.sh @@ -739,6 +739,10 @@ EOF systemctl stop user@"$uid".service } +testcase_varlink() { + varlinkctl introspect /run/systemd/io.systemd.Login +} + setup_test_user test_write_dropin run_testcases