diff options
author | Michal Bloch <m.bloch@partner.samsung.com> | 2020-11-02 14:21:26 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@review> | 2020-11-02 14:21:26 +0000 |
commit | 99df850b7a43bf4f40ed89036bcb526b656dc8d7 (patch) | |
tree | 0581b568ebfdc1f40a2bcd5cd78746933e1b3050 | |
parent | c493cc911313bad8af96bd11776d153a39eadc90 (diff) | |
parent | a25eb5e44fd394887658026c4916e8909e286932 (diff) | |
download | deviced-99df850b7a43bf4f40ed89036bcb526b656dc8d7.tar.gz deviced-99df850b7a43bf4f40ed89036bcb526b656dc8d7.tar.bz2 deviced-99df850b7a43bf4f40ed89036bcb526b656dc8d7.zip |
Merge "Implement shutdown et al. using deviced DBus interface" into tizen
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | packaging/deviced.spec | 19 | ||||
-rw-r--r-- | src/power-command/command.c | 363 |
3 files changed, 387 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 45d8cab0..8d00ac6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,11 @@ IF(POWER_MODULE STREQUAL on) SET(deviced-shutdown_LDFLAGS ${pkgs_LDFLAGS}) TARGET_LINK_LIBRARIES(deviced-shutdown ${pkgs_LDFLAGS} "-lrt -ldl -lm" shared) INSTALL(TARGETS deviced-shutdown DESTINATION /usr/lib/systemd) + + ADD_EXECUTABLE(deviced-power-command src/power-command/command.c) + SET(deviced-power-command_LDFLAGS ${pkgs_LDFLAGS}) + TARGET_LINK_LIBRARIES(deviced-power-command ${pkgs_LDFLAGS} "-lrt -ldl -lm" shared) + INSTALL(TARGETS deviced-power-command DESTINATION /usr/sbin) ENDIF() INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/deviced/ DESTINATION include/${PROJECT_NAME} diff --git a/packaging/deviced.spec b/packaging/deviced.spec index f23b4daa..f27ae503 100644 --- a/packaging/deviced.spec +++ b/packaging/deviced.spec @@ -186,9 +186,19 @@ rm -rf %{buildroot} mkdir -p %{buildroot}%{TZ_SYS_DUMPGEN} install -m 775 scripts/dump_pmstate_log.sh %{buildroot}%{TZ_SYS_DUMPGEN}/dump_pmstate_log.sh +# Assume power module is on (-DPOWER_MODULE=on) +touch %{buildroot}%{_sbindir}/reboot +touch %{buildroot}%{_sbindir}/halt +touch %{buildroot}%{_sbindir}/poweroff +touch %{buildroot}%{_sbindir}/shutdown + %post # Assume power module is on (-DPOWER_MODULE=on) update-alternatives --install %{_prefix}/lib/systemd/systemd-shutdown systemd-shutdown %{_prefix}/lib/systemd/deviced-shutdown 500 +update-alternatives --install %{_sbindir}/reboot reboot %{_sbindir}/deviced-power-command 500 +update-alternatives --install %{_sbindir}/halt halt %{_sbindir}/deviced-power-command 500 +update-alternatives --install %{_sbindir}/poweroff poweroff %{_sbindir}/deviced-power-command 500 +update-alternatives --install %{_sbindir}/shutdown shutdown %{_sbindir}/deviced-power-command 500 #memory type vconf key init users_gid=$(getent group %{TZ_SYS_USER_GROUP} | cut -f3 -d':') @@ -201,6 +211,10 @@ fi %preun # Assume power module is on (-DPOWER_MODULE=on) update-alternatives --remove systemd-shutdown %{_prefix}/lib/systemd/deviced-shutdown +update-alternatives --remove reboot %{_sbindir}/deviced-power-command +update-alternatives --remove halt %{_sbindir}/deviced-power-command +update-alternatives --remove poweroff %{_sbindir}/deviced-power-command +update-alternatives --remove shutdown %{_sbindir}/deviced-power-command %postun systemctl daemon-reload @@ -264,6 +278,11 @@ mv %{_libdir}/iot-display.so %{_libdir}/deviced/display.so # Assume power module is on (-DPOWER_MODULE=on) %{_prefix}/lib/systemd/deviced-shutdown +%{_sbindir}/deviced-power-command +%ghost %{_sbindir}/reboot +%ghost %{_sbindir}/halt +%ghost %{_sbindir}/poweroff +%ghost %{_sbindir}/shutdown %files -n libdeviced %manifest deviced.manifest diff --git a/src/power-command/command.c b/src/power-command/command.c new file mode 100644 index 00000000..629a1fcd --- /dev/null +++ b/src/power-command/command.c @@ -0,0 +1,363 @@ +/* + * deviced + * + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <getopt.h> +#include <libsyscommon/dbus-system.h> +#include <stdio.h> +#include <stdbool.h> +#include <core/common.h> +#include <stdlib.h> + +enum application { + APP_UNKNOWN, + APP_SHUTDOWN, + APP_REBOOT, + APP_HALT, + APP_POWEROFF, +}; + +struct { + const char *name; + enum application application; +} apps[] = { + { .name = "reboot", .application = APP_REBOOT, }, + { .name = "halt", .application = APP_HALT, }, + { .name = "poweroff", .application = APP_POWEROFF, }, + { .name = "shutdown", .application = APP_SHUTDOWN, }, +}; + +enum application parse_path(const char *name) +{ + const char *base = strrchr(name, '/'); + base = base ? base + 1 : name; + for (size_t i = 0; i < ARRAY_SIZE(apps); ++i) { + const char *target = apps[i].name; + + /* NB: We only check if the prefix matches, + * so that we can run "reboot" as, for example "reboot-custom". */ + if (strncmp(base, target, strlen(target)) == 0) + return apps[i].application; + } + return APP_UNKNOWN; +} + +/* NB: we are emulating different programs: the "shutdown" program, and the trio of "reboot", "halt" + * and "poweroff". These two cases have different option sets, so they need separate parsing methods + * and separate usage strings. */ + +struct parse_result { + enum { + DO_NOTHING, + SHOW_SHUTDOWN_PARSE_ERROR, + SHOW_REBOOT_ET_AL_PARSE_ERROR, + SHOW_SHUTDOWN_HELP, + SHOW_REBOOT_ET_AL_HELP, + DO_POWEROFF, + DO_EXIT, + DO_REBOOT, + } operation; + + const char *extra_option; + + bool show_sync_warning; + bool show_timings_warning; + bool show_wall_warning; + bool show_wtmp_warning; +}; + +struct parse_result parse_shutdown(int argc, char **argv) { + enum { + ACT_HALT, + ACT_REBOOT, + ACT_POWEROFF, + } action = ACT_POWEROFF; + bool will_do_nothing = false; + bool show_timings_warning = false; + bool show_wall_warning = false; + + for (;;) { + switch (getopt_long(argc, argv, "HPrhkc", (struct option []) { + { "help", no_argument, NULL, 0, }, + { "no-wall", no_argument, NULL, 1, }, + { "halt", no_argument, NULL, 'H', }, + { "poweroff", no_argument, NULL, 'P', }, + { "reboot", no_argument, NULL, 'r', }, + { NULL, 0, NULL, 0, }, + }, NULL)) { + case -1: + switch (argc - optind) { + case 2: + show_wall_warning = true; + show_timings_warning = true; + break; + + case 1: + show_timings_warning = true; + break; + + case 0: + break; + + default: + return (struct parse_result) { .operation = SHOW_SHUTDOWN_PARSE_ERROR, }; + } + + return (struct parse_result) { + .operation = will_do_nothing ? DO_NOTHING + : action == ACT_REBOOT ? DO_REBOOT + : action == ACT_HALT ? DO_EXIT // don't ask difficult questions like "why not DO_HALT" + : DO_POWEROFF, + .extra_option = NULL, + .show_sync_warning = false, + .show_timings_warning = show_timings_warning, + .show_wall_warning = show_wall_warning, + .show_wtmp_warning = false, + }; + + case 0: + return (struct parse_result) { .operation = SHOW_SHUTDOWN_HELP, }; + + case 1: + show_wall_warning = true; + break; + + case 'H': + action = ACT_HALT; + break; + + case 'P': + action = ACT_POWEROFF; + break; + + case 'r': + action = ACT_REBOOT; + break; + + case 'h': + if (action == ACT_REBOOT) + action = ACT_POWEROFF; + break; + + case 'k': + will_do_nothing = true; + show_wall_warning = true; + break; + + case 'c': + will_do_nothing = true; + show_timings_warning = true; + break; + + case '?': + return (struct parse_result) { .operation = SHOW_SHUTDOWN_PARSE_ERROR, }; + + default: + assert(false); + } + } +} + +struct parse_result parse_reboot_et_al(enum application application, int argc, char **argv) { + enum { + ACT_HALT, + ACT_REBOOT, + ACT_POWEROFF, + } action; + switch (application) { + case APP_REBOOT: + action = ACT_REBOOT; + break; + + case APP_HALT: + action = ACT_HALT; + break; + + case APP_POWEROFF: + action = ACT_POWEROFF; + break; + + default: + assert(false); + } + + bool will_do_nothing = false; + bool show_sync_warning = false; + bool show_timings_warning = false; + bool show_wall_warning = false; + bool show_wtmp_warning = false; + char *extra_option = NULL; + + for (;;) { + switch (getopt_long(argc, argv, "-pfwdn", (struct option []) { + { "help", no_argument, NULL, 0, }, + /* 1 reserved for options not in -foo format */ + { "halt", no_argument, NULL, 2, }, + { "reboot", no_argument, NULL, 3, }, + { "no-wall", no_argument, NULL, 4, }, + { "poweroff", no_argument, NULL, 'p', }, + { "force", no_argument, NULL, 'f', }, + { "wtmp-only", no_argument, NULL, 'w', }, + { "no-wtmp", no_argument, NULL, 'd', }, + { "no-sync", no_argument, NULL, 'n', }, + { NULL, 0, NULL, 0, }, + }, NULL)) { + case -1: + if (optind != argc) + return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_PARSE_ERROR, }; + + return (struct parse_result) { + .operation = will_do_nothing ? DO_NOTHING + : action == ACT_REBOOT ? DO_REBOOT + : action == ACT_HALT ? DO_EXIT + : DO_POWEROFF, + .extra_option = extra_option, + .show_sync_warning = show_sync_warning, + .show_timings_warning = show_timings_warning, + .show_wall_warning = show_wall_warning, + .show_wtmp_warning = show_wtmp_warning, + }; + + case 0: + return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_HELP, }; + + case 1: + if (extra_option) + return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_PARSE_ERROR, }; + extra_option = optarg; + break; + + case 2: + action = ACT_HALT; + break; + + case 3: + action = ACT_REBOOT; + break; + + case 4: + show_wall_warning = true; + break; + + case 'p': + action = ACT_POWEROFF; + break; + + case 'f': + show_timings_warning = false; + break; + + case 'w': + will_do_nothing = true; + show_wtmp_warning = true; + break; + + case 'd': + show_wtmp_warning = true; + break; + + case 'n': + show_sync_warning = true; + break; + + case '?': + return (struct parse_result) { .operation = SHOW_REBOOT_ET_AL_PARSE_ERROR, }; + + default: + assert(false); + } + } +} + +int call_deviced_poweroff(const char *type, const char *extra_option, const char *message) +{ + const char *command[2] = { type, extra_option }; + int ret = extra_option + ? dbus_handle_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF, "PowerOffWithOption", "ss", command) + : dbus_handle_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF, "PowerOff", "s", command) + ; + if (ret != 0) { + errno = -ret; + fprintf(stderr, "Error: %m"); + return EXIT_FAILURE; + } + + fprintf(stderr, "%s", message); + for (;;) + pause(); + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + enum application application = parse_path(argv[0]); + struct parse_result parse_result; + switch (application) { + case APP_UNKNOWN: + fprintf(stderr, "This program can only be used as a symlink to 'reboot', 'halt', 'poweroff' or 'shutdown'.\n"); + return EXIT_FAILURE; + + case APP_SHUTDOWN: + parse_result = parse_shutdown(argc, argv); + break; + + case APP_REBOOT: + case APP_HALT: + case APP_POWEROFF: + parse_result = parse_reboot_et_al(application, argc, argv); + break; + + default: + // This shouldn't be needed, but GCC complains otherwise. + assert(false); + return EXIT_FAILURE; + } + + if (parse_result.show_sync_warning) + fprintf(stderr, "Warning: Tizen always syncs, sync options ignored.\n"); + if (parse_result.show_timings_warning) + fprintf(stderr, "Warning: %s doesn't take care of delayed and pending operations on Tizen.\n", argv[0]); + if (parse_result.show_wall_warning) + fprintf(stderr, "Warning: %s doesn't take care of wall messages on Tizen.\n", argv[0]); + if (parse_result.show_wtmp_warning) + fprintf(stderr, "Warning: %s doesn't take care of wtmp entries on Tizen.\n", argv[0]); + + switch (parse_result.operation) { + case DO_NOTHING: + return EXIT_SUCCESS; + + case SHOW_SHUTDOWN_HELP: + case SHOW_SHUTDOWN_PARSE_ERROR: + fprintf(stderr, "Usage: %s [-HPrh]\n", argv[0]); + return parse_result.operation == SHOW_SHUTDOWN_HELP ? EXIT_SUCCESS : EXIT_FAILURE; + + case SHOW_REBOOT_ET_AL_HELP: + case SHOW_REBOOT_ET_AL_PARSE_ERROR: + fprintf(stderr, "Usage: %s [-p] [--halt|--poweroff|--reboot] [extra]\n", argv[0]); // FIXME: do we say something about the extras? if yes, do we mention deviced, the configs etc? + return parse_result.operation == SHOW_REBOOT_ET_AL_HELP ? EXIT_SUCCESS : EXIT_FAILURE; + + case DO_POWEROFF: + return call_deviced_poweroff("poweroff", parse_result.extra_option, "Shutting down...\n"); // should be "Powering off" really, "shutting down" is the generic term also encompassing halt/reboot >_> + case DO_EXIT: + return call_deviced_poweroff("exit", parse_result.extra_option, "Shutting down...\n"); // ditto: exit ultimately boils down to poweroff, too + case DO_REBOOT: + return call_deviced_poweroff("reboot", parse_result.extra_option, "Rebooting...\n"); + } + + assert(false); // unreachable + return EXIT_FAILURE; +} |