summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Bloch <m.bloch@partner.samsung.com>2020-11-02 14:21:26 +0000
committerGerrit Code Review <gerrit@review>2020-11-02 14:21:26 +0000
commit99df850b7a43bf4f40ed89036bcb526b656dc8d7 (patch)
tree0581b568ebfdc1f40a2bcd5cd78746933e1b3050
parentc493cc911313bad8af96bd11776d153a39eadc90 (diff)
parenta25eb5e44fd394887658026c4916e8909e286932 (diff)
downloaddeviced-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.txt5
-rw-r--r--packaging/deviced.spec19
-rw-r--r--src/power-command/command.c363
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;
+}