2 * Copyright (C) 2010 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
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
17 #include <dbus/dbus-glib.h>
21 #include "display-manager.h"
22 #include "display-manager-glue.h"
23 #include "xdmcp-server.h"
31 static guint signals[LAST_SIGNAL] = { 0 };
33 struct DisplayManagerPrivate
38 /* Directory to store authorization files */
41 /* Counter to generate unique authorization file names */
44 /* Directory to write log files to */
47 /* TRUE if running in test mode (i.e. as non-root for testing) */
50 /* The displays being managed */
54 XDMCPServer *xdmcp_server;
57 G_DEFINE_TYPE (DisplayManager, display_manager, G_TYPE_OBJECT);
60 display_manager_new (GKeyFile *config)
62 DisplayManager *self = g_object_new (DISPLAY_MANAGER_TYPE, NULL);
64 self->priv->config = config;
65 self->priv->test_mode = g_key_file_get_boolean (self->priv->config, "LightDM", "test-mode", NULL);
66 self->priv->auth_dir = g_key_file_get_string (self->priv->config, "LightDM", "authorization-directory", NULL);
67 if (!self->priv->auth_dir)
68 self->priv->auth_dir = g_strdup (XAUTH_DIR);
69 self->priv->log_dir = g_key_file_get_string (self->priv->config, "LightDM", "log-directory", NULL);
70 if (!self->priv->log_dir)
71 self->priv->log_dir = g_strdup (LOG_DIR);
77 display_number_used (DisplayManager *manager, guint display_number)
81 for (link = manager->priv->displays; link; link = link->next)
83 Display *display = link->data;
84 XServer *xserver = display_get_xserver (display);
85 if (xserver && xserver_get_hostname (xserver) == NULL && xserver_get_display_number (xserver) == display_number)
89 /* In test mode there is probably another display manager running so see if the server exists */
90 if (manager->priv->test_mode)
92 xcb_connection_t *connection;
96 address = g_strdup_printf (":%d", display_number);
97 connection = xcb_connect_to_display_with_auth_info (address, NULL, NULL);
99 is_used = !xcb_connection_has_error (connection);
100 xcb_disconnect (connection);
109 get_free_display_number (DisplayManager *manager)
111 guint display_number = 0;
113 while (display_number_used (manager, display_number))
116 return display_number;
120 get_authorization_path (DisplayManager *manager)
124 path = g_strdup_printf ("%s/%d", manager->priv->auth_dir, manager->priv->auth_counter);
125 manager->priv->auth_counter++;
131 start_session (Display *display, Session *session, gboolean is_greeter, DisplayManager *manager)
134 XAuthorization *authorization;
136 /* Connect using the session bus */
137 if (manager->priv->test_mode)
139 session_set_env (session, "DBUS_SESSION_BUS_ADDRESS", getenv ("DBUS_SESSION_BUS_ADDRESS"));
140 session_set_env (session, "XDG_SESSION_COOKIE", getenv ("XDG_SESSION_COOKIE"));
141 session_set_env (session, "LDM_BUS", "SESSION");
144 /* Address for greeter to connect to */
145 string = g_strdup_printf ("/org/lightdm/LightDisplayManager/Display%d", display_get_index (display));
146 session_set_env (session, "LDM_DISPLAY", string);
149 authorization = xserver_get_authorization (display_get_xserver (display));
154 path = get_authorization_path (manager);
155 session_set_authorization (session, authorization, path);
162 filename = g_strdup_printf ("%s-greeter.log", xserver_get_address (display_get_xserver (display)));
163 string = g_build_filename (manager->priv->log_dir, filename, NULL);
168 // FIXME: Copy old error file
169 if (manager->priv->test_mode)
170 string = g_strdup (".xsession-errors");
173 struct passwd *user_info = getpwnam (session_get_username (session));
175 string = g_build_filename (user_info->pw_dir, ".xsession-errors", NULL);
177 g_warning ("Failed to get user info for user '%s'", session_get_username (session));
180 g_debug ("Logging to %s", string);
181 session_set_log_file (session, string);
186 start_greeter_cb (Display *display, Session *session, DisplayManager *manager)
188 start_session (display, session, TRUE, manager);
192 start_session_cb (Display *display, Session *session, DisplayManager *manager)
194 start_session (display, session, FALSE, manager);
198 end_session_cb (Display *display, Session *session, DisplayManager *manager)
202 /* Change authorization for next session */
203 xserver = display_get_xserver (display);
204 if (xserver_get_server_type (xserver) == XSERVER_TYPE_LOCAL)
206 XAuthorization *authorization;
208 g_debug ("Generating new authorization cookie for %s", xserver_get_address (xserver));
209 authorization = xauth_new_cookie ();
210 xserver_set_authorization (xserver, authorization, NULL);
211 g_object_unref (authorization);
218 if (c >= '0' && c <= '9')
220 if (c >= 'a' && c <= 'f')
222 if (c >= 'A' && c <= 'F')
228 string_to_xdm_auth_key (const gchar *key, guchar *data)
232 memset (data, 0, sizeof (data));
233 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
235 for (i = 0; i < 8; i++)
237 if (key[i*2] == '\0')
239 data[i] |= atox (key[i*2]) << 8;
240 if (key[i*2+1] == '\0')
242 data[i] |= atox (key[i*2+1]);
247 for (i = 1; i < 8 && key[i-1]; i++)
253 make_xserver (DisplayManager *manager, gchar *config_section)
257 XAuthorization *authorization = NULL;
258 gchar *xdmcp_manager, *filename, *path, *xserver_command;
260 if (config_section && g_key_file_has_key (manager->priv->config, config_section, "display-number", NULL))
261 display_number = g_key_file_get_integer (manager->priv->config, config_section, "display-number", NULL);
263 display_number = get_free_display_number (manager);
265 xdmcp_manager = config_section ? g_key_file_get_string (manager->priv->config, config_section, "xdmcp-manager", NULL) : NULL;
271 xserver = xserver_new (XSERVER_TYPE_LOCAL_TERMINAL, xdmcp_manager, display_number);
273 port = g_key_file_get_integer (manager->priv->config, config_section, "xdmcp-port", NULL);
275 xserver_set_port (xserver, port);
276 key = g_key_file_get_string (manager->priv->config, config_section, "key", NULL);
281 string_to_xdm_auth_key (key, data);
282 xserver_set_authentication (xserver, "XDM-AUTHENTICATION-1", data, 8);
283 authorization = xauth_new ("XDM-AUTHORIZATION-1", data, 8);
288 xserver = xserver_new (XSERVER_TYPE_LOCAL, NULL, display_number);
289 authorization = xauth_new_cookie ();
291 g_free (xdmcp_manager);
293 path = get_authorization_path (manager);
294 xserver_set_authorization (xserver, authorization, path);
295 g_object_unref (authorization);
298 filename = g_strdup_printf ("%s.log", xserver_get_address (xserver));
299 path = g_build_filename (manager->priv->log_dir, filename, NULL);
300 g_debug ("Logging to %s", path);
301 xserver_set_log_file (xserver, path);
305 /* Allow X server to be Xephyr */
306 if (getenv ("DISPLAY"))
307 xserver_set_env (xserver, "DISPLAY", getenv ("DISPLAY"));
308 if (getenv ("XAUTHORITY"))
309 xserver_set_env (xserver, "XAUTHORITY", getenv ("XAUTHORITY"));
310 xserver_command = g_key_file_get_string (manager->priv->config, "LightDM", "xserver", NULL);
312 xserver_set_command (xserver, xserver_command);
313 g_free (xserver_command);
315 if (manager->priv->test_mode)
316 xserver_set_command (xserver, "Xephyr");
322 add_display (DisplayManager *manager)
326 display = display_new (g_list_length (manager->priv->displays));
327 g_signal_connect (display, "start-greeter", G_CALLBACK (start_greeter_cb), manager);
328 g_signal_connect (display, "start-session", G_CALLBACK (start_session_cb), manager);
329 g_signal_connect (display, "end-session", G_CALLBACK (end_session_cb), manager);
331 if (manager->priv->test_mode)
332 display_set_greeter_user (display, NULL);
334 manager->priv->displays = g_list_append (manager->priv->displays, display);
336 g_signal_emit (manager, signals[DISPLAY_ADDED], 0, display);
342 display_manager_add_display (DisplayManager *manager, GError *error)
347 g_debug ("Starting new display");
348 display = add_display (manager);
349 xserver = make_xserver (manager, NULL);
350 display_set_xserver (display, xserver);
351 display_start (display);
352 g_object_unref (xserver);
358 display_manager_switch_to_user (DisplayManager *manager, char *username, GError *error)
364 for (link = manager->priv->displays; link; link = link->next)
366 display = link->data;
367 const gchar *session_user;
369 session_user = display_get_session_user (display);
370 if (session_user && strcmp (session_user, username) == 0)
372 g_debug ("Switching to user %s session on display %s", username, xserver_get_address (display_get_xserver (display)));
373 //display_focus (display);
378 g_debug ("Starting new display for user %s", username);
379 display = add_display (manager);
380 xserver = make_xserver (manager, NULL);
381 display_set_xserver (display, xserver);
382 display_start (display);
383 g_object_unref (xserver);
389 xdmcp_session_cb (XDMCPServer *server, XDMCPSession *session, DisplayManager *manager)
396 // FIXME: Try IPv6 then fallback to IPv4
398 display = add_display (manager);
399 address = g_inet_address_to_string (G_INET_ADDRESS (xdmcp_session_get_address (session)));
400 xserver = xserver_new (XSERVER_TYPE_REMOTE, address, xdmcp_session_get_display_number (session));
401 if (strcmp (xdmcp_session_get_authorization_name (session), "") != 0)
403 XAuthorization *authorization = NULL;
406 authorization = xauth_new (xdmcp_session_get_authorization_name (session),
407 xdmcp_session_get_authorization_data (session),
408 xdmcp_session_get_authorization_data_length (session));
409 path = get_authorization_path (manager);
411 xserver_set_authorization (xserver, authorization, path);
413 g_object_unref (authorization);
417 display_set_xserver (display, xserver);
418 result = display_start (display);
419 g_object_unref (xserver);
422 g_object_unref (display);
428 setup_auth_dir (DisplayManager *manager)
431 GError *error = NULL;
433 g_mkdir_with_parents (manager->priv->auth_dir, S_IRWXU | S_IXGRP | S_IXOTH);
434 dir = g_dir_open (manager->priv->auth_dir, 0, &error);
437 g_warning ("Authorization dir not created: %s", error->message);
438 g_clear_error (&error);
442 /* Clear out the directory */
445 const gchar *filename;
449 filename = g_dir_read_name (dir);
453 path = g_build_filename (manager->priv->auth_dir, filename, NULL);
454 file = g_file_new_for_path (filename);
455 g_file_delete (file, NULL, NULL);
458 g_object_unref (file);
465 display_manager_start (DisplayManager *manager)
470 /* Make an empty authorization directory */
471 setup_auth_dir (manager);
473 /* Start the first display */
474 displays = g_key_file_get_string (manager->priv->config, "LightDM", "displays", NULL);
476 displays = g_strdup ("");
477 tokens = g_strsplit (displays, " ", -1);
480 for (i = tokens; *i; i++)
483 gchar *value, *default_user, *display_name;
488 g_debug ("Loading display %s", display_name);
490 display = add_display (manager);
492 value = g_key_file_get_string (manager->priv->config, display_name, "language", NULL);
494 display_set_default_language (display, value);
496 value = g_key_file_get_string (manager->priv->config, display_name, "layout", NULL);
498 display_set_default_layout (display, value);
500 value = g_key_file_get_string (manager->priv->config, display_name, "session", NULL);
502 display_set_default_session (display, value);
504 value = g_key_file_get_string (manager->priv->config, display_name, "user", NULL);
506 display_set_greeter_user (display, value);
508 value = g_key_file_get_string (manager->priv->config, display_name, "theme", NULL);
510 display_set_greeter_theme (display, value);
513 /* Automatically log in or start a greeter session */
514 default_user = g_key_file_get_string (manager->priv->config, display_name, "default-user", NULL);
515 user_timeout = g_key_file_get_integer (manager->priv->config, display_name, "default-user-timeout", NULL);
516 if (user_timeout < 0)
521 display_set_default_user (display, default_user);
522 display_set_default_user_timeout (display, user_timeout);
523 if (user_timeout == 0)
524 g_debug ("Starting session for user %s", default_user);
526 g_debug ("Starting session for user %s in %d seconds", default_user, user_timeout);
529 xserver = make_xserver (manager, display_name);
531 display_set_xserver (display, xserver);
532 display_start (display);
533 g_object_unref (xserver);
534 g_free (default_user);
538 if (g_key_file_get_boolean (manager->priv->config, "xdmcp", "enabled", NULL))
542 manager->priv->xdmcp_server = xdmcp_server_new ();
543 if (g_key_file_has_key (manager->priv->config, "xdmcp", "port", NULL))
546 port = g_key_file_get_integer (manager->priv->config, "xdmcp", "port", NULL);
548 xdmcp_server_set_port (manager->priv->xdmcp_server, port);
550 g_signal_connect (manager->priv->xdmcp_server, "new-session", G_CALLBACK (xdmcp_session_cb), manager);
552 key = g_key_file_get_string (manager->priv->config, "xdmcp", "key", NULL);
556 string_to_xdm_auth_key (key, data);
557 xdmcp_server_set_authentication (manager->priv->xdmcp_server, "XDM-AUTHENTICATION-1", data, 8);
558 xdmcp_server_set_authorization (manager->priv->xdmcp_server, "XDM-AUTHORIZATION-1", data, 8);
562 g_debug ("Starting XDMCP server on UDP/IP port %d", xdmcp_server_get_port (manager->priv->xdmcp_server));
563 xdmcp_server_start (manager->priv->xdmcp_server);
568 display_manager_init (DisplayManager *manager)
570 manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, DISPLAY_MANAGER_TYPE, DisplayManagerPrivate);
574 display_manager_finalize (GObject *object)
576 DisplayManager *self;
579 self = DISPLAY_MANAGER (object);
581 if (self->priv->xdmcp_server)
582 g_object_unref (self->priv->xdmcp_server);
583 for (link = self->priv->displays; link; link = link->next)
584 g_object_unref (link->data);
585 g_list_free (self->priv->displays);
589 display_manager_class_init (DisplayManagerClass *klass)
591 GObjectClass *object_class = G_OBJECT_CLASS (klass);
593 object_class->finalize = display_manager_finalize;
595 g_type_class_add_private (klass, sizeof (DisplayManagerPrivate));
597 signals[DISPLAY_ADDED] =
598 g_signal_new ("display-added",
599 G_TYPE_FROM_CLASS (klass),
601 G_STRUCT_OFFSET (DisplayManagerClass, display_added),
603 g_cclosure_marshal_VOID__OBJECT,
604 G_TYPE_NONE, 1, DISPLAY_TYPE);
606 dbus_g_object_type_install_info (DISPLAY_MANAGER_TYPE, &dbus_glib_display_manager_object_info);