-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
#include <errno.h>
#include <pwd.h>
#include "audit-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
+//#include "bus-unit-util.h"
#include "bus-util.h"
#include "dirent-util.h"
//#include "efivars.h"
#include "mkdir.h"
#include "path-util.h"
#include "process-util.h"
+//#include "cgroup-util.h"
#include "selinux-util.h"
#include "sleep-config.h"
//#include "special.h"
/// Additional includes needed by elogind
#include "elogind-dbus.h"
-int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
+static int get_sender_session(Manager *m, sd_bus_message *message, sd_bus_error *error, Session **ret) {
+
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ const char *name;
Session *session;
int r;
+ /* Get client login session. This is not what you are looking for these days,
+ * as apps may instead belong to a user service unit. This includes terminal
+ * emulators and hence command-line apps. */
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_session(creds, &name);
+ if (r == -ENXIO)
+ goto err_no_session;
+ if (r < 0)
+ return r;
+
+ session = hashmap_get(m->sessions, name);
+ if (!session)
+ goto err_no_session;
+
+ *ret = session;
+ return 0;
+
+err_no_session:
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
+ "Caller does not belong to any known session");
+}
+
+int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
+ Session *session;
+
assert(m);
assert(message);
assert(ret);
- if (isempty(name)) {
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
- if (r < 0)
- return r;
-
- r = sd_bus_creds_get_session(creds, &name);
- if (r == -ENXIO)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
- "Caller does not belong to any known session");
- if (r < 0)
- return r;
- }
+ if (isempty(name))
+ return get_sender_session(m, message, error, ret);
session = hashmap_get(m->sessions, name);
if (!session)
return 0;
}
-int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) {
+static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *error, User **ret) {
+
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ uid_t uid;
User *user;
int r;
+ /* Note that we get the owner UID of the session, not the actual client UID here! */
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_owner_uid(creds, &uid);
+ if (r == -ENXIO)
+ goto err_no_user;
+ if (r < 0)
+ return r;
+
+ user = hashmap_get(m->users, UID_TO_PTR(uid));
+ if (!user)
+ goto err_no_user;
+
+ *ret = user;
+ return 0;
+
+err_no_user:
+ return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "Caller does not belong to any logged in user or lingering user");
+}
+
+int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) {
+ User *user;
+
assert(m);
assert(message);
assert(ret);
- if (!uid_is_valid(uid)) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-
- /* Note that we get the owner UID of the session, not the actual client UID here! */
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
- if (r < 0)
- return r;
-
- r = sd_bus_creds_get_owner_uid(creds, &uid);
- if (r < 0)
- return r;
- }
+ if (!uid_is_valid(uid))
+ return get_sender_user(m, message, error, ret);
user = hashmap_get(m->users, UID_TO_PTR(uid));
if (!user)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "No user "UID_FMT" known or logged in", uid);
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "User ID "UID_FMT" is not logged in or lingering", uid);
*ret = user;
return 0;
assert(m);
if (streq(property, "PreparingForShutdown"))
- b = !!(m->action_what & INHIBIT_SHUTDOWN);
+ b = m->action_what & INHIBIT_SHUTDOWN;
else
- b = !!(m->action_what & INHIBIT_SLEEP);
+ b = m->action_what & INHIBIT_SLEEP;
return sd_bus_message_append(reply, "b", b);
}
}
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
-
-static int property_get_docked(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
-
- assert(bus);
- assert(reply);
- assert(m);
-
- return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m));
-}
-
-static int property_get_current_sessions(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
-
- assert(bus);
- assert(reply);
- assert(m);
-
- return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions));
-}
-
-static int property_get_current_inhibitors(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = userdata;
-
- assert(bus);
- assert(reply);
- assert(m);
-
- return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors));
-}
+static BUS_DEFINE_PROPERTY_GET(property_get_docked, "b", Manager, manager_is_docked_or_external_displays);
+static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_compat_user_tasks_max, "t", CGROUP_LIMIT_MAX);
+static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "t", Hashmap *, (uint64_t) hashmap_size);
static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
return sd_bus_reply_method_return(message, "o", p);
}
+/* Get login session of a process. This is not what you are looking for these days,
+ * as apps may instead belong to a user service unit. This includes terminal
+ * emulators and hence command-line apps. */
static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Session *session = NULL;
r = sd_bus_message_read(message, "u", &pid);
if (r < 0)
return r;
- if (!pid_is_valid((pid_t) pid))
+ if (pid < 0)
return -EINVAL;
if (pid == 0) {
r = sd_bus_message_read(message, "u", &pid);
if (r < 0)
return r;
- if (!pid_is_valid((pid_t) pid))
+ if (pid < 0)
return -EINVAL;
if (pid == 0) {
if (r < 0)
return r;
if (!user)
- return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "PID "PID_FMT" does not belong to any known or logged in user", pid);
+ return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID,
+ "PID "PID_FMT" does not belong to any logged in user or lingering user",
+ pid);
}
p = user_bus_path(user);
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;
- uint32_t audit_id = 0;
_cleanup_free_ char *id = NULL;
Session *session = NULL;
+ uint32_t audit_id = 0;
Manager *m = userdata;
User *user = NULL;
Seat *seat = NULL;
if (!uid_is_valid(uid))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
- if (leader < 0 || leader == 1)
+ if (leader < 0 || leader == 1 || leader == getpid_cached())
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (isempty(type))
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)
+ 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");
if (seat) {
if (seat_has_vts(seat)) {
- if (!vtnr || vtnr > 63)
+ 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)
}
}
- r = sd_bus_message_enter_container(message, 'a', "(sv)");
- if (r < 0)
- return r;
-
if (t == _SESSION_TYPE_INVALID) {
if (!isempty(display))
t = SESSION_X11;
return r;
}
- r = manager_get_session_by_pid(m, leader, NULL);
+ /* 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);
+ if (r < 0)
+ return r;
if (r > 0)
- return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session");
+ 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 audit data and let's better register a new
* ID */
if (hashmap_get(m->sessions, id)) {
- log_warning("Existing logind session ID %s used by new audit session, ignoring", id);
+ log_warning("Existing logind session ID %s used by new audit session, ignoring.", id);
audit_id = AUDIT_SESSION_INVALID;
-
id = mfree(id);
}
}
goto fail;
}
- r = session_start(session);
+ r = sd_bus_message_enter_container(message, 'a', "(sv)");
+ if (r < 0)
+ return r;
+
+ r = session_start(session, message);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_message_exit_container(message);
if (r < 0)
goto fail;
session->create_message = sd_bus_message_ref(message);
#if 0 /// UNNEEDED by elogind
- /* Now, let's wait until the slice unit and stuff got
- * created. We send the reply back from
+ /* Now, let's wait until the slice unit and stuff got created. We send the reply back from
* session_send_create_reply(). */
#else
/* We reply directly. */
}
static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ char *cc = NULL;
Manager *m = userdata;
int r, b, interactive;
struct passwd *pw;
const char *path;
- uint32_t uid;
- bool self = false;
+ uint32_t uid, auth_uid;
assert(message);
assert(m);
if (r < 0)
return r;
- if (!uid_is_valid(uid)) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-
- /* Note that we get the owner UID of the session, not the actual client UID here! */
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
- if (r < 0)
- return r;
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID |
+ SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
+ if (r < 0)
+ return r;
+ if (!uid_is_valid(uid)) {
+ /* Note that we get the owner UID of the session or user unit,
+ * not the actual client UID here! */
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r < 0)
return r;
+ }
- self = true;
-
- } else if (!uid_is_valid(uid))
- return -EINVAL;
+ /* owner_uid is racy, so for authorization we must use euid */
+ r = sd_bus_creds_get_euid(creds, &auth_uid);
+ if (r < 0)
+ return r;
errno = 0;
pw = getpwuid(uid);
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
- self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger",
+ uid == auth_uid ? "org.freedesktop.login1.set-self-linger" :
+ "org.freedesktop.login1.set-user-linger",
NULL,
interactive,
UID_INVALID,
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- mkdir_p_label("/var/lib/systemd", 0755);
+ mkdir_p_label("/var/lib/elogind", 0755);
- r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0);
+ r = mkdir_safe_label("/var/lib/elogind/linger", 0755, 0, 0, MKDIR_WARN_MODE);
if (r < 0)
return r;
if (!cc)
return -ENOMEM;
- path = strjoina("/var/lib/systemd/linger/", cc);
+ path = strjoina("/var/lib/elogind/linger/", cc);
if (b) {
User *u;
}
static int trigger_device(Manager *m, struct udev_device *d) {
- _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+ _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
struct udev_list_entry *first, *item;
int r;
if (!t)
return -ENOMEM;
- write_string_file(t, "change", WRITE_STRING_FILE_CREATE);
+ (void) write_string_file(t, "change", 0);
}
return 0;
}
static int attach_device(Manager *m, const char *seat, const char *sysfs) {
- _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_(udev_device_unrefp) struct udev_device *d = NULL;
_cleanup_free_ char *rule = NULL, *file = NULL;
const char *id_for_seat;
int r;
return log_struct(LOG_NOTICE,
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
p,
- q,
- NULL);
+ q);
}
#endif // 0
error:
/* Tell people that they now may take a lock again */
- send_prepare_for(m, m->action_what, false);
+ (void) send_prepare_for(m, w, false);
return r;
}
+#endif // 0
int manager_dispatch_delayed(Manager *manager, bool timeout) {
assert(manager);
+#if 0 /// elogind has no action_job, but a pending_action
if (manager->action_what == 0 || manager->action_job)
+#else
+ if ( (0 == manager->action_what) || (HANDLE_IGNORE == manager->pending_action) )
+#endif // 0
return 0;
if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
}
/* Actually do the operation */
+#if 0 /// elogind has no action_unit but a pending_action
r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+#else
+ r = execute_shutdown_or_sleep(manager, manager->action_what, manager->pending_action, &error);
+#endif // 0
if (r < 0) {
log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
bus_error_message(&error, r));
+
+#if 0 /// elogind has no action_unit but a pending_action
manager->action_unit = NULL;
manager->action_what = 0;
return r;
+#else
+ manager->pending_action = HANDLE_IGNORE;
+ manager->action_what = 0;
+ /* It is not a critical error for elogind if suspending fails */
+#endif // 0
}
return 1;
}
-#endif // 0
-#if 0 /// elogind-dbus.c needs to access this
static int manager_inhibit_timeout_handler(
-#else
-int manager_inhibit_timeout_handler(
-#endif // 0
sd_event_source *s,
uint64_t usec,
void *userdata) {
return (r < 0) ? r : 0;
}
-#if 0 /// elogind has its own variant in elogind-dbus.c
+#if 0 /// elogind does not have unit_name but action
static int delay_shutdown_or_sleep(
Manager *m,
InhibitWhat w,
const char *unit_name) {
+#else
+int delay_shutdown_or_sleep(
+ Manager *m,
+ InhibitWhat w,
+ HandleAction action) {
+#endif // 0
int r;
usec_t timeout_val;
assert(m);
assert(w >= 0);
assert(w < _INHIBIT_WHAT_MAX);
+#if 0 /// UNNEEDED by elogind
assert(unit_name);
+#endif // 0
timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max;
return r;
}
+#if 0 /// elogind does not have unit_name but pendig_action
m->action_unit = unit_name;
+#else
+ m->pending_action = action;
+#endif // 0
m->action_what = w;
return 0;
}
-#endif // 0
-#if 0 /// elogind has its own variant in elogind-dbus.c
int bus_manager_shutdown_or_sleep_now_or_later(
Manager *m,
+#if 0 /// elogind has HandleAction instead of const char* unit_name
const char *unit_name,
+
+ _cleanup_free_ char *load_state = NULL;
+#else
+ HandleAction unit_name,
+#endif // 0
InhibitWhat w,
sd_bus_error *error) {
-
bool delayed;
int r;
assert(m);
+#if 0 /// for elogind only w has to be checked.
assert(unit_name);
assert(w > 0);
assert(w <= _INHIBIT_WHAT_MAX);
assert(!m->action_job);
+ r = unit_load_state(m->bus, unit_name, &load_state);
+ if (r < 0)
+ return r;
+
+ if (!streq(load_state, "loaded")) {
+ log_notice("Unit %s is %s, refusing operation.", unit_name, load_state);
+ return -EACCES;
+ }
+#else
+ assert(w > 0);
+ assert(w <= _INHIBIT_WHAT_MAX);
+#endif // 0
+
/* Tell everybody to prepare for shutdown/sleep */
- send_prepare_for(m, w, true);
+ (void) send_prepare_for(m, w, true);
delayed =
m->inhibit_delay_max > 0 &&
manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL);
log_debug_elogind("%s called for %s (%sdelayed)", __FUNCTION__,
- handle_action_to_string(action),
+ handle_action_to_string(unit_name),
delayed ? "" : "NOT ");
if (delayed)
/* Shutdown is delayed, keep in mind what we
return r;
}
-#endif // 0
-#if 0 /// elogind-dbus.c needs to access this
static int verify_shutdown_creds(
-#else
-int verify_shutdown_creds(
-#endif // 0
Manager *m,
sd_bus_message *message,
InhibitWhat w,
return 0;
}
-#if 0 /// elogind has its own variant in elogind-dbus.c
static int method_do_shutdown_or_sleep(
Manager *m,
sd_bus_message *message,
+#if 0 /// elogind has HandleAction instead of const char* unit_name
const char *unit_name,
+#else
+ HandleAction unit_name,
+#endif // 0
InhibitWhat w,
const char *action,
const char *action_multiple_sessions,
assert(m);
assert(message);
+#if 0 /// elogind does not need this to be checked
assert(unit_name);
+#endif // 0
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
if (r < 0)
return r;
+ log_debug_elogind("%s called with action '%s', sleep '%s' (%sinteractive)",
+ __FUNCTION__, action, sleep_verb,
+ interactive ? "" : "NOT ");
/* Don't allow multiple jobs being executed at the same time */
if (m->action_what)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress");
if (sleep_verb) {
+#if 0 /// Within elogind the manager m must be provided, too
r = can_sleep(sleep_verb);
+ if (r == -ENOSPC)
+ return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
+ "Not enough swap space for hibernation");
+ if (r == 0)
+ return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
+ "Sleep verb \"%s\" not supported", sleep_verb);
+#else
+ r = can_sleep(m, sleep_verb);
+#endif // 0
if (r < 0)
return r;
-
- if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported");
}
+#if 0 /// Within elogind it does not make sense to verify shutdown creds when suspending
r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
action_ignore_inhibit, error);
- log_debug_elogind("verify_shutdown_creds() returned %d", r);
if (r != 0)
return r;
+#else
+ if (IN_SET(unit_name, HANDLE_HALT, HANDLE_POWEROFF, HANDLE_REBOOT)) {
+ r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions,
+ action_ignore_inhibit, error);
+ log_debug_elogind("verify_shutdown_creds() returned %d", r);
+ if (r != 0)
+ return r;
+ }
+#endif // 0
r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
if (r < 0)
static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ log_debug_elogind("%s called", __FUNCTION__);
return method_do_shutdown_or_sleep(
m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
SPECIAL_POWEROFF_TARGET,
+#else
+ HANDLE_POWEROFF,
+#endif // 0
INHIBIT_SHUTDOWN,
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ log_debug_elogind("%s called", __FUNCTION__);
return method_do_shutdown_or_sleep(
m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
SPECIAL_REBOOT_TARGET,
+#else
+ HANDLE_REBOOT,
+#endif // 0
INHIBIT_SHUTDOWN,
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ log_debug_elogind("%s called", __FUNCTION__);
return method_do_shutdown_or_sleep(
m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
SPECIAL_HALT_TARGET,
+#else
+ HANDLE_HALT,
+#endif // 0
INHIBIT_SHUTDOWN,
"org.freedesktop.login1.halt",
"org.freedesktop.login1.halt-multiple-sessions",
static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ log_debug_elogind("%s called", __FUNCTION__);
return method_do_shutdown_or_sleep(
m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
SPECIAL_SUSPEND_TARGET,
+#else
+ HANDLE_SUSPEND,
+#endif // 0
INHIBIT_SLEEP,
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ log_debug_elogind("%s called", __FUNCTION__);
return method_do_shutdown_or_sleep(
m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
SPECIAL_HIBERNATE_TARGET,
+#else
+ HANDLE_HIBERNATE,
+#endif // 0
INHIBIT_SLEEP,
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ log_debug_elogind("%s called", __FUNCTION__);
return method_do_shutdown_or_sleep(
m, message,
+#if 0 /// elogind uses HandleAction instead of const char* unti names
SPECIAL_HYBRID_SLEEP_TARGET,
INHIBIT_SLEEP,
"org.freedesktop.login1.hibernate",
"hybrid-sleep",
error);
}
+
+static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+
+ return method_do_shutdown_or_sleep(
+ m, message,
+ SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
+#else
+ HANDLE_HYBRID_SLEEP,
#endif // 0
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ "hybrid-sleep",
+ error);
+}
static int nologin_timeout_handler(
sd_event_source *s,
void *userdata) {
Manager *m = userdata;
- int r;
log_info("Creating /run/nologin, blocking further logins...");
- r = write_string_file_atomic_label("/run/nologin", "System is going down.");
- if (r < 0)
- log_error_errno(r, "Failed to create /run/nologin: %m");
- else
- m->unlink_nologin = true;
+ m->unlink_nologin =
+ create_shutdown_run_nologin_or_warn() >= 0;
return 0;
}
assert(m);
- r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
+ r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0, MKDIR_WARN_MODE);
if (r < 0)
return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m");
}
+#if 0 /// elogind must access this from elogind-dbus.c
static void reset_scheduled_shutdown(Manager *m) {
+#else
+void reset_scheduled_shutdown(Manager *m) {
+#endif // 0
assert(m);
m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
- (void) unlink("/run/nologin");
+ (void) unlink_or_warn("/run/nologin");
m->unlink_nologin = false;
}
uint64_t elapse;
char *type;
int r;
+ bool dry_run = false;
assert(m);
assert(message);
if (startswith(type, "dry-")) {
type += 4;
- m->shutdown_dry_run = true;
+ dry_run = true;
}
if (streq(type, "poweroff")) {
return log_oom();
}
+ m->shutdown_dry_run = dry_run;
+
if (m->nologin_timeout_source) {
r = sd_event_source_set_time(m->nologin_timeout_source, elapse);
if (r < 0)
cancelled = m->scheduled_shutdown_type != NULL;
reset_scheduled_shutdown(m);
- if (cancelled) {
+ if (cancelled && m->enable_wall_messages) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *tty = NULL;
uid_t uid = 0;
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ HandleAction handle;
bool multiple_sessions, challenge, blocked;
const char *result = NULL;
uid_t uid;
if (sleep_verb) {
#if 0 /// elogind needs to have the manager being passed
r = can_sleep(sleep_verb);
+ if (IN_SET(r, 0, -ENOSPC))
+ return sd_bus_reply_method_return(message, "s", "na");
#else
r = can_sleep(m, sleep_verb);
#endif // 0
if (r < 0)
return r;
- if (r == 0)
- return sd_bus_reply_method_return(message, "s", "na");
}
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ handle = handle_action_from_string(sleep_verb);
+ if (handle >= 0) {
+ const char *target;
+
+ target = manager_target_for_action(handle);
+ if (target) {
+ _cleanup_free_ char *load_state = NULL;
+
+ r = unit_load_state(m->bus, target, &load_state);
+ if (r < 0)
+ return r;
+
+ if (!streq(load_state, "loaded")) {
+ result = "no";
+ goto finish;
+ }
+ }
+ }
+
if (multiple_sessions) {
r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
result = "no";
}
+ finish:
return sd_bus_reply_method_return(message, "s", result);
}
error);
}
+static int method_can_suspend_then_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+
+ return method_can_shutdown_or_sleep(
+ m, message,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ "suspend-then-hibernate",
+ error);
+}
+
static int property_get_reboot_to_firmware_setup(
sd_bus *bus,
const char *path,
r = efi_get_reboot_to_firmware();
if (r < 0 && r != -EOPNOTSUPP)
- return r;
+ log_warning_errno(r, "Failed to determine reboot-to-firmware state: %m");
return sd_bus_message_append(reply, "b", r > 0);
#else
assert(m);
r = efi_reboot_to_firmware_supported();
- if (r == -EOPNOTSUPP)
+ if (r < 0) {
+ if (r != -EOPNOTSUPP)
+ log_warning_errno(errno, "Failed to determine whether reboot to firmware is supported: %m");
+
return sd_bus_reply_method_return(message, "s", "na");
- else if (r < 0)
- return r;
+ }
r = bus_test_polkit(message,
CAP_SYS_ADMIN,
int r;
Manager *m = userdata;
char *wall_message;
- int enable_wall_messages;
+ unsigned enable_wall_messages;
assert(message);
assert(m);
SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleLidSwitch", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("HandleLidSwitchExternalPower", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_ep), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandleLidSwitchDocked", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_docked), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HoldoffTimeoutUSec", "t", NULL, offsetof(Manager, holdoff_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IdleAction", "s", property_get_handle_action, offsetof(Manager, idle_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0),
SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0),
+ SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), 0),
SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0),
- SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0),
+ SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SuspendThenHibernate", "b", NULL, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CanSuspendThenHibernate", NULL, "s", method_can_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what));
/* Tell people that they now may take a lock again */
- send_prepare_for(m, m->action_what, false);
+ (void) send_prepare_for(m, m->action_what, false);
m->action_job = mfree(m->action_job);
m->action_unit = NULL;
return 1;
}
-int manager_start_slice(
- Manager *manager,
- const char *slice,
- const char *description,
- const char *after,
- const char *after2,
- uint64_t tasks_max,
- sd_bus_error *error,
- char **job) {
-
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
- int r;
-
- assert(manager);
- assert(slice);
- assert(job);
-
- r = sd_bus_message_new_method_call(
- manager->bus,
- &m,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "StartTransientUnit");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "ss", strempty(slice), "fail");
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'a', "(sv)");
- if (r < 0)
- return r;
-
- if (!isempty(description)) {
- r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
- if (r < 0)
- return r;
- }
-
- if (!isempty(after)) {
- r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
- if (r < 0)
- return r;
- }
-
- if (!isempty(after2)) {
- r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "a(sa(sv))", 0);
- if (r < 0)
- return r;
-
- r = sd_bus_call(manager->bus, m, 0, error, &reply);
- if (r < 0)
- return r;
-
- return strdup_job(reply, job);
-}
-
int manager_start_scope(
Manager *manager,
const char *scope,
const char *description,
const char *after,
const char *after2,
- uint64_t tasks_max,
+ sd_bus_message *more_properties,
sd_bus_error *error,
char **job) {
return r;
}
- /* cgroup empty notification is not available in containers
- * currently. To make this less problematic, let's shorten the
- * stop timeout for sessions, so that we don't wait
- * forever. */
-
- /* Make sure that the session shells are terminated with
- * SIGHUP since bash and friends tend to ignore SIGTERM */
+ /* Make sure that the session shells are terminated with SIGHUP since bash and friends tend to ignore
+ * SIGTERM */
r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true);
if (r < 0)
return r;
if (r < 0)
return r;
- r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max);
+ /* disable TasksMax= for the session scope, rely on the slice setting for it */
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", (uint64_t)-1);
if (r < 0)
- return r;
+ return bus_log_create_error(r);
+
+ if (more_properties) {
+ /* If TasksMax also appears here, it will overwrite the default value set above */
+ r = sd_bus_message_copy(m, more_properties, true);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_close_container(m);
if (r < 0)