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