summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2012-10-28 00:49:04 +0200
committerLennart Poettering <lennart@poettering.net>2012-10-28 00:50:35 +0200
commit6524990fdc98370ecba5d9f73e67161e8798c010 (patch)
tree870ed57ed3c756dd34a0223692d1ec5b9e5ef44d
parent2001208c2ab631a69896d1f670c26846b70d1fb7 (diff)
logind: support for hybrid sleep (i.e. suspend+hibernate at the same time)
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am17
-rw-r--r--man/logind.conf.xml5
-rw-r--r--man/systemctl.xml5
-rw-r--r--man/systemd-suspend.service.xml42
-rw-r--r--man/systemd.special.xml15
-rw-r--r--src/core/special.h1
-rw-r--r--src/login/logind-button.c21
-rw-r--r--src/login/logind-button.h1
-rw-r--r--src/login/logind-dbus.c69
-rw-r--r--src/shared/util.c24
-rw-r--r--src/shared/util.h1
-rw-r--r--src/sleep/sleep.c20
-rw-r--r--src/systemctl/systemctl.c17
-rw-r--r--src/test/test-sleep.c39
-rw-r--r--units/.gitignore1
-rw-r--r--units/hybrid-sleep.target13
-rw-r--r--units/systemd-hybrid-sleep.service.in17
18 files changed, 264 insertions, 45 deletions
diff --git a/.gitignore b/.gitignore
index 2fc2bfd28..94a85423a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
/test-journal-enum
+/test-sleep
/localectl
/hostnamectl
/timedatectl
diff --git a/Makefile.am b/Makefile.am
index 3d96500c8..fadc1c7b5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -273,6 +273,7 @@ dist_systemunit_DATA = \
units/nss-user-lookup.target \
units/mail-transfer-agent.target \
units/hibernate.target \
+ units/hybrid-sleep.target \
units/http-daemon.target \
units/poweroff.target \
units/reboot.target \
@@ -329,6 +330,7 @@ nodist_systemunit_DATA = \
units/rescue.service \
units/user@.service \
units/systemd-hibernate.service \
+ units/systemd-hybrid-sleep.service \
units/systemd-suspend.service \
units/systemd-halt.service \
units/systemd-poweroff.service \
@@ -379,6 +381,7 @@ EXTRA_DIST += \
units/systemd-udev-settle.service \
units/debug-shell.service.in \
units/systemd-hibernate.service.in \
+ units/systemd-hybrid-sleep.service.in \
units/systemd-suspend.service.in \
units/quotaon.service.in \
introspect.awk \
@@ -534,6 +537,7 @@ MANPAGES_ALIAS = \
man/systemd-shutdownd.socket.8 \
man/systemd-shutdownd.8 \
man/systemd-hibernate.service.8 \
+ man/systemd-hybrid-sleep.service.8 \
man/systemd-sleep.8 \
man/systemd-shutdown.8 \
man/systemd-poweroff.service.8 \
@@ -608,6 +612,7 @@ man/systemd-initctl.8: man/systemd-initctl.service.8
man/systemd-shutdownd.socket.8: man/systemd-shutdownd.service.8
man/systemd-shutdownd.8: man/systemd-shutdownd.service.8
man/systemd-hibernate.service.8: man/systemd-suspend.service.8
+man/systemd-hybrid-sleep.service.8: man/systemd-suspend.service.8
man/systemd-sleep.8: man/systemd-suspend.service.8
man/systemd-shutdown.8: man/systemd-halt.service.8
man/systemd-poweroff.service.8: man/systemd-halt.service.8
@@ -1176,7 +1181,8 @@ noinst_PROGRAMS += \
test-unit-name \
test-log \
test-unit-file \
- test-date
+ test-date \
+ test-sleep
TESTS += \
test-job-type \
@@ -1184,7 +1190,8 @@ TESTS += \
test-strv \
test-unit-name \
test-unit-file \
- test-date
+ test-date \
+ test-sleep
test_engine_SOURCES = \
src/test/test-engine.c
@@ -1252,6 +1259,12 @@ test_date_SOURCES = \
test_date_LDADD = \
libsystemd-core.la
+test_sleep_SOURCES = \
+ src/test/test-sleep.c
+
+test_sleep_LDADD = \
+ libsystemd-core.la
+
test_daemon_SOURCES = \
src/test/test-daemon.c
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
index 3d83d2c81..29469d37e 100644
--- a/man/logind.conf.xml
+++ b/man/logind.conf.xml
@@ -211,8 +211,9 @@
<literal>poweroff</literal>,
<literal>reboot</literal>,
<literal>halt</literal>,
- <literal>kexec</literal> and
- <literal>hibernate</literal>. If
+ <literal>kexec</literal>,
+ <literal>hibernate</literal> and
+ <literal>hybrid-sleep</literal>. If
<literal>ignore</literal> logind will
never handle these keys. Otherwise the
specified action will be taken in the
diff --git a/man/systemctl.xml b/man/systemctl.xml
index d54741069..786c2bdfc 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1181,6 +1181,11 @@
<listitem><para>Hibernate the system.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><command>hybrid-sleep</command></term>
+
+ <listitem><para>Hibernate and suspend the system.</para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><command>switch-root [ROOT] [INIT]</command></term>
<listitem><para>Switches to a
diff --git a/man/systemd-suspend.service.xml b/man/systemd-suspend.service.xml
index 604aab6f6..b9464c882 100644
--- a/man/systemd-suspend.service.xml
+++ b/man/systemd-suspend.service.xml
@@ -45,6 +45,7 @@
<refnamediv>
<refname>systemd-suspend.service</refname>
<refname>systemd-hibernate.service</refname>
+ <refname>systemd-hybrid-sleep.service</refname>
<refname>systemd-sleep</refname>
<refpurpose>System sleep state logic</refpurpose>
</refnamediv>
@@ -52,6 +53,7 @@
<refsynopsisdiv>
<para><filename>systemd-suspend.service</filename></para>
<para><filename>systemd-hibernate.service</filename></para>
+ <para><filename>systemd-hybrid-sleep.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-sleep</filename></para>
</refsynopsisdiv>
@@ -64,19 +66,25 @@
for the actual system suspend. Similar,
<filename>systemd-hibernate.service</filename> is
pulled in by <filename>hibernate.target</filename> to
- execute the actual hibernation.</para>
-
- <para>Immediately before entering system suspend and
- hibernation
- <filename>systemd-suspend.service</filename> will run
- all executables in
+ execute the actual hibernation. Finally,
+ <filename>systemd-hybrid-sleep.service</filename> is
+ pulled in by <filename>hybrid-sleep.target</filename>
+ to execute hybrid hibernation with system
+ suspend.</para>
+
+ <para>Immediately before entering system suspend
+ and/or hibernation
+ <filename>systemd-suspend.service</filename> (and the
+ other mentioned units, respectively) will run all
+ executables in
<filename>/usr/lib/systemd/system-sleep/</filename>
and pass two arguments to them. The first argument
will be "<literal>pre</literal>", the second either
- "<literal>suspend</literal>" or
- "<literal>hibernate</literal>", depending on the
+ "<literal>suspend</literal>",
+ "<literal>hibernate</literal>", or
+ "<literal>hybrid-sleep</literal>" depending on the
chosen action. Immediately after leaving system
- suspend and hibernation the same executables are run,
+ suspend and/or hibernation the same executables are run,
but the first argument is now
"<literal>post</literal>". All executables in this
directory are executed in parallel, and execution of
@@ -87,15 +95,17 @@
<filename>/usr/lib/systemd/system-sleep/</filename>
are intended for local use only and should be
considered hacks. If applications want to be notified
- of system suspend and resume there are much nicer
- interfaces available.</para>
+ of system suspend/hibernation and resume there are
+ much nicer interfaces available.</para>
<para>Note that
- <filename>systemd-suspend.service</filename> and
- <filename>systemd-hibernate.service</filename> should
- never be executed directly. Instead, trigger system
- sleep states with a command such as "<literal>systemctl
- suspend</literal>" or suchlike.</para>
+ <filename>systemd-suspend.service</filename>,
+ <filename>systemd-hibernate.service</filename> and
+ <filename>systemd-hybrid-sleep.service</filename>
+ should never be executed directly. Instead, trigger
+ system sleep states with a command such as
+ "<literal>systemctl suspend</literal>" or
+ similar.</para>
</refsect1>
<refsect1>
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index 3e5f65349..6b8e0ec7f 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -63,6 +63,7 @@
<filename>graphical.target</filename>,
<filename>hibernate.target</filename>,
<filename>http-daemon.target</filename>,
+ <filename>hybrid-sleep.target</filename>,
<filename>halt.target</filename>,
<filename>kbrequest.target</filename>,
<filename>kexec.target</filename>,
@@ -303,6 +304,15 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><filename>hybrid-sleep.target</filename></term>
+ <listitem>
+ <para>A special target unit
+ for hibernating and suspending the
+ system at the same time. This pulls in
+ <filename>sleep.target</filename>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><filename>halt.target</filename></term>
<listitem>
<para>A special target unit
@@ -652,9 +662,8 @@
<listitem>
<para>A special target unit
that is pulled in by
- <filename>suspend.target</filename>
- and
- <filename>hibernate.target</filename>
+ <filename>suspend.target</filename>,
+ <filename>hibernate.target</filename> and <filename>hybrid-sleep.target</filename>
and may be used to hook units
into the sleep state
logic.</para>
diff --git a/src/core/special.h b/src/core/special.h
index e3004a518..8923f340b 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -37,6 +37,7 @@
#define SPECIAL_EXIT_TARGET "exit.target"
#define SPECIAL_SUSPEND_TARGET "suspend.target"
#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
+#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
index 8fdab789b..753d95454 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -163,16 +163,18 @@ static int button_handle(
[HANDLE_HALT] = "Halting...",
[HANDLE_KEXEC] = "Rebooting via kexec...",
[HANDLE_SUSPEND] = "Suspending...",
- [HANDLE_HIBERNATE] = "Hibernating..."
+ [HANDLE_HIBERNATE] = "Hibernating...",
+ [HANDLE_HYBRID_SLEEP] = "Hibernating and suspend...",
};
static const char * const target_table[_HANDLE_BUTTON_MAX] = {
- [HANDLE_POWEROFF] = "poweroff.target",
- [HANDLE_REBOOT] = "reboot.target",
- [HANDLE_HALT] = "halt.target",
- [HANDLE_KEXEC] = "kexec.target",
- [HANDLE_SUSPEND] = "suspend.target",
- [HANDLE_HIBERNATE] = "hibernate.target"
+ [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
+ [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
+ [HANDLE_HALT] = SPECIAL_HALT_TARGET,
+ [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
+ [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
+ [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
+ [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
};
DBusError error;
@@ -193,7 +195,7 @@ static int button_handle(
return 0;
}
- inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
+ inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
/* If the actual operation is inhibited, warn and fail */
if (!ignore_inhibited &&
@@ -305,7 +307,8 @@ static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = {
[HANDLE_HALT] = "halt",
[HANDLE_KEXEC] = "kexec",
[HANDLE_SUSPEND] = "suspend",
- [HANDLE_HIBERNATE] = "hibernate"
+ [HANDLE_HIBERNATE] = "hibernate",
+ [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
};
DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton);
DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting");
diff --git a/src/login/logind-button.h b/src/login/logind-button.h
index ca820ed7e..827a03e46 100644
--- a/src/login/logind-button.h
+++ b/src/login/logind-button.h
@@ -32,6 +32,7 @@ typedef enum HandleButton {
HANDLE_KEXEC,
HANDLE_SUSPEND,
HANDLE_HIBERNATE,
+ HANDLE_HYBRID_SLEEP,
_HANDLE_BUTTON_MAX,
_HANDLE_BUTTON_INVALID = -1
} HandleButton;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index a7647e3c8..3bcb91bf1 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -145,6 +145,9 @@
" <method name=\"Hibernate\">\n" \
" <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
+ " <method name=\"HybridSleep\">\n" \
+ " <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"CanPowerOff\">\n" \
" <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
" </method>\n" \
@@ -157,6 +160,9 @@
" <method name=\"CanHibernate\">\n" \
" <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"CanHybridSleep\">\n" \
+ " <arg name=\"result\" type=\"s\" direction=\"out\"/>\n" \
+ " </method>\n" \
" <method name=\"Inhibit\">\n" \
" <arg name=\"what\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
@@ -1054,6 +1060,7 @@ static int bus_manager_can_shutdown_or_sleep(
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
const char *sleep_type,
+ const char *sleep_disk_type,
DBusError *error,
DBusMessage **_reply) {
@@ -1085,6 +1092,17 @@ static int bus_manager_can_shutdown_or_sleep(
}
}
+ if (sleep_disk_type) {
+ r = can_sleep_disk(sleep_disk_type);
+ if (r < 0)
+ return r;
+
+ if (r == 0) {
+ result = "na";
+ goto finish;
+ }
+ }
+
ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
if (ul == (unsigned long) -1)
return -EIO;
@@ -1234,6 +1252,7 @@ static int bus_manager_do_shutdown_or_sleep(
const char *action_multiple_sessions,
const char *action_ignore_inhibit,
const char *sleep_type,
+ const char *sleep_disk_type,
DBusError *error,
DBusMessage **_reply) {
@@ -1271,6 +1290,15 @@ static int bus_manager_do_shutdown_or_sleep(
return -ENOTSUP;
}
+ if (sleep_disk_type) {
+ r = can_sleep_disk(sleep_disk_type);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ return -ENOTSUP;
+ }
+
ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error);
if (ul == (unsigned long) -1)
return -EIO;
@@ -2065,7 +2093,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
- NULL,
+ NULL, NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2077,7 +2105,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
- NULL,
+ NULL, NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2090,7 +2118,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
- "mem",
+ "mem", NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2102,7 +2130,20 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
- "disk",
+ "disk", NULL,
+ &error, &reply);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "HybridSleep")) {
+ r = bus_manager_do_shutdown_or_sleep(
+ m, connection, message,
+ SPECIAL_HYBRID_SLEEP_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ "disk", "suspend",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2115,7 +2156,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit",
- NULL,
+ NULL, NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2126,7 +2167,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit",
- NULL,
+ NULL, NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2138,7 +2179,7 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit",
- "mem",
+ "mem", NULL,
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -2150,7 +2191,19 @@ static DBusHandlerResult manager_message_handler(
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
- "disk",
+ "disk", NULL,
+ &error, &reply);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHybridSleep")) {
+ r = bus_manager_can_shutdown_or_sleep(
+ m, connection, message,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ "disk", "suspend",
&error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
diff --git a/src/shared/util.c b/src/shared/util.c
index db8e75b62..2d4a4c110 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5691,6 +5691,30 @@ int can_sleep(const char *type) {
return false;
}
+int can_sleep_disk(const char *type) {
+ char *w, *state;
+ size_t l, k;
+ int r;
+ _cleanup_free_ char *p = NULL;
+
+ assert(type);
+
+ r = read_one_line_file("/sys/power/disk", &p);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+
+ k = strlen(type);
+ FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
+ if (l == k && memcmp(w, type, l) == 0)
+ return true;
+
+ if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']')
+ return true;
+ }
+
+ return false;
+}
+
bool is_valid_documentation_url(const char *url) {
assert(url);
diff --git a/src/shared/util.h b/src/shared/util.h
index f6a4c1ea6..f726263dd 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -529,6 +529,7 @@ int setrlimit_closest(int resource, const struct rlimit *rlim);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
int can_sleep(const char *type);
+int can_sleep_disk(const char *type);
bool is_valid_documentation_url(const char *url);
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 2f312675c..218de3a56 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -46,7 +46,7 @@ int main(int argc, char *argv[]) {
if (streq(argv[1], "suspend"))
verb = "mem";
- else if (streq(argv[1], "hibernate"))
+ else if (streq(argv[1], "hibernate") || streq(argv[1], "hybrid-sleep"))
verb = "disk";
else {
log_error("Unknown action '%s'.", argv[1]);
@@ -54,6 +54,16 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* Configure the hibernation mode */
+ if (streq(argv[1], "hibernate")) {
+ if (write_one_line_file("/sys/power/disk", "platform") < 0)
+ write_one_line_file("/sys/power/disk", "shutdown");
+ } else if (streq(argv[1], "hybrid-sleep")) {
+ if (write_one_line_file("/sys/power/disk", "suspend") < 0)
+ if (write_one_line_file("/sys/power/disk", "platform") < 0)
+ write_one_line_file("/sys/power/disk", "shutdown");
+ }
+
f = fopen("/sys/power/state", "we");
if (!f) {
log_error("Failed to open /sys/power/state: %m");
@@ -73,12 +83,18 @@ int main(int argc, char *argv[]) {
"MESSAGE=Suspending system...",
"SLEEP=suspend",
NULL);
- else
+ else if (streq(argv[1], "hibernate"))
log_struct(LOG_INFO,
MESSAGE_ID(SD_MESSAGE_SLEEP_START),
"MESSAGE=Hibernating system...",
"SLEEP=hibernate",
NULL);
+ else
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_SLEEP_START),
+ "MESSAGE=Hibernating and suspending system...",
+ "SLEEP=hybrid-sleep",
+ NULL);
fputs(verb, f);
fputc('\n', f);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 41dcefb67..b9e64a677 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -99,6 +99,7 @@ static enum action {
ACTION_EXIT,
ACTION_SUSPEND,
ACTION_HIBERNATE,
+ ACTION_HYBRID_SLEEP,
ACTION_RUNLEVEL2,
ACTION_RUNLEVEL3,
ACTION_RUNLEVEL4,
@@ -1608,6 +1609,8 @@ static enum action verb_to_action(const char *verb) {
return ACTION_SUSPEND;
else if (streq(verb, "hibernate"))
return ACTION_HIBERNATE;
+ else if (streq(verb, "hybrid-sleep"))
+ return ACTION_HYBRID_SLEEP;
else
return ACTION_INVALID;
}
@@ -1628,7 +1631,8 @@ static int start_unit(DBusConnection *bus, char **args) {
[ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
[ACTION_EXIT] = SPECIAL_EXIT_TARGET,
[ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET,
- [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET
+ [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
+ [ACTION_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
};
int r, ret = 0;
@@ -1764,6 +1768,10 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
method = "Hibernate";
break;
+ case ACTION_HYBRID_SLEEP:
+ method = "HybridSleep";
+ break;
+
default:
return -EINVAL;
}
@@ -1815,7 +1823,8 @@ static int start_special(DBusConnection *bus, char **args) {
(a == ACTION_POWEROFF ||
a == ACTION_REBOOT ||
a == ACTION_SUSPEND ||
- a == ACTION_HIBERNATE)) {
+ a == ACTION_HIBERNATE ||
+ a == ACTION_HYBRID_SLEEP)) {
r = reboot_with_logind(bus, a);
if (r >= 0)
return r;
@@ -3967,7 +3976,8 @@ static int systemctl_help(void) {
" exit Request user instance exit\n"
" switch-root [ROOT] [INIT] Change to a different root file system\n"
" suspend Suspend the system\n"
- " hibernate Hibernate the system\n",
+ " hibernate Hibernate the system\n"
+ " hybrid-sleep Hibernate and suspend the system\n",
program_invocation_short_name);
return 0;
@@ -4896,6 +4906,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
{ "kexec", EQUAL, 1, start_special },
{ "suspend", EQUAL, 1, start_special },
{ "hibernate", EQUAL, 1, start_special },
+ { "hybrid-sleep", EQUAL, 1, start_special },
{ "default", EQUAL, 1, start_special },
{ "rescue", EQUAL, 1, start_special },
{ "emergency", EQUAL, 1, start_special },
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
new file mode 100644
index 000000000..5a98ecda2
--- /dev/null
+++ b/src/test/test-sleep.c
@@ -0,0 +1,39 @@
+/*-*- 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 <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "util.h"
+#include "log.h"
+
+int main(int argc, char* argv[]) {
+ log_info("Can Suspend: %s", yes_no(can_sleep("mem") > 0));
+ log_info("Can Hibernate: %s", yes_no(can_sleep("disk") > 0));
+ log_info("Can Hibernate+Suspend (Hybrid-Sleep): %s", yes_no(can_sleep_disk("suspend") > 0));
+ log_info("Can Hibernate+Reboot: %s", yes_no(can_sleep_disk("reboot") > 0));
+ log_info("Can Hibernate+Platform: %s", yes_no(can_sleep_disk("platform") > 0));
+ log_info("Can Hibernate+Shutdown: %s", yes_no(can_sleep_disk("shutdown") > 0));
+
+ return 0;
+}
diff --git a/units/.gitignore b/units/.gitignore
index c72e2cbee..63c7ba06b 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -1,3 +1,4 @@
+/systemd-hybrid-sleep.service
/systemd-journal-gatewayd.service
/systemd-journal-flush.service
/systemd-hibernate.service
diff --git a/units/hybrid-sleep.target b/units/hybrid-sleep.target
new file mode 100644
index 000000000..d2d340922
--- /dev/null
+++ b/units/hybrid-sleep.target
@@ -0,0 +1,13 @@
+# This file is part of systemd.
+#
+# 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.
+
+[Unit]
+Description=Hybrid Suspend+Hibernate
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+BindsTo=systemd-hybrid-sleep.service
+After=systemd-hybrid-sleep.service
diff --git a/units/systemd-hybrid-sleep.service.in b/units/systemd-hybrid-sleep.service.in
new file mode 100644
index 000000000..914b686c3
--- /dev/null
+++ b/units/systemd-hybrid-sleep.service.in
@@ -0,0 +1,17 @@
+# This file is part of systemd.
+#
+# 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.
+
+[Unit]
+Description=Hybrid Suspend+Hibernate
+Documentation=man:systemd-suspend.service(8)
+DefaultDependencies=no
+Requires=sleep.target
+After=sleep.target
+
+[Service]
+Type=oneshot
+ExecStart=@rootlibexecdir@/systemd-sleep hybrid-sleep