+/systemd-inhibit
/systemd-remount-fs
/build-aux
/test-watchdog
man/systemd-machine-id-setup.1 \
man/systemd-detect-virt.1 \
man/journald.conf.5 \
- man/journalctl.1
+ man/journalctl.1 \
+ man/systemd-inhibit.1
MANPAGES_ALIAS = \
man/reboot.8 \
rootbin_PROGRAMS += \
loginctl
+systemd_inhibit_SOURCES = \
+ src/login/inhibit.c
+
+systemd_inhibit_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(DBUS_CFLAGS)
+
+systemd_inhibit_LDADD = \
+ libsystemd-shared.la \
+ libsystemd-dbus.la
+
+rootbin_PROGRAMS += \
+ systemd-inhibit
+
test_login_SOURCES = \
src/login/test-login.c
* improve !/proc/*/loginuid situation: make /proc/*/loginuid less dependent on CONFIG_AUDIT,
or use the users cgroup information when /proc/*/loginuid is not available.
+* pam_systemd: try to get old session id from cgroup, if audit sessionid cannot be determined
+
+* logind: auto-suspend, auto-shutdown:
+ IdleAction=(none|suspend|hibernate|poweroff)
+ IdleActionDelay=...
+ SessionIdleMode=(explicit|ignore|login)
+ ForceShutdown=(yes|no)
+
+* logind: use "sleep" as generic term for "suspend", "hibernate", ...
+
* services which create their own subcgroups break cgroup-empty notification (needs to be fixed in the kernel)
* don't delete /tmp/systemd-namespace-* before a process is gone down
defaults to
<literal>cpu</literal>.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>InhibitDelayMaxSec=</varname></term>
+
+ <listitem><para>Specifies the maximum
+ time a suspend or reboot is delayed
+ due to an inhibitor lock of type
+ <literal>delay</literal> being taken
+ before it is ignored and the operation
+ executed anyway. Defaults to
+ 5s.</para></listitem>
+
+ </varlistentry>
+
</variablelist>
<para>Note that setting
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="systemd-inhibit">
+
+ <refentryinfo>
+ <title>systemd-inhibit</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <email>lennart@poettering.net</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-inhibit</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-inhibit</refname>
+ <refpurpose>Execute a program with an inhibition lock taken</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-inhibit <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>COMMAND</arg> <arg choice="opt" rep="repeat">ARGUMENTS</arg></command>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-inhibit <arg choice="opt" rep="repeat">OPTIONS</arg> --list</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-inhibit</command> may be used
+ to execute a program with a shutdown, suspend or idle
+ inhibitor lock taken. The lock will be acquired before
+ the specified command line is executed and released
+ afterwards.</para>
+
+ <para>Inhibitor locks may be used to block or delay
+ suspend and shutdown requests from the user, as well
+ as automatic idle handling of the OS. This may be used
+ to avoid system suspends while an optical disc is
+ being recorded, or similar operations that should not
+ be interrupted.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--h</option></term>
+ <term><option>--help</option></term>
+
+ <listitem><para>Prints a short help
+ text and exits.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--version</option></term>
+
+ <listitem><para>Prints a short version
+ string and exits.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--what=</option></term>
+
+ <listitem><para>Takes a colon
+ separated list of one or more
+ operations to inhibit:
+ <literal>shutdown</literal>,
+ <literal>suspend</literal>,
+ <literal>idle</literal>, for
+ inhibiting reboot/power-off/halt/kexec,
+ suspending/hibernating, resp. the
+ automatic idle
+ detection.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--who=</option></term>
+
+ <listitem><para>Takes a short human
+ readable descriptive string for the
+ program taking the lock. If not passed
+ defaults to the command line
+ string.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--why=</option></term>
+
+ <listitem><para>Takes a short human
+ readable descriptive string for the
+ reason for taking the lock. Defaults
+ to "Unknown reason".</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--mode=</option></term>
+
+ <listitem><para>Takes either
+ <literal>block</literal> or
+ <literal>delay</literal> and describes
+ how the lock is applied. If
+ <literal>block</literal> is used (the
+ default), the lock prohibits any of
+ the requested operations without time
+ limit, and only privileged users may
+ override it. If
+ <literal>delay</literal> is used, the
+ lock can only delay the requested
+ operations for a limited time. If the
+ time elapses the lock is ignored and
+ the operation executed. The time limit
+ may be specified in
+ <citerefentry><refentrytitle>systemd-logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--list</option></term>
+
+ <listitem><para>Lists all active
+ inhibition locks instead of acquiring
+ one.</para></listitem>
+ </varlistentry>
+
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>Returns the exit status of the executed program.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <getopt.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dbus.h>
+#include <unistd.h>
+
+#include "dbus-common.h"
+#include "util.h"
+#include "build.h"
+#include "strv.h"
+
+static const char* arg_what = "idle:suspend:shutdown";
+static const char* arg_who = NULL;
+static const char* arg_why = "Unknown reason";
+static const char* arg_mode = "block";
+
+static enum {
+ ACTION_INHIBIT,
+ ACTION_LIST
+} arg_action = ACTION_INHIBIT;
+
+static int inhibit(DBusConnection *bus, DBusError *error) {
+ DBusMessage *m = NULL, *reply = NULL;
+ int fd;
+
+ assert(bus);
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "Inhibit");
+ if (!m)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &arg_what,
+ DBUS_TYPE_STRING, &arg_who,
+ DBUS_TYPE_STRING, &arg_why,
+ DBUS_TYPE_STRING, &arg_mode,
+ DBUS_TYPE_INVALID)) {
+ fd = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ fd = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, error,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID)){
+ fd = -EIO;
+ goto finish;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return fd;
+}
+
+static int print_inhibitors(DBusConnection *bus, DBusError *error) {
+ DBusMessage *m, *reply;
+ unsigned n = 0;
+ DBusMessageIter iter, sub, sub2;
+ int r;
+
+ assert(bus);
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListInhibitors");
+ if (!m)
+ return -ENOMEM;
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ r = -EIO;
+ goto finish;
+ }
+ dbus_message_iter_recurse(&iter, &sub);
+
+ printf("%-21s %-20s %-20s %-5s %6s %6s\n",
+ "WHAT",
+ "WHO",
+ "WHY",
+ "MODE",
+ "UID",
+ "PID");
+
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *what, *who, *why, *mode;
+ char *ewho, *ewhy;
+ dbus_uint32_t uid, pid;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ ewho = ellipsize(who, 20, 66);
+ ewhy = ellipsize(why, 20, 66);
+
+ printf("%-21s %-20s %-20s %-5s %6lu %6lu\n",
+ what, ewho ? ewho : who, ewhy ? ewhy : why, mode, (unsigned long) uid, (unsigned long) pid);
+
+ free(ewho);
+ free(ewhy);
+
+ dbus_message_iter_next(&sub);
+
+ n++;
+ }
+
+ printf("\n%u inhibitors listed.\n", n);
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Execute a process while inhibiting shutdown/suspend/idle.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --what=WHAT Operations to inhibit, colon separated list of idle,\n"
+ " suspend, shutdown\n"
+ " --who=STRING A descriptive string who is inhibiting\n"
+ " --why=STRING A descriptive string why is being inhibited\n"
+ " --mode=MODE One of block or delay\n"
+ " --list List active inhibitors\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_WHAT,
+ ARG_WHO,
+ ARG_WHY,
+ ARG_MODE,
+ ARG_LIST,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "what", required_argument, NULL, ARG_WHAT },
+ { "who", required_argument, NULL, ARG_WHO },
+ { "why", required_argument, NULL, ARG_WHY },
+ { "mode", required_argument, NULL, ARG_MODE },
+ { "list", no_argument, NULL, ARG_LIST },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(DISTRIBUTION);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_WHAT:
+ arg_what = optarg;
+ break;
+
+ case ARG_WHO:
+ arg_who = optarg;
+ break;
+
+ case ARG_WHY:
+ arg_why = optarg;
+ break;
+
+ case ARG_MODE:
+ arg_mode = optarg;
+ break;
+
+ case ARG_LIST:
+ arg_action = ACTION_LIST;
+ break;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (arg_action == ACTION_INHIBIT && optind >= argc) {
+ log_error("Missing command line to execute.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r, exit_code = 0;
+ DBusConnection *bus = NULL;
+ DBusError error;
+ int fd = -1;
+
+ dbus_error_init(&error);
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+ if (!bus) {
+ log_error("Failed to connect to bus: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (arg_action == ACTION_LIST) {
+
+ r = print_inhibitors(bus, &error);
+ if (r < 0) {
+ log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r));
+ goto finish;
+ }
+
+ } else {
+ char *w = NULL;
+ pid_t pid;
+
+ if (!arg_who)
+ arg_who = w = strv_join(argv + optind, " ");
+
+ fd = inhibit(bus, &error);
+ free(w);
+
+ if (fd < 0) {
+ log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r));
+ r = fd;
+ goto finish;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if (pid == 0) {
+ /* Child */
+
+ close_nointr_nofail(fd);
+ execvp(argv[optind], argv + optind);
+ log_error("Failed to execute %s: %m", argv[optind]);
+ _exit(EXIT_FAILURE);
+ }
+
+ r = wait_for_terminate_and_warn(argv[optind], pid);
+ if (r >= 0)
+ exit_code = r;
+ }
+
+finish:
+ if (bus) {
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ }
+
+ dbus_error_free(&error);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r < 0 ? EXIT_FAILURE : exit_code;
+}
" <arg name=\"what\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"why\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListInhibitors\">\n" \
- " <arg name=\"inhibitors\" type=\"a(sssuu)\" direction=\"out\"/>\n" \
+ " <arg name=\"inhibitors\" type=\"a(ssssuu)\" direction=\"out\"/>\n" \
" </method>\n" \
" <signal name=\"SessionNew\">\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
+ " <signal name=\"PrepareForShutdown\">\n" \
+ " <arg name=\"active\" type=\"b\"/>\n" \
+ " </signal>\n" \
" <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"Inhibited\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"BlockInhibited\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DelayInhibited\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"InhibitDelayMaxUSec\" type=\"t\" access=\"read\"/>\n" \
" </interface>\n"
#define INTROSPECTION_BEGIN \
InhibitWhat w;
const char *p;
- w = manager_inhibit_what(m);
+ w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY);
p = inhibit_what_to_string(w);
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &p))
static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessage *message, DBusError *error, DBusMessage **_reply) {
Inhibitor *i = NULL;
char *id = NULL;
- const char *who, *why, *what;
+ const char *who, *why, *what, *mode;
pid_t pid;
InhibitWhat w;
+ InhibitMode mm;
unsigned long ul;
int r, fifo_fd = -1;
DBusMessage *reply = NULL;
DBUS_TYPE_STRING, &what,
DBUS_TYPE_STRING, &who,
DBUS_TYPE_STRING, &why,
+ DBUS_TYPE_STRING, &mode,
DBUS_TYPE_INVALID)) {
r = -EIO;
goto fail;
goto fail;
}
- r = verify_polkit(connection, message, "org.freedesktop.login1.inhibit", false, NULL, error);
+ mm = inhibit_mode_from_string(mode);
+ if (mm < 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ r = verify_polkit(connection, message,
+ m == INHIBIT_BLOCK ?
+ "org.freedesktop.login1.inhibit-block" :
+ "org.freedesktop.login1.inhibit-delay", false, NULL, error);
if (r < 0)
goto fail;
goto fail;
i->what = w;
+ i->mode = mm;
i->pid = pid;
i->uid = (uid_t) ul;
i->why = strdup(why);
return false;
}
+static int send_start_unit(DBusConnection *connection, const char *name, DBusError *error) {
+ DBusMessage *message, *reply;
+ const char *mode = "replace";
+
+ assert(connection);
+ assert(name);
+
+ message = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartUnit");
+ if (!message)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(message);
+ return -ENOMEM;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(connection, message, -1, error);
+ dbus_message_unref(message);
+
+ if (!reply)
+ return -EIO;
+
+ dbus_message_unref(reply);
+ return 0;
+}
+
+static int send_prepare_for_shutdown(Manager *m, bool _active) {
+ dbus_bool_t active = _active;
+ DBusMessage *message;
+ int r = 0;
+
+ assert(m);
+
+ message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForShutdown");
+ if (!message)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) ||
+ !dbus_connection_send(m->bus, message, NULL))
+ r = -ENOMEM;
+
+ dbus_message_unref(message);
+ return r;
+}
+
+static int delay_shutdown(Manager *m, const char *name) {
+ assert(m);
+
+ if (!m->delayed_shutdown) {
+ /* Tell everybody to prepare for shutdown */
+ send_prepare_for_shutdown(m, true);
+
+ /* Update timestamp for timeout */
+ m->delayed_shutdown_timestamp = now(CLOCK_MONOTONIC);
+ }
+
+ /* Remember what we want to do, possibly overriding what kind
+ * of shutdown we previously queued. */
+ m->delayed_shutdown = name;
+
+ return 0;
+}
+
static const BusProperty bus_login_manager_properties[] = {
{ "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
{ "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
{ "IdleHint", bus_manager_append_idle_hint, "b", 0 },
{ "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 },
{ "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 },
- { "Inhibited", bus_manager_append_inhibited, "s", 0 },
+ { "BlockInhibited", bus_manager_append_inhibited, "s", 0 },
+ { "DelayInhibited", bus_manager_append_inhibited, "s", 0 },
+ { "InhibitDelayMaxUSec", bus_property_append_usec, "t", offsetof(Manager, inhibit_delay_max) },
{ NULL, }
};
dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssuu)", &sub))
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssuu)", &sub))
goto oom;
HASHMAP_FOREACH(inhibitor, m->inhibitors, i) {
DBusMessageIter sub2;
dbus_uint32_t uid, pid;
- const char *what, *who, *why;
+ const char *what, *who, *why, *mode;
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
goto oom;
- what = inhibit_what_to_string(inhibitor->what);
+ what = strempty(inhibit_what_to_string(inhibitor->what));
who = strempty(inhibitor->who);
why = strempty(inhibitor->why);
+ mode = strempty(inhibit_mode_to_string(inhibitor->mode));
uid = (dbus_uint32_t) inhibitor->uid;
pid = (dbus_uint32_t) inhibitor->pid;
if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &what) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &who) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &why) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &mode) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid))
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") ||
dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) {
dbus_bool_t interactive;
- bool multiple_sessions, inhibit;
- DBusMessage *forward, *freply;
+ bool multiple_sessions, blocked, delayed;
const char *name, *action;
- const char *mode = "replace";
if (!dbus_message_get_args(
message,
return bus_send_error_reply(connection, message, &error, r);
multiple_sessions = r > 0;
- inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, NULL);
+ blocked = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
if (multiple_sessions) {
action = streq(dbus_message_get_member(message), "PowerOff") ?
return bus_send_error_reply(connection, message, &error, r);
}
- if (inhibit) {
+ if (blocked) {
action = streq(dbus_message_get_member(message), "PowerOff") ?
"org.freedesktop.login1.power-off-ignore-inhibit" :
"org.freedesktop.login1.reboot-ignore-inhibit";
return bus_send_error_reply(connection, message, &error, r);
}
- if (!multiple_sessions && !inhibit) {
+ if (!multiple_sessions && !blocked) {
action = streq(dbus_message_get_member(message), "PowerOff") ?
"org.freedesktop.login1.power-off" :
"org.freedesktop.login1.reboot";
return bus_send_error_reply(connection, message, &error, r);
}
- forward = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "StartUnit");
- if (!forward)
- return bus_send_error_reply(connection, message, NULL, -ENOMEM);
-
name = streq(dbus_message_get_member(message), "PowerOff") ?
SPECIAL_POWEROFF_TARGET : SPECIAL_REBOOT_TARGET;
- if (!dbus_message_append_args(forward,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &mode,
- DBUS_TYPE_INVALID)) {
- dbus_message_unref(forward);
- return bus_send_error_reply(connection, message, NULL, -ENOMEM);
- }
-
- freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error);
- dbus_message_unref(forward);
+ delayed =
+ m->inhibit_delay_max > 0 &&
+ manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
- if (!freply)
- return bus_send_error_reply(connection, message, &error, -EIO);
-
- dbus_message_unref(freply);
+ if (delayed) {
+ /* Shutdown is delayed, keep in mind what we
+ * want to do, and start a timeout */
+ r = delay_shutdown(m, name);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+ } else {
+ /* Shutdown is not delayed, execute it
+ * immediately */
+ r = send_start_unit(connection, name, &error);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+ }
reply = dbus_message_new_method_return(message);
if (!reply)
return bus_send_error_reply(connection, message, &error, r);
multiple_sessions = r > 0;
- inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, NULL);
+ inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL);
if (multiple_sessions) {
action = streq(dbus_message_get_member(message), "CanPowerOff") ?
return r;
}
+
+int manager_dispatch_delayed_shutdown(Manager *manager) {
+ const char *name;
+ DBusError error;
+ bool delayed;
+ int r;
+
+ assert(manager);
+
+ if (!manager->delayed_shutdown)
+ return 0;
+
+ /* Continue delay? */
+ delayed =
+ manager->delayed_shutdown_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) &&
+ manager_is_inhibited(manager, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL);
+ if (delayed)
+ return 0;
+
+ /* Reset delay data */
+ name = manager->delayed_shutdown;
+ manager->delayed_shutdown = NULL;
+
+ /* Actually do the shutdown */
+ dbus_error_init(&error);
+ r = send_start_unit(manager->bus, name, &error);
+ if (r < 0) {
+ log_warning("Failed to send delayed shutdown message: %s", bus_error_message_or_strerror(&error, -r));
+ return r;
+ }
+
+ /* Tell people about it */
+ send_prepare_for_shutdown(manager, false);
+
+ return 1;
+}
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers)
Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers)
+Login.InhibitDelayMaxSec,config_parse_usec, 0, offsetof(Manager, inhibit_delay_max)
fprintf(f,
"# This is private data. Do not parse.\n"
"WHAT=%s\n"
+ "MODE=%s\n"
"UID=%lu\n"
"PID=%lu\n",
inhibit_what_to_string(i->what),
+ inhibit_mode_to_string(i->mode),
(unsigned long) i->uid,
(unsigned long) i->pid);
dual_timestamp_get(&i->since);
- log_debug("Inhibitor %s (%s) pid=%lu uid=%lu started.",
+ log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s started.",
strna(i->who), strna(i->why),
- (unsigned long) i->pid, (unsigned long) i->uid);
+ (unsigned long) i->pid, (unsigned long) i->uid,
+ inhibit_mode_to_string(i->mode));
inhibitor_save(i);
assert(i);
if (i->started)
- log_debug("Inhibitor %s (%s) pid=%lu uid=%lu stopped.",
+ log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s stopped.",
strna(i->who), strna(i->why),
- (unsigned long) i->pid, (unsigned long) i->uid);
+ (unsigned long) i->pid, (unsigned long) i->uid,
+ inhibit_mode_to_string(i->mode));
if (i->state_file)
unlink(i->state_file);
int inhibitor_load(Inhibitor *i) {
InhibitWhat w;
+ InhibitMode mm;
int r;
char *cc,
*what = NULL,
*uid = NULL,
*pid = NULL,
*who = NULL,
- *why = NULL;
+ *why = NULL,
+ *mode = NULL;
r = parse_env_file(i->state_file, NEWLINE,
"WHAT", &what,
"PID", &pid,
"WHO", &who,
"WHY", &why,
+ "MODE", &mode,
"FIFO", &i->fifo_path,
NULL);
if (r < 0)
goto finish;
- w = inhibit_what_from_string(what);
+ w = what ? inhibit_what_from_string(what) : 0;
if (w >= 0)
i->what = w;
- parse_uid(uid, &i->uid);
- parse_pid(pid, &i->pid);
+ mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK;
+ if (mm >= 0)
+ i->mode = mm;
+
+ if (uid)
+ parse_uid(uid, &i->uid);
+
+ if (pid)
+ parse_pid(pid, &i->pid);
if (who) {
cc = cunescape(who);
}
}
-InhibitWhat manager_inhibit_what(Manager *m) {
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
Inhibitor *i;
Iterator j;
InhibitWhat what = 0;
assert(m);
HASHMAP_FOREACH(i, m->inhibitor_fds, j)
- what |= i->what;
+ if (i->mode == mm)
+ what |= i->what;
return what;
}
-bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) {
+bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since) {
Inhibitor *i;
Iterator j;
struct dual_timestamp ts = { 0, 0 };
if (!(i->what & w))
continue;
+ if (i->mode != mm)
+ continue;
+
if (!inhibited ||
i->since.monotonic < ts.monotonic)
ts = i->since;
return what;
}
+
+static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
+ [INHIBIT_BLOCK] = "block",
+ [INHIBIT_DELAY] = "delay"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);
_INHIBIT_WHAT_INVALID = -1
} InhibitWhat;
+typedef enum InhibitMode {
+ INHIBIT_BLOCK,
+ INHIBIT_DELAY,
+ _INHIBIT_MODE_MAX,
+ _INHIBIT_MODE_INVALID = -1
+} InhibitMode;
+
struct Inhibitor {
Manager *manager;
InhibitWhat what;
char *who;
char *why;
+ InhibitMode mode;
pid_t pid;
uid_t uid;
int inhibitor_create_fifo(Inhibitor *i);
void inhibitor_remove_fifo(Inhibitor *i);
-InhibitWhat manager_inhibit_what(Manager *m);
-bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since);
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm);
+bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since);
const char *inhibit_what_to_string(InhibitWhat k);
InhibitWhat inhibit_what_from_string(const char *s);
+const char *inhibit_mode_to_string(InhibitMode k);
+InhibitMode inhibit_mode_from_string(const char *s);
+
#endif
m->udev_vcsa_fd = -1;
m->epoll_fd = -1;
m->n_autovts = 6;
+ m->inhibit_delay_max = 5 * USEC_PER_SEC;
m->devices = hashmap_new(string_hash_func, string_compare_func);
m->seats = hashmap_new(string_hash_func, string_compare_func);
assert(m);
- idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, t);
+ idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t);
HASHMAP_FOREACH(s, m->sessions, i) {
dual_timestamp k;
for (;;) {
struct epoll_event event;
int n;
+ int msec = -1;
manager_gc(m, true);
+ if (manager_dispatch_delayed_shutdown(m) > 0)
+ continue;
+
if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
continue;
manager_gc(m, true);
- n = epoll_wait(m->epoll_fd, &event, 1, -1);
+ if (m->delayed_shutdown) {
+ usec_t x, y;
+
+ x = now(CLOCK_MONOTONIC);
+ y = m->delayed_shutdown_timestamp + m->inhibit_delay_max;
+
+ msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC);
+ }
+
+ n = epoll_wait(m->epoll_fd, &event, 1, msec);
if (n < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -errno;
}
+ if (n == 0)
+ continue;
+
switch (event.data.u32) {
case FD_SEAT_UDEV:
#KillExcludeUsers=root
#Controllers=
#ResetControllers=cpu
+#InhibitDelayMaxSec=5
Hashmap *cgroups;
Hashmap *session_fds;
Hashmap *inhibitor_fds;
+
+ /* If a shutdown was delayed due to a inhibitor this contains
+ the unit name we are supposed to start after the delay is
+ over */
+ const char *delayed_shutdown;
+ usec_t delayed_shutdown_timestamp;
+
+ usec_t inhibit_delay_max;
};
enum {
int manager_send_changed(Manager *manager, const char *properties);
+int manager_dispatch_delayed_shutdown(Manager *manager);
+
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
<vendor>The systemd Project</vendor>
<vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
- <action id="org.freedesktop.login1.inhibit">
+ <action id="org.freedesktop.login1.inhibit-block">
<_description>Allow applications to inhibit system shutdown and suspend</_description>
- <_message>Authentication is required to allow an application to inhibit system shutdown or suspend</_message>
+ <_message>Authentication is required to allow an application to inhibit system shutdown or suspend.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>yes</allow_inactive>
</defaults>
</action>
+ <action id="org.freedesktop.login1.inhibit-delay">
+ <_description>Allow applications to delay system shutdown and suspend</_description>
+ <_message>Authentication is required to allow an application to delay system shutdown or suspend.</_message>
+ <defaults>
+ <allow_any>yes</allow_any>
+ <allow_inactive>yes</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.login1.set-user-linger">
<_description>Allow non-logged-in users to run programs</_description>
- <_message>Authentication is required to allow a non-logged-in user to run programs</_message>
+ <_message>Authentication is required to allow a non-logged-in user to run programs.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.attach-device">
<_description>Allow attaching devices to seats</_description>
- <_message>Authentication is required to allow attaching a device to a seat</_message>
+ <_message>Authentication is required to allow attaching a device to a seat.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.flush-devices">
<_description>Flush device to seat attachments</_description>
- <_message>Authentication is required to allow resetting how devices are attached to seats</_message>
+ <_message>Authentication is required to allow resetting how devices are attached to seats.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.power-off">
<_description>Power off the system</_description>
- <_message>Authentication is required to allow powering off the system</_message>
+ <_message>Authentication is required to allow powering off the system.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.power-off-multiple-sessions">
<_description>Power off the system when other users are logged in</_description>
- <_message>Authentication is required to allow powering off the system while other users are logged in</_message>
+ <_message>Authentication is required to allow powering off the system while other users are logged in.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.power-off-ignore-inhibit">
<_description>Power off the system when an application asked to inhibit it</_description>
- <_message>Authentication is required to allow powering off the system while an application asked to inhibit it</_message>
+ <_message>Authentication is required to allow powering off the system while an application asked to inhibit it.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.reboot">
<_description>Reboot the system</_description>
- <_message>Authentication is required to allow rebooting the system</_message>
+ <_message>Authentication is required to allow rebooting the system.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.reboot-multiple-sessions">
<_description>Reboot the system when other users are logged in</_description>
- <_message>Authentication is required to allow rebooting the system while other users are logged in</_message>
+ <_message>Authentication is required to allow rebooting the system while other users are logged in.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.reboot-ignore-inhibit">
<_description>Reboot the system when an application asked to inhibit it</_description>
- <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it</_message>
+ <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
}
const char *bus_error_message(const DBusError *error) {
- assert(error);
+ if (!error)
+ return NULL;
/* Sometimes the D-Bus server is a little bit too verbose with
* its error messages, so let's override them here */
return error->message;
}
+const char *bus_error_message_or_strerror(const DBusError *error, int err) {
+
+ if (error && dbus_error_is_set(error))
+ return bus_error_message(error);
+
+ return strerror(err);
+}
+
DBusHandlerResult bus_default_message_handler(
DBusConnection *c,
DBusMessage *message,
int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error);
const char *bus_error_message(const DBusError *error);
+const char *bus_error_message_or_strerror(const DBusError *error, int err);
typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, void *data);
typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property, void *data);