From 43b4f60da9b468ffb3e782d91b9c01c845a339af Mon Sep 17 00:00:00 2001 From: Ray Strode 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 +#include + +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