gdm-product-slave.c 44.9 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
 *
 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>

#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

#include <X11/Xlib.h> /* for Display */

#include "gdm-common.h"

#include "gdm-product-slave.h"
#include "gdm-product-slave-glue.h"

#include "gdm-server.h"
#include "gdm-session.h"

extern char **environ;

#define GDM_PRODUCT_SLAVE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_PRODUCT_SLAVE, GdmProductSlavePrivate))

56
#define GDM_DBUS_NAME                      "org.gnome.DisplayManager"
57 58
#define GDM_DBUS_PRODUCT_DISPLAY_INTERFACE "org.gnome.DisplayManager.ProductDisplay"

59 60 61
#define SERVER_DBUS_PATH      "/org/gnome/DisplayManager/SessionRelay"
#define SERVER_DBUS_INTERFACE "org.gnome.DisplayManager.SessionRelay"

62 63 64 65
#define MAX_CONNECT_ATTEMPTS 10

struct GdmProductSlavePrivate
{
66 67
        char             *id;
        GPid              pid;
68 69 70
        guint             output_watch_id;
        guint             error_watch_id;

71
        char             *relay_address;
72

73 74 75
        GPid              server_pid;
        Display          *server_display;
        guint             connection_attempts;
76

77 78 79 80
        /* user selected */
        char             *selected_session;
        char             *selected_language;
        char             *selected_user;
81

82 83 84
        GdmServer        *server;
        GdmSession       *session;
        DBusGProxy       *session_relay_proxy;
85
        DBusGConnection  *session_relay_connection;
86
        DBusGProxy       *product_display_proxy;
87 88 89 90
        DBusGConnection  *connection;
};

enum {
91 92
        PROP_0,
        PROP_DISPLAY_ID,
93 94
};

95 96 97
static void     gdm_product_slave_class_init    (GdmProductSlaveClass *klass);
static void     gdm_product_slave_init          (GdmProductSlave      *product_slave);
static void     gdm_product_slave_finalize      (GObject             *object);
98 99 100 101 102 103 104

G_DEFINE_TYPE (GdmProductSlave, gdm_product_slave, GDM_TYPE_SLAVE)

static void
gdm_product_slave_whack_temp_auth_file (GdmProductSlave *product_slave)
{
#if 0
105 106 107 108 109 110 111 112 113 114 115 116
        uid_t old;

        old = geteuid ();
        if (old != 0)
                seteuid (0);
        if (d->parent_temp_auth_file != NULL) {
                VE_IGNORE_EINTR (g_unlink (d->parent_temp_auth_file));
        }
        g_free (d->parent_temp_auth_file);
        d->parent_temp_auth_file = NULL;
        if (old != 0)
                seteuid (old);
117 118 119 120 121 122 123 124
#endif
}


static void
create_temp_auth_file (GdmProductSlave *product_slave)
{
#if 0
125 126 127 128 129 130 131 132 133 134 135
        if (d->type == TYPE_FLEXI_XNEST &&
            d->parent_auth_file != NULL) {
                if (d->parent_temp_auth_file != NULL) {
                        VE_IGNORE_EINTR (g_unlink (d->parent_temp_auth_file));
                }
                g_free (d->parent_temp_auth_file);
                d->parent_temp_auth_file =
                        copy_auth_file (d->server_uid,
                                        gdm_daemon_config_get_gdmuid (),
                                        d->parent_auth_file);
        }
136 137 138 139 140
#endif
}

static void
listify_hash (const char *key,
141 142
              const char *value,
              GPtrArray  *env)
143
{
144 145 146 147
        char *str;
        str = g_strdup_printf ("%s=%s", key, value);
        g_debug ("environment: %s", str);
        g_ptr_array_add (env, str);
148 149 150 151
}

static GPtrArray *
get_script_environment (GdmProductSlave *slave,
152
                        const char     *username)
153
{
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
        GPtrArray     *env;
        GHashTable    *hash;
        struct passwd *pwent;
        char          *x_servers_file;
        char          *display_name;
        char          *display_hostname;
        char          *display_x11_authority_file;
        gboolean       display_is_local;

        g_object_get (slave,
                      "display-name", &display_name,
                      "display-hostname", &display_hostname,
                      "display-is-local", &display_is_local,
                      "display-x11-authority-file", &display_x11_authority_file,
                      NULL);

        env = g_ptr_array_new ();

        /* create a hash table of current environment, then update keys has necessary */
        hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);

        /* modify environment here */
        g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/"));
        g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup ("/"));
        g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup ("/bin/sh"));

        g_hash_table_insert (hash, g_strdup ("LOGNAME"), g_strdup (username));
        g_hash_table_insert (hash, g_strdup ("USER"), g_strdup (username));
        g_hash_table_insert (hash, g_strdup ("USERNAME"), g_strdup (username));

        pwent = getpwnam (username);
        if (pwent != NULL) {
                if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') {
                        g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir));
                        g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup (pwent->pw_dir));
                }

                g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell));
        }
193 194

#if 0
195 196
        if (display_is_parented) {
                g_hash_table_insert (hash, g_strdup ("GDM_PARENT_DISPLAY"), g_strdup (parent_display_name));
197

198 199
                /*g_hash_table_insert (hash, "GDM_PARENT_XAUTHORITY"), slave->priv->parent_temp_auth_file));*/
        }
200 201
#endif

202 203 204 205 206
        /* some env for use with the Pre and Post scripts */
        x_servers_file = gdm_make_filename (AUTHDIR,
                                            display_name,
                                            ".Xservers");
        g_hash_table_insert (hash, g_strdup ("X_SERVERS"), x_servers_file);
207

208 209 210
        if (! display_is_local) {
                g_hash_table_insert (hash, g_strdup ("REMOTE_HOST"), g_strdup (display_hostname));
        }
211

212 213 214
        /* Runs as root */
        g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (display_x11_authority_file));
        g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (display_name));
215

216
        /*g_setenv ("PATH", gdm_daemon_config_get_value_string (GDM_KEY_ROOT_PATH), TRUE);*/
217

218
        g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true"));
219 220

#if 0
221 222
        if ( ! ve_string_empty (d->theme_name))
                g_setenv ("GDM_GTK_THEME", d->theme_name, TRUE);
223
#endif
224
        g_hash_table_remove (hash, "MAIL");
225 226


227 228
        g_hash_table_foreach (hash, (GHFunc)listify_hash, env);
        g_hash_table_destroy (hash);
229

230
        g_ptr_array_add (env, NULL);
231

232 233 234
        g_free (display_name);
        g_free (display_hostname);
        g_free (display_x11_authority_file);
235

236
        return env;
237 238 239 240
}

static gboolean
gdm_product_slave_exec_script (GdmProductSlave *slave,
241 242
                              const char     *dir,
                              const char     *login)
243
{
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        char      *script;
        char     **argv;
        gint       status;
        GError    *error;
        GPtrArray *env;
        gboolean   res;
        gboolean   ret;
        char      *display_name;
        char      *display_hostname;

        g_assert (dir != NULL);
        g_assert (login != NULL);

        g_object_get (slave,
                      "display-name", &display_name,
                      "display-hostname", &display_hostname,
                      NULL);

        script = g_build_filename (dir, display_name, NULL);
        if (g_access (script, R_OK|X_OK) != 0) {
                g_free (script);
                script = NULL;
        }

        if (script == NULL &&
            display_hostname != NULL) {
                script = g_build_filename (dir, display_hostname, NULL);
                if (g_access (script, R_OK|X_OK) != 0) {
                        g_free (script);
                        script = NULL;
                }
        }
276 277

#if 0
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
        if (script == NULL &&
            SERVER_IS_XDMCP (d)) {
                script = g_build_filename (dir, "XDMCP", NULL);
                if (g_access (script, R_OK|X_OK) != 0) {
                        g_free (script);
                        script = NULL;
                }
        }
        if (script == NULL &&
            SERVER_IS_FLEXI (d)) {
                script = g_build_filename (dir, "Flexi", NULL);
                if (g_access (script, R_OK|X_OK) != 0) {
                        g_free (script);
                        script = NULL;
                }
        }
294 295
#endif

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
        if (script == NULL) {
                script = g_build_filename (dir, "Default", NULL);
                if (g_access (script, R_OK|X_OK) != 0) {
                        g_free (script);
                        script = NULL;
                }
        }

        if (script == NULL) {
                return TRUE;
        }

        create_temp_auth_file (slave);

        g_debug ("Running process: %s", script);
        error = NULL;
        if (! g_shell_parse_argv (script, NULL, &argv, &error)) {
                g_warning ("Could not parse command: %s", error->message);
                g_error_free (error);
                goto out;
        }

        env = get_script_environment (slave, login);

        res = g_spawn_sync (NULL,
                            argv,
                            (char **)env->pdata,
                            G_SPAWN_SEARCH_PATH,
                            NULL,
                            NULL,
                            NULL,
                            NULL,
                            &status,
                            &error);

        g_ptr_array_foreach (env, (GFunc)g_free, NULL);
332 333
        g_ptr_array_free (env, TRUE);

334
        gdm_product_slave_whack_temp_auth_file (slave);
335

336 337 338 339 340 341
        if (WIFEXITED (status)) {
                g_debug ("Process exit status: %d", WEXITSTATUS (status));
                ret = WEXITSTATUS (status) != 0;
        } else {
                ret = TRUE;
        }
342 343

 out:
344 345 346
        g_free (script);
        g_free (display_name);
        g_free (display_hostname);
347

348
        return ret;
349 350 351
}

static void
352 353
relay_session_started (GdmProductSlave *slave)
{
354 355 356 357 358 359 360 361 362 363 364 365 366
        GError *error;
        gboolean res;

        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "SessionStarted",
                                 &error,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to send SessionStarted: %s", error->message);
                g_error_free (error);
        }
367 368
}

369
static void
370
relay_session_opened (GdmProductSlave *slave)
371
{
372 373 374
        GError *error;
        gboolean res;

375 376 377 378 379 380
        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "Opened",
                                 &error,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
381
        if (! res) {
382
                g_warning ("Unable to send Opened: %s", error->message);
383 384
                g_error_free (error);
        }
385 386
}

387 388 389 390 391 392 393 394 395
static void
on_session_opened (GdmSession      *session,
                   GdmProductSlave *slave)
{
        g_debug ("session opened");

        relay_session_opened (slave);
}

396 397 398
static void
disconnect_relay (GdmProductSlave *slave)
{
399 400
        /* drop the connection */
        g_object_unref (slave->priv->session_relay_proxy);
401

402 403
        dbus_connection_close (dbus_g_connection_get_connection (slave->priv->session_relay_connection));
        slave->priv->session_relay_connection = NULL;
404 405
}

406 407 408
static void
on_session_started (GdmSession      *session,
                    GPid             pid,
409
                    GdmProductSlave *slave)
410
{
411
        g_debug ("session started on pid %d", (int) pid);
412

413
        relay_session_started (slave);
414

415
        disconnect_relay (slave);
416 417 418
}

static void
William Jon McCann's avatar
William Jon McCann committed
419 420
on_session_exited (GdmSession      *session,
                   int              exit_code,
421
                   GdmProductSlave *slave)
422
{
423
        g_debug ("session exited with code %d", exit_code);
William Jon McCann's avatar
William Jon McCann committed
424

425
        gdm_slave_stopped (GDM_SLAVE (slave));
426 427 428 429 430
}

static void
on_session_died (GdmSession *session,
                 int         signal_number,
431
                 GdmProductSlave   *slave)
432
{
433 434 435
        g_debug ("session died with signal %d, (%s)",
                 signal_number,
                 g_strsignal (signal_number));
William Jon McCann's avatar
William Jon McCann committed
436

437
        gdm_slave_stopped (GDM_SLAVE (slave));
438 439 440 441 442
}

static gboolean
is_prog_in_path (const char *prog)
{
443 444
        char    *f;
        gboolean ret;
445

446 447 448 449
        f = g_find_program_in_path (prog);
        ret = (f != NULL);
        g_free (f);
        return ret;
450 451 452 453
}

static gboolean
get_session_command (const char *file,
454
                     char      **command)
455
{
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
        GKeyFile   *key_file;
        GError     *error;
        char       *full_path;
        char       *exec;
        gboolean    ret;
        gboolean    res;
        const char *search_dirs[] = {
                "/etc/X11/sessions/",
                DMCONFDIR "/Sessions/",
                DATADIR "/gdm/BuiltInSessions/",
                DATADIR "/xsessions/",
                NULL
        };

        exec = NULL;
        ret = FALSE;
        if (command != NULL) {
                *command = NULL;
        }

        key_file = g_key_file_new ();

        error = NULL;
        full_path = NULL;
        res = g_key_file_load_from_dirs (key_file,
                                         file,
                                         search_dirs,
                                         &full_path,
                                         G_KEY_FILE_NONE,
                                         &error);
        if (! res) {
                g_debug ("File '%s' not found: %s", file, error->message);
                g_error_free (error);
                if (command != NULL) {
                        *command = NULL;
                }
                goto out;
        }

        error = NULL;
        res = g_key_file_get_boolean (key_file,
                                      G_KEY_FILE_DESKTOP_GROUP,
                                      G_KEY_FILE_DESKTOP_KEY_HIDDEN,
                                      &error);
        if (error == NULL && res) {
                g_debug ("Session %s is marked as hidden", file);
                goto out;
        }

        error = NULL;
        exec = g_key_file_get_string (key_file,
                                      G_KEY_FILE_DESKTOP_GROUP,
                                      G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
                                      &error);
        if (exec == NULL) {
                g_debug ("%s key not found", G_KEY_FILE_DESKTOP_KEY_TRY_EXEC);
                goto out;
        }

        res = is_prog_in_path (exec);
        g_free (exec);

        if (! res) {
                g_debug ("Command not found: %s", G_KEY_FILE_DESKTOP_KEY_TRY_EXEC);
                goto out;
        }

        error = NULL;
        exec = g_key_file_get_string (key_file,
                                      G_KEY_FILE_DESKTOP_GROUP,
                                      G_KEY_FILE_DESKTOP_KEY_EXEC,
                                      &error);
        if (error != NULL) {
                g_debug ("%s key not found: %s",
                         G_KEY_FILE_DESKTOP_KEY_EXEC,
                         error->message);
                g_error_free (error);
                goto out;
        }

        if (command != NULL) {
                *command = g_strdup (exec);
        }
        ret = TRUE;
540 541

out:
542
        g_free (exec);
543

544
        return ret;
545 546 547 548 549
}

static void
setup_session_environment (GdmProductSlave *slave)
{
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
        char *display_name;
        char *auth_file;

        g_object_get (slave,
                      "display-name", &display_name,
                      "display-x11-authority-file", &auth_file,
                      NULL);

        gdm_session_set_environment_variable (slave->priv->session,
                                              "GDMSESSION",
                                              slave->priv->selected_session);
        gdm_session_set_environment_variable (slave->priv->session,
                                              "DESKTOP_SESSION",
                                              slave->priv->selected_session);

        gdm_session_set_environment_variable (slave->priv->session,
                                              "LANG",
                                              slave->priv->selected_language);
        gdm_session_set_environment_variable (slave->priv->session,
                                              "GDM_LANG",
                                              slave->priv->selected_language);

        gdm_session_set_environment_variable (slave->priv->session,
                                              "DISPLAY",
                                              display_name);
        gdm_session_set_environment_variable (slave->priv->session,
                                              "XAUTHORITY",
                                              auth_file);

        gdm_session_set_environment_variable (slave->priv->session,
                                              "PATH",
                                              "/bin:/usr/bin:" BINDIR);

        g_free (display_name);
        g_free (auth_file);
585 586 587
}

static void
588 589
setup_server (GdmProductSlave *slave)
{
590 591 592
        gboolean       display_is_local;
        char          *display_name;
        char          *auth_file;
593

594 595 596 597 598
        g_object_get (slave,
                      "display-is-local", &display_is_local,
                      "display-name", &display_name,
                      "display-x11-authority-file", &auth_file,
                      NULL);
599

600
        /* Set the busy cursor */
601
        gdm_slave_set_busy_cursor (GDM_SLAVE (slave));
602

603
        /* FIXME: send a signal back to the master */
604 605 606

#if 0

607 608 609
        /* OK from now on it's really the user whacking us most likely,
         * we have already started up well */
        do_xfailed_on_xio_error = FALSE;
610 611 612
#endif

#if 0
613 614
        /* checkout xinerama */
        gdm_screen_init (slave);
615 616
#endif

617 618
        /* Run the init script. gdmslave suspends until script has terminated */
        gdm_product_slave_exec_script (slave,
619 620
                                       GDMCONFDIR"/Init",
                                       "gdm");
621

622 623
        g_free (display_name);
        g_free (auth_file);
624 625 626 627
}

static gboolean
setup_session (GdmProductSlave *slave)
628
{
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
        char    *username;
        char    *command;
        char    *filename;
        gboolean res;

        username = gdm_session_get_username (slave->priv->session);

        g_debug ("%s%ssuccessfully authenticated\n",
                 username ? username : "",
                 username ? " " : "");
        g_free (username);

        if (slave->priv->selected_session != NULL) {
                filename = g_strdup (slave->priv->selected_session);
        } else {
                filename = g_strdup ("gnome.desktop");
        }
646

647
        setup_session_environment (slave);
648

649 650 651 652 653
        res = get_session_command (filename, &command);
        if (! res) {
                g_warning ("Could find session file: %s", filename);
                return FALSE;
        }
654

655
        gdm_session_start_program (slave->priv->session, command);
656

657 658
        g_free (filename);
        g_free (command);
659

660
        return TRUE;
661 662 663 664 665
}

static gboolean
idle_connect_to_display (GdmProductSlave *slave)
{
666
        gboolean res;
667

668
        slave->priv->connection_attempts++;
669

670
        res = gdm_slave_connect_to_x11_display (GDM_SLAVE (slave));
671 672
        if (res) {
                /* FIXME: handle wait-for-go */
673

674 675 676 677 678 679 680 681
                setup_server (slave);
                setup_session (slave);
        } else {
                if (slave->priv->connection_attempts >= MAX_CONNECT_ATTEMPTS) {
                        g_warning ("Unable to connect to display after %d tries - bailing out", slave->priv->connection_attempts);
                        exit (1);
                }
        }
682

683
        return FALSE;
684 685 686 687
}

static void
server_ready_cb (GdmServer *server,
688
                 GdmProductSlave  *slave)
689
{
690
        g_timeout_add (500, (GSourceFunc)idle_connect_to_display, slave);
691 692 693 694 695
}

static gboolean
gdm_product_slave_create_server (GdmProductSlave *slave)
{
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
        char    *display_name;
        gboolean display_is_local;

        g_object_get (slave,
                      "display-is-local", &display_is_local,
                      "display-name", &display_name,
                      NULL);

        /* if this is local display start a server if one doesn't
         * exist */
        if (display_is_local) {
                gboolean res;

                slave->priv->server = gdm_server_new (display_name);

                g_signal_connect (slave->priv->server,
                                  "ready",
                                  G_CALLBACK (server_ready_cb),
                                  slave);

                res = gdm_server_start (slave->priv->server);
                if (! res) {
                        g_warning (_("Could not start the X "
                                     "server (your graphical environment) "
                                     "due to some internal error. "
                                     "Please contact your system administrator "
                                     "or check your syslog to diagnose. "
                                     "In the meantime this display will be "
                                     "disabled.  Please restart GDM when "
                                     "the problem is corrected."));
                        exit (1);
                }

                g_debug ("Started X server");
        } else {
                g_timeout_add (500, (GSourceFunc)idle_connect_to_display, slave);
        }

        g_free (display_name);

        return TRUE;
737 738 739
}

static void
740 741
on_session_user_verified (GdmSession      *session,
                          GdmProductSlave *slave)
742
{
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
        GError  *error;
        gboolean res;

        g_debug ("Session user verified");

        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "UserVerified",
                                 &error,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to send UserVerified: %s", error->message);
                g_error_free (error);
        }

759
        gdm_product_slave_create_server (slave);
760 761 762
}

static void
763 764 765
on_session_user_verification_error (GdmSession      *session,
                                    GError          *error,
                                    GdmProductSlave *slave)
766
{
767 768 769
        char    *username;
        GError  *local_error;
        gboolean res;
770

771
        username = gdm_session_get_username (session);
772

773 774 775 776
        g_debug ("%s%scould not be successfully authenticated: %s\n",
                 username ? username : "",
                 username ? " " : "",
                 error->message);
777

778
        g_free (username);
779

780
        local_error = NULL;
781
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
782 783 784
                                 "UserVerificationError",
                                 &local_error,
                                 G_TYPE_STRING, error->message,
785 786 787
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
788 789
                g_warning ("Unable to send UserVerificationError: %s", local_error->message);
                g_error_free (local_error);
790
        }
791 792
}

793
static void
794 795 796
on_session_info (GdmSession      *session,
                 const char      *text,
                 GdmProductSlave *slave)
797
{
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
        GError *error;
        gboolean res;

        g_debug ("Info: %s", text);

        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "Info",
                                 &error,
                                 G_TYPE_STRING, text,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to send Info: %s", error->message);
                g_error_free (error);
        }
814 815 816
}

static void
817 818 819
on_session_problem (GdmSession      *session,
                    const char      *text,
                    GdmProductSlave *slave)
820
{
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
        GError *error;
        gboolean res;

        g_debug ("Problem: %s", text);

        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "Problem",
                                 &error,
                                 G_TYPE_STRING, text,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to send Problem: %s", error->message);
                g_error_free (error);
        }
837

838 839 840
}

static void
841 842 843
on_session_info_query (GdmSession      *session,
                       const char      *text,
                       GdmProductSlave *slave)
844
{
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
        GError  *error;
        gboolean res;

        g_debug ("Info query: %s", text);

        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "InfoQuery",
                                 &error,
                                 G_TYPE_STRING, text,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to send InfoQuery: %s", error->message);
                g_error_free (error);
        }
861 862 863
}

static void
864 865 866
on_session_secret_info_query (GdmSession      *session,
                              const char      *text,
                              GdmProductSlave *slave)
867
{
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
        GError *error;
        gboolean res;


        g_debug ("Secret info query: %s", text);

        error = NULL;
        res = dbus_g_proxy_call (slave->priv->session_relay_proxy,
                                 "SecretInfoQuery",
                                 &error,
                                 G_TYPE_STRING, text,
                                 G_TYPE_INVALID,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to send SecretInfoQuery: %s", error->message);
                g_error_free (error);
        }
885 886
}

887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
static void
on_relay_begin_verification (DBusGProxy *proxy,
                             const char *username,
                             gpointer    data)
{
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
        GError          *error;
        gboolean         res;

        g_debug ("Relay Begin Verification");

        error = NULL;
        res = gdm_session_begin_verification (slave->priv->session,
                                              username,
                                              &error);
        if (! res) {
                g_warning ("Unable to begin verification: %s", error->message);
                g_error_free (error);
        }
}

908
static void
909
on_relay_answer (DBusGProxy *proxy,
910 911
                 const char *text,
                 gpointer    data)
912
{
913
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
914

915
        g_debug ("Relay Answer");
916

917
        gdm_session_answer_query (slave->priv->session, text);
918 919 920
}

static void
921
on_relay_session_selected (DBusGProxy *proxy,
922 923
                           const char *text,
                           gpointer    data)
924
{
925
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
926

927
        g_debug ("Session: %s", text);
928

929 930
        g_free (slave->priv->selected_session);
        slave->priv->selected_session = g_strdup (text);
931 932 933
}

static void
934
on_relay_language_selected (DBusGProxy *proxy,
935 936
                            const char *text,
                            gpointer    data)
937
{
938
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
939

940
        g_debug ("Language: %s", text);
941

942 943
        g_free (slave->priv->selected_language);
        slave->priv->selected_language = g_strdup (text);
944 945
}

946 947 948
static gboolean
reset_session (GdmProductSlave *slave)
{
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
        gboolean         res;
        GError          *error;

        gdm_session_close (slave->priv->session);
        res = gdm_session_open (slave->priv->session,
                                "gdm",
                                "",
                                "/dev/console",
                                &error);
        if (! res) {
                g_warning ("Unable to open session: %s", error->message);
                g_error_free (error);
        }

        return res;
964 965
}

966 967
static void
on_relay_user_selected (DBusGProxy *proxy,
968 969
                        const char *text,
                        gpointer    data)
970
{
971
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
972

973
        g_debug ("User: %s", text);
974

975 976
        g_free (slave->priv->selected_user);
        slave->priv->selected_user = g_strdup (text);
977

978
        reset_session (slave);
979 980
}

981
static void
982
on_relay_open (DBusGProxy *proxy,
983
               gpointer    data)
984
{
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
        gboolean         res;
        GError          *error;

        g_debug ("Relay open: opening session");

        error = NULL;
        res = gdm_session_open (slave->priv->session,
                                "gdm",
                                "",
                                "/dev/console",
                                &error);
        if (! res) {
                g_warning ("Unable to open session: %s", error->message);
                g_error_free (error);
        }
1001 1002
}

1003 1004 1005
static void
create_new_session (GdmProductSlave *slave)
{
1006 1007 1008 1009
        slave->priv->session = gdm_session_new ();

        g_signal_connect (slave->priv->session,
                          "opened",
1010
                          G_CALLBACK (on_session_opened),
1011 1012 1013 1014
                          slave);

        g_signal_connect (slave->priv->session,
                          "info",
1015
                          G_CALLBACK (on_session_info),
1016 1017 1018 1019
                          slave);

        g_signal_connect (slave->priv->session,
                          "problem",
1020
                          G_CALLBACK (on_session_problem),
1021 1022 1023 1024
                          slave);

        g_signal_connect (slave->priv->session,
                          "info-query",
1025
                          G_CALLBACK (on_session_info_query),
1026 1027 1028 1029
                          slave);

        g_signal_connect (slave->priv->session,
                          "secret-info-query",
1030
                          G_CALLBACK (on_session_secret_info_query),
1031 1032 1033 1034
                          slave);

        g_signal_connect (slave->priv->session,
                          "user-verified",
1035
                          G_CALLBACK (on_session_user_verified),
1036 1037 1038 1039
                          slave);

        g_signal_connect (slave->priv->session,
                          "user-verification-error",
1040
                          G_CALLBACK (on_session_user_verification_error),
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
                          slave);

        g_signal_connect (slave->priv->session,
                          "session-started",
                          G_CALLBACK (on_session_started),
                          slave);
        g_signal_connect (slave->priv->session,
                          "session-exited",
                          G_CALLBACK (on_session_exited),
                          slave);
        g_signal_connect (slave->priv->session,
                          "session-died",
                          G_CALLBACK (on_session_died),
                          slave);
1055 1056 1057
}

static void
1058
on_relay_cancelled (DBusGProxy *proxy,
1059
                    gpointer    data)
1060
{
1061
        GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
1062

1063
        g_debug ("Relay cancelled");
1064

1065 1066 1067 1068
        if (slave->priv->session != NULL) {
                gdm_session_close (slave->priv->session);
                g_object_unref (slave->priv->session);
        }
1069

1070
        create_new_session (slave);
1071 1072
}

1073
static void
1074
session_relay_proxy_destroyed (GObject         *object,
1075
                               GdmProductSlave *slave)
1076
{
1077
        g_debug ("Session server relay destroyed");
1078

1079
        slave->priv->session_relay_proxy = NULL;
1080 1081 1082 1083 1084
}

static void
get_relay_address (GdmProductSlave *slave)
{
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
        GError  *error;
        char    *text;
        gboolean res;

        text = NULL;
        error = NULL;
        res = dbus_g_proxy_call (slave->priv->product_display_proxy,
                                 "GetRelayAddress",
                                 &error,
                                 G_TYPE_INVALID,
                                 G_TYPE_STRING, &text,
                                 G_TYPE_INVALID);
        if (! res) {
                g_warning ("Unable to get relay address: %s", error->message);
                g_error_free (error);
        } else {
                g_free (slave->priv->relay_address);
                slave->priv->relay_address = g_strdup (text);
                g_debug ("Got relay address: %s", slave->priv->relay_address);
        }

        g_free (text);
1107 1108 1109 1110 1111
}

static gboolean
connect_to_session_relay (GdmProductSlave *slave)
{
1112 1113
        DBusError       error;
        DBusConnection *connection;
1114

1115
        get_relay_address (slave);
1116

1117
        g_debug ("connecting to session relay address: %s", slave->priv->relay_address);
1118

1119 1120 1121
        dbus_error_init (&error);
        connection = dbus_connection_open_private (slave->priv->relay_address, &error);
        dbus_connection_setup_with_g_main (connection, NULL);
1122 1123

        slave->priv->session_relay_connection = dbus_connection_get_g_connection (connection);
1124
        if (slave->priv->session_relay_connection == NULL) {
1125 1126 1127
                if (dbus_error_is_set (&error)) {
                        g_warning ("error opening connection: %s", error.message);
                        dbus_error_free (&error);
1128
                } else {
1129 1130 1131
                        g_warning ("Unable to open connection");
                }
                exit (1);
1132 1133
        }

1134
        g_debug ("creating session server proxy for peer: %s", SERVER_DBUS_PATH);
1135
        slave->priv->session_relay_proxy = dbus_g_proxy_new_for_peer (slave->priv->session_relay_connection,
1136 1137 1138 1139 1140 1141 1142 1143
                                                                      SERVER_DBUS_PATH,
                                                                      SERVER_DBUS_INTERFACE);
        if (slave->priv->session_relay_proxy == NULL) {
                g_warning ("Unable to create proxy for peer");
                exit (1);
        }

        /* FIXME: not sure why introspection isn't working */
1144 1145 1146 1147
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "BeginVerification",
                                 G_TYPE_STRING,
                                 G_TYPE_INVALID);
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "AnswerQuery",
                                 G_TYPE_STRING,
                                 G_TYPE_INVALID);
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "SessionSelected",
                                 G_TYPE_STRING,
                                 G_TYPE_INVALID);
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "LanguageSelected",
                                 G_TYPE_STRING,
                                 G_TYPE_INVALID);
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "UserSelected",
                                 G_TYPE_STRING,
                                 G_TYPE_INVALID);
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "Open",
                                 G_TYPE_INVALID);
        dbus_g_proxy_add_signal (slave->priv->session_relay_proxy,
                                 "Cancelled",
                                 G_TYPE_INVALID);

1171 1172 1173 1174 1175
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "BeginVerification",
                                     G_CALLBACK (on_relay_begin_verification),
                                     slave,
                                     NULL);
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "AnswerQuery",
                                     G_CALLBACK (on_relay_answer),
                                     slave,
                                     NULL);
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "SessionSelected",
                                     G_CALLBACK (on_relay_session_selected),
                                     slave,
                                     NULL);
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "LanguageSelected",
                                     G_CALLBACK (on_relay_language_selected),
                                     slave,
                                     NULL);
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "UserSelected",
                                     G_CALLBACK (on_relay_user_selected),
                                     slave,
                                     NULL);
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "Open",
                                     G_CALLBACK (on_relay_open),
                                     slave,
                                     NULL);
        dbus_g_proxy_connect_signal (slave->priv->session_relay_proxy,
                                     "Cancelled",
                                     G_CALLBACK (on_relay_cancelled),
                                     slave,
                                     NULL);

        g_signal_connect (slave->priv->session_relay_proxy,
                          "destroy",
                          G_CALLBACK (session_relay_proxy_destroyed),
                          slave);

        return TRUE;
1213 1214
}

1215 1216 1217
static gboolean
gdm_product_slave_start (GdmSlave *slave)
{
1218 1219 1220 1221
        gboolean ret;
        gboolean res;
        GError  *error;
        char    *display_id;
1222

1223
        ret = FALSE;
1224

1225
        res = GDM_SLAVE_CLASS (gdm_product_slave_parent_class)->start (slave);
1226

1227 1228 1229
        g_object_get (slave,
                      "display-id", &display_id,
                      NULL);
1230

1231
        error = NULL;
1232 1233 1234 1235 1236 1237 1238 1239 1240
        GDM_PRODUCT_SLAVE (slave)->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
        if (GDM_PRODUCT_SLAVE (slave)->priv->connection == NULL) {
                if (error != NULL) {
                        g_critical ("error getting system bus: %s", error->message);
                        g_error_free (error);
                }
                exit (1);
        }

1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
        error = NULL;
        GDM_PRODUCT_SLAVE (slave)->priv->product_display_proxy = dbus_g_proxy_new_for_name_owner (GDM_PRODUCT_SLAVE (slave)->priv->connection,
                                                                                                  GDM_DBUS_NAME,
                                                                                                  display_id,
                                                                                                  GDM_DBUS_PRODUCT_DISPLAY_INTERFACE,
                                                                                                  &error);
        if (GDM_PRODUCT_SLAVE (slave)->priv->product_display_proxy == NULL) {
                if (error != NULL) {
                        g_warning ("Failed to create display proxy %s: %s", display_id, error->message);
                        g_error_free (error);
                } else {
                        g_warning ("Unable to create display proxy");
                }
                goto out;
        }
1256

1257
        create_new_session (GDM_PRODUCT_SLAVE (slave));
1258

1259
        connect_to_session_relay (GDM_PRODUCT_SLAVE (slave));
1260

1261
        ret = TRUE;
1262 1263

 out:
1264
        g_free (display_id);
1265

1266
        return ret;
1267 1268 1269 1270 1271
}

static gboolean
gdm_product_slave_stop (GdmSlave *slave)
{
1272
        gboolean res;
1273

1274
        g_debug ("Stopping product_slave");
1275

1276
        res = GDM_SLAVE_CLASS (gdm_product_slave_parent_class)->stop (slave);
1277

1278 1279 1280 1281 1282
        if (GDM_PRODUCT_SLAVE (slave)->priv->session != NULL) {
                gdm_session_close (GDM_PRODUCT_SLAVE (slave)->priv->session);
                g_object_unref (GDM_PRODUCT_SLAVE (slave)->priv->session);
                GDM_PRODUCT_SLAVE (slave)->priv->session = NULL;
        }
1283

1284 1285 1286 1287 1288
        if (GDM_PRODUCT_SLAVE (slave)->priv->server != NULL) {
                gdm_server_stop (GDM_PRODUCT_SLAVE (slave)->priv->server);
                g_object_unref (GDM_PRODUCT_SLAVE (slave)->priv->server);
                GDM_PRODUCT_SLAVE (slave)->priv->server = NULL;
        }
1289

1290 1291 1292
        if (GDM_PRODUCT_SLAVE (slave)->priv->product_display_proxy != NULL) {
                g_object_unref (GDM_PRODUCT_SLAVE (slave)->priv->product_display_proxy);
        }
1293

1294
        return TRUE;
1295 1296 1297 1298
}

static void
gdm_product_slave_set_property (GObject      *object,
1299 1300 1301
                                guint         prop_id,
                                const GValue *value,
                                GParamSpec   *pspec)
1302
{
1303
        GdmProductSlave *self;
1304

1305
        self = GDM_PRODUCT_SLAVE (object);
1306

1307 1308 1309 1310 1311
        switch (prop_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
1312 1313 1314 1315
}

static void
gdm_product_slave_get_property (GObject    *object,
1316 1317 1318
                                guint       prop_id,
                                GValue     *value,
                                GParamSpec *pspec)
1319
{
1320
        GdmProductSlave *self;
1321

1322
        self = GDM_PRODUCT_SLAVE (object);
1323

1324 1325 1326 1327 1328
        switch (prop_id) {
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
1329 1330 1331 1332
}

static GObject *
gdm_product_slave_constructor (GType                  type,
1333 1334
                               guint                  n_construct_properties,
                               GObjectConstructParam *construct_properties)
1335 1336 1337 1338 1339 1340 1341
{
        GdmProductSlave      *product_slave;
        GdmProductSlaveClass *klass;

        klass = GDM_PRODUCT_SLAVE_CLASS (g_type_class_peek (GDM_TYPE_PRODUCT_SLAVE));

        product_slave = GDM_PRODUCT_SLAVE (G_OBJECT_CLASS (gdm_product_slave_parent_class)->constructor (type,
1342 1343
                                                                                                         n_construct_properties,
                                                                                                         construct_properties));
1344 1345 1346 1347 1348 1349
        return G_OBJECT (product_slave);
}

static void
gdm_product_slave_class_init (GdmProductSlaveClass *klass)
{
1350 1351
        GObjectClass  *object_class = G_OBJECT_CLASS (klass);
        GdmSlaveClass *slave_class = GDM_SLAVE_CLASS (klass);
1352

1353 1354
        object_class->get_property = gdm_product_slave_get_property;
        object_class->set_property = gdm_product_slave_set_property;
1355
        object_class->constructor = gdm_product_slave_constructor;
1356
        object_class->finalize = gdm_product_slave_finalize;
1357

1358 1359
        slave_class->start = gdm_product_slave_start;
        slave_class->stop = gdm_product_slave_stop;
1360

1361
        g_type_class_add_private (klass, sizeof (GdmProductSlavePrivate));
1362

1363
        dbus_g_object_type_install_info (GDM_TYPE_PRODUCT_SLAVE, &dbus_glib_gdm_product_slave_object_info);
1364 1365 1366 1367 1368 1369
}

static void
gdm_product_slave_init (GdmProductSlave *product_slave)
{

1370
        product_slave->priv = GDM_PRODUCT_SLAVE_GET_PRIVATE (product_slave);
1371

1372
        product_slave->priv->pid = -1;
1373 1374 1375 1376 1377
}

static void
gdm_product_slave_finalize (GObject *object)
{
1378
        GdmProductSlave *slave;
1379

1380 1381
        g_return_if_fail (object != NULL);
        g_return_if_fail (GDM_IS_PRODUCT_SLAVE (object));
1382

1383
        slave = GDM_PRODUCT_SLAVE (object);
1384

1385
        g_return_if_fail (slave->priv != NULL);
1386

1387
        gdm_product_slave_stop (GDM_SLAVE (slave));
1388

1389
        G_OBJECT_CLASS (gdm_product_slave_parent_class)->finalize (object);
1390 1391 1392 1393 1394
}

GdmSlave *
gdm_product_slave_new (const char *id)
{
1395
        GObject *object;
1396

1397 1398 1399
        object = g_object_new (GDM_TYPE_PRODUCT_SLAVE,
                               "display-id", id,
                               NULL);
1400

1401
        return GDM_SLAVE (object);
1402
}