]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/seat.c
move where we insert our own utility PATH
[sojka/lightdm.git] / src / seat.c
1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <string.h>
13
14 #include "seat.h"
15 #include "configuration.h"
16 #include "display.h"
17 #include "xserver.h"
18 #include "guest-account.h"
19
20 enum {
21     STARTED,
22     DISPLAY_ADDED,
23     DISPLAY_REMOVED,
24     STOPPED,
25     LAST_SIGNAL
26 };
27 static guint signals[LAST_SIGNAL] = { 0 };
28
29 struct SeatPrivate
30 {
31     /* Configuration for this seat */
32     gchar *config_section;
33
34     /* TRUE if able to switch users */
35     gboolean can_switch;
36
37     /* TRUE if allowed to log into guest account */
38     gboolean allow_guest;
39
40     /* Name of guest account */
41     gchar *guest_username;
42
43     /* User to automatically log in as */
44     gchar *autologin_username;
45     gboolean autologin_guest;
46     guint autologin_timeout;
47
48     /* The displays for this seat */
49     GList *displays;
50
51     /* The active display */
52     Display *active_display;
53
54     /* TRUE if stopping this seat (waiting for displays to stop) */
55     gboolean stopping;
56 };
57
58 G_DEFINE_TYPE (Seat, seat, G_TYPE_OBJECT);
59
60 typedef struct
61 {
62     const gchar *name;
63     GType type;
64 } SeatModule;
65 static GHashTable *seat_modules = NULL;
66
67 void
68 seat_register_module (const gchar *name, GType type)
69 {
70     SeatModule *module;
71
72     if (!seat_modules)
73         seat_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
74
75     g_debug ("Registered seat module %s", name);
76
77     module = g_malloc0 (sizeof (SeatModule));
78     module->name = g_strdup (name);
79     module->type = type;
80     g_hash_table_insert (seat_modules, g_strdup (name), module);
81 }
82
83 Seat *
84 seat_new (const gchar *module, const gchar *config_section)
85 {
86     Seat *seat;
87     SeatModule *m = NULL;
88
89     if (seat_modules)
90         m = g_hash_table_lookup (seat_modules, module);
91     if (!m)
92         return NULL;
93
94     seat = g_object_new (m->type, NULL);
95     seat->priv->config_section = g_strdup (config_section);
96
97     return seat;
98 }
99
100 const gchar *
101 seat_get_config_section (Seat *seat)
102 {
103     g_return_val_if_fail (seat != NULL, NULL);
104     return seat->priv->config_section;
105 }
106
107 void
108 seat_set_can_switch (Seat *seat, gboolean can_switch)
109 {
110     g_return_if_fail (seat != NULL);
111
112     seat->priv->can_switch = can_switch;
113 }
114
115 gboolean
116 seat_start (Seat *seat)
117 {
118     g_return_val_if_fail (seat != NULL, FALSE);
119   
120     SEAT_GET_CLASS (seat)->setup (seat);
121     return SEAT_GET_CLASS (seat)->start (seat);
122 }
123
124 GList *
125 seat_get_displays (Seat *seat)
126 {
127     g_return_val_if_fail (seat != NULL, NULL);
128     return seat->priv->displays;
129 }
130
131 void
132 seat_set_active_display (Seat *seat, Display *display)
133 {
134     g_return_if_fail (seat != NULL);
135     display_unlock (display);
136     SEAT_GET_CLASS (seat)->set_active_display (seat, display);
137 }
138
139 Display *
140 seat_get_active_display (Seat *seat)
141 {
142     g_return_val_if_fail (seat != NULL, NULL);
143     return seat->priv->active_display;
144 }
145
146 gboolean
147 seat_get_can_switch (Seat *seat)
148 {
149     g_return_val_if_fail (seat != NULL, FALSE);
150     return seat->priv->can_switch;
151 }
152
153 gboolean
154 seat_get_allow_guest (Seat *seat)
155 {
156     g_return_val_if_fail (seat != NULL, FALSE);  
157     return seat->priv->allow_guest && guest_account_is_installed ();
158 }
159
160 static gboolean
161 switch_to_user (Seat *seat, const gchar *username)
162 {
163     GList *link;
164
165     /* Switch to active display if it exists */
166     for (link = seat->priv->displays; link; link = link->next)
167     {
168         Display *display = link->data;
169
170         /* If already logged in, then switch to that display */
171         if (g_strcmp0 (display_get_username (display), username) == 0)
172         {
173             // FIXME: Use display_get_name
174             g_debug ("Switching to user %s session on display %s", username, xserver_get_address (XSERVER (display_get_display_server (display))));
175             seat_set_active_display (seat, display);
176             return TRUE;
177         }
178     }
179
180     return FALSE;
181 }
182
183 static gboolean
184 display_switch_to_user_cb (Display *display, User *user, Seat *seat)
185 {
186     return switch_to_user (seat, user_get_name (user));
187 }
188
189 static gboolean
190 display_switch_to_guest_cb (Display *display, Seat *seat)
191 {
192     /* No guest account */
193     if (!seat->priv->guest_username)
194         return FALSE;
195
196     return switch_to_user (seat, seat->priv->guest_username);
197 }
198
199 static const gchar *
200 display_get_guest_username_cb (Display *display, Seat *seat)
201 {
202     if (seat->priv->guest_username)
203         return seat->priv->guest_username;
204
205     seat->priv->guest_username = guest_account_setup ();
206     return g_strdup (seat->priv->guest_username);
207 }
208
209 static void
210 display_ready_cb (Display *display, Seat *seat)
211 {
212     /* Switch to this new display */
213     SEAT_GET_CLASS (seat)->set_active_display (seat, display);
214 }
215
216 static void
217 display_session_stopped_cb (Display *display, Seat *seat)
218 {
219     Session *session;
220
221     session = display_get_session (display);
222     if (seat->priv->guest_username && strcmp (user_get_name (session_get_user (session)), seat->priv->guest_username) == 0)
223     {
224         guest_account_cleanup (seat->priv->guest_username);
225         g_free (seat->priv->guest_username);
226         seat->priv->guest_username = NULL;
227     }
228 }
229
230 static gboolean
231 check_stopped (Seat *seat)
232 {
233     if (g_list_length (seat->priv->displays) == 0)
234     {
235         g_debug ("Seat stopped");
236         g_signal_emit (seat, signals[STOPPED], 0);
237         return TRUE;
238     }
239     return FALSE;
240 }
241
242 static void
243 display_stopped_cb (Display *display, Seat *seat)
244 {
245     seat->priv->displays = g_list_remove (seat->priv->displays, display);
246     g_signal_handlers_disconnect_matched (display, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
247     g_signal_emit (seat, signals[DISPLAY_REMOVED], 0, display);
248     g_object_unref (display);
249
250     if (seat->priv->stopping)
251         check_stopped (seat);
252 }
253
254 static gboolean
255 switch_to_user_or_start_greeter (Seat *seat, const gchar *username, gboolean is_guest, const gchar *session_name, gboolean autologin)
256 {
257     GList *link;
258     Display *new_display = NULL;
259
260     /* Switch to active display if it exists */
261     for (link = seat->priv->displays; link; link = link->next)
262     {
263         Display *display = link->data;
264
265         /* If already logged in, then switch to that display and stop the greeter display */
266         if (g_strcmp0 (display_get_username (display), username) == 0)
267         {
268             // FIXME: Use display_get_name
269             if (username)
270                 g_debug ("Switching to user %s session on display %s", username, xserver_get_address (XSERVER (display_get_display_server (display))));
271             else
272                 g_debug ("Switching to greeter on display %s", xserver_get_address (XSERVER (display_get_display_server (display))));
273             seat_set_active_display (seat, display);
274             return TRUE;
275         }
276     }
277
278     /* They don't exist, so start a greeter */
279     if (autologin)
280     {
281         if (is_guest)
282             g_debug ("Starting new display for automatic guest login");
283         else if (username)
284             g_debug ("Starting new display for automatic login as user %s", username);
285         else
286             g_debug ("Starting new display for greeter");
287     }
288     else
289     {
290         if (is_guest)
291             g_debug ("Starting new display for greeter with guest selected");
292         else if (username)
293             g_debug ("Starting new display for greeter with user %s selected", username);
294         else
295             g_debug ("Starting new display for greeter");
296     }
297
298     new_display = SEAT_GET_CLASS (seat)->add_display (seat);
299     display_load_config (DISPLAY (new_display), seat->priv->config_section);
300     g_signal_connect (new_display, "switch-to-user", G_CALLBACK (display_switch_to_user_cb), seat);
301     g_signal_connect (new_display, "switch-to-guest", G_CALLBACK (display_switch_to_guest_cb), seat);
302     g_signal_connect (new_display, "get-guest-username", G_CALLBACK (display_get_guest_username_cb), seat);
303     g_signal_connect (new_display, "ready", G_CALLBACK (display_ready_cb), seat);
304     g_signal_connect (new_display, "session-stopped", G_CALLBACK (display_session_stopped_cb), seat);
305     g_signal_connect (new_display, "stopped", G_CALLBACK (display_stopped_cb), seat);
306     display_set_allow_guest (new_display, seat_get_allow_guest (seat));
307     if (autologin)
308         display_set_autologin_user (new_display, username, is_guest, 0);
309     else
310         display_set_select_user_hint (new_display, username, is_guest);
311     display_set_user_session (new_display, session_name);
312
313     seat->priv->displays = g_list_append (seat->priv->displays, new_display);
314     g_signal_emit (seat, signals[DISPLAY_ADDED], 0, new_display);
315
316     /* Switch to this new display */
317     if (!seat->priv->active_display)
318         seat_set_active_display (seat, new_display);
319
320     return display_start (new_display);
321 }
322
323 gboolean
324 seat_switch_to_greeter (Seat *seat)
325 {
326     g_return_val_if_fail (seat != NULL, FALSE);
327
328     if (!seat->priv->can_switch)
329         return FALSE;
330
331     g_debug ("Switching to greeter");
332     return switch_to_user_or_start_greeter (seat, NULL, FALSE, NULL, FALSE);
333 }
334
335 gboolean
336 seat_switch_to_user (Seat *seat, const gchar *username, const gchar *session_name)
337 {
338     g_return_val_if_fail (seat != NULL, FALSE);
339     g_return_val_if_fail (username != NULL, FALSE);
340
341     if (!seat->priv->can_switch)
342         return FALSE;
343
344     g_debug ("Switching to user %s", username);
345     return switch_to_user_or_start_greeter (seat, username, FALSE, session_name, FALSE);
346 }
347
348 gboolean
349 seat_switch_to_guest (Seat *seat, const gchar *session_name)
350 {
351     g_return_val_if_fail (seat != NULL, FALSE);
352
353     if (!seat->priv->can_switch || !seat_get_allow_guest (seat))
354         return FALSE;
355
356     if (seat->priv->guest_username)
357         g_debug ("Switching to existing guest account %s", seat->priv->guest_username);
358     else
359         g_debug ("Switching to new guest account");
360     return switch_to_user_or_start_greeter (seat, seat->priv->guest_username, TRUE, session_name, TRUE);
361 }
362
363 void
364 seat_stop (Seat *seat)
365 {
366     g_return_if_fail (seat != NULL);
367
368     if (seat->priv->stopping)
369         return;
370
371     g_debug ("Stopping seat");
372     seat->priv->stopping = TRUE;
373     SEAT_GET_CLASS (seat)->stop (seat);
374 }
375
376 static void
377 seat_real_setup (Seat *seat)
378 {
379     if (seat->priv->config_section && config_has_key (config_get_instance (), seat->priv->config_section, "allow-guest"))
380         seat->priv->allow_guest = config_get_boolean (config_get_instance (), seat->priv->config_section, "allow-guest");
381     else if (config_has_key (config_get_instance (), "SeatDefaults", "allow-guest"))
382         seat->priv->allow_guest = config_get_boolean (config_get_instance (), "SeatDefaults", "allow-guest");
383     if (seat->priv->config_section && config_has_key (config_get_instance (), seat->priv->config_section, "autologin-guest"))
384         seat->priv->autologin_guest = config_get_boolean (config_get_instance (), seat->priv->config_section, "autologin-guest");
385     else if (config_has_key (config_get_instance (), "SeatDefaults", "autologin-guest"))
386         seat->priv->autologin_guest = config_get_boolean (config_get_instance (), "SeatDefaults", "autologin-guest");
387     if (seat->priv->config_section)
388         seat->priv->autologin_username = config_get_string (config_get_instance (), seat->priv->config_section, "autologin-user");
389     if (!seat->priv->autologin_username)
390         seat->priv->autologin_username = config_get_string (config_get_instance (), "SeatDefaults", "autologin-user");
391     if (seat->priv->config_section && config_has_key (config_get_instance (), seat->priv->config_section, "autologin-user-timeout"))
392         seat->priv->autologin_timeout = config_get_integer (config_get_instance (), seat->priv->config_section, "autologin-user-timeout");
393     else
394         seat->priv->autologin_timeout = config_get_integer (config_get_instance (), "SeatDefaults", "autologin-user-timeout");
395     if (seat->priv->autologin_timeout < 0)
396         seat->priv->autologin_timeout = 0;
397 }
398
399 static gboolean
400 seat_real_start (Seat *seat)
401 {
402     g_debug ("Starting seat");
403
404     /* Start showing a greeter */
405     if (seat->priv->autologin_username)
406         return switch_to_user_or_start_greeter (seat, seat->priv->autologin_username, FALSE, NULL, TRUE);
407     else if (seat->priv->autologin_guest)
408         return switch_to_user_or_start_greeter (seat, NULL, TRUE, NULL, TRUE);
409     else
410         return switch_to_user_or_start_greeter (seat, NULL, FALSE, NULL, FALSE);
411 }
412
413 static Display *
414 seat_real_add_display (Seat *seat)
415 {
416     return NULL;
417 }
418
419 static void
420 seat_real_set_active_display (Seat *seat, Display *display)
421 {
422     if (display == seat->priv->active_display)
423         return;
424
425     if (seat->priv->active_display)
426     {
427         /* Stop the existing display if it is a greeter */
428         if (!display_get_username (seat->priv->active_display))
429         {
430             g_debug ("Stopping greeter display being switched from");
431             display_stop (seat->priv->active_display);
432         }
433         g_object_unref (seat->priv->active_display);
434     }
435     seat->priv->active_display = g_object_ref (display);
436 }
437
438 static void
439 seat_real_stop (Seat *seat)
440 {
441     GList *link;
442
443     if (check_stopped (seat))
444         return;
445
446     for (link = seat->priv->displays; link; link = link->next)
447     {
448         Display *display = link->data;
449         display_stop (display);
450     }
451 }
452
453 static void
454 seat_init (Seat *seat)
455 {
456     seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_TYPE, SeatPrivate);
457 }
458
459 static void
460 seat_finalize (GObject *object)
461 {
462     Seat *self;
463
464     self = SEAT (object);
465
466     g_free (self->priv->config_section);
467     g_list_free_full (self->priv->displays, g_object_unref);
468     if (self->priv->active_display)
469         g_object_unref (self->priv->active_display);
470
471     G_OBJECT_CLASS (seat_parent_class)->finalize (object);
472 }
473
474 static void
475 seat_class_init (SeatClass *klass)
476 {
477     GObjectClass *object_class = G_OBJECT_CLASS (klass);
478
479     klass->setup = seat_real_setup;
480     klass->start = seat_real_start;
481     klass->add_display = seat_real_add_display;
482     klass->set_active_display = seat_real_set_active_display;
483     klass->stop = seat_real_stop;
484
485     object_class->finalize = seat_finalize;
486
487     g_type_class_add_private (klass, sizeof (SeatPrivate));
488
489     signals[STARTED] =
490         g_signal_new ("started",
491                       G_TYPE_FROM_CLASS (klass),
492                       G_SIGNAL_RUN_LAST,
493                       G_STRUCT_OFFSET (SeatClass, started),
494                       NULL, NULL,
495                       g_cclosure_marshal_VOID__VOID,
496                       G_TYPE_NONE, 0);
497     signals[DISPLAY_ADDED] =
498         g_signal_new ("display-added",
499                       G_TYPE_FROM_CLASS (klass),
500                       G_SIGNAL_RUN_LAST,
501                       G_STRUCT_OFFSET (SeatClass, display_added),
502                       NULL, NULL,
503                       g_cclosure_marshal_VOID__OBJECT,
504                       G_TYPE_NONE, 1, DISPLAY_TYPE);
505     signals[DISPLAY_REMOVED] =
506         g_signal_new ("display-removed",
507                       G_TYPE_FROM_CLASS (klass),
508                       G_SIGNAL_RUN_LAST,
509                       G_STRUCT_OFFSET (SeatClass, display_removed),
510                       NULL, NULL,
511                       g_cclosure_marshal_VOID__OBJECT,
512                       G_TYPE_NONE, 1, DISPLAY_TYPE);
513     signals[STOPPED] =
514         g_signal_new ("stopped",
515                       G_TYPE_FROM_CLASS (klass),
516                       G_SIGNAL_RUN_LAST,
517                       G_STRUCT_OFFSET (SeatClass, stopped),
518                       NULL, NULL,
519                       g_cclosure_marshal_VOID__VOID,
520                       G_TYPE_NONE, 0);
521 }