Commit eecd1362 authored by Lennart Poettering's avatar Lennart Poettering
Browse files

logind: implement delay inhibitor locks in addition to block inhibitor locks

This is useful to allow applications to synchronously save data before
the system is suspended or shut down.
parent a26336da
/systemd-inhibit
/systemd-remount-fs
/build-aux
/test-watchdog
......
......@@ -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
......
......@@ -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
......
......@@ -146,6 +146,20 @@
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
......
<?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>
/*-*- 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;
}
......@@ -144,10 +144,11 @@
" <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" \
......@@ -173,6 +174,9 @@
" <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" \
......@@ -183,7 +187,9 @@
" <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \