elementaryart team mailing list archive
-
elementaryart team
-
Mailing list archive
-
Message #02185
[Merge] lp:~victored/cerbere/session-manager-client into lp:cerbere
Victor Eduardo has proposed merging lp:~victored/cerbere/session-manager-client into lp:cerbere.
Requested reviews:
elementary Pantheon team (elementary-pantheon)
Related bugs:
Bug #1007977 in Cerbere: "Lots of stuff takes forever to start because Cerbere doesn't register in session manager on launch"
https://bugs.launchpad.net/cerbere/+bug/1007977
For more details, see:
https://code.launchpad.net/~victored/cerbere/session-manager-client/+merge/113806
Say goodbye to the long desktop startup times ...
--
https://code.launchpad.net/~victored/cerbere/session-manager-client/+merge/113806
Your team elementaryart (old) is subscribed to branch lp:cerbere.
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2012-06-14 22:29:47 +0000
+++ src/CMakeLists.txt 2012-07-06 21:51:19 +0000
@@ -16,6 +16,7 @@
VALA_C
Cerbere.vala
SettingsManager.vala
+ SessionManager.vala
Watchdog.vala
ProcessInfo.vala
PACKAGES
@@ -26,6 +27,7 @@
--target-glib=2.32 # Remember to keep this updated.
)
-add_executable(cerbere ${VALA_C})
-target_link_libraries(cerbere ${DEPS_LIBRARIES})
-install(TARGETS cerbere RUNTIME DESTINATION bin)
+set(EXEC_NAME ${CMAKE_PROJECT_NAME})
+add_executable(${EXEC_NAME} ${VALA_C})
+target_link_libraries(${EXEC_NAME} ${DEPS_LIBRARIES})
+install(TARGETS ${EXEC_NAME} RUNTIME DESTINATION bin)
=== modified file 'src/Cerbere.vala'
--- src/Cerbere.vala 2012-06-14 22:28:44 +0000
+++ src/Cerbere.vala 2012-07-06 21:51:19 +0000
@@ -28,17 +28,28 @@
public class Cerbere : Application {
public static SettingsManager settings { get; private set; default = null; }
- private Watchdog watchdog;
+ private Watchdog? watchdog = null;
+ private SessionManager.ClientService? sm_client = null;
construct {
application_id = "org.pantheon.cerbere";
flags = ApplicationFlags.IS_SERVICE;
+ Log.set_handler (null, LogLevelFlags.LEVEL_MASK, log_handler);
+ }
+
+ private static void log_handler (string? domain, LogLevelFlags level, string message) {
+ if (level >= LogLevelFlags.LEVEL_INFO)
+ level = LogLevelFlags.LEVEL_MESSAGE;
+ Log.default_handler (domain, level, message);
}
protected override void startup () {
+ // Try to register Cerbere with the session manager.
+ // This is a non-blocking action.
+ register_session_client_async ();
+
this.settings = new SettingsManager ();
-
- this.start_processes (this.settings.process_list);
+ start_processes (this.settings.process_list);
// Monitor changes to the process list
this.settings.process_list_changed.connect (this.start_processes);
@@ -47,6 +58,27 @@
main_loop.run ();
}
+ private async void register_session_client_async () {
+ if (this.sm_client != null)
+ return;
+
+ this.sm_client = new SessionManager.ClientService (this.application_id);
+
+ try {
+ this.sm_client.register ();
+ } catch (SessionManager.ConnectionError e) {
+ critical (e.message);
+ }
+
+ if (this.sm_client != null) {
+ // The session manager may ask us to quit the service, and so we do.
+ this.sm_client.stop_service.connect ( () => {
+ message ("Exiting...");
+ this.quit_mainloop ();
+ });
+ }
+ }
+
private void start_processes (string[] process_list) {
if (this.watchdog == null) {
this.watchdog = new Watchdog ();
=== modified file 'src/ProcessInfo.vala'
--- src/ProcessInfo.vala 2012-06-17 16:22:40 +0000
+++ src/ProcessInfo.vala 2012-07-06 21:51:19 +0000
@@ -43,7 +43,7 @@
public void reset_crash_count () {
this.crash_count = 0;
- debug ("Crash count of '%s' has been reset", this.command);
+ message ("Crash count of '%s' has been reset", this.command);
}
public async void run_async () {
@@ -51,10 +51,10 @@
}
private void run () {
- message ("STARTING process: %s", command);
+ debug ("STARTING process: %s", command);
if (this.status == Status.RUNNING) {
- message ("Process %s is already running", command);
+ warning ("Process %s is already running. Not starting it again...", command);
return;
}
@@ -102,7 +102,7 @@
if (pid != this.pid)
return;
- message ("Process '%s' exited", command);
+ message ("Process '%s' watch exit", command);
// Check exit status
if (Process.if_exited (status) || Process.if_signaled (status) || Process.core_dump (status)) {
@@ -126,13 +126,12 @@
double elapsed_secs = this.timer.elapsed ();
double crash_time_interval_secs = (double)Cerbere.settings.crash_time_interval / 1000.0;
- debug ("Elapsed time = %f secs", elapsed_secs);
- debug ("Min allowed time = %f secs", crash_time_interval_secs);
+ message ("ET = %f secs\tMin allowed time = %f", elapsed_secs, crash_time_interval_secs);
if (elapsed_secs <= crash_time_interval_secs) { // process crashed
this.crash_count ++;
normal_exit = false;
- message ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count);
+ warning ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count);
}
// Remove the current timer
=== added file 'src/SessionManager.vala'
--- src/SessionManager.vala 1970-01-01 00:00:00 +0000
+++ src/SessionManager.vala 2012-07-06 21:51:19 +0000
@@ -0,0 +1,205 @@
+/* -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2012 Victor Eduardo <victoreduardm@xxxxxxxxx>
+ *
+ * Cerbere is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Cerbere is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cerbere; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors: Victor Eduardo <victoreduardm@xxxxxxxxx>
+ */
+
+namespace SessionManager {
+
+ public errordomain ConnectionError {
+ CONNECTION_FAILED,
+ CLIENT_REGISTRATION_FAILED
+ }
+
+
+ /**
+ * GNOME Session Manager DBus API
+ *
+ * API Reference: [[http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html]]
+ * (Consulted on July 4, 2012.)
+ */
+
+ private const string DBUS_NAME = "org.gnome.SessionManager";
+ private const string DBUS_PATH = "/org/gnome/SessionManager";
+
+ [DBus (name = "org.gnome.SessionManager")]
+ private interface SessionManager : Object {
+ // Many API methods have been left out. Feel free to add them when required.
+ public abstract void RegisterClient (string app_id, string client_startup_id,
+ out string client_id) throws IOError;
+ public abstract void UnregisterClient (ObjectPath client_id) throws IOError;
+ }
+
+ [DBus (name = "org.gnome.SessionManager.ClientPrivate")]
+ private interface ClientPrivate : Object {
+ public abstract void EndSessionResponse (bool is_ok, string reason) throws IOError;
+ public signal void QueryEndSession (uint flags);
+ public signal void EndSession (uint flags);
+ public signal void CancelEndSession ();
+ public signal void Stop ();
+ }
+
+
+ /**
+ * CLIENT
+ *
+ * This class handles both the registration of the service,
+ * and action requests coming from the session-manager side.
+ */
+
+ public class ClientService : Object {
+ public signal void stop_service ();
+
+ private SessionManager? session_manager = null;
+ private ClientPrivate? client = null;
+ private string? client_id = null;
+ private string? app_id = null;
+
+ public ClientService (string app_id) {
+ this.app_id = app_id;
+ }
+
+ public void register () throws ConnectionError {
+ bool connected = true;
+
+ if (session_manager == null) {
+ connected = connect_session ();
+
+ if (!connected) {
+ throw new ConnectionError.CONNECTION_FAILED ("Could not connect to session manager");
+ }
+ }
+
+ connected = register_client ();
+
+ if (!connected) {
+ // Disconnect from SM
+ session_manager = null;
+ throw new ConnectionError.CLIENT_REGISTRATION_FAILED ("Unable to register client with session manager");
+ }
+
+ }
+
+ public void unregister () {
+ return_if_fail (session_manager != null && client_id != null);
+
+ debug ("Unregistering client: %s", client_id);
+
+ try {
+ session_manager.UnregisterClient (new ObjectPath (client_id));
+ } catch (IOError e) {
+ critical (e.message);
+ }
+ }
+
+ private bool connect_session () {
+ if (session_manager == null) {
+ try {
+ session_manager = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, DBUS_PATH);
+ } catch (IOError e) {
+ critical (e.message);
+ }
+ }
+
+ return session_manager != null;
+ }
+
+ private bool register_client () {
+ return_val_if_fail (session_manager != null && app_id != null, false);
+
+ string? startup_id = Environment.get_variable ("DESKTOP_AUTOSTART_ID");
+
+ if (startup_id == null) {
+ critical ("Could not get value of environment variable DESKTOP_AUTOSTART_ID");
+ return false;
+ }
+
+ // Register client
+ if (client == null) {
+ try {
+ session_manager.RegisterClient (app_id, startup_id, out client_id);
+ } catch (IOError e) {
+ critical ("Could not register client: %s", e.message);
+ }
+
+ return_val_if_fail (client_id != null, false);
+
+ debug ("Registered session manager client: %s", client_id);
+
+ // Get client
+ try {
+ client = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, client_id);
+ } catch (IOError e) {
+ critical ("Could not get client: %s", e.message);
+ return_val_if_reached (false);
+ }
+
+ debug ("Obtained gnome-session client proxy");
+
+ // Connect signals
+ client.QueryEndSession.connect (on_client_query_end_session);
+ client.EndSession.connect (on_client_end_session);
+ client.CancelEndSession.connect (on_client_cancel_end_session);
+ client.Stop.connect (on_client_stop);
+ }
+
+ return client != null;
+ }
+
+
+ /** ClientPrivate Signal handlers **/
+
+ private void on_client_query_end_session (uint flags) {
+ debug ("Client query end session");
+ return_if_fail (client != null);
+
+ send_end_session_response (true);
+ }
+
+ private void on_client_end_session (uint flags) {
+ debug ("Client end session");
+ return_if_fail (client != null);
+
+ send_end_session_response (true);
+ on_client_stop ();
+ }
+
+ private void on_client_cancel_end_session () {
+ debug ("Client: Received EndSessionCanceled signal");
+ }
+
+ private void on_client_stop () {
+ debug ("Client: Received Stop signal");
+ this.unregister ();
+ this.stop_service ();
+ }
+
+ private inline void send_end_session_response (bool is_okay, string reason = "") {
+ return_if_fail (client != null);
+
+ // Tell the session manager whether it's okay to logout, shut down, etc.
+ try {
+ debug ("Sending is_okay = %s to session manager", is_okay.to_string ());
+ client.EndSessionResponse (true, "");
+ } catch (IOError e) {
+ warning ("Couldn't reply to session manager: %s", e.message);
+ }
+ }
+ }
+}
=== modified file 'src/SettingsManager.vala'
--- src/SettingsManager.vala 2012-06-14 22:28:44 +0000
+++ src/SettingsManager.vala 2012-07-06 21:51:19 +0000
@@ -30,7 +30,7 @@
static const string CRASH_TIME_INTERVAL_KEY = "crash-time-interval";
static const string MONITORED_PROCESSES_KEY = "monitored-processes";
- public string[] process_list { get; set; }
+ public string[] process_list { get; set; }
public uint max_crashes { get; set; default = 0; }
public uint crash_time_interval { get; set; default = 0; }
@@ -47,6 +47,6 @@
}
private void on_process_list_changed () {
- this.process_list_changed (this.settings.get_strv (MONITORED_PROCESSES_KEY));
+ this.process_list_changed (this.process_list);
}
}
=== modified file 'src/Watchdog.vala'
--- src/Watchdog.vala 2012-06-17 04:31:37 +0000
+++ src/Watchdog.vala 2012-07-06 21:51:19 +0000
@@ -25,10 +25,10 @@
* Victor Eduardo <victoreduardm@xxxxxxxxx>
*/
-public class Watchdog {
+public class Watchdog : Object {
+
// Contains ALL the processes that are being monitored
private Gee.HashMap<string, ProcessInfo> processes;
- private Mutex data_lock;
public Watchdog () {
this.processes = new Gee.HashMap<string, ProcessInfo> ();
@@ -43,16 +43,17 @@
return;
// Check if a process for this command has already been created
- if (this.processes.has_key (command))
+ if (this.processes.has_key (command)) {
return;
+ }
// Create new process
var process = new ProcessInfo (command);
// Add it to the table
- this.data_lock.lock ();
- this.processes[command] = process;
- this.data_lock.unlock ();
+ lock (this.processes) {
+ this.processes[command] = process;
+ }
// Exit handler. Respawning occurs here
process.exited.connect ( (normal_exit) => {
@@ -62,6 +63,8 @@
process.reset_crash_count ();
}
+ bool remove_process = false;
+
// if still in the list, relaunch if possible
if (command in Cerbere.settings.process_list) {
// Check if the process is still present in the table since it could have been removed.
@@ -73,25 +76,28 @@
process.run_async (); // Reload right away
}
else {
- message ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again",
- command, max_crashes.to_string ());
+ warning ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again", command, max_crashes.to_string ());
+ remove_process = true;
}
}
else {
- // If a process is not in the table, it means it wasn't re-launched after it exited, so
- // in theory this code is never reached.
- warning ("You should NEVER get this message. If you're getting it, file a bug!");
+ // If a process is not in the table, it means it wasn't re-launched
+ // after it exited, so in theory this code is never reached.
+ critical ("Please file a bug at http://launchpad.net/cerbere and attach your ~/.xsession-errors file.");
}
}
else {
- message ("'%s' is no longer on settings. It will not be monitored anymore", command);
-
+ warning ("'%s' is no longer in settings (not monitored))", command);
process.reset_crash_count (); // reset
+ remove_process = true;
+ }
- // Remove from the list. At this point the reference count should drop to 0 and free the process.
- this.data_lock.lock ();
- this.processes.unset (command);
- this.data_lock.unlock ();
+ // Remove from the table. At this point the reference count should
+ // drop to 0 and free the process info.
+ if (remove_process) {
+ lock (this.processes) {
+ this.processes.unset (command);
+ }
}
});
Follow ups