Blob Blame History Raw
From 43b4f60da9b468ffb3e782d91b9c01c845a339af Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 17 Jul 2020 16:06:44 -0400
Subject: [PATCH] src: die during shutdown with everything else

plymouthd currently avoids getting killed at shutdown.  This causes
filesystems to fail to remount read-only in some cases.

This commit changes things up so that plymouthd dies with everyone else,
but spawns a process to hold open the drm device that can keep the splash
up until the very end.
---
 src/Makefile.am                            |  7 +++
 src/main.c                                 | 11 ++--
 src/plugins/renderers/drm/Makefile.am      |  3 +-
 src/plugins/renderers/drm/plugin.c         | 62 ++++++++++++++++++++++
 src/plymouthd-drm-escrow.c                 | 18 +++++++
 systemd-units/plymouth-halt.service.in     |  1 +
 systemd-units/plymouth-poweroff.service.in |  1 +
 systemd-units/plymouth-reboot.service.in   |  1 +
 8 files changed, 100 insertions(+), 4 deletions(-)
 create mode 100644 src/plymouthd-drm-escrow.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 95ed019..78f3f78 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,52 +1,59 @@
 SUBDIRS = libply libply-splash-core libply-splash-graphics . plugins client
 if ENABLE_UPSTART_MONITORING
 SUBDIRS += upstart-bridge
 endif
 AM_CPPFLAGS = -I$(top_srcdir)                                                 \
            -I$(srcdir)/libply                                                 \
            -I$(srcdir)/libply-splash-core                                     \
            -I$(srcdir)                                                        \
+           -DPLYMOUTH_DRM_ESCROW_DIRECTORY=\"$(libexecdir)/plymouth\"         \
            -DPLYMOUTH_LOG_DIRECTORY=\"$(localstatedir)/log\"                  \
            -DPLYMOUTH_SPOOL_DIRECTORY=\"$(localstatedir)/spool/plymouth\"     \
            -DPLYMOUTH_TIME_DIRECTORY=\"$(localstatedir)/lib/plymouth/\"       \
            -DPLYMOUTH_LOGO_FILE=\"$(logofile)\"
 
 plymouthdbindir = $(plymouthdaemondir)
 plymouthdbin_PROGRAMS = plymouthd
 
 plymouthd_CFLAGS = $(PLYMOUTH_CFLAGS)                                         \
 		   -rdynamic                                                  \
 		   -DPLYMOUTH_PLUGIN_PATH=\"$(PLYMOUTH_PLUGIN_PATH)\"         \
 		   -DPLYMOUTH_THEME_PATH=\"$(PLYMOUTH_THEME_PATH)/\"          \
 		   -DPLYMOUTH_POLICY_DIR=\"$(PLYMOUTH_POLICY_DIR)/\"          \
 		   -DPLYMOUTH_RUNTIME_DIR=\"$(PLYMOUTH_RUNTIME_DIR)\"         \
 		   -DPLYMOUTH_CONF_DIR=\"$(PLYMOUTH_CONF_DIR)/\"              \
 		   -DPLYMOUTH_RUNTIME_THEME_PATH=\"$(PLYMOUTH_RUNTIME_THEME_PATH)/\"
 plymouthd_LDADD = $(PLYMOUTH_LIBS) libply/libply.la libply-splash-core/libply-splash-core.la
 plymouthd_SOURCES =                                                            \
                    ply-boot-protocol.h                                        \
                    ply-boot-server.h                                          \
                    ply-boot-server.c                                          \
                    plugins/splash/details/plugin.c                  \
                    main.c
 
+escrowdir = $(libexecdir)/plymouth
+escrow_PROGRAMS = plymouthd-drm-escrow
+
+plymouthd_drm_escrow_LDFLAGS = -all-static
+plymouthd_drm_escrow_SOURCES = plymouthd-drm-escrow.c
+
 plymouthdrundir = $(localstatedir)/run/plymouth
 plymouthdspooldir = $(localstatedir)/spool/plymouth
 plymouthdtimedir = $(localstatedir)/lib/plymouth
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = ply-splash-core.pc ply-splash-graphics.pc
 
 plymouthd_defaultsdir = $(PLYMOUTH_POLICY_DIR)
 dist_plymouthd_defaults_DATA = plymouthd.defaults
 
 plymouthd_confdir = $(PLYMOUTH_CONF_DIR)
 dist_plymouthd_conf_DATA = plymouthd.conf
 
 install-data-hook:
 	-mkdir -p $(DESTDIR)$(plymouthdrundir)
 	-mkdir -p $(DESTDIR)$(plymouthdspooldir)
 	-mkdir -p $(DESTDIR)$(plymouthdtimedir)
 
 EXTRA_DIST = ply-splash-core.pc.in ply-splash-graphics.pc.in
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/main.c b/src/main.c
index 8848ad0..8372f2f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2181,65 +2181,70 @@ main (int    argc,
 
                 if (daemon_handle == NULL) {
                         ply_error ("plymouthd: cannot daemonize: %m");
                         return EX_UNAVAILABLE;
                 }
         }
 
         if (debug)
                 debug_buffer = ply_buffer_new ();
 
         signal (SIGABRT, on_crash);
         signal (SIGSEGV, on_crash);
 
         /* before do anything we need to make sure we have a working
          * environment.
          */
         if (!initialize_environment (&state)) {
                 if (errno == 0) {
                         if (daemon_handle != NULL)
                                 ply_detach_daemon (daemon_handle, 0);
                         return 0;
                 }
 
                 ply_error ("plymouthd: could not setup basic operating environment: %m");
                 if (daemon_handle != NULL)
                         ply_detach_daemon (daemon_handle, EX_OSERR);
                 return EX_OSERR;
         }
 
         /* Make the first byte in argv be '@' so that we can survive systemd's killing
-         * spree when going from initrd to /, and so we stay alive all the way until
-         * the power is killed at shutdown.
+         * spree when going from initrd to /
          * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons
+         *
+         * If the system is shutting down, we let systemd slay us because otherwise we
+         * may prevent the root fs from getting remounted read-only.
          */
-        argv[0][0] = '@';
+        if (state.mode != PLY_BOOT_SPLASH_MODE_SHUTDOWN &&
+	    state.mode != PLY_BOOT_SPLASH_MODE_REBOOT) {
+                argv[0][0] = '@';
+        }
 
         state.boot_server = start_boot_server (&state);
 
         if (state.boot_server == NULL) {
                 ply_trace ("plymouthd is already running");
 
                 if (daemon_handle != NULL)
                         ply_detach_daemon (daemon_handle, EX_OK);
                 return EX_OK;
         }
 
         state.boot_buffer = ply_buffer_new ();
 
         if (attach_to_session) {
                 state.should_be_attached = attach_to_session;
                 if (!attach_to_running_session (&state)) {
                         ply_trace ("could not redirect console session: %m");
                         if (!no_daemon)
                                 ply_detach_daemon (daemon_handle, EX_UNAVAILABLE);
                         return EX_UNAVAILABLE;
                 }
         }
 
         state.progress = ply_progress_new ();
         state.splash_delay = NAN;
         state.device_timeout = NAN;
 
         ply_progress_load_cache (state.progress,
                                  get_cache_file_for_mode (state.mode));
 
diff --git a/src/plugins/renderers/drm/Makefile.am b/src/plugins/renderers/drm/Makefile.am
index 271b17f..22a819b 100644
--- a/src/plugins/renderers/drm/Makefile.am
+++ b/src/plugins/renderers/drm/Makefile.am
@@ -1,23 +1,24 @@
 if ENABLE_DRM_RENDERER
 AM_CPPFLAGS = -I$(top_srcdir)                                                 \
            -I$(srcdir)/../../../libply                                        \
            -I$(srcdir)/../../../libply-splash-core                            \
            -I$(srcdir)/../../..                                               \
            -I$(srcdir)/../..                                                  \
            -I$(srcdir)/..                                                     \
-           -I$(srcdir)
+           -I$(srcdir)                                                        \
+           -DPLYMOUTH_DRM_ESCROW_DIRECTORY=\"$(libexecdir)/plymouth\"
 
 plugindir = $(libdir)/plymouth/renderers
 plugin_LTLIBRARIES = drm.la
 
 drm_la_CFLAGS = $(PLYMOUTH_CFLAGS) $(DRM_CFLAGS)
 
 drm_la_LDFLAGS = -module -avoid-version -export-dynamic
 drm_la_LIBADD = $(PLYMOUTH_LIBS) $(DRM_LIBS)                                  \
                          ../../../libply/libply.la                            \
                          ../../../libply-splash-core/libply-splash-core.la
 drm_la_SOURCES = $(srcdir)/plugin.c
 
 endif
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/plugins/renderers/drm/plugin.c b/src/plugins/renderers/drm/plugin.c
index 4dbf8da..38bae36 100644
--- a/src/plugins/renderers/drm/plugin.c
+++ b/src/plugins/renderers/drm/plugin.c
@@ -131,73 +131,79 @@ typedef struct
         bool connected;
         bool uses_hw_rotation;
 } ply_output_t;
 
 struct _ply_renderer_backend
 {
         ply_event_loop_t                *loop;
         ply_terminal_t                  *terminal;
 
         int                              device_fd;
         char                            *device_name;
         drmModeRes                      *resources;
 
         ply_renderer_input_source_t      input_source;
         ply_list_t                      *heads;
         ply_hashtable_t                 *heads_by_controller_id;
 
         ply_hashtable_t                 *output_buffers;
 
         ply_output_t                    *outputs;
         int                              outputs_len;
         int                              connected_count;
 
         int32_t                          dither_red;
         int32_t                          dither_green;
         int32_t                          dither_blue;
 
         uint32_t                         is_active : 1;
         uint32_t        requires_explicit_flushing : 1;
         uint32_t                use_preferred_mode : 1;
+        uint32_t          watching_for_termination : 1;
 
         int                              panel_width;
         int                              panel_height;
         ply_pixel_buffer_rotation_t      panel_rotation;
         int                              panel_scale;
 };
 
 ply_renderer_plugin_interface_t *ply_renderer_backend_get_interface (void);
 static bool open_input_source (ply_renderer_backend_t      *backend,
                                ply_renderer_input_source_t *input_source);
 static void flush_head (ply_renderer_backend_t *backend,
                         ply_renderer_head_t    *head);
 
+static void close_device (ply_renderer_backend_t *backend);
+
+static void watch_for_termination (ply_renderer_backend_t *backend);
+static void stop_watching_for_termination (ply_renderer_backend_t *backend);
+
 /* A small helper to determine if we should try to keep the current mode
  * or pick the best mode ourselves, we keep the current mode only if the
  * user specified a specific mode using video= on the commandline.
  */
 static bool
 should_use_preferred_mode (void)
 {
         bool use_preferred_mode = true;
 
         if (ply_kernel_command_line_get_string_after_prefix ("video="))
                 use_preferred_mode = false;
 
         ply_trace ("should_use_preferred_mode: %d", use_preferred_mode);
 
         return use_preferred_mode;
 }
 
 static bool
 ply_renderer_buffer_map (ply_renderer_backend_t *backend,
                          ply_renderer_buffer_t  *buffer)
 {
         struct drm_mode_map_dumb map_dumb_buffer_request;
         void *map_address;
 
         if (buffer->map_address != MAP_FAILED) {
                 buffer->map_count++;
                 return true;
         }
 
         memset (&map_dumb_buffer_request, 0, sizeof(struct drm_mode_map_dumb));
@@ -918,158 +924,214 @@ static void
 destroy_backend (ply_renderer_backend_t *backend)
 {
         ply_trace ("destroying renderer backend for device %s", backend->device_name);
         free_heads (backend);
 
         free (backend->device_name);
         ply_hashtable_free (backend->output_buffers);
         ply_hashtable_free (backend->heads_by_controller_id);
 
         free (backend->outputs);
         free (backend);
 }
 
 static void
 activate (ply_renderer_backend_t *backend)
 {
         ply_renderer_head_t *head;
         ply_list_node_t *node;
 
         ply_trace ("taking master and scanning out");
         backend->is_active = true;
 
         drmSetMaster (backend->device_fd);
         node = ply_list_get_first_node (backend->heads);
         while (node != NULL) {
                 head = (ply_renderer_head_t *) ply_list_node_get_data (node);
                 /* Flush out any pending drawing to the buffer */
                 flush_head (backend, head);
                 node = ply_list_get_next_node (backend->heads, node);
         }
+
+        watch_for_termination (backend);
 }
 
 static void
 deactivate (ply_renderer_backend_t *backend)
 {
         ply_trace ("dropping master");
         drmDropMaster (backend->device_fd);
         backend->is_active = false;
+
+        stop_watching_for_termination (backend);
 }
 
 static void
 on_active_vt_changed (ply_renderer_backend_t *backend)
 {
         if (ply_terminal_is_active (backend->terminal)) {
                 ply_trace ("activating on vt change");
                 activate (backend);
         } else {
                 ply_trace ("deactivating on vt change");
                 deactivate (backend);
         }
 }
 
 static bool
 load_driver (ply_renderer_backend_t *backend)
 {
         int device_fd;
 
         ply_trace ("Opening '%s'", backend->device_name);
         device_fd = open (backend->device_name, O_RDWR);
 
         if (device_fd < 0) {
                 ply_trace ("open failed: %m");
                 return false;
         }
 
         backend->device_fd = device_fd;
 
         drmDropMaster (device_fd);
 
         return true;
 }
 
 static void
 unload_backend (ply_renderer_backend_t *backend)
 {
         if (backend == NULL)
                 return;
 
         ply_trace ("unloading backend");
 
         if (backend->device_fd >= 0) {
                 drmClose (backend->device_fd);
                 backend->device_fd = -1;
         }
 
         destroy_backend (backend);
         backend = NULL;
 
 }
 
+static void
+on_term_signal (ply_renderer_backend_t *backend)
+{
+        pid_t pid;
+
+        ply_trace ("got SIGTERM, launching drm escrow to protect splash, and dying");
+
+        pid = fork();
+
+        if (pid == 0) {
+                const char *argv[] = { PLYMOUTH_DRM_ESCROW_DIRECTORY "/plymouthd-drm-escrow", NULL };
+
+                dup (backend->device_fd);
+                execve (argv[0], (char * const *) argv, NULL);
+
+		ply_trace ("could not launch drm escrow process: %m");
+
+                _exit (1);
+        }
+
+
+	close_device (backend);
+
+        exit (0);
+}
+
+static void
+watch_for_termination (ply_renderer_backend_t *backend)
+{
+        if (backend->watching_for_termination)
+                return;
+
+	ply_trace ("watching for termination signal");
+        ply_event_loop_watch_signal (backend->loop, SIGTERM, (ply_event_handler_t) on_term_signal, backend);
+        backend->watching_for_termination = true;
+}
+
+static void
+stop_watching_for_termination (ply_renderer_backend_t *backend)
+{
+        if (!backend->watching_for_termination)
+                return;
+
+	ply_trace ("stopping watching for termination signal");
+        ply_event_loop_stop_watching_signal (backend->loop, SIGTERM);
+        backend->watching_for_termination = false;
+}
+
 static bool
 open_device (ply_renderer_backend_t *backend)
 {
         assert (backend != NULL);
         assert (backend->device_name != NULL);
 
         if (!load_driver (backend))
                 return false;
 
         if (backend->terminal == NULL)
                 return true;
 
         if (!ply_terminal_open (backend->terminal)) {
                 ply_trace ("could not open terminal: %m");
                 return false;
         }
 
         if (!ply_terminal_is_vt (backend->terminal)) {
                 ply_trace ("terminal is not a VT");
                 ply_terminal_close (backend->terminal);
                 return false;
         }
 
         ply_terminal_watch_for_active_vt_change (backend->terminal,
                                                  (ply_terminal_active_vt_changed_handler_t)
                                                  on_active_vt_changed,
                                                  backend);
 
+        watch_for_termination (backend);
+
         return true;
 }
 
 static void
 close_device (ply_renderer_backend_t *backend)
 {
         ply_trace ("closing device");
 
         free_heads (backend);
 
+        stop_watching_for_termination (backend);
+
         if (backend->terminal != NULL) {
                 ply_terminal_stop_watching_for_active_vt_change (backend->terminal,
                                                                  (ply_terminal_active_vt_changed_handler_t)
                                                                  on_active_vt_changed,
                                                                  backend);
         }
 
         unload_backend (backend);
 }
 
 static void
 output_get_controller_info (ply_renderer_backend_t *backend,
                             drmModeConnector       *connector,
                             ply_output_t           *output)
 {
         int i;
         drmModeEncoder *encoder;
 
         assert (backend != NULL);
 
         output->possible_controllers = 0xffffffff;
 
         for (i = 0; i < connector->count_encoders; i++) {
                 encoder = drmModeGetEncoder (backend->device_fd,
                                              connector->encoders[i]);
 
                 if (encoder == NULL)
                         continue;
 
                 if (encoder->encoder_id == connector->encoder_id && encoder->crtc_id) {
diff --git a/src/plymouthd-drm-escrow.c b/src/plymouthd-drm-escrow.c
new file mode 100644
index 0000000..9097db9
--- /dev/null
+++ b/src/plymouthd-drm-escrow.c
@@ -0,0 +1,18 @@
+#include <signal.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+	signal (SIGTERM, SIG_IGN);
+
+        /* Make the first byte in argv be '@' so that we can survive systemd's killing
+         * spree until the power is killed at shutdown.
+         * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons
+         */
+        argv[0][0] = '@';
+
+        while (pause());
+
+        return 0;
+}
diff --git a/systemd-units/plymouth-halt.service.in b/systemd-units/plymouth-halt.service.in
index cb87c1f..00f7eed 100644
--- a/systemd-units/plymouth-halt.service.in
+++ b/systemd-units/plymouth-halt.service.in
@@ -1,13 +1,14 @@
 [Unit]
 Description=Show Plymouth Halt Screen
 After=getty@tty1.service display-manager.service plymouth-start.service
 Before=systemd-halt.service
 DefaultDependencies=no
 ConditionKernelCommandLine=!plymouth.enable=0
 ConditionVirtualization=!container
 
 [Service]
 ExecStart=@PLYMOUTH_DAEMON_DIR@/plymouthd --mode=shutdown --attach-to-session
 ExecStartPost=-@PLYMOUTH_CLIENT_DIR@/plymouth show-splash
+KillMode=none
 Type=forking
 RemainAfterExit=yes
diff --git a/systemd-units/plymouth-poweroff.service.in b/systemd-units/plymouth-poweroff.service.in
index cf05e47..a1f78eb 100644
--- a/systemd-units/plymouth-poweroff.service.in
+++ b/systemd-units/plymouth-poweroff.service.in
@@ -1,13 +1,14 @@
 [Unit]
 Description=Show Plymouth Power Off Screen
 After=getty@tty1.service display-manager.service plymouth-start.service
 Before=systemd-poweroff.service
 DefaultDependencies=no
 ConditionKernelCommandLine=!plymouth.enable=0
 ConditionVirtualization=!container
 
 [Service]
 ExecStart=@PLYMOUTH_DAEMON_DIR@/plymouthd --mode=shutdown --attach-to-session
 ExecStartPost=-@PLYMOUTH_CLIENT_DIR@/plymouth show-splash
+KillMode=none
 Type=forking
 RemainAfterExit=yes
diff --git a/systemd-units/plymouth-reboot.service.in b/systemd-units/plymouth-reboot.service.in
index 3624550..8fff576 100644
--- a/systemd-units/plymouth-reboot.service.in
+++ b/systemd-units/plymouth-reboot.service.in
@@ -1,13 +1,14 @@
 [Unit]
 Description=Show Plymouth Reboot Screen
 After=getty@tty1.service display-manager.service plymouth-start.service
 Before=systemd-reboot.service
 DefaultDependencies=no
 ConditionKernelCommandLine=!plymouth.enable=0
 ConditionVirtualization=!container
 
 [Service]
 ExecStart=@PLYMOUTH_DAEMON_DIR@/plymouthd --mode=reboot --attach-to-session
 ExecStartPost=-@PLYMOUTH_CLIENT_DIR@/plymouth show-splash
+KillMode=none
 Type=forking
 RemainAfterExit=yes
-- 
2.26.0