Merge lp:~victored/cerbere/session-manager-client into lp:~elementary-pantheon/cerbere/cerbere
- session-manager-client
- Merge into cerbere
Proposed by
Victor Martinez
Status: | Merged |
---|---|
Merged at revision: | 30 |
Proposed branch: | lp:~victored/cerbere/session-manager-client |
Merge into: | lp:~elementary-pantheon/cerbere/cerbere |
Diff against target: |
455 lines (+276/-32) 6 files modified
src/CMakeLists.txt (+5/-3) src/Cerbere.vala (+35/-3) src/ProcessInfo.vala (+6/-7) src/SessionManager.vala (+205/-0) src/SettingsManager.vala (+2/-2) src/Watchdog.vala (+23/-17) |
To merge this branch: | bzr merge lp:~victored/cerbere/session-manager-client |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cody Garver (community) | Approve | ||
Review via email: mp+113806@code.launchpad.net |
Commit message
Description of the change
Say goodbye to the long desktop startup times ...
To post a comment you must log in.
Revision history for this message
Cody Garver (codygarver) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/CMakeLists.txt' |
2 | --- src/CMakeLists.txt 2012-06-14 22:29:47 +0000 |
3 | +++ src/CMakeLists.txt 2012-07-06 21:51:19 +0000 |
4 | @@ -16,6 +16,7 @@ |
5 | VALA_C |
6 | Cerbere.vala |
7 | SettingsManager.vala |
8 | + SessionManager.vala |
9 | Watchdog.vala |
10 | ProcessInfo.vala |
11 | PACKAGES |
12 | @@ -26,6 +27,7 @@ |
13 | --target-glib=2.32 # Remember to keep this updated. |
14 | ) |
15 | |
16 | -add_executable(cerbere ${VALA_C}) |
17 | -target_link_libraries(cerbere ${DEPS_LIBRARIES}) |
18 | -install(TARGETS cerbere RUNTIME DESTINATION bin) |
19 | +set(EXEC_NAME ${CMAKE_PROJECT_NAME}) |
20 | +add_executable(${EXEC_NAME} ${VALA_C}) |
21 | +target_link_libraries(${EXEC_NAME} ${DEPS_LIBRARIES}) |
22 | +install(TARGETS ${EXEC_NAME} RUNTIME DESTINATION bin) |
23 | |
24 | === modified file 'src/Cerbere.vala' |
25 | --- src/Cerbere.vala 2012-06-14 22:28:44 +0000 |
26 | +++ src/Cerbere.vala 2012-07-06 21:51:19 +0000 |
27 | @@ -28,17 +28,28 @@ |
28 | public class Cerbere : Application { |
29 | |
30 | public static SettingsManager settings { get; private set; default = null; } |
31 | - private Watchdog watchdog; |
32 | + private Watchdog? watchdog = null; |
33 | + private SessionManager.ClientService? sm_client = null; |
34 | |
35 | construct { |
36 | application_id = "org.pantheon.cerbere"; |
37 | flags = ApplicationFlags.IS_SERVICE; |
38 | + Log.set_handler (null, LogLevelFlags.LEVEL_MASK, log_handler); |
39 | + } |
40 | + |
41 | + private static void log_handler (string? domain, LogLevelFlags level, string message) { |
42 | + if (level >= LogLevelFlags.LEVEL_INFO) |
43 | + level = LogLevelFlags.LEVEL_MESSAGE; |
44 | + Log.default_handler (domain, level, message); |
45 | } |
46 | |
47 | protected override void startup () { |
48 | + // Try to register Cerbere with the session manager. |
49 | + // This is a non-blocking action. |
50 | + register_session_client_async (); |
51 | + |
52 | this.settings = new SettingsManager (); |
53 | - |
54 | - this.start_processes (this.settings.process_list); |
55 | + start_processes (this.settings.process_list); |
56 | |
57 | // Monitor changes to the process list |
58 | this.settings.process_list_changed.connect (this.start_processes); |
59 | @@ -47,6 +58,27 @@ |
60 | main_loop.run (); |
61 | } |
62 | |
63 | + private async void register_session_client_async () { |
64 | + if (this.sm_client != null) |
65 | + return; |
66 | + |
67 | + this.sm_client = new SessionManager.ClientService (this.application_id); |
68 | + |
69 | + try { |
70 | + this.sm_client.register (); |
71 | + } catch (SessionManager.ConnectionError e) { |
72 | + critical (e.message); |
73 | + } |
74 | + |
75 | + if (this.sm_client != null) { |
76 | + // The session manager may ask us to quit the service, and so we do. |
77 | + this.sm_client.stop_service.connect ( () => { |
78 | + message ("Exiting..."); |
79 | + this.quit_mainloop (); |
80 | + }); |
81 | + } |
82 | + } |
83 | + |
84 | private void start_processes (string[] process_list) { |
85 | if (this.watchdog == null) { |
86 | this.watchdog = new Watchdog (); |
87 | |
88 | === modified file 'src/ProcessInfo.vala' |
89 | --- src/ProcessInfo.vala 2012-06-17 16:22:40 +0000 |
90 | +++ src/ProcessInfo.vala 2012-07-06 21:51:19 +0000 |
91 | @@ -43,7 +43,7 @@ |
92 | |
93 | public void reset_crash_count () { |
94 | this.crash_count = 0; |
95 | - debug ("Crash count of '%s' has been reset", this.command); |
96 | + message ("Crash count of '%s' has been reset", this.command); |
97 | } |
98 | |
99 | public async void run_async () { |
100 | @@ -51,10 +51,10 @@ |
101 | } |
102 | |
103 | private void run () { |
104 | - message ("STARTING process: %s", command); |
105 | + debug ("STARTING process: %s", command); |
106 | |
107 | if (this.status == Status.RUNNING) { |
108 | - message ("Process %s is already running", command); |
109 | + warning ("Process %s is already running. Not starting it again...", command); |
110 | return; |
111 | } |
112 | |
113 | @@ -102,7 +102,7 @@ |
114 | if (pid != this.pid) |
115 | return; |
116 | |
117 | - message ("Process '%s' exited", command); |
118 | + message ("Process '%s' watch exit", command); |
119 | |
120 | // Check exit status |
121 | if (Process.if_exited (status) || Process.if_signaled (status) || Process.core_dump (status)) { |
122 | @@ -126,13 +126,12 @@ |
123 | double elapsed_secs = this.timer.elapsed (); |
124 | double crash_time_interval_secs = (double)Cerbere.settings.crash_time_interval / 1000.0; |
125 | |
126 | - debug ("Elapsed time = %f secs", elapsed_secs); |
127 | - debug ("Min allowed time = %f secs", crash_time_interval_secs); |
128 | + message ("ET = %f secs\tMin allowed time = %f", elapsed_secs, crash_time_interval_secs); |
129 | |
130 | if (elapsed_secs <= crash_time_interval_secs) { // process crashed |
131 | this.crash_count ++; |
132 | normal_exit = false; |
133 | - message ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count); |
134 | + warning ("PROCESS '%s' CRASHED (#%u)", this.command, this.crash_count); |
135 | } |
136 | |
137 | // Remove the current timer |
138 | |
139 | === added file 'src/SessionManager.vala' |
140 | --- src/SessionManager.vala 1970-01-01 00:00:00 +0000 |
141 | +++ src/SessionManager.vala 2012-07-06 21:51:19 +0000 |
142 | @@ -0,0 +1,205 @@ |
143 | +/* -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- */ |
144 | +/* |
145 | + * Copyright (C) 2012 Victor Eduardo <victoreduardm@gmail.com> |
146 | + * |
147 | + * Cerbere is free software; you can redistribute it and/or modify |
148 | + * it under the terms of the GNU General Public License as published by |
149 | + * the Free Software Foundation; either version 2 of the License, or |
150 | + * (at your option) any later version. |
151 | + * |
152 | + * Cerbere is distributed in the hope that it will be useful, |
153 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
154 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
155 | + * GNU General Public License for more details. |
156 | + * |
157 | + * You should have received a copy of the GNU General Public License |
158 | + * along with Cerbere; if not, write to the Free Software |
159 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, |
160 | + * Boston, MA 02110-1301 USA |
161 | + * |
162 | + * Authors: Victor Eduardo <victoreduardm@gmail.com> |
163 | + */ |
164 | + |
165 | +namespace SessionManager { |
166 | + |
167 | + public errordomain ConnectionError { |
168 | + CONNECTION_FAILED, |
169 | + CLIENT_REGISTRATION_FAILED |
170 | + } |
171 | + |
172 | + |
173 | + /** |
174 | + * GNOME Session Manager DBus API |
175 | + * |
176 | + * API Reference: [[http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html]] |
177 | + * (Consulted on July 4, 2012.) |
178 | + */ |
179 | + |
180 | + private const string DBUS_NAME = "org.gnome.SessionManager"; |
181 | + private const string DBUS_PATH = "/org/gnome/SessionManager"; |
182 | + |
183 | + [DBus (name = "org.gnome.SessionManager")] |
184 | + private interface SessionManager : Object { |
185 | + // Many API methods have been left out. Feel free to add them when required. |
186 | + public abstract void RegisterClient (string app_id, string client_startup_id, |
187 | + out string client_id) throws IOError; |
188 | + public abstract void UnregisterClient (ObjectPath client_id) throws IOError; |
189 | + } |
190 | + |
191 | + [DBus (name = "org.gnome.SessionManager.ClientPrivate")] |
192 | + private interface ClientPrivate : Object { |
193 | + public abstract void EndSessionResponse (bool is_ok, string reason) throws IOError; |
194 | + public signal void QueryEndSession (uint flags); |
195 | + public signal void EndSession (uint flags); |
196 | + public signal void CancelEndSession (); |
197 | + public signal void Stop (); |
198 | + } |
199 | + |
200 | + |
201 | + /** |
202 | + * CLIENT |
203 | + * |
204 | + * This class handles both the registration of the service, |
205 | + * and action requests coming from the session-manager side. |
206 | + */ |
207 | + |
208 | + public class ClientService : Object { |
209 | + public signal void stop_service (); |
210 | + |
211 | + private SessionManager? session_manager = null; |
212 | + private ClientPrivate? client = null; |
213 | + private string? client_id = null; |
214 | + private string? app_id = null; |
215 | + |
216 | + public ClientService (string app_id) { |
217 | + this.app_id = app_id; |
218 | + } |
219 | + |
220 | + public void register () throws ConnectionError { |
221 | + bool connected = true; |
222 | + |
223 | + if (session_manager == null) { |
224 | + connected = connect_session (); |
225 | + |
226 | + if (!connected) { |
227 | + throw new ConnectionError.CONNECTION_FAILED ("Could not connect to session manager"); |
228 | + } |
229 | + } |
230 | + |
231 | + connected = register_client (); |
232 | + |
233 | + if (!connected) { |
234 | + // Disconnect from SM |
235 | + session_manager = null; |
236 | + throw new ConnectionError.CLIENT_REGISTRATION_FAILED ("Unable to register client with session manager"); |
237 | + } |
238 | + |
239 | + } |
240 | + |
241 | + public void unregister () { |
242 | + return_if_fail (session_manager != null && client_id != null); |
243 | + |
244 | + debug ("Unregistering client: %s", client_id); |
245 | + |
246 | + try { |
247 | + session_manager.UnregisterClient (new ObjectPath (client_id)); |
248 | + } catch (IOError e) { |
249 | + critical (e.message); |
250 | + } |
251 | + } |
252 | + |
253 | + private bool connect_session () { |
254 | + if (session_manager == null) { |
255 | + try { |
256 | + session_manager = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, DBUS_PATH); |
257 | + } catch (IOError e) { |
258 | + critical (e.message); |
259 | + } |
260 | + } |
261 | + |
262 | + return session_manager != null; |
263 | + } |
264 | + |
265 | + private bool register_client () { |
266 | + return_val_if_fail (session_manager != null && app_id != null, false); |
267 | + |
268 | + string? startup_id = Environment.get_variable ("DESKTOP_AUTOSTART_ID"); |
269 | + |
270 | + if (startup_id == null) { |
271 | + critical ("Could not get value of environment variable DESKTOP_AUTOSTART_ID"); |
272 | + return false; |
273 | + } |
274 | + |
275 | + // Register client |
276 | + if (client == null) { |
277 | + try { |
278 | + session_manager.RegisterClient (app_id, startup_id, out client_id); |
279 | + } catch (IOError e) { |
280 | + critical ("Could not register client: %s", e.message); |
281 | + } |
282 | + |
283 | + return_val_if_fail (client_id != null, false); |
284 | + |
285 | + debug ("Registered session manager client: %s", client_id); |
286 | + |
287 | + // Get client |
288 | + try { |
289 | + client = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, client_id); |
290 | + } catch (IOError e) { |
291 | + critical ("Could not get client: %s", e.message); |
292 | + return_val_if_reached (false); |
293 | + } |
294 | + |
295 | + debug ("Obtained gnome-session client proxy"); |
296 | + |
297 | + // Connect signals |
298 | + client.QueryEndSession.connect (on_client_query_end_session); |
299 | + client.EndSession.connect (on_client_end_session); |
300 | + client.CancelEndSession.connect (on_client_cancel_end_session); |
301 | + client.Stop.connect (on_client_stop); |
302 | + } |
303 | + |
304 | + return client != null; |
305 | + } |
306 | + |
307 | + |
308 | + /** ClientPrivate Signal handlers **/ |
309 | + |
310 | + private void on_client_query_end_session (uint flags) { |
311 | + debug ("Client query end session"); |
312 | + return_if_fail (client != null); |
313 | + |
314 | + send_end_session_response (true); |
315 | + } |
316 | + |
317 | + private void on_client_end_session (uint flags) { |
318 | + debug ("Client end session"); |
319 | + return_if_fail (client != null); |
320 | + |
321 | + send_end_session_response (true); |
322 | + on_client_stop (); |
323 | + } |
324 | + |
325 | + private void on_client_cancel_end_session () { |
326 | + debug ("Client: Received EndSessionCanceled signal"); |
327 | + } |
328 | + |
329 | + private void on_client_stop () { |
330 | + debug ("Client: Received Stop signal"); |
331 | + this.unregister (); |
332 | + this.stop_service (); |
333 | + } |
334 | + |
335 | + private inline void send_end_session_response (bool is_okay, string reason = "") { |
336 | + return_if_fail (client != null); |
337 | + |
338 | + // Tell the session manager whether it's okay to logout, shut down, etc. |
339 | + try { |
340 | + debug ("Sending is_okay = %s to session manager", is_okay.to_string ()); |
341 | + client.EndSessionResponse (true, ""); |
342 | + } catch (IOError e) { |
343 | + warning ("Couldn't reply to session manager: %s", e.message); |
344 | + } |
345 | + } |
346 | + } |
347 | +} |
348 | |
349 | === modified file 'src/SettingsManager.vala' |
350 | --- src/SettingsManager.vala 2012-06-14 22:28:44 +0000 |
351 | +++ src/SettingsManager.vala 2012-07-06 21:51:19 +0000 |
352 | @@ -30,7 +30,7 @@ |
353 | static const string CRASH_TIME_INTERVAL_KEY = "crash-time-interval"; |
354 | static const string MONITORED_PROCESSES_KEY = "monitored-processes"; |
355 | |
356 | - public string[] process_list { get; set; } |
357 | + public string[] process_list { get; set; } |
358 | public uint max_crashes { get; set; default = 0; } |
359 | public uint crash_time_interval { get; set; default = 0; } |
360 | |
361 | @@ -47,6 +47,6 @@ |
362 | } |
363 | |
364 | private void on_process_list_changed () { |
365 | - this.process_list_changed (this.settings.get_strv (MONITORED_PROCESSES_KEY)); |
366 | + this.process_list_changed (this.process_list); |
367 | } |
368 | } |
369 | |
370 | === modified file 'src/Watchdog.vala' |
371 | --- src/Watchdog.vala 2012-06-17 04:31:37 +0000 |
372 | +++ src/Watchdog.vala 2012-07-06 21:51:19 +0000 |
373 | @@ -25,10 +25,10 @@ |
374 | * Victor Eduardo <victoreduardm@gmail.com> |
375 | */ |
376 | |
377 | -public class Watchdog { |
378 | +public class Watchdog : Object { |
379 | + |
380 | // Contains ALL the processes that are being monitored |
381 | private Gee.HashMap<string, ProcessInfo> processes; |
382 | - private Mutex data_lock; |
383 | |
384 | public Watchdog () { |
385 | this.processes = new Gee.HashMap<string, ProcessInfo> (); |
386 | @@ -43,16 +43,17 @@ |
387 | return; |
388 | |
389 | // Check if a process for this command has already been created |
390 | - if (this.processes.has_key (command)) |
391 | + if (this.processes.has_key (command)) { |
392 | return; |
393 | + } |
394 | |
395 | // Create new process |
396 | var process = new ProcessInfo (command); |
397 | |
398 | // Add it to the table |
399 | - this.data_lock.lock (); |
400 | - this.processes[command] = process; |
401 | - this.data_lock.unlock (); |
402 | + lock (this.processes) { |
403 | + this.processes[command] = process; |
404 | + } |
405 | |
406 | // Exit handler. Respawning occurs here |
407 | process.exited.connect ( (normal_exit) => { |
408 | @@ -62,6 +63,8 @@ |
409 | process.reset_crash_count (); |
410 | } |
411 | |
412 | + bool remove_process = false; |
413 | + |
414 | // if still in the list, relaunch if possible |
415 | if (command in Cerbere.settings.process_list) { |
416 | // Check if the process is still present in the table since it could have been removed. |
417 | @@ -73,25 +76,28 @@ |
418 | process.run_async (); // Reload right away |
419 | } |
420 | else { |
421 | - message ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again", |
422 | - command, max_crashes.to_string ()); |
423 | + warning ("'%s' exceeded the maximum number of crashes allowed (%s). It won't be launched again", command, max_crashes.to_string ()); |
424 | + remove_process = true; |
425 | } |
426 | } |
427 | else { |
428 | - // If a process is not in the table, it means it wasn't re-launched after it exited, so |
429 | - // in theory this code is never reached. |
430 | - warning ("You should NEVER get this message. If you're getting it, file a bug!"); |
431 | + // If a process is not in the table, it means it wasn't re-launched |
432 | + // after it exited, so in theory this code is never reached. |
433 | + critical ("Please file a bug at http://launchpad.net/cerbere and attach your ~/.xsession-errors file."); |
434 | } |
435 | } |
436 | else { |
437 | - message ("'%s' is no longer on settings. It will not be monitored anymore", command); |
438 | - |
439 | + warning ("'%s' is no longer in settings (not monitored))", command); |
440 | process.reset_crash_count (); // reset |
441 | + remove_process = true; |
442 | + } |
443 | |
444 | - // Remove from the list. At this point the reference count should drop to 0 and free the process. |
445 | - this.data_lock.lock (); |
446 | - this.processes.unset (command); |
447 | - this.data_lock.unlock (); |
448 | + // Remove from the table. At this point the reference count should |
449 | + // drop to 0 and free the process info. |
450 | + if (remove_process) { |
451 | + lock (this.processes) { |
452 | + this.processes.unset (command); |
453 | + } |
454 | } |
455 | }); |
456 |