out u vtnr,
out b existing);
@org.freedesktop.systemd1.Privileged("true")
+ CreateSessionWithPIDFD(in u uid,
+ in h pidfd,
+ in s service,
+ in s type,
+ in s class,
+ in s desktop,
+ in s seat_id,
+ in u vtnr,
+ in s tty,
+ in s display,
+ in b remote,
+ in s remote_user,
+ in s remote_host,
+ in t flags,
+ in a(sv) properties,
+ out s session_id,
+ out o object_path,
+ out s runtime_path,
+ out h fifo_fd,
+ out u uid,
+ out s seat_id,
+ out u vtnr,
+ out b existing);
+ @org.freedesktop.systemd1.Privileged("true")
ReleaseSession(in s session_id);
ActivateSession(in s session_id);
ActivateSessionOnSeat(in s session_id,
<variablelist class="dbus-method" generated="True" extra-ref="CreateSession()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="CreateSessionWithPIDFD()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="ReleaseSession()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ActivateSession()"/>
structures consisting of <varname>what</varname>, <varname>who</varname>, <varname>why</varname>,
<varname>mode</varname>, <varname>uid</varname> (user ID), and <varname>pid</varname> (process ID).</para>
- <para><function>CreateSession()</function> and <function>ReleaseSession()</function> may be used to
- open or close login sessions. These calls should <emphasis>never</emphasis> be invoked directly by
- clients. Creating/closing sessions is exclusively the job of PAM and its
- <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <para><function>CreateSession()</function>, <function>CreateSessionWithPIDFD()</function>, and
+ <function>ReleaseSession()</function> may be used to open or close login sessions. These calls should
+ <emphasis>never</emphasis> be invoked directly by clients. Creating/closing sessions is exclusively the job
+ of PAM and its <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
module.</para>
<para><function>ActivateSession()</function> brings the session with the specified ID into the
<varname>HandleSuspendKeyLongPress</varname>, and
<varname>HandleHibernateKeyLongPress</varname> were added in version 251.</para>
<para><varname>StopIdleSessionUSec</varname> was added in version 252.</para>
- <para><function>PrepareForShutdownWithMetadata</function> was added in version 255.</para>
+ <para><function>PrepareForShutdownWithMetadata</function> and
+ <function>CreateSessionWithPIDFD()</function> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Session Objects</title>
return sd_bus_send(NULL, reply, NULL);
}
-static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
+static int create_session(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error,
+ uid_t uid,
+ pid_t pid,
+ int 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) {
+
+ _cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
+ Manager *m = ASSERT_PTR(userdata);
_cleanup_free_ char *id = NULL;
Session *session = NULL;
uint32_t audit_id = 0;
- Manager *m = ASSERT_PTR(userdata);
User *user = NULL;
Seat *seat = NULL;
- pid_t leader;
- uid_t uid;
- int remote;
- uint32_t vtnr = 0;
SessionType t;
SessionClass c;
int r;
assert(message);
- assert_cc(sizeof(pid_t) == sizeof(uint32_t));
- assert_cc(sizeof(uid_t) == sizeof(uint32_t));
-
- r = sd_bus_message_read(message, "uusssssussbss",
- &uid, &leader, &service, &type, &class, &desktop, &cseat,
- &vtnr, &tty, &display, &remote, &remote_user, &remote_host);
- if (r < 0)
- return r;
-
if (!uid_is_valid(uid))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
- if (leader < 0 || leader == 1 || leader == getpid_cached())
+
+ if (flags != 0)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
+
+ if (pidfd >= 0) {
+ r = pidref_set_pidfd(&leader, pidfd);
+ if (r < 0)
+ return r;
+ } else if (pid == 0) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ pid_t p;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &p);
+ if (r < 0)
+ return r;
+
+ r = pidref_set_pid(&leader, p);
+ if (r < 0)
+ return r;
+ } else {
+ assert(pid > 0);
+
+ r = pidref_set_pid(&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))
c = SESSION_USER;
}
- if (leader == 0) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
- if (r < 0)
- return r;
-
- r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
- if (r < 0)
- return r;
- }
-
/* Check if we are already in a logind session. Or if we are in user@.service
* which is a special PAM session that avoids creating a logind session. */
- r = manager_get_user_by_pid(m, leader, NULL);
+ r = manager_get_user_by_pid(m, leader.pid, NULL);
if (r < 0)
return r;
if (r > 0)
"Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.",
m->sessions_max);
- (void) audit_session_from_pid(leader, &audit_id);
+ (void) audit_session_from_pid(leader.pid, &audit_id);
if (audit_session_is_valid(audit_id)) {
/* Keep our session IDs and the audit session IDs in sync */
goto fail;
session_set_user(session, user);
- r = session_set_leader(session, leader);
+ r = session_set_leader_consume(session, TAKE_PIDREF(leader));
if (r < 0)
goto fail;
return r;
}
+static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
+ pid_t leader;
+ uid_t uid;
+ int remote;
+ uint32_t vtnr = 0;
+ int r;
+
+ assert(message);
+
+ assert_cc(sizeof(pid_t) == sizeof(uint32_t));
+ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+
+ r = sd_bus_message_read(message,
+ "uusssssussbss",
+ &uid,
+ &leader,
+ &service,
+ &type,
+ &class,
+ &desktop,
+ &cseat,
+ &vtnr,
+ &tty,
+ &display,
+ &remote,
+ &remote_user,
+ &remote_host);
+ if (r < 0)
+ return r;
+
+ return create_session(
+ message,
+ userdata,
+ error,
+ uid,
+ leader,
+ /* pidfd = */ -EBADF,
+ service,
+ type,
+ class,
+ desktop,
+ cseat,
+ vtnr,
+ tty,
+ display,
+ remote,
+ remote_user,
+ remote_host,
+ /* flags = */ 0);
+}
+
+static int method_create_session_pidfd(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop;
+ int leaderfd = -EBADF;
+ uid_t uid;
+ int remote;
+ uint32_t vtnr = 0;
+ uint64_t flags;
+ int r;
+
+ r = sd_bus_message_read(message,
+ "uhsssssussbsst",
+ &uid,
+ &leaderfd,
+ &service,
+ &type,
+ &class,
+ &desktop,
+ &cseat,
+ &vtnr,
+ &tty,
+ &display,
+ &remote,
+ &remote_user,
+ &remote_host,
+ &flags);
+ if (r < 0)
+ return r;
+
+ return create_session(
+ message,
+ userdata,
+ error,
+ uid,
+ /* pid = */ 0,
+ leaderfd,
+ service,
+ type,
+ class,
+ desktop,
+ cseat,
+ vtnr,
+ tty,
+ display,
+ remote,
+ remote_user,
+ remote_host,
+ flags);
+}
+
static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
Session *session;
"b", existing),
method_create_session,
0),
+ SD_BUS_METHOD_WITH_ARGS("CreateSessionWithPIDFD",
+ SD_BUS_ARGS("u", uid,
+ "h", pidfd,
+ "s", service,
+ "s", type,
+ "s", class,
+ "s", desktop,
+ "s", seat_id,
+ "u", vtnr,
+ "s", tty,
+ "s", display,
+ "b", remote,
+ "s", remote_user,
+ "s", remote_host,
+ "t", flags,
+ "a(sv)", properties),
+ SD_BUS_RESULT("s", session_id,
+ "o", object_path,
+ "s", runtime_path,
+ "h", fifo_fd,
+ "u", uid,
+ "s", seat_id,
+ "u", vtnr,
+ "b", existing),
+ method_create_session_pidfd,
+ 0),
SD_BUS_METHOD_WITH_ARGS("ReleaseSession",
SD_BUS_ARGS("s", session_id),
SD_BUS_NO_RESULT,
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_close_ int fifo_fd = -EBADF;
_cleanup_free_ char *p = NULL;
+ int r;
assert(s);
if (fifo_fd < 0)
return fifo_fd;
+ r = session_watch_pidfd(s);
+ if (r < 0)
+ return r;
+
/* Update the session state file before we notify the client about the result. */
session_save(s);
user_update_last_session_timer(u);
}
-int session_set_leader(Session *s, pid_t pid) {
- _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+int session_set_leader_consume(Session *s, PidRef _leader) {
+ _cleanup_(pidref_done) PidRef pidref = _leader;
int r;
assert(s);
+ assert(pidref_is_set(&pidref));
- if (!pid_is_valid(pid))
- return -EINVAL;
-
- if (s->leader.pid == pid)
+ if (pidref_equal(&s->leader, &pidref))
return 0;
- r = pidref_set_pid(&pidref, pid);
- if (r < 0)
- return r;
-
r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pidref.pid), s);
if (r < 0)
return r;
}
if (leader) {
- pid_t pid;
+ _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
- r = parse_pid(leader, &pid);
+ r = pidref_set_pidstr(&p, leader);
if (r < 0)
log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
else {
- r = session_set_leader(s, pid);
+ r = session_set_leader_consume(s, TAKE_PIDREF(p));
if (r < 0)
log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
}
return 0;
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source);
if (s->seat)
seat_evict_position(s->seat, s);
LOG_MESSAGE("Removed session %s.", s->id));
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ s->leader_pidfd_event_source = sd_event_source_unref(s->leader_pidfd_event_source);
if (s->seat)
seat_evict_position(s->seat, s);
seat_save(s->seat);
}
+ pidref_done(&s->leader);
+
user_save(s->user);
user_send_changed(s->user, "Display", NULL);
}
}
+static int session_dispatch_leader_pidfd(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+ Session *s = ASSERT_PTR(userdata);
+
+ assert(s->leader.fd == fd);
+ session_stop(s, /* force= */ false);
+
+ return 1;
+}
+
+int session_watch_pidfd(Session *s) {
+ int r;
+
+ assert(s);
+
+ if (s->leader.fd < 0)
+ return 0;
+
+ r = sd_event_add_io(s->manager->event, &s->leader_pidfd_event_source, s->leader.fd, EPOLLIN, session_dispatch_leader_pidfd, s);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(s->leader_pidfd_event_source, SD_EVENT_PRIORITY_IMPORTANT);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s->leader_pidfd_event_source, "session-pidfd");
+
+ return 0;
+}
+
bool session_may_gc(Session *s, bool drop_not_started) {
int r;
if (!s->user)
return true;
+ r = pidref_is_alive(&s->leader);
+ if (r < 0)
+ log_debug_errno(r, "Unable to determine if leader PID " PID_FMT " is still alive, assuming not.", s->leader.pid);
+ if (r > 0)
+ return false;
+
if (s->fifo_fd >= 0) {
if (pipe_eof(s->fifo_fd) <= 0)
return false;
if (s->stopping || s->timer_event_source)
return SESSION_CLOSING;
- if (s->scope_job || s->fifo_fd < 0)
+ if (s->scope_job || (!pidref_is_set(&s->leader) && s->fifo_fd < 0))
return SESSION_OPENING;
if (session_is_active(s))
char *fifo_path;
sd_event_source *fifo_event_source;
+ sd_event_source *leader_pidfd_event_source;
bool idle_hint;
dual_timestamp idle_hint_timestamp;
DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free);
void session_set_user(Session *s, User *u);
-int session_set_leader(Session *s, pid_t pid);
+int session_set_leader_consume(Session *s, PidRef _leader);
bool session_may_gc(Session *s, bool drop_not_started);
void session_add_to_gc_queue(Session *s);
int session_activate(Session *s);
int session_set_display(Session *s, const char *display);
int session_set_tty(Session *s, const char *tty);
int session_create_fifo(Session *s);
+int session_watch_pidfd(Session *s);
int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
int session_stop(Session *s, bool force);
int session_finalize(Session *s);
#include "locale-util.h"
#include "login-util.h"
#include "macro.h"
+#include "missing_syscall.h"
#include "pam-util.h"
#include "parse-util.h"
#include "path-util.h"
(streq_ptr(service, "systemd-user") || !isempty(seat)) ? (UINT64_C(1) << CAP_WAKE_ALARM) : UINT64_MAX;
}
+typedef struct SessionContext {
+ const uid_t uid;
+ const pid_t pid;
+ const char *service;
+ const char *type;
+ const char *class;
+ const char *desktop;
+ const char *seat;
+ const uint32_t vtnr;
+ const char *tty;
+ const char *display;
+ const bool remote;
+ const char *remote_user;
+ const char *remote_host;
+ const char *memory_max;
+ const char *tasks_max;
+ const char *cpu_weight;
+ const char *io_weight;
+ const char *runtime_max_sec;
+} SessionContext;
+
+static int create_session_message(sd_bus *bus, pam_handle_t *handle, const SessionContext *context, bool avoid_pidfd, sd_bus_message **ret) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ int r, pidfd = -EBADFD;
+
+ assert(bus);
+ assert(handle);
+ assert(context);
+
+ if (!avoid_pidfd) {
+ pidfd = pidfd_open(getpid_cached(), 0);
+ if (pidfd < 0 && !ERRNO_IS_NOT_SUPPORTED(errno))
+ return -errno;
+ }
+
+ r = bus_message_new_method_call(bus, &m, bus_login_mgr, pidfd >= 0 ? "CreateSessionWithPIDFD" : "CreateSession");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m,
+ pidfd >= 0 ? "uhsssssussbss" : "uusssssussbss",
+ (uint32_t) context->uid,
+ pidfd >= 0 ? pidfd : context->pid,
+ context->service,
+ context->type,
+ context->class,
+ context->desktop,
+ context->seat,
+ context->vtnr,
+ context->tty,
+ context->display,
+ context->remote,
+ context->remote_user,
+ context->remote_host);
+ if (r < 0)
+ return r;
+
+ if (pidfd >= 0) {
+ r = sd_bus_message_append(m, "t", UINT64_C(0));
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return r;
+
+ r = append_session_memory_max(handle, m, context->memory_max);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = append_session_runtime_max_sec(handle, m, context->runtime_max_sec);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = append_session_tasks_max(handle, m, context->tasks_max);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = append_session_cpu_weight(handle, m, context->cpu_weight);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = append_session_io_weight(handle, m, context->io_weight);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
_public_ PAM_EXTERN int pam_sm_open_session(
pam_handle_t *handle,
int flags,
"memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec));
- r = bus_message_new_method_call(bus, &m, bus_login_mgr, "CreateSession");
- if (r < 0)
- return pam_bus_log_create_error(handle, r);
-
- r = sd_bus_message_append(m, "uusssssussbss",
- (uint32_t) ur->uid,
- 0,
- service,
- type,
- class,
- desktop,
- seat,
- vtnr,
- tty,
- display,
- remote,
- remote_user,
- remote_host);
- if (r < 0)
- return pam_bus_log_create_error(handle, r);
-
- r = sd_bus_message_open_container(m, 'a', "(sv)");
- if (r < 0)
- return pam_bus_log_create_error(handle, r);
-
- r = append_session_memory_max(handle, m, memory_max);
- if (r != PAM_SUCCESS)
- return r;
-
- r = append_session_runtime_max_sec(handle, m, runtime_max_sec);
- if (r != PAM_SUCCESS)
- return r;
-
- r = append_session_tasks_max(handle, m, tasks_max);
- if (r != PAM_SUCCESS)
- return r;
-
- r = append_session_cpu_weight(handle, m, cpu_weight);
- if (r != PAM_SUCCESS)
- return r;
-
- r = append_session_io_weight(handle, m, io_weight);
- if (r != PAM_SUCCESS)
- return r;
-
- r = sd_bus_message_close_container(m);
+ const SessionContext context = {
+ .uid = ur->uid,
+ .pid = 0,
+ .service = service,
+ .type = type,
+ .class = class,
+ .desktop = desktop,
+ .seat = seat,
+ .vtnr = vtnr,
+ .tty = tty,
+ .display = display,
+ .remote = remote,
+ .remote_user = remote_user,
+ .remote_host = remote_host,
+ .memory_max = memory_max,
+ .tasks_max = tasks_max,
+ .cpu_weight = cpu_weight,
+ .io_weight = io_weight,
+ .runtime_max_sec = runtime_max_sec,
+ };
+
+ r = create_session_message(bus,
+ handle,
+ &context,
+ false /* avoid_pidfd = */,
+ &m);
if (r < 0)
return pam_bus_log_create_error(handle, r);
"Not creating session: %s", bus_error_message(&error, r));
/* We are already in a session, don't do anything */
goto success;
- } else {
+ } else if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ pam_debug_syslog(handle, debug,
+ "CreateSessionWithPIDFD() API is not available, retrying with CreateSession().");
+
+ m = sd_bus_message_unref(m);
+ r = create_session_message(bus,
+ handle,
+ &context,
+ true /* avoid_pidfd = */,
+ &m);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ sd_bus_error_free(&error);
+ r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+ }
+ if (r < 0) {
pam_syslog(handle, LOG_ERR,
"Failed to create session: %s", bus_error_message(&error, r));
return PAM_SESSION_ERR;