chiark / gitweb /
logind: implement delay inhibitor locks in addition to block inhibitor locks
authorLennart Poettering <lennart@poettering.net>
Fri, 4 May 2012 22:34:48 +0000 (00:34 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 4 May 2012 22:36:08 +0000 (00:36 +0200)
This is useful to allow applications to synchronously save data before
the system is suspended or shut down.

16 files changed:
.gitignore
Makefile.am
TODO
man/logind.conf.xml
man/systemd-inhibit.xml [new file with mode: 0644]
src/login/inhibit.c [new file with mode: 0644]
src/login/logind-dbus.c
src/login/logind-gperf.gperf
src/login/logind-inhibit.c
src/login/logind-inhibit.h
src/login/logind.c
src/login/logind.conf
src/login/logind.h
src/login/org.freedesktop.login1.policy.in
src/shared/dbus-common.c
src/shared/dbus-common.h

index a921c76..7f22255 100644 (file)
@@ -1,3 +1,4 @@
+/systemd-inhibit
 /systemd-remount-fs
 /build-aux
 /test-watchdog
index fd08001..c85a401 100644 (file)
@@ -481,7 +481,8 @@ MANPAGES = \
        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 \
@@ -2666,6 +2667,20 @@ loginctl_LDADD = \
 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
 
diff --git a/TODO b/TODO
index d39c8fc..b241a51 100644 (file)
--- a/TODO
+++ b/TODO
@@ -25,6 +25,16 @@ Features:
 * 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
index 6a10fa9..deca1cd 100644 (file)
                                 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
diff --git a/man/systemd-inhibit.xml b/man/systemd-inhibit.xml
new file mode 100644 (file)
index 0000000..3409590
--- /dev/null
@@ -0,0 +1,179 @@
+<?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>
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
new file mode 100644 (file)
index 0000000..a817c84
--- /dev/null
@@ -0,0 +1,353 @@
+/*-*- 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;
+}
index 3fe6c87..a361b93 100644 (file)
         "   <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                                             \
@@ -239,7 +245,7 @@ static int bus_manager_append_inhibited(DBusMessageIter *i, const char *property
         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))
@@ -638,9 +644,10 @@ fail:
 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;
@@ -657,6 +664,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
                             DBUS_TYPE_STRING, &what,
                             DBUS_TYPE_STRING, &who,
                             DBUS_TYPE_STRING, &why,
+                            DBUS_TYPE_STRING, &mode,
                             DBUS_TYPE_INVALID)) {
                 r = -EIO;
                 goto fail;
@@ -668,7 +676,16 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
                 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;
 
@@ -701,6 +718,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa
                 goto fail;
 
         i->what = w;
+        i->mode = mm;
         i->pid = pid;
         i->uid = (uid_t) ul;
         i->why = strdup(why);
@@ -918,6 +936,76 @@ static int have_multiple_sessions(
         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 },
@@ -929,7 +1017,9 @@ static const BusProperty bus_login_manager_properties[] = {
         { "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, }
 };
 
@@ -1228,26 +1318,28 @@ static DBusHandlerResult manager_message_handler(
 
                 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;
@@ -1641,10 +1733,8 @@ static DBusHandlerResult manager_message_handler(
         } 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,
@@ -1658,7 +1748,7 @@ static DBusHandlerResult manager_message_handler(
                         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") ?
@@ -1670,7 +1760,7 @@ static DBusHandlerResult manager_message_handler(
                                 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";
@@ -1680,7 +1770,7 @@ static DBusHandlerResult manager_message_handler(
                                 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";
@@ -1690,32 +1780,26 @@ static DBusHandlerResult manager_message_handler(
                                 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)
@@ -1732,7 +1816,7 @@ static DBusHandlerResult manager_message_handler(
                         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") ?
@@ -1943,3 +2027,39 @@ finish:
 
         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;
+}
index 940fe10..d8ef92a 100644 (file)
@@ -20,3 +20,4 @@ Login.KillOnlyUsers,     config_parse_strv,     0, offsetof(Manager, kill_only_u
 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)
index 78afee3..e4eefd0 100644 (file)
@@ -97,9 +97,11 @@ int inhibitor_save(Inhibitor *i) {
         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);
 
@@ -152,9 +154,10 @@ int inhibitor_start(Inhibitor *i) {
 
         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);
 
@@ -169,9 +172,10 @@ int inhibitor_stop(Inhibitor *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);
@@ -185,13 +189,15 @@ int inhibitor_stop(Inhibitor *i) {
 
 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,
@@ -199,17 +205,25 @@ int inhibitor_load(Inhibitor *i) {
                            "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);
@@ -314,7 +328,7 @@ void inhibitor_remove_fifo(Inhibitor *i) {
         }
 }
 
-InhibitWhat manager_inhibit_what(Manager *m) {
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) {
         Inhibitor *i;
         Iterator j;
         InhibitWhat what = 0;
@@ -322,12 +336,13 @@ InhibitWhat manager_inhibit_what(Manager *m) {
         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 };
@@ -340,6 +355,9 @@ bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) {
                 if (!(i->what & w))
                         continue;
 
+                if (i->mode != mm)
+                        continue;
+
                 if (!inhibited ||
                     i->since.monotonic < ts.monotonic)
                         ts = i->since;
@@ -391,3 +409,10 @@ InhibitWhat inhibit_what_from_string(const char *s) {
         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);
index 1d47cfc..823af39 100644 (file)
@@ -37,6 +37,13 @@ typedef enum InhibitWhat {
         _INHIBIT_WHAT_INVALID = -1
 } InhibitWhat;
 
+typedef enum InhibitMode {
+        INHIBIT_BLOCK,
+        INHIBIT_DELAY,
+        _INHIBIT_MODE_MAX,
+        _INHIBIT_MODE_INVALID = -1
+} InhibitMode;
+
 struct Inhibitor {
         Manager *manager;
 
@@ -48,6 +55,7 @@ struct Inhibitor {
         InhibitWhat what;
         char *who;
         char *why;
+        InhibitMode mode;
 
         pid_t pid;
         uid_t uid;
@@ -70,10 +78,13 @@ int inhibitor_stop(Inhibitor *i);
 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
index 6b7012e..5860bfc 100644 (file)
@@ -50,6 +50,7 @@ Manager *manager_new(void) {
         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);
@@ -1163,7 +1164,7 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
 
         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;
@@ -1264,15 +1265,28 @@ int manager_run(Manager *m) {
         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;
@@ -1281,6 +1295,9 @@ int manager_run(Manager *m) {
                         return -errno;
                 }
 
+                if (n == 0)
+                        continue;
+
                 switch (event.data.u32) {
 
                 case FD_SEAT_UDEV:
index 6a2cefa..c0779a7 100644 (file)
@@ -14,3 +14,4 @@
 #KillExcludeUsers=root
 #Controllers=
 #ResetControllers=cpu
+#InhibitDelayMaxSec=5
index 4e9dcd5..2c05452 100644 (file)
@@ -81,6 +81,14 @@ struct Manager {
         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 {
@@ -132,6 +140,8 @@ DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, vo
 
 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);
 
index a2dc402..76ed2be 100644 (file)
@@ -16,9 +16,9 @@
         <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>
@@ -38,7 +48,7 @@
 
         <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>
@@ -48,7 +58,7 @@
 
         <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>
@@ -58,7 +68,7 @@
 
         <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>
@@ -68,7 +78,7 @@
 
         <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>
@@ -78,7 +88,7 @@
 
         <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>
@@ -88,7 +98,7 @@
 
         <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>
index e161273..ddb50b1 100644 (file)
@@ -245,7 +245,8 @@ int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) {
 }
 
 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 */
@@ -255,6 +256,14 @@ const char *bus_error_message(const DBusError *error) {
         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,
index edb8107..9ae35df 100644 (file)
@@ -91,6 +91,7 @@ int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **
 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);