Rework reboot/shutdown/poweroff/halt command 00/258400/2
authorKarol Lewandowski <k.lewandowsk@samsung.com>
Mon, 10 May 2021 13:44:22 +0000 (15:44 +0200)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 18 May 2021 14:06:57 +0000 (16:06 +0200)
The provided utilities did not support all cases in which reboot and similar
utilities needs to support, ie.
 - running when dbus service is not running (eg. in upgrade target)
 - being invoked from vip handler when system must reboot immediately

This commit drops deviced custom tools - systemd native tools should be used
instead.

To ensure deviced is used during shutdown following is added:

 - deviced-request-shutdown utility and related service files

   These are installed as dependencies of systemd-standard shutdown
   procedure, causing these to be pulled automatically.

   Separate utility is added as, eg. dbus-send might not be available on
   production images.

 - /run/systemd/reboot-param handling is added

   This file is written by standard systemd reboot tool, so we need to both
   + read this file for reboot param in deviced-shutdown (needed if deviced was not used during shutdown)
   + read this file for deviced PowerOff argument (needed for normal operation)

Change-Id: Ic013e6fa1f4848a17abc025cf99cffeea9916542

CMakeLists.txt
packaging/deviced.spec
src/core/common.c
src/core/common.h
src/power-command/command.c
src/power-shutdown/shutdown.c
systemd/deviced-request-shutdown@.service [new file with mode: 0644]

index 4a7515c..be6292a 100644 (file)
@@ -247,11 +247,12 @@ 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)
+       INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/deviced-request-shutdown@.service DESTINATION /usr/lib/systemd/system)
 
-       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)
+       ADD_EXECUTABLE(deviced-request-shutdown src/power-command/command.c src/core/common.c src/core/execute.c)
+       SET(deviced-request-shutdown_LDFLAGS ${pkgs_LDFLAGS})
+       TARGET_LINK_LIBRARIES(deviced-request-shutdown ${pkgs_LDFLAGS} "-lrt -ldl -lm" shared)
+       INSTALL(TARGETS deviced-request-shutdown DESTINATION /usr/sbin)
 ENDIF()
 
 INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/deviced/ DESTINATION include/${PROJECT_NAME}
index 89ce53e..143dbbf 100644 (file)
@@ -184,22 +184,18 @@ rm -rf %{buildroot}
 %install_service basic.target.wants sdb-prestart.service
 %endif
 
+for i in reboot halt poweroff exit; do
+  D=%{buildroot}%{_unitdir}/systemd-${i}.service.wants
+  mkdir -p $D
+  ln -s ../deviced-request-shutdown@.service $D/deviced-request-shutdown@${i}.service
+done
+
 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':')
@@ -213,15 +209,17 @@ fi
 # Assume power module is on (-DPOWER_MODULE=on)
 if [ $1 -eq 0 ] ; then
    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
 
    systemctl daemon-reload
    systemctl stop deviced.service
 fi
 
+%posttrans
+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 || :
+
 %post -n libdeviced -p /sbin/ldconfig
 
 %postun -n libdeviced -p /sbin/ldconfig
@@ -282,12 +280,13 @@ mv %{_libdir}/iot-display.so %{_libdir}/deviced/display.so
 %{_unitdir}/mtp-responder-dummy.service
 
 # Assume power module is on (-DPOWER_MODULE=on)
+%{_unitdir}/deviced-request-shutdown@.service
+%{_unitdir}/systemd-exit.service.wants/deviced-request-shutdown@exit.service
+%{_unitdir}/systemd-halt.service.wants/deviced-request-shutdown@halt.service
+%{_unitdir}/systemd-reboot.service.wants/deviced-request-shutdown@reboot.service
+%{_unitdir}/systemd-poweroff.service.wants/deviced-request-shutdown@poweroff.service
 %{_prefix}/lib/systemd/deviced-shutdown
-%{_sbindir}/deviced-power-command
-%ghost %{_sbindir}/reboot
-%ghost %{_sbindir}/halt
-%ghost %{_sbindir}/poweroff
-%ghost %{_sbindir}/shutdown
+%{_sbindir}/deviced-request-shutdown
 
 %files -n libdeviced
 %manifest deviced.manifest
index ad9e7fb..cee9f52 100644 (file)
@@ -219,6 +219,20 @@ int sys_set_str(char *fname, char *val)
        return r;
 }
 
+int get_systemd_reboot_param(char *buf, unsigned bufsize)
+{
+       int r = sys_read_buf("/run/systemd/reboot-param", buf, bufsize);
+       if (r < 0)
+               return r;
+
+       if (r > 0 && buf[r - 1] == '\n') {
+               buf[r - 1] = '\0';
+               r -= 1;
+       }
+
+       return r;
+}
+
 static int terminate_processes_on_partition(const char *partition, bool force)
 {
        const char *argv[7] = {"/usr/bin/fuser", "-m", "-k", "-s", NULL, NULL, NULL};
index 3c82365..bd02047 100644 (file)
 #define DATA_VALUE_INT(x)      (*(int *)(x))
 #define DATA_VALUE_BOOL(x)     (*(bool *)(x))
 
+#define REBOOT_PARAM_MAXLEN    256 /* max param for reboot(2) *arg(ument), value checked in kernel v5.10:linux/kernel/reboot.c */
+
 #ifndef safe_free
 #define safe_free(x) safe_free_memory((void**)&(x))
 #endif
@@ -189,6 +191,7 @@ int sys_get_int(char *fname, int *val);
 int sys_set_int(char *fname, int val);
 int sys_get_str(char *fname, char *str, int len);
 int sys_set_str(char *fname, char *val);
+int get_systemd_reboot_param(char *buf, unsigned bufsize);
 int terminate_process(const char *partition, bool force);
 int mount_check(const char* path);
 void suspend_path(const char *frz_name, const char *path, const int max_retry);
index 808aa18..9b70fee 100644 (file)
  * limitations under the License.
  */
 
+#include <ctype.h>
+#include <errno.h>
 #include <getopt.h>
-#include <libsyscommon/libgdbus.h>
 #include <stdio.h>
 #include <stdbool.h>
-#include <core/common.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.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);
-               }
-       }
-}
+#include <core/common.h>
+#include <libsyscommon/libgdbus.h>
 
-int call_deviced_poweroff(const char *type, const char *extra_option, const char *message)
+int call_deviced_poweroff(const char *type, const char *extra_option)
 {
        int ret_dbus = extra_option
                ? gdbus_call_sync_with_reply_int(DEVICED_BUS_NAME, DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF, "PowerOffWithOption", g_variant_new("(ss)", type, extra_option), NULL)
@@ -293,69 +39,22 @@ int call_deviced_poweroff(const char *type, const char *extra_option, const char
                fprintf(stderr, "Error: %d", ret_dbus);
                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;
+       const char *option = argc > 2 ? argv[2] : NULL;
+       const char *action = argc > 1 ? argv[1] : NULL;
+       char buf[REBOOT_PARAM_MAXLEN] = {0, };
 
-       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);
+       if (!action) {
+               fprintf(stderr, "Usage: %s {action} [extra]\n", argv[0]);
                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");
-       }
+       if (!option && get_systemd_reboot_param(buf, sizeof buf) > 0)
+               option = buf;
 
-       assert(false); // unreachable
-       return EXIT_FAILURE;
+       return call_deviced_poweroff(action, option);
 }
index 010ac0b..92c6532 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include <assert.h>
+#include <ctype.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <limits.h>
@@ -76,18 +77,11 @@ static void sigchld(int signo)
 
 static void autoboot(void)
 {
-       static char buf[4096] = {0,};
-       int fd = open("/run/reboot", O_RDONLY); // XXX POWER_FLAG_REBOOT
-       int ret;
-
-       if (fd >= 0) {
-               ret = read(fd, buf, sizeof(buf) - 1);
-               if (ret < 0) {
-                       ret = -errno;
-                       printf("%s: fail to read reboot data(%d)\n", progname, ret);
-               }
-               close(fd);
-       }
+       static char buf[REBOOT_PARAM_MAXLEN] = {0, };
+
+       int r = sys_read_buf("/run/reboot", buf, sizeof buf);
+       if (r <= 0)
+               r = get_systemd_reboot_param(buf, sizeof buf);
 
        printf("%s: reboot param is <%s>\n", progname, buf);
        syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
diff --git a/systemd/deviced-request-shutdown@.service b/systemd/deviced-request-shutdown@.service
new file mode 100644 (file)
index 0000000..a202742
--- /dev/null
@@ -0,0 +1,24 @@
+[Unit]
+Description=Shutdown via deviced
+DefaultDependencies=no
+Before=final.target
+
+[Service]
+Type=oneshot
+#
+# Request shutdown to be performed by deviced.
+#
+ExecStart=/usr/sbin/deviced-request-shutdown %i
+#
+# /bin/sleep below will be called only if above dbus call succeeded.
+# deviced shutdown procedure includes handling client-defined timers,
+# which is ability for some user process to delay shutdown in order
+# to perform its deinitialization/shutdown procedure.
+# deviced hardcodes that timers can take at most 10 seconds (from all
+# clients combined), so we have to wait a bit more here to be sure
+# that both deviced timers and other actions are performed completely.
+#
+# If dbus-send failed, the command below will not be executed - and systemd
+# fallback action will be called immiedetely.
+#
+ExecStart=/bin/sleep 15