/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
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/>.
***/
#include <errno.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include "sd-messages.h"
#include "alloc-util.h"
#include "audit-util.h"
#include "bus-common-errors.h"
#include "bus-error.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 "fs-util.h"
#include "logind.h"
#include "mkdir.h"
#include "path-util.h"
#include "process-util.h"
#include "selinux-util.h"
#include "sleep-config.h"
#include "special.h"
#include "strv.h"
#include "terminal-util.h"
#include "udev-util.h"
#include "unit-name.h"
#include "user-util.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) {
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
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) {
int r;
assert(m);
if (uid == UID_INVALID) {
/* Note that we get the owner UID of the session, not the actual client UID here! */
if (r < 0)
return r;
if (r < 0)
return r;
}
if (!user)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "No user "UID_FMT" known or logged in", uid);
return 0;
}
int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) {
int r;
assert(m);
if (r < 0)
return r;
if (!seat)
} else {
if (!seat)
}
return 0;
}
static int property_get_idle_hint(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
assert(m);
}
static int property_get_idle_since_hint(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
assert(m);
manager_get_idle_hint(m, &t);
return sd_bus_message_append(reply, "t", streq(property, "IdleSinceHint") ? t.realtime : t.monotonic);
}
static int property_get_inhibited(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
InhibitWhat w;
assert(m);
}
static int property_get_preparing(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
bool b;
assert(m);
b = !!(m->action_what & INHIBIT_SHUTDOWN);
else
b = !!(m->action_what & INHIBIT_SLEEP);
}
static int property_get_scheduled_shutdown(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
return sd_bus_message_close_container(reply);
}
static int property_get_docked(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
assert(m);
}
_cleanup_free_ char *p = NULL;
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
p = session_bus_path(session);
if (!p)
return -ENOMEM;
}
static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
int r;
assert(m);
if (r < 0)
return r;
if (pid < 0)
return -EINVAL;
if (pid == 0) {
if (r < 0)
return r;
} else {
if (r < 0)
return r;
if (!session)
return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid);
}
p = session_bus_path(session);
if (!p)
return -ENOMEM;
}
_cleanup_free_ char *p = NULL;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
p = user_bus_path(user);
if (!p)
return -ENOMEM;
}
_cleanup_free_ char *p = NULL;
int r;
assert(m);
if (r < 0)
return r;
if (pid < 0)
return -EINVAL;
if (pid == 0) {
if (r < 0)
return r;
} else {
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);
}
p = user_bus_path(user);
if (!p)
return -ENOMEM;
}
_cleanup_free_ char *p = NULL;
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
p = seat_bus_path(seat);
if (!p)
return -ENOMEM;
}
Iterator i;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
_cleanup_free_ char *p = NULL;
p = session_bus_path(session);
if (!p)
return -ENOMEM;
p);
if (r < 0)
return r;
}
if (r < 0)
return r;
}
Iterator i;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
_cleanup_free_ char *p = NULL;
p = user_bus_path(user);
if (!p)
return -ENOMEM;
p);
if (r < 0)
return r;
}
if (r < 0)
return r;
}
Iterator i;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
_cleanup_free_ char *p = NULL;
p = seat_bus_path(seat);
if (!p)
return -ENOMEM;
if (r < 0)
return r;
}
if (r < 0)
return r;
}
Iterator i;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
}
if (r < 0)
return r;
}
int remote;
SessionType t;
SessionClass c;
int r;
assert(m);
r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host);
if (r < 0)
return r;
if (!uid_is_valid(uid))
else {
t = session_type_from_string(type);
if (t < 0)
}
else {
if (c < 0)
}
else {
if (!string_is_safe(desktop))
}
else {
if (!seat)
}
int v;
if (!seat)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
v = vtnr_from_tty(tty);
if (v <= 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty);
if (!vtnr)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match");
} else if (tty_is_console(tty)) {
if (!seat)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0");
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0");
}
if (seat) {
if (seat_has_vts(seat)) {
} else {
if (vtnr != 0)
}
}
if (r < 0)
return r;
if (t == _SESSION_TYPE_INVALID) {
t = SESSION_X11;
t = SESSION_TTY;
else
t = SESSION_UNSPECIFIED;
}
if (c == _SESSION_CLASS_INVALID) {
if (t == SESSION_UNSPECIFIED)
c = SESSION_BACKGROUND;
else
c = SESSION_USER;
}
if (leader == 0) {
if (r < 0)
return r;
if (r < 0)
return r;
}
if (r > 0)
/*
* Old gdm and lightdm start the user-session on the same VT as
* the greeter session. But they destroy the greeter session
* after the user-session and want the user-session to take
* over the VT. We need to support this for
* backwards-compatibility, so make sure we allow new sessions
* on a VT that a greeter is running on. Furthermore, to allow
* re-logins, we have to allow a greeter to take over a used VT for
* the exact same reasons.
*/
if (c != SESSION_GREETER &&
vtnr > 0 &&
if (audit_id > 0) {
/* Keep our session IDs and the audit session IDs in sync */
return -ENOMEM;
/* Wut? There's already a session by this name and we
* didn't find it above? Weird, then let's not trust
* the audit data and let's better register a new
* ID */
audit_id = 0;
}
}
if (!id) {
do {
return -ENOMEM;
}
if (r < 0)
goto fail;
if (r < 0)
goto fail;
r = -ENOMEM;
goto fail;
}
}
r = -ENOMEM;
goto fail;
}
}
if (!isempty(remote_user)) {
if (!session->remote_user) {
r = -ENOMEM;
goto fail;
}
}
if (!isempty(remote_host)) {
if (!session->remote_host) {
r = -ENOMEM;
goto fail;
}
}
r = -ENOMEM;
goto fail;
}
}
r = -ENOMEM;
goto fail;
}
}
if (seat) {
if (r < 0)
goto fail;
}
r = session_start(session);
if (r < 0)
goto fail;
/* Now, let's wait until the slice unit and stuff got
* created. We send the reply back from
* session_send_create_reply(). */
return 1;
fail:
if (session)
if (user)
return r;
}
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
r = session_release(session);
if (r < 0)
return r;
}
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) {
int r;
assert(m);
/* Same as ActivateSession() but refuses to work if
* the seat doesn't match */
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name);
r = session_activate(session);
if (r < 0)
return r;
}
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
int r;
assert(m);
"org.freedesktop.login1.lock-sessions",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (r < 0)
return r;
}
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
const char *name;
int r;
assert(m);
if (r < 0)
return r;
if (r < 0)
return r;
}
int b, r;
const char *path;
int interactive;
assert(m);
if (r < 0)
return r;
if (uid == UID_INVALID) {
/* Note that we get the owner UID of the session, not the actual client UID here! */
if (r < 0)
return r;
if (r < 0)
return r;
} else if (!uid_is_valid(uid))
return -EINVAL;
errno = 0;
if (!pw)
"org.freedesktop.login1.set-user-linger",
NULL,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (r < 0)
return r;
if (!cc)
return -ENOMEM;
if (b) {
User *u;
if (r < 0)
return r;
if (manager_add_user_by_uid(m, uid, &u) >= 0)
user_start(u);
} else {
User *u;
return -errno;
if (u)
}
}
int r;
assert(m);
e = udev_enumerate_new(m->udev);
if (!e)
return -ENOMEM;
if (d) {
r = udev_enumerate_add_match_parent(e, d);
if (r < 0)
return r;
}
r = udev_enumerate_scan_devices(e);
if (r < 0)
return r;
_cleanup_free_ char *t = NULL;
const char *p;
p = udev_list_entry_get_name(item);
t = strappend(p, "/uevent");
if (!t)
return -ENOMEM;
}
return 0;
}
const char *id_for_seat;
int r;
assert(m);
if (!d)
return -ENODEV;
if (!udev_device_has_tag(d, "seat"))
return -ENODEV;
if (!id_for_seat)
return -ENODEV;
return -ENOMEM;
if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0)
return -ENOMEM;
if (r < 0)
return r;
return trigger_device(m, d);
}
assert(m);
if (!d) {
} else {
if (!dirent_is_file(de))
continue;
continue;
continue;
}
}
return trigger_device(m, NULL);
}
int interactive, r;
assert(m);
if (r < 0)
return r;
if (!seat_name_is_valid(seat))
"org.freedesktop.login1.attach-device",
NULL,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (r < 0)
return r;
}
int interactive, r;
assert(m);
if (r < 0)
return r;
"org.freedesktop.login1.flush-devices",
NULL,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = flush_devices(m);
if (r < 0)
return r;
}
static int have_multiple_sessions(
Manager *m,
Iterator i;
assert(m);
/* Check for other users' sessions. Greeter sessions do not
* count, and non-login sessions do not count either. */
return true;
return false;
}
static int bus_manager_log_shutdown(
Manager *m,
InhibitWhat w,
const char *unit_name) {
const char *p, *q;
assert(m);
if (w != INHIBIT_SHUTDOWN)
return 0;
p = "MESSAGE=System is powering down";
q = "SHUTDOWN=power-off";
p = "MESSAGE=System is halting";
q = "SHUTDOWN=halt";
p = "MESSAGE=System is rebooting";
q = "SHUTDOWN=reboot";
p = "MESSAGE=System is rebooting with kexec";
q = "SHUTDOWN=kexec";
} else {
p = "MESSAGE=System is shutting down";
q = NULL;
}
if (isempty(m->wall_message))
p = strjoina(p, ".");
else
return log_struct(LOG_NOTICE,
p,
q,
NULL);
}
assert(e);
assert(m);
return 0;
}
int r;
assert(m);
return 0;
/* We want to ignore the lid switch for a while after each
* suspend, and after boot-up. Hence let's install a timer for
* this. As long as the event source exists we ignore the lid
* switch. */
if (m->lid_switch_ignore_event_source) {
usec_t u;
r = sd_event_source_get_time(m->lid_switch_ignore_event_source, &u);
if (r < 0)
return r;
if (until <= u)
return 0;
} else
r = sd_event_add_time(
m->event,
until, 0,
return r;
}
m->scheduled_shutdown_timeout = 0;
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
m->unlink_nologin = false;
}
}
static int execute_shutdown_or_sleep(
Manager *m,
InhibitWhat w,
const char *unit_name,
sd_bus_error *error) {
char *c = NULL;
const char *p;
int r;
assert(m);
assert(w >= 0);
assert(w < _INHIBIT_WHAT_MAX);
bus_manager_log_shutdown(m, w, unit_name);
if (m->shutdown_dry_run) {
log_info("Running in dry run, suppressing action.");
} else {
r = sd_bus_call_method(
m->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&reply,
if (r < 0)
return r;
if (r < 0)
return r;
c = strdup(p);
if (!c)
return -ENOMEM;
}
m->action_unit = unit_name;
free(m->action_job);
m->action_job = c;
m->action_what = w;
/* Make sure the lid switch is ignored for a while */
return 0;
}
int r;
return 0;
if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
if (!timeout)
return 0;
log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.",
}
/* Actually do the operation */
if (r < 0) {
manager->action_what = 0;
return r;
}
return 1;
}
static int manager_inhibit_timeout_handler(
sd_event_source *s,
void *userdata) {
int r;
r = manager_dispatch_delayed(manager, true);
return (r < 0) ? r : 0;
}
static int delay_shutdown_or_sleep(
Manager *m,
InhibitWhat w,
const char *unit_name) {
int r;
assert(m);
assert(w >= 0);
assert(w < _INHIBIT_WHAT_MAX);
if (m->inhibit_timeout_source) {
if (r < 0)
return log_error_errno(r, "sd_event_source_set_time() failed: %m");
if (r < 0)
return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
} else {
if (r < 0)
return r;
}
m->action_unit = unit_name;
m->action_what = w;
return 0;
}
[INHIBIT_SHUTDOWN] = "PrepareForShutdown",
[INHIBIT_SLEEP] = "PrepareForSleep"
};
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);
}
Manager *m,
const char *unit_name,
InhibitWhat w,
sd_bus_error *error) {
bool delayed;
int r;
assert(m);
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
assert(!m->action_job);
send_prepare_for(m, w, true);
delayed =
m->inhibit_delay_max > 0 &&
if (delayed)
/* Shutdown is delayed, keep in mind what we
* want to do, and start a timeout */
r = delay_shutdown_or_sleep(m, w, unit_name);
else
/* Shutdown is not delayed, execute it
* immediately */
return r;
}
static int verify_shutdown_creds(
Manager *m,
InhibitWhat w,
bool interactive,
const char *action,
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
sd_bus_error *error) {
int r;
assert(m);
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
if (r < 0)
return r;
if (r < 0)
return r;
r = have_multiple_sessions(m, uid);
if (r < 0)
return r;
multiple_sessions = r > 0;
if (multiple_sessions && action_multiple_sessions) {
r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
if (blocked && action_ignore_inhibit) {
r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
return 0;
}
static int method_do_shutdown_or_sleep(
Manager *m,
const char *unit_name,
InhibitWhat w,
const char *action,
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
const char *sleep_verb,
sd_bus_error *error) {
int interactive, r;
assert(m);
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
if (r < 0)
return r;
/* 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) {
r = can_sleep(sleep_verb);
if (r < 0)
return r;
if (r == 0)
}
if (r != 0)
return r;
if (r < 0)
return r;
}
return method_do_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
NULL,
error);
}
return method_do_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
NULL,
error);
}
return method_do_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
"suspend",
error);
}
static int nologin_timeout_handler(
sd_event_source *s,
void *userdata) {
int r;
if (r < 0)
log_error_errno(r, "Failed to create /run/nologin: %m");
else
m->unlink_nologin = true;
return 0;
}
int r;
assert(m);
if (r < 0)
return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
if (r < 0)
return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
fprintf(f,
"WARN_WALL=%i\n"
"MODE=%s\n",
if (!isempty(m->wall_message)) {
_cleanup_free_ char *t;
t = cescape(m->wall_message);
if (!t) {
r = -ENOMEM;
goto fail;
}
fprintf(f, "WALL_MESSAGE=%s\n", t);
}
r = fflush_and_check(f);
if (r < 0)
goto fail;
r = -errno;
goto fail;
}
return 0;
fail:
return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m");
}
static int manager_scheduled_shutdown_handler(
sd_event_source *s,
void *userdata) {
const char *target;
int r;
assert(m);
if (isempty(m->scheduled_shutdown_type))
return 0;
else
if (r < 0)
return 0;
}
char *type;
int r;
assert(m);
if (r < 0)
return r;
type += 4;
m->shutdown_dry_run = true;
}
action = "org.freedesktop.login1.reboot";
action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
action = "org.freedesktop.login1.halt";
action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
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 (r != 0)
return r;
if (m->scheduled_shutdown_timeout_source) {
if (r < 0)
return log_error_errno(r, "sd_event_source_set_time() failed: %m");
if (r < 0)
return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
} else {
if (r < 0)
return log_error_errno(r, "sd_event_add_time() failed: %m");
}
if (r < 0) {
return log_oom();
}
if (m->nologin_timeout_source) {
if (r < 0)
return log_error_errno(r, "sd_event_source_set_time() failed: %m");
if (r < 0)
return log_error_errno(r, "sd_event_source_set_enabled() failed: %m");
} else {
if (r < 0)
return log_error_errno(r, "sd_event_add_time() failed: %m");
}
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
if (r >= 0) {
if (r < 0) {
return log_oom();
}
}
r = manager_setup_wall_message_timer(m);
if (r < 0)
return r;
r = update_schedule_file(m);
if (r < 0)
return r;
} else
}
static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
bool cancelled;
assert(m);
if (cancelled) {
int r;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
if (r >= 0) {
}
utmp_wall("The system shutdown has been cancelled",
}
}
return method_do_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"hibernate",
error);
}
return method_do_shutdown_or_sleep(
m, message,
"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,
InhibitWhat w,
const char *action,
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
const char *sleep_verb,
sd_bus_error *error) {
int r;
assert(m);
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
if (sleep_verb) {
r = can_sleep(sleep_verb);
if (r < 0)
return r;
if (r == 0)
}
if (r < 0)
return r;
if (r < 0)
return r;
r = have_multiple_sessions(m, uid);
if (r < 0)
return r;
multiple_sessions = r > 0;
if (multiple_sessions) {
r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
if (r > 0)
result = "yes";
else if (challenge)
result = "challenge";
else
result = "no";
}
if (blocked) {
r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
if (r > 0 && !result)
result = "yes";
result = "challenge";
else
result = "no";
}
if (!multiple_sessions && !blocked) {
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
if (r < 0)
return r;
if (r > 0)
result = "yes";
else if (challenge)
result = "challenge";
else
result = "no";
}
}
return method_can_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
NULL,
error);
}
return method_can_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
NULL,
error);
}
return method_can_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
"suspend",
error);
}
return method_can_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"hibernate",
error);
}
return method_can_shutdown_or_sleep(
m, message,
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"hybrid-sleep",
error);
}
static int property_get_reboot_to_firmware_setup(
const char *path,
const char *interface,
const char *property,
void *userdata,
sd_bus_error *error) {
int r;
r = efi_get_reboot_to_firmware();
if (r < 0 && r != -EOPNOTSUPP)
return r;
}
static int method_set_reboot_to_firmware_setup(
void *userdata,
sd_bus_error *error) {
int b, r;
assert(m);
if (r < 0)
return r;
"org.freedesktop.login1.set-reboot-to-firmware-setup",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = efi_set_reboot_to_firmware(b);
if (r < 0)
return r;
}
static int method_can_reboot_to_firmware_setup(
void *userdata,
sd_bus_error *error) {
int r;
bool challenge;
const char *result;
assert(m);
if (r == -EOPNOTSUPP)
else if (r < 0)
return r;
r = bus_test_polkit(message,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
NULL,
error);
if (r < 0)
return r;
if (r > 0)
result = "yes";
else if (challenge)
result = "challenge";
else
result = "no";
}
static int method_set_wall_message(
void *userdata,
sd_bus_error *error) {
int r;
char *wall_message;
int enable_wall_messages;
assert(m);
if (r < 0)
return r;
"org.freedesktop.login1.set-wall-message",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
if (isempty(wall_message))
else {
if (r < 0)
return log_oom();
}
}
InhibitWhat w;
int r;
assert(m);
if (r < 0)
return r;
w = inhibit_what_from_string(what);
if (w <= 0)
if (mm < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep");
/* Don't allow taking delay locks while we are already
* executing the operation. We shouldn't create the impression
* that the lock was successful if the machine is about to go
if (m->action_what & w)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running");
w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" :
w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
"org.freedesktop.login1.inhibit-handle-lid-switch",
NULL,
false,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
do {
return -ENOMEM;
r = manager_add_inhibitor(m, id, &i);
if (r < 0)
return r;
i->what = w;
r = -ENOMEM;
goto fail;
}
fifo_fd = inhibitor_create_fifo(i);
if (fifo_fd < 0) {
r = fifo_fd;
goto fail;
}
inhibitor_start(i);
fail:
if (i)
inhibitor_free(i);
return r;
}
SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 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),
SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST),
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("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("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, 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("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
};
int r = 0;
assert(s);
if (!s->started)
return r;
r = session_send_create_reply(s, NULL);
else {
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);
}
return r;
}
int r;
assert(m);
if (r < 0) {
return 0;
}
/* Tell people that they now may take a lock again */
send_prepare_for(m, m->action_what, false);
m->action_unit = NULL;
m->action_what = 0;
return 0;
}
}
if (user &&
}
return 0;
}
int r;
assert(m);
if (r < 0) {
return 0;
}
if (session)
if (user)
return 0;
}
const char *path;
int r;
assert(m);
if (!path)
return 0;
if (r == -EINVAL) /* not a unit */
return 0;
if (r < 0) {
log_oom();
return 0;
}
if (session)
if (user)
return 0;
}
Iterator i;
int b, r;
assert(m);
if (r < 0) {
return 0;
}
if (b)
return 0;
/* systemd finished reloading, let's recheck all our sessions */
log_debug("System manager has been reloaded, rechecking sessions...");
return 0;
}
char **l;
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
l);
}
const char *j;
char *copy;
int r;
if (r < 0)
return r;
if (!copy)
return -ENOMEM;
return 1;
}
int manager_start_slice(
const char *slice,
const char *description,
const char *after,
const char *after2,
char **job) {
int r;
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (!isempty(description)) {
if (r < 0)
return r;
}
if (r < 0)
return r;
}
if (r < 0)
return r;
}
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;
if (r < 0)
return r;
}
int manager_start_scope(
const char *scope,
const char *slice,
const char *description,
const char *after,
const char *after2,
char **job) {
int r;
&m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
}
if (!isempty(description)) {
if (r < 0)
return r;
}
if (r < 0)
return r;
}
if (r < 0)
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 */
if (r < 0)
return r;
if (r < 0)
return r;
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;
if (r < 0)
return r;
}
int r;
r = sd_bus_call_method(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&reply,
if (r < 0)
return r;
}
int r;
r = sd_bus_call_method(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StopUnit",
&reply,
if (r < 0) {
return 0;
}
return r;
}
}
int r;
if (!path)
return -ENOMEM;
r = sd_bus_call_method(
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Scope",
"Abandon",
NULL,
NULL);
if (r < 0) {
return 0;
}
return r;
}
return 1;
}
int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) {
return sd_bus_call_method(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"KillUnit",
NULL,
}
const char *state;
int r;
if (!path)
return -ENOMEM;
r = sd_bus_get_property(
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"ActiveState",
&error,
&reply,
"s");
if (r < 0) {
/* systemd might have droppped off momentarily, let's
* not make this an error */
return true;
/* If the unit is already unloaded then it's not
* active */
return false;
return r;
}
if (r < 0)
return -EINVAL;
}
int r;
r = sd_bus_get_property(
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Job",
"State",
&error,
&reply,
"s");
if (r < 0) {
return true;
return false;
return r;
}
/* We don't actually care about the state really. The fact
* that we could read the job state is enough for us */
return true;
}