1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 
21 #include "config.h"
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <pwd.h>
32 #include <grp.h>
33 #include <sys/wait.h>
34 #include <locale.h>
35 #include <signal.h>
36 
37 #include <glib.h>
38 #include <glib/gi18n.h>
39 #include <glib/gstdio.h>
40 #include <glib-object.h>
41 #include <gio/gio.h>
42 
43 #include "gdm-manager.h"
44 #include "gdm-log.h"
45 #include "gdm-common.h"
46 
47 #include "gdm-settings.h"
48 #include "gdm-settings-direct.h"
49 #include "gdm-settings-keys.h"
50 
51 #define GDM_DBUS_NAME "org.gnome.DisplayManager"
52 
53 static GDBusConnection *get_system_bus (void);
54 static gboolean         bus_reconnect (void);
55 
56 extern char **environ;
57 
58 static GdmManager      *manager       = NULL;
59 static int              name_id       = -1;
60 static GdmSettings     *settings      = NULL;
61 static uid_t            gdm_uid       = -1;
62 static gid_t            gdm_gid       = -1;
63 
64 static gboolean
timed_exit_cb(GMainLoop * loop)65 timed_exit_cb (GMainLoop *loop)
66 {
67         g_main_loop_quit (loop);
68         return FALSE;
69 }
70 
71 static void
bus_connection_closed(void)72 bus_connection_closed (void)
73 {
74         g_debug ("Disconnected from D-Bus");
75 
76         if (manager == NULL) {
77                 /* probably shutting down or something */
78                 return;
79         }
80 
81         g_clear_object (&manager);
82 
83         g_timeout_add_seconds (3, (GSourceFunc)bus_reconnect, NULL);
84 }
85 
86 static GDBusConnection *
get_system_bus(void)87 get_system_bus (void)
88 {
89         GError          *error;
90         GDBusConnection *bus;
91 
92         error = NULL;
93         bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
94         if (bus == NULL) {
95                 g_warning ("Couldn't connect to system bus: %s",
96                            error->message);
97                 g_error_free (error);
98                 goto out;
99         }
100 
101         g_signal_connect (bus, "closed",
102                           G_CALLBACK (bus_connection_closed), NULL);
103         g_dbus_connection_set_exit_on_close (bus, FALSE);
104 
105  out:
106         return bus;
107 }
108 
109 static void
delete_pid(void)110 delete_pid (void)
111 {
112         g_unlink (GDM_PID_FILE);
113 }
114 
115 static void
write_pid(void)116 write_pid (void)
117 {
118         int     pf;
119         ssize_t written;
120         char    pid[9];
121 
122         errno = 0;
123         pf = open (GDM_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
124         if (pf < 0) {
125                 g_warning (_("Cannot write PID file %s: possibly out of disk space: %s"),
126                            GDM_PID_FILE,
127                            g_strerror (errno));
128 
129                 return;
130         }
131 
132         snprintf (pid, sizeof (pid), "%lu\n", (long unsigned) getpid ());
133         errno = 0;
134         written = write (pf, pid, strlen (pid));
135         close (pf);
136 
137         if (written < 0) {
138                 g_warning (_("Cannot write PID file %s: possibly out of disk space: %s"),
139                            GDM_PID_FILE,
140                            g_strerror (errno));
141                 return;
142         }
143 
144         atexit (delete_pid);
145 }
146 
147 static gboolean
ensure_dir_with_perms(const char * path,uid_t uid,gid_t gid,mode_t mode,GError ** error)148 ensure_dir_with_perms (const char *path,
149                        uid_t       uid,
150                        gid_t       gid,
151                        mode_t      mode,
152                        GError    **error)
153 {
154         gboolean ret = FALSE;
155 
156         if (g_mkdir_with_parents (path, 0755) == -1) {
157                 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno));
158                 goto out;
159         }
160         if (g_chmod (path, mode) == -1) {
161                 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno));
162                 goto out;
163         }
164         if (chown (path, uid, gid) == -1) {
165                 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno));
166                 goto out;
167         }
168 
169         ret = TRUE;
170  out:
171         return ret;
172 }
173 
174 static void
gdm_daemon_ensure_dirs(uid_t uid,gid_t gid)175 gdm_daemon_ensure_dirs (uid_t uid,
176                         gid_t gid)
177 {
178         GError *error = NULL;
179 
180         /* Set up /var/run/gdm */
181         if (!ensure_dir_with_perms (GDM_RAN_ONCE_MARKER_DIR, 0, gid, 0711, &error)) {
182                 gdm_fail (_("Failed to create ran once marker dir %s: %s"),
183                           GDM_RAN_ONCE_MARKER_DIR, error->message);
184         }
185 
186         /* Set up /var/log/gdm */
187         if (!ensure_dir_with_perms (LOGDIR, 0, gid, 0711, &error)) {
188                 gdm_fail (_("Failed to create LogDir %s: %s"),
189                           LOGDIR, error->message);
190         }
191 }
192 
193 static void
gdm_daemon_lookup_user(uid_t * uidp,gid_t * gidp)194 gdm_daemon_lookup_user (uid_t *uidp,
195                         gid_t *gidp)
196 {
197         char          *username;
198         char          *groupname;
199         uid_t          uid;
200         gid_t          gid;
201         struct passwd *pwent;
202         struct group  *grent;
203 
204         username = NULL;
205         groupname = NULL;
206         uid = 0;
207         gid = 0;
208 
209         gdm_settings_direct_get_string (GDM_KEY_USER, &username);
210         gdm_settings_direct_get_string (GDM_KEY_GROUP, &groupname);
211 
212         if (username == NULL || groupname == NULL) {
213                 return;
214         }
215 
216         g_debug ("Changing user:group to %s:%s", username, groupname);
217 
218         /* Lookup user and groupid for the GDM user */
219         gdm_get_pwent_for_name (username, &pwent);
220 
221         /* Set uid and gid */
222         if G_UNLIKELY (pwent == NULL) {
223                 gdm_fail (_("Can’t find the GDM user “%s”. Aborting!"), username);
224         } else {
225                 uid = pwent->pw_uid;
226         }
227 
228         if G_UNLIKELY (uid == 0) {
229                 gdm_fail (_("The GDM user should not be root. Aborting!"));
230         }
231 
232         grent = getgrnam (groupname);
233 
234         if G_UNLIKELY (grent == NULL) {
235                 gdm_fail (_("Can’t find the GDM group “%s”. Aborting!"), groupname);
236         } else  {
237                 gid = grent->gr_gid;
238         }
239 
240         if G_UNLIKELY (gid == 0) {
241                 gdm_fail (_("The GDM group should not be root. Aborting!"));
242         }
243 
244         if (uidp != NULL) {
245                 *uidp = uid;
246         }
247 
248         if (gidp != NULL) {
249                 *gidp = gid;
250         }
251 
252         g_free (username);
253         g_free (groupname);
254 }
255 
256 static gboolean
on_shutdown_signal_cb(gpointer user_data)257 on_shutdown_signal_cb (gpointer user_data)
258 {
259         GMainLoop *mainloop = user_data;
260 
261         g_main_loop_quit (mainloop);
262 
263         return FALSE;
264 }
265 
266 static gboolean
on_sighup_cb(gpointer user_data)267 on_sighup_cb (gpointer user_data)
268 {
269         g_debug ("Got HUP signal");
270         /* Reread config stuff like system config files, VPN service
271          * files, etc
272          */
273         g_object_unref (settings);
274         settings = gdm_settings_new ();
275         if (settings != NULL) {
276                 if (! gdm_settings_direct_init (settings, DATADIR "/gdm/gdm.schemas", "/")) {
277                         g_warning ("Unable to initialize settings");
278                 }
279         }
280 
281         return TRUE;
282 }
283 
284 static gboolean
is_debug_set(void)285 is_debug_set (void)
286 {
287         gboolean debug;
288         gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
289         return debug;
290 }
291 
292 /* SIGUSR1 is used by the X server to tell us that we're ready, so
293  * block it. We'll unblock it in the worker thread in gdm-server.c
294  */
295 static void
block_sigusr1(void)296 block_sigusr1 (void)
297 {
298         sigset_t mask;
299 
300         sigemptyset (&mask);
301         sigaddset (&mask, SIGUSR1);
302         sigprocmask (SIG_BLOCK, &mask, NULL);
303 }
304 
305 int
main(int argc,char ** argv)306 main (int    argc,
307       char **argv)
308 {
309         GMainLoop          *main_loop;
310         GOptionContext     *context;
311         GError             *error = NULL;
312         gboolean            res;
313         static gboolean     do_timed_exit    = FALSE;
314         static gboolean     print_version    = FALSE;
315         static gboolean     fatal_warnings   = FALSE;
316         static GOptionEntry entries []   = {
317                 { "fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &fatal_warnings, N_("Make all warnings fatal"), NULL },
318                 { "timed-exit", 0, 0, G_OPTION_ARG_NONE, &do_timed_exit, N_("Exit after a time (for debugging)"), NULL },
319                 { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, N_("Print GDM version"), NULL },
320 
321                 { NULL }
322         };
323 
324         block_sigusr1 ();
325 
326         bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
327         textdomain (GETTEXT_PACKAGE);
328         setlocale (LC_ALL, "");
329 
330         context = g_option_context_new (_("GNOME Display Manager"));
331         g_option_context_add_main_entries (context, entries, NULL);
332 
333         error = NULL;
334         res = g_option_context_parse (context, &argc, &argv, &error);
335         g_option_context_free (context);
336         if (! res) {
337                 g_printerr ("Failed to parse options: %s\n", error->message);
338                 g_error_free (error);
339                 return EXIT_FAILURE;
340         }
341 
342         if (print_version) {
343                 g_print ("GDM %s\n", VERSION);
344                 return EXIT_SUCCESS;
345         }
346 
347         /* XDM compliant error message */
348         if (getuid () != 0) {
349                 /* make sure the pid file doesn't get wiped */
350                 g_printerr ("%s\n", _("Only the root user can run GDM"));
351                 return EXIT_FAILURE;
352         }
353 
354         if (fatal_warnings) {
355                 GLogLevelFlags fatal_mask;
356 
357                 fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
358                 fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
359                 g_log_set_always_fatal (fatal_mask);
360         }
361 
362         gdm_log_init ();
363 
364         settings = gdm_settings_new ();
365         if (! gdm_settings_direct_init (settings, DATADIR "/gdm/gdm.schemas", "/")) {
366                 g_warning ("Unable to initialize settings");
367                 return EXIT_FAILURE;
368         }
369 
370         gdm_log_set_debug (is_debug_set ());
371 
372         gdm_daemon_lookup_user (&gdm_uid, &gdm_gid);
373 
374         gdm_daemon_ensure_dirs (gdm_uid, gdm_gid);
375 
376         /* Connect to the bus, own the name and start the manager */
377         bus_reconnect ();
378 
379         /* pid file */
380         delete_pid ();
381         write_pid ();
382 
383         g_chdir ("/");
384 
385         main_loop = g_main_loop_new (NULL, FALSE);
386 
387         g_unix_signal_add (SIGTERM, on_shutdown_signal_cb, main_loop);
388         g_unix_signal_add (SIGINT, on_shutdown_signal_cb, main_loop);
389         g_unix_signal_add (SIGHUP, on_sighup_cb, NULL);
390 
391         if (do_timed_exit) {
392                 g_timeout_add_seconds (30, (GSourceFunc) timed_exit_cb, main_loop);
393         }
394 
395         g_main_loop_run (main_loop);
396 
397         g_debug ("GDM finished, cleaning up...");
398 
399         g_clear_object (&manager);
400         g_clear_object (&settings);
401 
402         gdm_settings_direct_shutdown ();
403         gdm_log_shutdown ();
404 
405         g_main_loop_unref (main_loop);
406 
407         return EXIT_SUCCESS;
408 }
409 
410 static void
on_name_acquired(GDBusConnection * bus,const char * name,gpointer user_data)411 on_name_acquired (GDBusConnection *bus,
412                   const char      *name,
413                   gpointer         user_data)
414 {
415         gboolean xdmcp_enabled;
416         gboolean show_local_greeter;
417 
418         manager = gdm_manager_new ();
419         if (manager == NULL) {
420                 g_warning ("Could not construct manager object");
421                 exit (EXIT_FAILURE);
422         }
423 
424         g_debug ("Successfully connected to D-Bus");
425 
426         show_local_greeter = TRUE;
427         gdm_settings_direct_get_boolean (GDM_KEY_SHOW_LOCAL_GREETER, &show_local_greeter);
428         gdm_manager_set_show_local_greeter (manager, show_local_greeter);
429 
430         xdmcp_enabled = FALSE;
431         gdm_settings_direct_get_boolean (GDM_KEY_XDMCP_ENABLE, &xdmcp_enabled);
432         gdm_manager_set_xdmcp_enabled (manager, xdmcp_enabled);
433 
434         gdm_manager_start (manager);
435 }
436 
437 static void
on_name_lost(GDBusConnection * bus,const char * name,gpointer user_data)438 on_name_lost (GDBusConnection *bus,
439               const char      *name,
440               gpointer         user_data)
441 {
442         g_debug ("Lost GDM name on bus");
443 
444         bus_connection_closed ();
445 }
446 
447 static gboolean
bus_reconnect()448 bus_reconnect ()
449 {
450         GDBusConnection *bus;
451         gboolean         ret;
452 
453         ret = TRUE;
454 
455         bus = get_system_bus ();
456         if (bus == NULL) {
457                 goto out;
458         }
459 
460         name_id = g_bus_own_name_on_connection (bus,
461                                                 GDM_DBUS_NAME,
462                                                 G_BUS_NAME_OWNER_FLAGS_NONE,
463                                                 on_name_acquired,
464                                                 on_name_lost,
465                                                 NULL,
466                                                 NULL);
467 
468         ret = FALSE;
469  out:
470         return ret;
471 }
472