]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Shut down display manager more carefully
authorRobert Ancell <robert.ancell@canonical.com>
Mon, 27 Jun 2011 15:15:19 +0000 (16:15 +0100)
committerRobert Ancell <robert.ancell@canonical.com>
Mon, 27 Jun 2011 15:15:19 +0000 (16:15 +0100)
src/child-process.c
src/child-process.h
src/display-manager.c
src/display-manager.h
src/display.c
src/display.h
src/greeter.c
src/greeter.h
src/lightdm.c

index f54da56c87fe2e720f8ec9d666282d60f93efaba..8709386654cc64235622c2352d6d1a4416860952 100644 (file)
 #include "child-process.h"
 
 enum {
+    STARTED,
     GOT_DATA,
     GOT_SIGNAL,  
     EXITED,
     TERMINATED,
+    STOPPED,
     LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -45,6 +47,9 @@ struct ChildProcessPrivate
     GIOChannel *to_child_channel;
     GIOChannel *from_child_channel;
 
+    /* Timeout waiting for process to quit */
+    guint quit_timeout;
+
     /* Process ID */
     GPid pid;
 };
@@ -54,7 +59,6 @@ G_DEFINE_TYPE (ChildProcess, child_process, G_TYPE_OBJECT);
 static ChildProcess *parent_process = NULL;
 static GHashTable *processes = NULL;
 static int signal_pipe[2];
-static gboolean stopping = FALSE;
 
 ChildProcess *
 child_process_get_parent (void)
@@ -116,12 +120,13 @@ child_process_watch_cb (GPid pid, gint status, gpointer data)
         g_signal_emit (process, signals[TERMINATED], 0, WTERMSIG (status));
     }
 
+    if (process->priv->quit_timeout)
+        g_source_remove (process->priv->quit_timeout);
+    process->priv->quit_timeout = 0;  
     process->priv->pid = 0;
     g_hash_table_remove (processes, GINT_TO_POINTER (pid));
 
-    /* Stop when all processes quit */
-    if (stopping && g_hash_table_size (processes) == 0)
-        exit (EXIT_SUCCESS);
+    g_signal_emit (process, signals[STOPPED], 0);
 }
 
 static void
@@ -325,6 +330,8 @@ child_process_start (ChildProcess *process,
     g_hash_table_insert (processes, GINT_TO_POINTER (process->priv->pid), g_object_ref (process));
     g_child_watch_add (process->priv->pid, child_process_watch_cb, process);
 
+    g_signal_emit (process, signals[STARTED], 0);  
+
     return TRUE;
 }
 
@@ -363,24 +370,20 @@ child_process_get_from_child_channel (ChildProcess *process)
     return process->priv->from_child_channel;
 }
 
-void
-child_process_stop_all (void)
+static gboolean
+quit_timeout_cb (ChildProcess *process)
 {
-    GHashTableIter iter;
-    gpointer key, value;
-
-    stopping = TRUE;
-
-    /* If no processes, then just quit */
-    if (g_hash_table_size (processes) == 0)
-        exit (EXIT_SUCCESS);
+    process->priv->quit_timeout = 0;
+    child_process_signal (process, SIGKILL);
+    return FALSE;
+}
 
-    g_hash_table_iter_init (&iter, processes);
-    while (g_hash_table_iter_next (&iter, &key, &value))
-    {
-        ChildProcess *process = (ChildProcess *)value;
-        child_process_signal (process, SIGTERM);
-    }
+void
+child_process_stop (ChildProcess *process)
+{
+    /* Send SIGTERM, and then SIGKILL if no response */
+    process->priv->quit_timeout = g_timeout_add (5000, (GSourceFunc) quit_timeout_cb, process);
+    child_process_signal (process, SIGTERM);
 }
 
 static void
@@ -455,6 +458,14 @@ child_process_class_init (ChildProcessClass *klass)
 
     g_type_class_add_private (klass, sizeof (ChildProcessPrivate));
 
+    signals[STARTED] =
+        g_signal_new ("started",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (ChildProcessClass, started),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0); 
     signals[GOT_DATA] =
         g_signal_new ("got-data",
                       G_TYPE_FROM_CLASS (klass),
@@ -487,6 +498,14 @@ child_process_class_init (ChildProcessClass *klass)
                       NULL, NULL,
                       g_cclosure_marshal_VOID__INT,
                       G_TYPE_NONE, 1, G_TYPE_INT);
+    signals[STOPPED] =
+        g_signal_new ("stopped",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (ChildProcessClass, stopped),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
 
     /* Catch signals and feed them to the main loop via a pipe */
     processes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
index a4ca9749622a848550785b1b8d6697ee9da31156..0fb0d4739a748acf7636535f4eb3bdf9c29a390b 100644 (file)
@@ -33,10 +33,12 @@ typedef struct
 typedef struct
 {
     GObjectClass parent_class;
+    void (*started)(ChildProcess *process);
     void (*got_data)(ChildProcess *process);
     void (*got_signal)(ChildProcess *process, int signum);
     void (*exited)(ChildProcess *process, int status);
-    void (*terminated) (ChildProcess *process, int signum);
+    void (*terminated)(ChildProcess *process, int signum);
+    void (*stopped)(ChildProcess *process);
 } ChildProcessClass;
 
 GType child_process_get_type (void);
@@ -66,7 +68,9 @@ GIOChannel *child_process_get_to_child_channel (ChildProcess *process);
 
 GIOChannel *child_process_get_from_child_channel (ChildProcess *process);
 
-void child_process_stop_all (void);
+void child_process_stop (ChildProcess *process);
+
+//void child_process_stop_all (void);
 
 G_END_DECLS
 
index 94b5ed2f53c8ac8e2aa2c64cbc20184f64eb48c0..e6515a4612d9b1628ecb900836024487dfa141bf 100644 (file)
@@ -31,7 +31,9 @@
 #include "theme.h"
 
 enum {
+    STARTED,
     DISPLAY_ADDED,
+    STOPPED,
     LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -52,6 +54,9 @@ struct DisplayManagerPrivate
 
     /* XDMCP server */
     XDMCPServer *xdmcp_server;
+
+    /* TRUE if stopping the display manager (waiting for displays to stop) */
+    gboolean stopping;
 };
 
 G_DEFINE_TYPE (DisplayManager, display_manager, G_TYPE_OBJECT);
@@ -196,6 +201,26 @@ end_session_cb (Display *display, Session *session, DisplayManager *manager)
     }
 }
 
+static gboolean
+check_stopped (DisplayManager *manager)
+{
+    if (g_list_length (manager->priv->displays) == 0)
+    {
+        g_debug ("Display manager stopped");
+        g_signal_emit (manager, signals[STOPPED], 0);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static void
+stopped_cb (Display *display, DisplayManager *manager)
+{
+    manager->priv->displays = g_list_remove (manager->priv->displays, display);
+    if (manager->priv->stopping)
+        check_stopped (manager);
+}
+
 static guchar
 atox (char c)
 {
@@ -399,6 +424,7 @@ add_display (DisplayManager *manager)
     g_signal_connect (display, "start-greeter", G_CALLBACK (start_greeter_cb), manager);
     g_signal_connect (display, "start-session", G_CALLBACK (start_session_cb), manager);
     g_signal_connect (display, "end-session", G_CALLBACK (end_session_cb), manager);
+    g_signal_connect (display, "stopped", G_CALLBACK (stopped_cb), manager);
 
     value = config_get_string (config_get_instance (), "LightDM", "session-wrapper");
     if (value)
@@ -747,8 +773,29 @@ display_manager_start (DisplayManager *manager)
         g_debug ("Starting XDMCP server on UDP/IP port %d", xdmcp_server_get_port (manager->priv->xdmcp_server));
         xdmcp_server_start (manager->priv->xdmcp_server); 
     }
+
+    g_signal_emit (manager, signals[STARTED], 0);
 }
 
+void
+display_manager_stop (DisplayManager *manager)
+{
+    GList *link;
+
+    g_debug ("Stopping display manager");
+
+    manager->priv->stopping = TRUE;
+  
+    if (check_stopped (manager))
+        return;
+
+    for (link = manager->priv->displays; link; link = link->next)
+    {
+        Display *display = link->data;
+        display_stop (display);
+    }
+}
+  
 static void
 display_manager_init (DisplayManager *manager)
 {
@@ -781,6 +828,14 @@ display_manager_class_init (DisplayManagerClass *klass)
 
     g_type_class_add_private (klass, sizeof (DisplayManagerPrivate));
 
+    signals[STARTED] =
+        g_signal_new ("started",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (DisplayManagerClass, started),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
     signals[DISPLAY_ADDED] =
         g_signal_new ("display-added",
                       G_TYPE_FROM_CLASS (klass),
@@ -789,4 +844,12 @@ display_manager_class_init (DisplayManagerClass *klass)
                       NULL, NULL,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1, DISPLAY_TYPE);
+    signals[STOPPED] =
+        g_signal_new ("stopped",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (DisplayManagerClass, stopped),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
 }
index 56cea304f88b9bb309f0505a068e10d54632f11f..3f2e541fcc73b3fb9f8a293afe15afe30dbb104a 100644 (file)
@@ -32,7 +32,9 @@ typedef struct
 {
     GObjectClass parent_class;
 
+    void (*started)(DisplayManager *manager);
     void (*display_added)(DisplayManager *manager, Display *display);
+    void (*stopped)(DisplayManager *manager);
 } DisplayManagerClass;
 
 GType display_manager_get_type (void);
@@ -47,6 +49,8 @@ void display_manager_switch_to_guest (DisplayManager *manager);
 
 void display_manager_start (DisplayManager *manager);
 
+void display_manager_stop (DisplayManager *manager);
+
 G_END_DECLS
 
 #endif /* _DISPLAY_MANAGER_H_ */
index b8343ae2aaff07ec538e3c54c41ac75f85f9120f..9bbd7c6ad2c6db2656f1177706de27f51e2a7add 100644 (file)
 #define USER_SESSION_TIMEOUT 5000
 
 enum {
+    STARTED,
     START_GREETER,
     END_GREETER,
     START_SESSION,
     END_SESSION,
-    EXITED,
+    STOPPED,
     LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -93,6 +94,9 @@ struct DisplayPrivate
 
     /* Default session */
     gchar *default_session;
+
+    /* TRUE if stopping the display (waiting for xserver, greeter and session to stop) */
+    gboolean stopping;    
 };
 
 G_DEFINE_TYPE (Display, display, G_TYPE_OBJECT);
@@ -416,9 +420,6 @@ end_user_session (Display *display, gboolean clean_exit)
         display->priv->user_session_timer = 0;
     }
 
-    g_object_unref (display->priv->user_session);
-    display->priv->user_session = NULL;
-
     pam_session_end (display->priv->user_pam_session);
     g_object_unref (display->priv->user_pam_session);
     display->priv->user_pam_session = NULL;
@@ -437,13 +438,38 @@ end_user_session (Display *display, gboolean clean_exit)
 static void
 user_session_exited_cb (Session *session, gint status, Display *display)
 {
-    end_user_session (display, status == 0);
+    if (!display->priv->stopping)
+        end_user_session (display, status == 0);
 }
 
 static void
 user_session_terminated_cb (Session *session, gint signum, Display *display)
 {
-    end_user_session (display, FALSE);
+    if (!display->priv->stopping)
+        end_user_session (display, FALSE);
+}
+
+static void
+check_stopped (Display *display)
+{
+    if (display->priv->stopping &&
+        display->priv->xserver == NULL &&        
+        display->priv->greeter_session == NULL &&
+        display->priv->user_session == NULL)
+    {
+        g_debug ("Display stopped");
+        g_signal_emit (display, signals[STOPPED], 0);
+    }
+}
+
+static void
+user_session_stopped_cb (Session *session, Display *display)
+{
+    g_signal_handlers_disconnect_matched (display->priv->user_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+
+    g_object_unref (display->priv->user_session);
+    display->priv->user_session = NULL;
+    check_stopped (display);
 }
 
 static void
@@ -575,6 +601,7 @@ start_user_session (Display *display, const gchar *session, const gchar *languag
     display->priv->user_session = session_new ();
     g_signal_connect (G_OBJECT (display->priv->user_session), "exited", G_CALLBACK (user_session_exited_cb), display);
     g_signal_connect (G_OBJECT (display->priv->user_session), "terminated", G_CALLBACK (user_session_terminated_cb), display);
+    g_signal_connect (G_OBJECT (display->priv->user_session), "stopped", G_CALLBACK (user_session_stopped_cb), display);
 
     session_set_user (display->priv->user_session, user);
     session_set_command (display->priv->user_session, session_command);
@@ -689,7 +716,7 @@ greeter_start_session_cb (Greeter *greeter, const gchar *session, const gchar *l
 }
 
 static void
-greeter_quit_cb (Greeter *greeter, Display *display)
+greeter_stopped_cb (Greeter *greeter, Display *display)
 {
     g_debug ("Greeter quit");
 
@@ -699,16 +726,24 @@ greeter_quit_cb (Greeter *greeter, Display *display)
     g_object_unref (display->priv->greeter_pam_session);
     display->priv->greeter_pam_session = NULL;
 
-    g_object_unref (display->priv->greeter_session);
-    display->priv->greeter_session = NULL;
-
     end_ck_session (display->priv->greeter_ck_cookie);
     g_free (display->priv->greeter_ck_cookie);
     display->priv->greeter_ck_cookie = NULL;   
 
-    /* Start session if waiting for greeter to quit */
-    if (display->priv->user_session && child_process_get_pid (CHILD_PROCESS (display->priv->user_session)) == 0)
-        really_start_user_session (display);
+    g_signal_handlers_disconnect_matched (display->priv->greeter_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+    g_object_unref (display->priv->greeter_session);
+    display->priv->greeter_session = NULL;
+  
+    if (display->priv->stopping)
+    {
+        check_stopped (display);
+    }
+    else
+    {
+        /* Start session if waiting for greeter to quit */
+        if (display->priv->user_session && child_process_get_pid (CHILD_PROCESS (display->priv->user_session)) == 0)
+            really_start_user_session (display);
+    }
 }
 
 static gboolean
@@ -751,7 +786,7 @@ start_greeter (Display *display)
     greeter_set_default_user (display->priv->greeter_session, display->priv->default_user, display->priv->timeout);
     greeter_set_default_session (display->priv->greeter_session, display->priv->default_session);
     g_signal_connect (G_OBJECT (display->priv->greeter_session), "start-session", G_CALLBACK (greeter_start_session_cb), display);
-    g_signal_connect (G_OBJECT (display->priv->greeter_session), "quit", G_CALLBACK (greeter_quit_cb), display);
+    g_signal_connect (G_OBJECT (display->priv->greeter_session), "stopped", G_CALLBACK (greeter_stopped_cb), display);
     session_set_user (SESSION (display->priv->greeter_session), user);
     command = theme_get_command (theme);
     session_set_command (SESSION (display->priv->greeter_session), command);
@@ -771,27 +806,29 @@ start_greeter (Display *display)
     return result;
 }
 
-static void
-end_display (Display *display)
-{
-    g_object_unref (display->priv->xserver);
-    display->priv->xserver = NULL;
-    g_signal_emit (display, signals[EXITED], 0);
-}
-
 static void
 xserver_exit_cb (XServer *server, int status, Display *display)
 {
     if (status != 0)
         g_debug ("X server exited with value %d", status);
-    end_display (display);
 }
 
 static void
 xserver_terminate_cb (XServer *server, int signum, Display *display)
 {
     g_debug ("X server terminated with signal %d", signum);
-    end_display (display);
+}
+
+static void
+xserver_stopped_cb (XServer *server, Display *display)
+{
+    g_signal_handlers_disconnect_matched (display->priv->xserver, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
+    g_object_unref (display->priv->xserver);
+    display->priv->xserver = NULL;
+
+    // FIXME: Should restart xserver
+
+    check_stopped (display);
 }
 
 static void
@@ -824,13 +861,35 @@ xserver_ready_cb (XServer *xserver, Display *display)
 gboolean
 display_start (Display *display)
 {
+    gboolean result;
+
     g_return_val_if_fail (display != NULL, FALSE);
     g_return_val_if_fail (display->priv->xserver != NULL, FALSE);
 
     g_signal_connect (G_OBJECT (display->priv->xserver), "ready", G_CALLBACK (xserver_ready_cb), display);
     g_signal_connect (G_OBJECT (display->priv->xserver), "exited", G_CALLBACK (xserver_exit_cb), display);
+    g_signal_connect (G_OBJECT (display->priv->xserver), "stopped", G_CALLBACK (xserver_stopped_cb), display);
     g_signal_connect (G_OBJECT (display->priv->xserver), "terminated", G_CALLBACK (xserver_terminate_cb), display);
-    return xserver_start (display->priv->xserver);
+    result = xserver_start (display->priv->xserver);
+  
+    g_signal_emit (display, signals[STARTED], 0);
+
+    return result;
+}
+
+void
+display_stop (Display *display)
+{
+    g_debug ("Stopping display");
+
+    display->priv->stopping = TRUE;
+
+    if (display->priv->xserver)
+        child_process_stop (CHILD_PROCESS (display->priv->xserver));
+    if (display->priv->greeter_session)
+        child_process_stop (CHILD_PROCESS (display->priv->greeter_session));
+    if (display->priv->user_session)
+        child_process_stop (CHILD_PROCESS (display->priv->user_session));
 }
 
 static void
@@ -886,6 +945,14 @@ display_class_init (DisplayClass *klass)
 
     g_type_class_add_private (klass, sizeof (DisplayPrivate));
 
+    signals[STARTED] =
+        g_signal_new ("started",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (DisplayClass, started),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
     signals[START_GREETER] =
         g_signal_new ("start-greeter",
                       G_TYPE_FROM_CLASS (klass),
@@ -894,7 +961,6 @@ display_class_init (DisplayClass *klass)
                       NULL, NULL,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1, SESSION_TYPE);
-
     signals[END_GREETER] =
         g_signal_new ("end-greeter",
                       G_TYPE_FROM_CLASS (klass),
@@ -903,7 +969,6 @@ display_class_init (DisplayClass *klass)
                       NULL, NULL,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1, SESSION_TYPE);
-  
     signals[START_SESSION] =
         g_signal_new ("start-session",
                       G_TYPE_FROM_CLASS (klass),
@@ -912,7 +977,6 @@ display_class_init (DisplayClass *klass)
                       NULL, NULL,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1, SESSION_TYPE);
-
     signals[END_SESSION] =
         g_signal_new ("end-session",
                       G_TYPE_FROM_CLASS (klass),
@@ -921,12 +985,11 @@ display_class_init (DisplayClass *klass)
                       NULL, NULL,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1, SESSION_TYPE);
-
-    signals[EXITED] =
-        g_signal_new ("exited",
+    signals[STOPPED] =
+        g_signal_new ("stopped",
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (DisplayClass, exited),
+                      G_STRUCT_OFFSET (DisplayClass, stopped),
                       NULL, NULL,
                       g_cclosure_marshal_VOID__VOID,
                       G_TYPE_NONE, 0);
index 5c781479436937191823322e8c1b8cb1eaf9f9db..5483bc8a47bc6559f08f8128e0048332bb05aa68 100644 (file)
@@ -34,11 +34,12 @@ typedef struct
 {
     GObjectClass parent_class;
   
+    void (*started)(Display *display);
     void (*start_greeter)(Display *display, Session *session);
     void (*end_greeter)(Display *display, Session *session);  
     void (*start_session)(Display *display, Session *session);
     void (*end_session)(Display *display, Session *session);
-    void (*exited)(Display *display);
+    void (*stopped)(Display *display);
 } DisplayClass;
 
 GType display_get_type (void);
@@ -91,6 +92,8 @@ gint display_get_vt (Display *display);
 
 gboolean display_start (Display *display);
 
+void display_stop (Display *display);
+
 G_END_DECLS
 
 #endif /* _DISPLAY_H_ */
index 46dd4420ec9b72e31cbce6ca5e40e18e5aa70536..1981ef9f0621fd590aa595cd2f71d0a0e7f12fc6 100644 (file)
@@ -24,7 +24,6 @@
 
 enum {
     START_SESSION,
-    QUIT,
     LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -598,8 +597,6 @@ end_session (Greeter *greeter, gboolean clean_exit)
         return;*/
 
     // FIXME: Issue with greeter, don't want to start a new one, report error to user
-
-    g_signal_emit (greeter, signals[QUIT], 0);
 }
 
 static void
@@ -654,14 +651,6 @@ greeter_class_init (GreeterClass *klass)
                       NULL, NULL,
                       ldm_marshal_VOID__STRING_STRING,
                       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
-    signals[QUIT] =
-        g_signal_new ("quit",
-                      G_TYPE_FROM_CLASS (klass),
-                      G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (GreeterClass, quit),
-                      NULL, NULL,
-                      g_cclosure_marshal_VOID__VOID,
-                      G_TYPE_NONE, 0);
 
     g_type_class_add_private (klass, sizeof (GreeterPrivate));
 }
index 4b1e5b487cb0ecfb7c6d4a85f37b2b02aaa9ae39..51d11e407e38cfe6858b9a6837cfb049729c8c82 100644 (file)
@@ -32,7 +32,6 @@ typedef struct
 {
     SessionClass parent_class;
     void (*start_session)(Greeter *greeter, const gchar *session, const gchar *language);
-    void (*quit)(Greeter *greeter);
 } GreeterClass;
 
 GType greeter_get_type (void);
index 96e4741e4de46d2f781891f7155253491eb3c64f..757b28df0263189028840dbcebeeca9f4e3a35fd 100644 (file)
@@ -105,8 +105,15 @@ static void
 signal_cb (ChildProcess *process, int signum)
 {
     /* Quit when all child processes have ended */
-    g_debug ("Caught %s signal, exiting", g_strsignal (signum));
-    child_process_stop_all ();
+    g_debug ("Caught %s signal, shutting down", g_strsignal (signum));
+    display_manager_stop (display_manager);
+}
+
+static void
+display_manager_stopped_cb (DisplayManager *display_manager)
+{
+    g_debug ("All processes complete, exiting");
+    exit (EXIT_SUCCESS);
 }
 
 static void
@@ -431,6 +438,7 @@ main(int argc, char **argv)
     g_debug ("Loaded configuration from %s", config_path);
 
     display_manager = display_manager_new ();
+    g_signal_connect (display_manager, "stopped", G_CALLBACK (display_manager_stopped_cb), NULL);
 
     display_manager_start (display_manager);