sleep: convert to "verbs", using the new option+verb macros
We had verb-like dispatch, but done in a manual way. We have a fairly heavy preperation steps that wraps all operations in the same way, so we don't want to call the operation implementation functions directly. But let's use the generic verb machinery and pass the state directly using the userdata pointer and the recently added verb data pointer. --help output is substantially the same, but options are now in a new section below the verbs.
This commit is contained in:
committed by
Zbigniew Jędrzejewski-Szmek
parent
b332dfb033
commit
34b9dfe1be
+108
-101
@@ -5,7 +5,6 @@
|
||||
***/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/timerfd.h>
|
||||
@@ -33,21 +32,22 @@
|
||||
#include "exec-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "hibernate-util.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "os-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "sleep-config.h"
|
||||
#include "special.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
#define DEFAULT_HIBERNATE_DELAY_USEC_NO_BATTERY (2 * USEC_PER_HOUR)
|
||||
|
||||
static SleepOperation arg_operation = _SLEEP_OPERATION_INVALID;
|
||||
|
||||
#if ENABLE_EFI
|
||||
static int determine_auto_swap(sd_device *device) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *origin = NULL;
|
||||
@@ -235,15 +235,16 @@ static int lock_all_homes(void) {
|
||||
|
||||
static int execute(
|
||||
const SleepConfig *sleep_config,
|
||||
SleepOperation main_operation,
|
||||
SleepOperation operation,
|
||||
const char *action) {
|
||||
|
||||
const char *arguments[] = {
|
||||
NULL,
|
||||
"pre",
|
||||
/* NB: we use 'arg_operation' instead of 'operation' here, as we want to communicate the overall
|
||||
* operation here, not the specific one, in case of s2h. */
|
||||
sleep_operation_to_string(arg_operation),
|
||||
/* NB: we use 'main_operation' instead of 'operation' here, as we want to communicate
|
||||
* the overall operation here, not the specific one, in case of s2h. */
|
||||
sleep_operation_to_string(main_operation),
|
||||
NULL
|
||||
};
|
||||
static const char* const dirs[] = {
|
||||
@@ -325,19 +326,19 @@ static int execute(
|
||||
log_struct(LOG_INFO,
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START_STR),
|
||||
LOG_MESSAGE("Performing sleep operation '%s'...", sleep_operation_to_string(operation)),
|
||||
LOG_ITEM("SLEEP=%s", sleep_operation_to_string(arg_operation)));
|
||||
LOG_ITEM("SLEEP=%s", sleep_operation_to_string(main_operation)));
|
||||
|
||||
r = write_state(state_fd, sleep_config->states[operation]);
|
||||
if (r < 0)
|
||||
log_struct_errno(LOG_ERR, r,
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP_STR),
|
||||
LOG_MESSAGE("Failed to put system to sleep. System resumed again: %m"),
|
||||
LOG_ITEM("SLEEP=%s", sleep_operation_to_string(arg_operation)));
|
||||
LOG_ITEM("SLEEP=%s", sleep_operation_to_string(main_operation)));
|
||||
else
|
||||
log_struct(LOG_INFO,
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP_STR),
|
||||
LOG_MESSAGE("System returned from sleep operation '%s'.", sleep_operation_to_string(arg_operation)),
|
||||
LOG_ITEM("SLEEP=%s", sleep_operation_to_string(arg_operation)));
|
||||
LOG_MESSAGE("System returned from sleep operation '%s'.", sleep_operation_to_string(main_operation)),
|
||||
LOG_ITEM("SLEEP=%s", sleep_operation_to_string(main_operation)));
|
||||
|
||||
arguments[1] = "post";
|
||||
(void) execute_directories(
|
||||
@@ -396,7 +397,7 @@ static int check_wakeup_type(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int custom_timer_suspend(const SleepConfig *sleep_config) {
|
||||
static int custom_timer_suspend(const SleepConfig *sleep_config, SleepOperation main_operation) {
|
||||
usec_t hibernate_timestamp;
|
||||
int r;
|
||||
|
||||
@@ -456,7 +457,7 @@ static int custom_timer_suspend(const SleepConfig *sleep_config) {
|
||||
if (timerfd_settime(tfd, 0, &ts, NULL) < 0)
|
||||
return log_error_errno(errno, "Error setting battery estimate timer: %m");
|
||||
|
||||
r = execute(sleep_config, SLEEP_SUSPEND, NULL);
|
||||
r = execute(sleep_config, main_operation, SLEEP_SUSPEND, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -506,7 +507,7 @@ static int custom_timer_suspend(const SleepConfig *sleep_config) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int execute_s2h(const SleepConfig *sleep_config) {
|
||||
static int execute_s2h(const SleepConfig *sleep_config, SleepOperation main_operation) {
|
||||
_cleanup_close_ int tfd = -EBADF;
|
||||
usec_t hibernate_timestamp = 0;
|
||||
int r;
|
||||
@@ -559,7 +560,7 @@ static int execute_s2h(const SleepConfig *sleep_config) {
|
||||
}
|
||||
|
||||
log_debug("Attempting to suspend...");
|
||||
r = execute(sleep_config, SLEEP_SUSPEND, NULL);
|
||||
r = execute(sleep_config, main_operation, SLEEP_SUSPEND, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -592,7 +593,7 @@ static int execute_s2h(const SleepConfig *sleep_config) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
r = custom_timer_suspend(sleep_config);
|
||||
r = custom_timer_suspend(sleep_config, main_operation);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Suspend cycle with manual battery discharge rate estimation failed: %m");
|
||||
if (r == 0)
|
||||
@@ -602,11 +603,11 @@ static int execute_s2h(const SleepConfig *sleep_config) {
|
||||
/* For above custom timer, if 1 is returned, system will directly hibernate */
|
||||
|
||||
log_debug("Attempting to hibernate");
|
||||
r = execute(sleep_config, SLEEP_HIBERNATE, NULL);
|
||||
r = execute(sleep_config, main_operation, SLEEP_HIBERNATE, NULL);
|
||||
if (r < 0) {
|
||||
log_notice("Couldn't hibernate, will try to suspend again.");
|
||||
|
||||
r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hibernate");
|
||||
r = execute(sleep_config, main_operation, SLEEP_SUSPEND, "suspend-after-failed-hibernate");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -616,95 +617,63 @@ static int execute_s2h(const SleepConfig *sleep_config) {
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-suspend.service", "8", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s COMMAND\n\n"
|
||||
"Suspend the system, hibernate the system, or both.\n\n"
|
||||
" -h --help Show this help and exit\n"
|
||||
" --version Print version string and exit\n"
|
||||
"\nCommands:\n"
|
||||
" suspend Suspend the system\n"
|
||||
" hibernate Hibernate the system\n"
|
||||
" hybrid-sleep Both hibernate and suspend the system\n"
|
||||
" suspend-then-hibernate Initially suspend and then hibernate\n"
|
||||
" the system after a fixed period of time or\n"
|
||||
" when battery is low\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{}
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
return help();
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
}
|
||||
|
||||
if (argc - optind != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Usage: %s COMMAND",
|
||||
program_invocation_short_name);
|
||||
|
||||
arg_operation = sleep_operation_from_string(argv[optind]);
|
||||
if (arg_operation < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command '%s'.", argv[optind]);
|
||||
|
||||
return 1 /* work to do */;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(unit_freezer_freep) UnitFreezer *user_slice_freezer = NULL;
|
||||
_cleanup_(sleep_config_freep) SleepConfig *sleep_config = NULL;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = parse_sleep_config(&sleep_config);
|
||||
r = verbs_get_help_table(&verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!sleep_config->allow[arg_operation])
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) table_sync_column_widths(0, verbs, options);
|
||||
|
||||
printf("%s [OPTIONS…] COMMAND\n"
|
||||
"\n%sSuspend the system, hibernate the system, or both.%s\n"
|
||||
"\nCommands:\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
|
||||
r = table_print_or_warn(verbs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nOptions:\n");
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
printf("\nSee the %s for details.\n", link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
VERB_FULL(verb_operate, "suspend", NULL, VERB_ANY, 1, 0, SLEEP_SUSPEND,
|
||||
"Suspend the system");
|
||||
VERB_FULL(verb_operate, "hibernate", NULL, VERB_ANY, 1, 0, SLEEP_HIBERNATE,
|
||||
"Hibernate the system");
|
||||
VERB_FULL(verb_operate, "hybrid-sleep", NULL, VERB_ANY, 1, 0, SLEEP_HYBRID_SLEEP,
|
||||
"Both hibernate and suspend the system");
|
||||
VERB_FULL(verb_operate, "suspend-then-hibernate", NULL, VERB_ANY, 1, 0, SLEEP_SUSPEND_THEN_HIBERNATE,
|
||||
"Initially suspend and then hibernate the system after a fixed period of time or when battery is low");
|
||||
static int verb_operate(int argc, char *argv[], uintptr_t data, void *userdata) {
|
||||
_cleanup_(unit_freezer_freep) UnitFreezer *user_slice_freezer = NULL;
|
||||
SleepOperation operation = data;
|
||||
const SleepConfig *sleep_config = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(0 <= operation && operation < _SLEEP_OPERATION_MAX);
|
||||
|
||||
if (!sleep_config->allow[operation])
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EACCES),
|
||||
"Sleep operation \"%s\" is disabled by configuration, refusing.",
|
||||
sleep_operation_to_string(arg_operation));
|
||||
sleep_operation_to_string(operation));
|
||||
|
||||
/* Freeze the user sessions */
|
||||
r = getenv_bool("SYSTEMD_SLEEP_FREEZE_USER_SESSIONS");
|
||||
@@ -721,14 +690,14 @@ static int run(int argc, char *argv[]) {
|
||||
"This is not recommended, and might result in unexpected behavior, particularly\n"
|
||||
"in suspend-then-hibernate operations or setups with encrypted home directories.");
|
||||
|
||||
switch (arg_operation) {
|
||||
switch (operation) {
|
||||
|
||||
case SLEEP_SUSPEND_THEN_HIBERNATE:
|
||||
r = execute_s2h(sleep_config);
|
||||
r = execute_s2h(sleep_config, operation);
|
||||
break;
|
||||
|
||||
case SLEEP_HYBRID_SLEEP:
|
||||
r = execute(sleep_config, SLEEP_HYBRID_SLEEP, NULL);
|
||||
r = execute(sleep_config, operation, SLEEP_HYBRID_SLEEP, NULL);
|
||||
if (r < 0) {
|
||||
/* If we can't hybrid sleep, then let's try to suspend at least. After all, the user
|
||||
* asked us to do both: suspend + hibernate, and it's almost certainly the
|
||||
@@ -736,14 +705,13 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
log_notice_errno(r, "Couldn't hybrid sleep, will try to suspend instead: %m");
|
||||
|
||||
r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hybrid-sleep");
|
||||
r = execute(sleep_config, operation, SLEEP_SUSPEND, "suspend-after-failed-hybrid-sleep");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SLEEP_SUSPEND:
|
||||
case SLEEP_HIBERNATE:
|
||||
r = execute(sleep_config, arg_operation, NULL);
|
||||
r = execute(sleep_config, operation, operation, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -756,4 +724,43 @@ static int run(int argc, char *argv[]) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[], char ***ret_args) {
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(ret_args);
|
||||
|
||||
OptionParser state = { argc, argv };
|
||||
|
||||
FOREACH_OPTION(&state, c, /* arg= */ NULL, /* on_error= */ return c)
|
||||
switch (c) {
|
||||
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
}
|
||||
|
||||
*ret_args = option_parser_get_args(&state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(sleep_config_freep) SleepConfig *sleep_config = NULL;
|
||||
char **args = NULL;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv, &args);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = parse_sleep_config(&sleep_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dispatch_verb_with_args(args, sleep_config);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
Reference in New Issue
Block a user