Blob Blame History Raw
From b5bacf2b5e5d9e58cbe96fda0a56baf5dfa11358 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 10 Oct 2018 20:07:37 +0100
Subject: [PATCH] device-manager: don't watch for udev events when deactivated

If a device gets added when we're already deactivated, plymouth shouldn't
process the device, since processing it effectively activates plymouth.

This commit pulls the udev monitor fd out of the event loop while
plymouth is deactivated so new events are deferred until reactivation.

Modified by Iain Lane <iain.lane@canonical.com>: Also deactivate the
timer that finds all devices known to udev after an interval, when
paused.
---
 src/libply-splash-core/ply-device-manager.c | 74 +++++++++++++++++----
 src/libply-splash-core/ply-device-manager.h |  2 +
 src/main.c                                  |  3 +
 3 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/src/libply-splash-core/ply-device-manager.c b/src/libply-splash-core/ply-device-manager.c
index b637fb8..82f0137 100644
--- a/src/libply-splash-core/ply-device-manager.c
+++ b/src/libply-splash-core/ply-device-manager.c
@@ -36,74 +36,78 @@
 
 #include "ply-logger.h"
 #include "ply-event-loop.h"
 #include "ply-hashtable.h"
 #include "ply-list.h"
 #include "ply-utils.h"
 
 #define SUBSYSTEM_DRM "drm"
 #define SUBSYSTEM_FRAME_BUFFER "graphics"
 
 #ifdef HAVE_UDEV
 static void create_devices_from_udev (ply_device_manager_t *manager);
 #endif
 
 static bool create_devices_for_terminal_and_renderer_type (ply_device_manager_t *manager,
                                                            const char           *device_path,
                                                            ply_terminal_t       *terminal,
                                                            ply_renderer_type_t   renderer_type);
 struct _ply_device_manager
 {
         ply_device_manager_flags_t flags;
         ply_event_loop_t          *loop;
         ply_hashtable_t           *terminals;
         ply_hashtable_t           *renderers;
         ply_terminal_t            *local_console_terminal;
         ply_list_t                *keyboards;
         ply_list_t                *text_displays;
         ply_list_t                *pixel_displays;
         struct udev               *udev_context;
         struct udev_monitor       *udev_monitor;
+        ply_fd_watch_t            *fd_watch;
 
         ply_keyboard_added_handler_t         keyboard_added_handler;
         ply_keyboard_removed_handler_t       keyboard_removed_handler;
         ply_pixel_display_added_handler_t    pixel_display_added_handler;
         ply_pixel_display_removed_handler_t  pixel_display_removed_handler;
         ply_text_display_added_handler_t     text_display_added_handler;
         ply_text_display_removed_handler_t   text_display_removed_handler;
         void                                *event_handler_data;
 
         uint32_t                    local_console_managed : 1;
         uint32_t                    local_console_is_text : 1;
         uint32_t                    serial_consoles_detected : 1;
         uint32_t                    renderers_activated : 1;
         uint32_t                    keyboards_activated : 1;
+
+        uint32_t                    paused : 1;
+        uint32_t                    device_timeout_elapsed : 1;
 };
 
 static void
 detach_from_event_loop (ply_device_manager_t *manager)
 {
         assert (manager != NULL);
 
         manager->loop = NULL;
 }
 
 static void
 attach_to_event_loop (ply_device_manager_t *manager,
                       ply_event_loop_t     *loop)
 {
         assert (manager != NULL);
         assert (loop != NULL);
         assert (manager->loop == NULL);
 
         manager->loop = loop;
 
         ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t)
                                        detach_from_event_loop,
                                        manager);
 }
 
 static void
 free_displays_for_renderer (ply_device_manager_t *manager,
                             ply_renderer_t       *renderer)
 {
         ply_list_node_t *node;
@@ -348,77 +352,92 @@ on_udev_event (ply_device_manager_t *manager)
                 return;
 
         if (strcmp (action, "add") == 0) {
                 const char *subsystem;
 
                 subsystem = udev_device_get_subsystem (device);
 
                 if (strcmp (subsystem, SUBSYSTEM_DRM) == 0) {
                         if (manager->local_console_managed && manager->local_console_is_text)
                                 ply_trace ("ignoring since we're already using text splash for local console");
                         else
                                 create_devices_for_udev_device (manager, device);
                 } else {
                         ply_trace ("ignoring since we only handle subsystem %s devices after timeout", subsystem);
                 }
         } else if (strcmp (action, "remove") == 0) {
                 free_devices_for_udev_device (manager, device);
         }
 
         udev_device_unref (device);
 }
 
 static void
 watch_for_udev_events (ply_device_manager_t *manager)
 {
         int fd;
 
         assert (manager != NULL);
         assert (manager->udev_monitor == NULL);
 
+        if (manager->fd_watch != NULL)
+                return;
+
         ply_trace ("watching for udev graphics device add and remove events");
 
-        manager->udev_monitor = udev_monitor_new_from_netlink (manager->udev_context, "udev");
+        if (manager->udev_monitor == NULL) {
+                manager->udev_monitor = udev_monitor_new_from_netlink (manager->udev_context, "udev");
 
-        udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_DRM, NULL);
-        udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_FRAME_BUFFER, NULL);
-        udev_monitor_filter_add_match_tag (manager->udev_monitor, "seat");
-        udev_monitor_enable_receiving (manager->udev_monitor);
+                udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_DRM, NULL);
+                udev_monitor_filter_add_match_subsystem_devtype (manager->udev_monitor, SUBSYSTEM_FRAME_BUFFER, NULL);
+                udev_monitor_filter_add_match_tag (manager->udev_monitor, "seat");
+                udev_monitor_enable_receiving (manager->udev_monitor);
+        }
 
         fd = udev_monitor_get_fd (manager->udev_monitor);
-        ply_event_loop_watch_fd (manager->loop,
-                                 fd,
-                                 PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
-                                 (ply_event_handler_t)
-                                 on_udev_event,
-                                 NULL,
-                                 manager);
+        manager->fd_watch = ply_event_loop_watch_fd (manager->loop,
+                                                     fd,
+                                                     PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+                                                     (ply_event_handler_t)
+                                                     on_udev_event,
+                                                     NULL,
+                                                     manager);
+}
+
+static void
+stop_watching_for_udev_events (ply_device_manager_t *manager)
+{
+        if (manager->fd_watch == NULL)
+                return;
+
+        ply_event_loop_stop_watching_fd (manager->loop, manager->fd_watch);
+        manager->fd_watch = NULL;
 }
 #endif
 
 static void
 free_terminal (char                 *device,
                ply_terminal_t       *terminal,
                ply_device_manager_t *manager)
 {
         ply_hashtable_remove (manager->terminals, device);
 
         ply_terminal_free (terminal);
 }
 
 static void
 free_terminals (ply_device_manager_t *manager)
 {
         ply_hashtable_foreach (manager->terminals,
                                (ply_hashtable_foreach_func_t *)
                                free_terminal,
                                manager);
 }
 
 static ply_terminal_t *
 get_terminal (ply_device_manager_t *manager,
               const char           *device_name)
 {
         char *full_name = NULL;
         ply_terminal_t *terminal;
 
         if (strncmp (device_name, "/dev/", strlen ("/dev/")) == 0)
@@ -774,60 +793,67 @@ create_devices_from_terminals (ply_device_manager_t *manager)
 
         if (has_serial_consoles) {
                 ply_trace ("serial consoles detected, managing them with details forced");
                 manager->serial_consoles_detected = true;
 
                 ply_hashtable_foreach (manager->terminals,
                                        (ply_hashtable_foreach_func_t *)
                                        create_devices_for_terminal,
                                        manager);
                 return true;
         }
 
         return false;
 }
 
 static void
 create_non_graphical_devices (ply_device_manager_t *manager)
 {
         create_devices_for_terminal_and_renderer_type (manager,
                                                        NULL,
                                                        manager->local_console_terminal,
                                                        PLY_RENDERER_TYPE_NONE);
 }
 
 #ifdef HAVE_UDEV
 static void
 create_devices_from_udev (ply_device_manager_t *manager)
 {
         bool found_drm_device, found_fb_device;
 
+        manager->device_timeout_elapsed = true;
+
+        if (manager->paused) {
+                ply_trace ("create_devices_from_udev timeout elapsed while paused, deferring execution");
+                return;
+        }
+
         ply_trace ("Timeout elapsed, looking for devices from udev");
 
         found_drm_device = create_devices_for_subsystem (manager, SUBSYSTEM_DRM);
         found_fb_device = create_devices_for_subsystem (manager, SUBSYSTEM_FRAME_BUFFER);
 
         if (found_drm_device || found_fb_device)
                 return;
 
         ply_trace ("Creating non-graphical devices, since there's no suitable graphics hardware");
         create_non_graphical_devices (manager);
 }
 #endif
 
 static void
 create_fallback_devices (ply_device_manager_t *manager)
 {
         create_devices_for_terminal_and_renderer_type (manager,
                                                        NULL,
                                                        manager->local_console_terminal,
                                                        PLY_RENDERER_TYPE_AUTO);
 }
 
 void
 ply_device_manager_watch_devices (ply_device_manager_t                *manager,
                                   double                               device_timeout,
                                   ply_keyboard_added_handler_t         keyboard_added_handler,
                                   ply_keyboard_removed_handler_t       keyboard_removed_handler,
                                   ply_pixel_display_added_handler_t    pixel_display_added_handler,
                                   ply_pixel_display_removed_handler_t  pixel_display_removed_handler,
                                   ply_text_display_added_handler_t     text_display_added_handler,
@@ -965,30 +991,54 @@ ply_device_manager_activate_keyboards (ply_device_manager_t *manager)
 
                 ply_keyboard_watch_for_input (keyboard);
 
                 node = next_node;
         }
 
         manager->keyboards_activated = true;
 }
 
 void
 ply_device_manager_deactivate_keyboards (ply_device_manager_t *manager)
 {
         ply_list_node_t *node;
 
         ply_trace ("deactivating keyboards");
         node = ply_list_get_first_node (manager->keyboards);
         while (node != NULL) {
                 ply_keyboard_t *keyboard;
                 ply_list_node_t *next_node;
 
                 keyboard = ply_list_node_get_data (node);
                 next_node = ply_list_get_next_node (manager->keyboards, node);
 
                 ply_keyboard_stop_watching_for_input (keyboard);
 
                 node = next_node;
         }
 
         manager->keyboards_activated = false;
 }
+
+void
+ply_device_manager_pause (ply_device_manager_t *manager)
+{
+        ply_trace ("ply_device_manager_pause() called, stopping watching for udev events");
+        manager->paused = true;
+#ifdef HAVE_UDEV
+        stop_watching_for_udev_events (manager);
+#endif
+}
+
+void
+ply_device_manager_unpause (ply_device_manager_t *manager)
+{
+        ply_trace ("ply_device_manager_unpause() called, resuming watching for udev events");
+        manager->paused = false;
+#ifdef HAVE_UDEV
+        if (manager->device_timeout_elapsed) {
+                ply_trace ("ply_device_manager_unpause(): timeout elapsed while paused, looking for udev devices");
+                create_devices_from_udev (manager);
+        }
+        watch_for_udev_events (manager);
+#endif
+}
diff --git a/src/libply-splash-core/ply-device-manager.h b/src/libply-splash-core/ply-device-manager.h
index ad05897..389b636 100644
--- a/src/libply-splash-core/ply-device-manager.h
+++ b/src/libply-splash-core/ply-device-manager.h
@@ -28,46 +28,48 @@
 #include "ply-text-display.h"
 
 typedef enum
 {
         PLY_DEVICE_MANAGER_FLAGS_NONE = 0,
         PLY_DEVICE_MANAGER_FLAGS_IGNORE_SERIAL_CONSOLES = 1 << 0,
         PLY_DEVICE_MANAGER_FLAGS_IGNORE_UDEV = 1 << 1,
         PLY_DEVICE_MANAGER_FLAGS_SKIP_RENDERERS = 1 << 2
 } ply_device_manager_flags_t;
 
 typedef struct _ply_device_manager ply_device_manager_t;
 typedef void (* ply_keyboard_added_handler_t) (void *, ply_keyboard_t *);
 typedef void (* ply_keyboard_removed_handler_t) (void *, ply_keyboard_t *);
 typedef void (* ply_pixel_display_added_handler_t) (void *, ply_pixel_display_t *);
 typedef void (* ply_pixel_display_removed_handler_t) (void *, ply_pixel_display_t *);
 typedef void (* ply_text_display_added_handler_t) (void *, ply_text_display_t *);
 typedef void (* ply_text_display_removed_handler_t) (void *, ply_text_display_t *);
 
 #ifndef PLY_HIDE_FUNCTION_DECLARATIONS
 ply_device_manager_t *ply_device_manager_new (const char                *default_tty,
                                               ply_device_manager_flags_t flags);
 void ply_device_manager_watch_devices (ply_device_manager_t                *manager,
                                        double                               device_timeout,
                                        ply_keyboard_added_handler_t         keyboard_added_handler,
                                        ply_keyboard_removed_handler_t       keyboard_removed_handler,
                                        ply_pixel_display_added_handler_t    pixel_display_added_handler,
                                        ply_pixel_display_removed_handler_t  pixel_display_removed_handler,
                                        ply_text_display_added_handler_t     text_display_added_handler,
                                        ply_text_display_removed_handler_t   text_display_removed_handler,
                                        void                                *data);
+void ply_device_manager_pause (ply_device_manager_t *manager);
+void ply_device_manager_unpause (ply_device_manager_t *manager);
 bool ply_device_manager_has_serial_consoles (ply_device_manager_t *manager);
 bool ply_device_manager_has_displays (ply_device_manager_t *manager);
 ply_list_t *ply_device_manager_get_keyboards (ply_device_manager_t *manager);
 ply_list_t *ply_device_manager_get_pixel_displays (ply_device_manager_t *manager);
 ply_list_t *ply_device_manager_get_text_displays (ply_device_manager_t *manager);
 void ply_device_manager_free (ply_device_manager_t *manager);
 void ply_device_manager_activate_keyboards (ply_device_manager_t *manager);
 void ply_device_manager_deactivate_keyboards (ply_device_manager_t *manager);
 void ply_device_manager_activate_renderers (ply_device_manager_t *manager);
 void ply_device_manager_deactivate_renderers (ply_device_manager_t *manager);
 ply_terminal_t *ply_device_manager_get_default_terminal (ply_device_manager_t *manager);
 
 #endif
 
 #endif
 /* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */
diff --git a/src/main.c b/src/main.c
index e44de7b..3253aa9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1305,94 +1305,97 @@ on_boot_splash_idle (state_t *state)
                 ply_trace ("quitting program");
                 quit_program (state);
         } else if (state->deactivate_trigger != NULL) {
                 ply_trace ("deactivating splash");
                 deactivate_splash (state);
         }
 }
 
 static void
 on_deactivate (state_t       *state,
                ply_trigger_t *deactivate_trigger)
 {
         if (state->is_inactive) {
                 ply_trigger_pull (deactivate_trigger, NULL);
                 return;
         }
 
         if (state->deactivate_trigger != NULL) {
                 ply_trigger_add_handler (state->deactivate_trigger,
                                          (ply_trigger_handler_t)
                                          ply_trigger_pull,
                                          deactivate_trigger);
                 return;
         }
 
         state->deactivate_trigger = deactivate_trigger;
 
         ply_trace ("deactivating");
         cancel_pending_delayed_show (state);
 
+        ply_device_manager_pause (state->device_manager);
         ply_device_manager_deactivate_keyboards (state->device_manager);
 
         if (state->boot_splash != NULL) {
                 ply_boot_splash_become_idle (state->boot_splash,
                                              (ply_boot_splash_on_idle_handler_t)
                                              on_boot_splash_idle,
                                              state);
         } else {
                 ply_trace ("deactivating splash");
                 deactivate_splash (state);
         }
 }
 
 static void
 on_reactivate (state_t *state)
 {
         if (!state->is_inactive)
                 return;
 
         if (state->local_console_terminal != NULL) {
                 ply_terminal_open (state->local_console_terminal);
                 ply_terminal_watch_for_vt_changes (state->local_console_terminal);
                 ply_terminal_set_unbuffered_input (state->local_console_terminal);
                 ply_terminal_ignore_mode_changes (state->local_console_terminal, false);
         }
 
         if ((state->session != NULL) && state->should_be_attached) {
                 ply_trace ("reactivating terminal session");
                 attach_to_running_session (state);
         }
 
         ply_device_manager_activate_keyboards (state->device_manager);
         ply_device_manager_activate_renderers (state->device_manager);
 
+        ply_device_manager_unpause (state->device_manager);
+
         state->is_inactive = false;
 
         update_display (state);
 }
 
 static void
 on_quit (state_t       *state,
          bool           retain_splash,
          ply_trigger_t *quit_trigger)
 {
         ply_trace ("quitting (retain splash: %s)", retain_splash ? "true" : "false");
 
         if (state->quit_trigger != NULL) {
                 ply_trace ("quit trigger already pending, so chaining to it");
                 ply_trigger_add_handler (state->quit_trigger,
                                          (ply_trigger_handler_t)
                                          ply_trigger_pull,
                                          quit_trigger);
                 return;
         }
 
         if (state->system_initialized) {
                 ply_trace ("system initialized so saving boot-duration file");
                 ply_create_directory (PLYMOUTH_TIME_DIRECTORY);
                 ply_progress_save_cache (state->progress,
                                          get_cache_file_for_mode (state->mode));
         } else {
                 ply_trace ("system not initialized so skipping saving boot-duration file");
         }
         state->quit_trigger = quit_trigger;
-- 
2.21.0