X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Flogind-dbus.c;h=26a4ed889817b69a971d5202bccdc88ece7069a0;hb=d9537fde9944a03f48054e0f4b6a9aea7d3c2a9d;hp=b92430b41ca19cea74a4c2383e25a354f229af66;hpb=5f6ccb514798532d84fa6164d4358d066ccdfdd0;p=elogind.git diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index b92430b41..26a4ed889 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1,22 +1,8 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** 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 . ***/ #include @@ -30,18 +16,20 @@ #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 "escape.h" #include "fd-util.h" #include "fileio-label.h" -#include "formats-util.h" +#include "format-util.h" #include "fs-util.h" #include "logind.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" @@ -50,26 +38,52 @@ #include "udev-util.h" #include "unit-name.h" #include "user-util.h" -//#include "utmp-wtmp.h" +#include "utmp-wtmp.h" -int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; +/// Additional includes needed by elogind +#include "elogind-dbus.h" + +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 < 0) - return r; - } + if (isempty(name)) + return get_sender_session(m, message, error, ret); session = hashmap_get(m->sessions, name); if (!session) @@ -79,30 +93,48 @@ int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const ch 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 == UID_INVALID) { - _cleanup_bus_creds_unref_ 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; @@ -124,7 +156,6 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char return r; seat = session->seat; - if (!seat) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat."); } else { @@ -214,9 +245,9 @@ static int property_get_preparing( 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); } @@ -249,24 +280,9 @@ static int property_get_scheduled_shutdown( } 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 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; @@ -293,6 +309,9 @@ static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_er 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; @@ -383,7 +402,9 @@ static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bu 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); @@ -419,7 +440,7 @@ static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error } static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; Session *session; Iterator i; @@ -461,7 +482,7 @@ static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_ } static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; User *user; Iterator i; @@ -501,7 +522,7 @@ static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_err } static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; Seat *seat; Iterator i; @@ -538,7 +559,7 @@ static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_err } static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; Inhibitor *inhibitor; Iterator i; @@ -577,9 +598,9 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu 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; @@ -603,7 +624,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus 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)) @@ -649,7 +670,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus 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"); @@ -667,7 +688,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus 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) @@ -675,10 +696,6 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus } } - 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; @@ -696,7 +713,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus } if (leader == 0) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + _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) @@ -707,9 +724,13 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus 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 @@ -728,8 +749,11 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus m->seat0->positions[vtnr]->class != SESSION_GREETER) return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); - audit_session_from_pid(leader, &audit_id); - if (audit_id > 0) { + 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)) { /* Keep our session IDs and the audit session IDs in sync */ if (asprintf(&id, "%"PRIu32, audit_id) < 0) @@ -740,9 +764,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus * 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); - audit_id = 0; - + log_warning("Existing logind session ID %s used by new audit session, ignoring.", id); + audit_id = AUDIT_SESSION_INVALID; id = mfree(id); } } @@ -828,19 +851,30 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus 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); - /* Here upstream systemd starts cgroups and the user systemd, - and arranges to reply asynchronously. We reply - directly. */ +#if 0 /// UNNEEDED by elogind + /* 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. */ r = session_send_create_reply(session, NULL); if (r < 0) goto fail; +#endif // 0 return 1; @@ -875,8 +909,9 @@ static int method_release_session(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; +#if 1 /// elogind must queue this session session_add_to_gc_queue(session); - +#endif // 1 return sd_bus_reply_method_return(message, NULL); } @@ -1084,13 +1119,13 @@ static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus } 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 b, r; + int r, b, interactive; struct passwd *pw; const char *path; - uint32_t uid; - int interactive; + uint32_t uid, auth_uid; assert(message); assert(m); @@ -1099,30 +1134,34 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - if (uid == UID_INVALID) { - _cleanup_bus_creds_unref_ 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; + } - } 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); if (!pw) - return errno ? -errno : -ENOENT; + return errno > 0 ? -errno : -ENOENT; r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "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, @@ -1133,9 +1172,9 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu 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; @@ -1143,7 +1182,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu if (!cc) return -ENOMEM; - path = strjoina("/var/lib/systemd/linger/", cc); + path = strjoina("/var/lib/elogind/linger/", cc); if (b) { User *u; @@ -1170,7 +1209,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu } 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; @@ -1201,14 +1240,14 @@ static int trigger_device(Manager *m, struct udev_device *d) { 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; @@ -1235,7 +1274,6 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) { return -ENOMEM; mkdir_p_label("/etc/udev/rules.d", 0755); - mac_selinux_init("/etc"); r = write_string_file_atomic_label(file, rule); if (r < 0) return r; @@ -1255,8 +1293,7 @@ static int flush_devices(Manager *m) { } else { struct dirent *de; - while ((de = readdir(d))) { - + FOREACH_DIRENT_ALL(de, d, break) { if (!dirent_is_file(de)) continue; @@ -1364,10 +1401,9 @@ static int have_multiple_sessions( return false; } +#if 0 /// elogind has its own variant in elogind-dbus.c static int bus_manager_log_shutdown( Manager *m, - InhibitWhat w, -#if 0 /// elogind does not support systemd units const char *unit_name) { const char *p, *q; @@ -1375,18 +1411,15 @@ static int bus_manager_log_shutdown( assert(m); assert(unit_name); - if (w != INHIBIT_SHUTDOWN) - return 0; - if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) { p = "MESSAGE=System is powering down"; q = "SHUTDOWN=power-off"; - } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { - p = "MESSAGE=System is halting"; - q = "SHUTDOWN=halt"; } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) { p = "MESSAGE=System is rebooting"; q = "SHUTDOWN=reboot"; + } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { + p = "MESSAGE=System is halting"; + q = "SHUTDOWN=halt"; } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { p = "MESSAGE=System is rebooting with kexec"; q = "SHUTDOWN=kexec"; @@ -1394,49 +1427,18 @@ static int bus_manager_log_shutdown( p = "MESSAGE=System is shutting down"; q = NULL; } -#else - HandleAction action) { - - const char *p, *q; - - assert(m); - - if (w != INHIBIT_SHUTDOWN) - return 0; - - switch (action) { - case HANDLE_POWEROFF: - p = "MESSAGE=System is powering down."; - q = "SHUTDOWN=power-off"; - break; - case HANDLE_HALT: - p = "MESSAGE=System is halting."; - q = "SHUTDOWN=halt"; - break; - case HANDLE_REBOOT: - p = "MESSAGE=System is rebooting."; - q = "SHUTDOWN=reboot"; - break; - case HANDLE_KEXEC: - p = "MESSAGE=System is rebooting with kexec."; - q = "SHUTDOWN=kexec"; - break; - default: - p = "MESSAGE=System is shutting down."; - q = NULL; - } -#endif // 0 + if (isempty(m->wall_message)) p = strjoina(p, "."); else p = strjoina(p, " (", m->wall_message, ")."); return log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), + "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, p, - q, - NULL); + q); } +#endif // 0 static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { Manager *m = userdata; @@ -1483,44 +1485,52 @@ int manager_set_lid_switch_ignore(Manager *m, usec_t until) { return r; } -static void reset_scheduled_shutdown(Manager *m) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); - m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); - m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); - m->scheduled_shutdown_timeout = 0; - m->shutdown_dry_run = false; +#if 0 /// elogind-dbus.c needs to access this +static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { +#else +int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { +#endif // 0 - if (m->unlink_nologin) { - (void) unlink("/run/nologin"); - m->unlink_nologin = false; - } + static const char * const signal_name[_INHIBIT_WHAT_MAX] = { + [INHIBIT_SHUTDOWN] = "PrepareForShutdown", + [INHIBIT_SLEEP] = "PrepareForSleep" + }; + + int active = _active; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(signal_name[w]); + + return sd_bus_emit_signal(m->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + signal_name[w], + "b", + active); } +#if 0 /// elogind has its own variant in elogind-dbus.c static int execute_shutdown_or_sleep( Manager *m, InhibitWhat w, - HandleAction action, + const char *unit_name, sd_bus_error *error) { -#if 0 /// elogind does not need these, we do it ourselves - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; char *c = NULL; const char *p; -#endif // 0 int r; assert(m); - assert(w >= 0); + assert(w > 0); assert(w < _INHIBIT_WHAT_MAX); + assert(unit_name); - bus_manager_log_shutdown(m, w, action); + if (w == INHIBIT_SHUTDOWN) + bus_manager_log_shutdown(m, unit_name); -#if 0 /// elogind does it directly without depending on systemd running the system - if (m->shutdown_dry_run) { - log_info("Running in dry run, suppressing action."); - reset_scheduled_shutdown(m); - } else { r = sd_bus_call_method( m->bus, "org.freedesktop.systemd1", @@ -1529,48 +1539,51 @@ static int execute_shutdown_or_sleep( "StartUnit", error, &reply, - "ss", NULL, "replace-irreversibly"); -#else - r = shutdown_or_sleep(m, action); - - /* no more pending actions, whether this failed or not */ - m->pending_action = HANDLE_IGNORE; - m->action_what = 0; -#endif // 0 + "ss", unit_name, "replace-irreversibly"); if (r < 0) - return r; + goto error; -#if 0 /// elogind neither needs a dbus reply, nor supports systemd action jobs r = sd_bus_message_read(reply, "o", &p); if (r < 0) - return r; + goto error; c = strdup(p); - if (!c) - return -ENOMEM; + if (!c) { + r = -ENOMEM; + goto error; } m->action_unit = unit_name; free(m->action_job); m->action_job = c; m->action_what = w; -#endif // 0 /* Make sure the lid switch is ignored for a while */ manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec); return 0; + +error: + /* Tell people that they now may take a lock again */ + (void) send_prepare_for(m, w, false); + + return r; } +#endif // 0 int manager_dispatch_delayed(Manager *manager, bool timeout) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Inhibitor *offending = NULL; int r; 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)) { @@ -1588,13 +1601,25 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) { } /* 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("Failed to send delayed message: %s", bus_error_message(&error, r)); + 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; - return r; + /* It is not a critical error for elogind if suspending fails */ +#endif // 0 } return 1; @@ -1615,17 +1640,27 @@ static int manager_inhibit_timeout_handler( return (r < 0) ? r : 0; } +#if 0 /// elogind does not have unit_name but action static int delay_shutdown_or_sleep( Manager *m, InhibitWhat w, - HandleAction action) { + 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; @@ -1644,62 +1679,68 @@ static int delay_shutdown_or_sleep( 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; } -static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { - - static const char * const signal_name[_INHIBIT_WHAT_MAX] = { - [INHIBIT_SHUTDOWN] = "PrepareForShutdown", - [INHIBIT_SLEEP] = "PrepareForSleep" - }; - - int active = _active; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(signal_name[w]); - - return sd_bus_emit_signal(m->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - signal_name[w], - "b", - active); -} - int bus_manager_shutdown_or_sleep_now_or_later( Manager *m, - HandleAction action, +#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); - assert(w >= 0); +#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(unit_name), + delayed ? "" : "NOT "); if (delayed) /* Shutdown is delayed, keep in mind what we * want to do, and start a timeout */ - r = delay_shutdown_or_sleep(m, w, action); + r = delay_shutdown_or_sleep(m, w, unit_name); else /* Shutdown is not delayed, execute it * immediately */ - r = execute_shutdown_or_sleep(m, w, action, error); + r = execute_shutdown_or_sleep(m, w, unit_name, error); return r; } @@ -1714,7 +1755,7 @@ static int verify_shutdown_creds( const char *action_ignore_inhibit, sd_bus_error *error) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; bool multiple_sessions, blocked; uid_t uid; int r; @@ -1769,7 +1810,11 @@ static int verify_shutdown_creds( static int method_do_shutdown_or_sleep( Manager *m, sd_bus_message *message, - HandleAction sleep_action, +#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, @@ -1781,6 +1826,9 @@ static int method_do_shutdown_or_sleep( 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); @@ -1788,25 +1836,45 @@ static int method_do_shutdown_or_sleep( 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); 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, sleep_action, w, error); + r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); if (r < 0) return r; @@ -1816,9 +1884,14 @@ static int method_do_shutdown_or_sleep( 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", @@ -1830,9 +1903,14 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error 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", @@ -1841,17 +1919,93 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error * error); } -static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { +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, - HANDLE_SUSPEND, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - "suspend", +#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", + "org.freedesktop.login1.halt-ignore-inhibit", + NULL, + error); +} + +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", + "org.freedesktop.login1.suspend-ignore-inhibit", + "suspend", + error); +} + +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", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hibernate", + error); +} + +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", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "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); } @@ -1861,15 +2015,11 @@ static int nologin_timeout_handler( 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; } @@ -1881,7 +2031,7 @@ static int update_schedule_file(Manager *m) { 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"); @@ -1923,20 +2073,44 @@ static int update_schedule_file(Manager *m) { return 0; fail: - (void) unlink(temp_path); - (void) unlink("/run/systemd/shutdown/scheduled"); + (void) unlink(temp_path); + (void) unlink("/run/systemd/shutdown/scheduled"); 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->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); + m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); + + m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); + m->scheduled_shutdown_timeout = 0; + m->shutdown_dry_run = false; + + if (m->unlink_nologin) { + (void) unlink_or_warn("/run/nologin"); + m->unlink_nologin = false; + } + + (void) unlink("/run/systemd/shutdown/scheduled"); +} + +#if 0 /// elogind has its own variant in elogind-dbus.c static int manager_scheduled_shutdown_handler( sd_event_source *s, uint64_t usec, void *userdata) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Manager *m = userdata; - HandleAction action; + const char *target; int r; assert(m); @@ -1944,43 +2118,78 @@ static int manager_scheduled_shutdown_handler( if (isempty(m->scheduled_shutdown_type)) return 0; - if (streq(m->scheduled_shutdown_type, "halt")) - action = HANDLE_HALT; - else if (streq(m->scheduled_shutdown_type, "poweroff")) - action = HANDLE_POWEROFF; + if (streq(m->scheduled_shutdown_type, "poweroff")) + target = SPECIAL_POWEROFF_TARGET; + else if (streq(m->scheduled_shutdown_type, "reboot")) + target = SPECIAL_REBOOT_TARGET; + else if (streq(m->scheduled_shutdown_type, "halt")) + target = SPECIAL_HALT_TARGET; else - action = HANDLE_REBOOT; + assert_not_reached("unexpected shutdown type"); - r = execute_shutdown_or_sleep(m, 0, action, &error); - if (r < 0) - return log_error_errno(r, "Unable to execute transition to %s: %m", m->scheduled_shutdown_type); + /* Don't allow multiple jobs being executed at the same time */ + if (m->action_what) { + r = -EALREADY; + log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", target); + goto error; + } + + if (m->shutdown_dry_run) { + /* We do not process delay inhibitors here. Otherwise, we + * would have to be considered "in progress" (like the check + * above) for some seconds after our admin has seen the final + * wall message. */ + + bus_manager_log_shutdown(m, target); + log_info("Running in dry run, suppressing action."); + reset_scheduled_shutdown(m); + + return 0; + } + + r = bus_manager_shutdown_or_sleep_now_or_later(m, target, INHIBIT_SHUTDOWN, &error); + if (r < 0) { + log_error_errno(r, "Scheduled shutdown to %s failed: %m", target); + goto error; + } return 0; + +error: + reset_scheduled_shutdown(m); + return r; } +#endif // 0 static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; const char *action_multiple_sessions = NULL; const char *action_ignore_inhibit = NULL; const char *action = NULL; uint64_t elapse; char *type; int r; + bool dry_run = false; assert(m); assert(message); + log_debug_elogind("%s called", __FUNCTION__); r = sd_bus_message_read(message, "st", &type, &elapse); if (r < 0) return r; if (startswith(type, "dry-")) { type += 4; - m->shutdown_dry_run = true; + dry_run = true; } - if (streq(type, "reboot")) { + if (streq(type, "poweroff")) { + action = "org.freedesktop.login1.power-off"; + action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit"; + } else if (streq(type, "reboot")) { action = "org.freedesktop.login1.reboot"; action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; @@ -1988,10 +2197,6 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ action = "org.freedesktop.login1.halt"; action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; - } else if (streq(type, "poweroff")) { - action = "org.freedesktop.login1.poweroff"; - action_multiple_sessions = "org.freedesktop.login1.poweroff-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.poweroff-ignore-inhibit"; } else return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); @@ -2021,6 +2226,8 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ 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) @@ -2052,18 +2259,13 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ } } -#if 0 /// elogind does not support utmp-wtmp r = manager_setup_wall_message_timer(m); if (r < 0) return r; -#endif // 0 - if (!isempty(type)) { - r = update_schedule_file(m); - if (r < 0) - return r; - } else - (void) unlink("/run/systemd/shutdown/scheduled"); + r = update_schedule_file(m); + if (r < 0) + return r; return sd_bus_reply_method_return(message, NULL); } @@ -2071,16 +2273,19 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; bool cancelled; +#if 1 /// elogind needs to construct the message to allow extra wall messages + _cleanup_free_ char *l = NULL; +#endif // 1 assert(m); assert(message); + log_debug_elogind("%s called", __FUNCTION__); cancelled = m->scheduled_shutdown_type != NULL; reset_scheduled_shutdown(m); -#if 0 /// elogind does not support utmp-wtmp - if (cancelled) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + if (cancelled && m->enable_wall_messages) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; const char *tty = NULL; uid_t uid = 0; int r; @@ -2091,42 +2296,25 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd (void) sd_bus_creds_get_tty(creds, &tty); } +#if 0 /// elogind wants to allow extra cancellation messages utmp_wall("The system shutdown has been cancelled", uid_to_name(uid), tty, logind_wall_tty_filter, m); - } +#else + r = asprintf(&l, "%s%sThe system shutdown has been cancelled!", + strempty(m->wall_message), + isempty(m->wall_message) ? "" : "\n"); + if (r < 0) { + log_oom(); + return 0; + } + + utmp_wall(l, uid_to_name(uid), tty, logind_wall_tty_filter, m); #endif // 0 + } return sd_bus_reply_method_return(message, "b", cancelled); } -static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - HANDLE_HIBERNATE, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hibernate", - error); -} - -static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - HANDLE_HYBRID_SLEEP, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hybrid-sleep", - error); -} - static int method_can_shutdown_or_sleep( Manager *m, sd_bus_message *message, @@ -2137,7 +2325,8 @@ static int method_can_shutdown_or_sleep( const char *sleep_verb, sd_bus_error *error) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + HandleAction handle; bool multiple_sessions, challenge, blocked; const char *result = NULL; uid_t uid; @@ -2152,11 +2341,15 @@ static int method_can_shutdown_or_sleep( assert(action_ignore_inhibit); 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); @@ -2174,6 +2367,25 @@ static int method_can_shutdown_or_sleep( 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) @@ -2216,6 +2428,7 @@ static int method_can_shutdown_or_sleep( result = "no"; } + finish: return sd_bus_reply_method_return(message, "s", result); } @@ -2245,6 +2458,19 @@ static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_err error); } +static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.halt", + "org.freedesktop.login1.halt-multiple-sessions", + "org.freedesktop.login1.halt-ignore-inhibit", + NULL, + error); +} + static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; @@ -2284,6 +2510,19 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b 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, @@ -2301,11 +2540,11 @@ static int property_get_reboot_to_firmware_setup( 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 - return sd_bus_message_append(reply, "b", -EOPNOTSUPP); + return sd_bus_message_append(reply, "b", false); #endif // 0 } @@ -2361,10 +2600,12 @@ static int method_can_reboot_to_firmware_setup( 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, @@ -2385,7 +2626,7 @@ static int method_can_reboot_to_firmware_setup( return sd_bus_reply_method_return(message, "s", result); #else - return sd_bus_reply_method_return(message, "s", "na"); + return sd_bus_reply_method_return(message, "s", "no"); #endif // 0 } @@ -2397,7 +2638,7 @@ static int method_set_wall_message( int r; Manager *m = userdata; char *wall_message; - int enable_wall_messages; + unsigned enable_wall_messages; assert(message); assert(m); @@ -2406,6 +2647,7 @@ static int method_set_wall_message( if (r < 0) return r; +#if 0 /// elogind only calls this for shutdown/reboot, which already needs authorization. r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-wall-message", @@ -2418,14 +2660,11 @@ static int method_set_wall_message( return r; if (r == 0) return 1; /* Will call us back */ +#endif // 0 - if (isempty(wall_message)) - m->wall_message = mfree(m->wall_message); - else { - r = free_and_strdup(&m->wall_message, wall_message); - if (r < 0) - return log_oom(); - } + r = free_and_strdup(&m->wall_message, empty_to_null(wall_message)); + if (r < 0) + return log_oom(); m->enable_wall_messages = enable_wall_messages; @@ -2433,7 +2672,7 @@ static int method_set_wall_message( } static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; const char *who, *why, *what, *mode; _cleanup_free_ char *id = NULL; _cleanup_close_ int fifo_fd = -1; @@ -2503,6 +2742,9 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; + if (hashmap_size(m->inhibitors) >= m->inhibitors_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); + do { id = mfree(id); @@ -2550,7 +2792,9 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), -// SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), +#if 0 /// UNNEEDED by elogind + SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), +#endif // 0 SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2565,6 +2809,7 @@ const sd_bus_vtable manager_vtable[] = { 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), @@ -2573,6 +2818,13 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), 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", 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_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_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), @@ -2601,14 +2853,18 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Halt", "b", NULL, method_halt, 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), @@ -2641,7 +2897,7 @@ static int session_jobs_reply(Session *s, const char *unit, const char *result) if (streq(result, "done")) r = session_send_create_reply(s, NULL); else { - _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); r = session_send_create_reply(s, &e); @@ -2671,7 +2927,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err 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; @@ -2814,89 +3070,20 @@ int manager_send_changed(Manager *manager, const char *property, ...) { } #if 0 /// UNNEEDED by elogind -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_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; +static int strdup_job(sd_bus_message *reply, char **job) { + const char *j; + char *copy; int r; - assert(manager); - assert(slice); - - 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); + r = sd_bus_message_read(reply, "o", &j); 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; - - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } + copy = strdup(j); + if (!copy) + return -ENOMEM; + *job = copy; return 1; } @@ -2908,16 +3095,17 @@ int manager_start_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) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; int r; assert(manager); assert(scope); assert(pid > 1); + assert(job); r = sd_bus_message_new_method_call( manager->bus, @@ -2961,13 +3149,8 @@ int manager_start_scope( 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; @@ -2976,9 +3159,17 @@ int manager_start_scope( 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) @@ -2992,30 +3183,16 @@ int manager_start_scope( if (r < 0) return r; - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int r; assert(manager); assert(unit); + assert(job); r = sd_bus_call_method( manager->bus, @@ -3029,30 +3206,16 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, if (r < 0) return r; - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int r; assert(manager); assert(unit); + assert(job); r = sd_bus_call_method( manager->bus, @@ -3067,9 +3230,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { - if (job) - *job = NULL; - + *job = NULL; sd_bus_error_free(error); return 0; } @@ -3077,22 +3238,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c return r; } - if (job) { - const char *j; - char *copy; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - } - - return 1; + return strdup_job(reply, job); } int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { @@ -3145,8 +3291,8 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo } int manager_unit_is_active(Manager *manager, const char *unit) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; const char *state; int r; @@ -3191,8 +3337,8 @@ int manager_unit_is_active(Manager *manager, const char *unit) { } int manager_job_is_active(Manager *manager, const char *path) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int r; assert(manager); @@ -3223,4 +3369,4 @@ int manager_job_is_active(Manager *manager, const char *path) { return true; } -#endif // +#endif // 0