gdm_session_worker_start_session (GdmSessionWorker  *worker,
                                  GError           **error)
{
        struct passwd *passwd_entry;
        pid_t session_pid;
        int   error_code;

        gdm_get_pwent_for_name (worker->priv->username, &passwd_entry);
        if (worker->priv->is_program_session) {
                g_debug ("GdmSessionWorker: opening session for program '%s'",
                         worker->priv->arguments[0]);
        } else {
                g_debug ("GdmSessionWorker: opening user session with program '%s'",
                         worker->priv->arguments[0]);
        }

        error_code = PAM_SUCCESS;

        /* If we're in new vt mode, jump to the new vt now. There's no need to jump for
         * the other two modes: in the logind case, the session will activate itself when
         * ready, and in the reuse server case, we're already on the correct VT. */
        if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
                if (worker->priv->display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) {
                        jump_to_vt (worker, worker->priv->session_vt);
                }
        }

        if (!worker->priv->is_program_session && !run_script (worker, GDMCONFDIR "/PostLogin")) {
                g_set_error (error,
                             GDM_SESSION_WORKER_ERROR,
                             GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
                             "Failed to execute PostLogin script");
                error_code = PAM_ABORT;
                goto out;
        }

        if (!worker->priv->is_program_session && !run_script (worker, GDMCONFDIR "/PreSession")) {
                g_set_error (error,
                             GDM_SESSION_WORKER_ERROR,
                             GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
                             "Failed to execute PreSession script");
                error_code = PAM_ABORT;
                goto out;
        }

        session_pid = fork ();

        if (session_pid < 0) {
                g_set_error (error,
                             GDM_SESSION_WORKER_ERROR,
                             GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
                             "%s", g_strerror (errno));
                error_code = PAM_ABORT;
                goto out;
        }

        if (session_pid == 0) {
                const char * const * environment;
                char  *home_dir;
                int    stdin_fd = -1, stdout_fd = -1, stderr_fd = -1;
                gboolean has_journald = FALSE, needs_controlling_terminal = FALSE;
                /* Leak the TTY into the session as stdin so that it stays open
                 * without any races. */
                if (worker->priv->session_tty_fd > 0) {
                        dup2 (worker->priv->session_tty_fd, STDIN_FILENO);
                        close (worker->priv->session_tty_fd);
                        worker->priv->session_tty_fd = -1;
                        needs_controlling_terminal = TRUE;
                } else {
                        stdin_fd = open ("/dev/null", O_RDWR);
                        dup2 (stdin_fd, STDIN_FILENO);
                        close (stdin_fd);
                }

#ifdef ENABLE_SYSTEMD_JOURNAL
                has_journald = sd_booted() > 0;
#endif
                if (!has_journald && worker->priv->is_program_session) {
                        stdout_fd = _open_program_session_log (worker->priv->log_file);
                        stderr_fd = dup (stdout_fd);
                }

                if (setsid () < 0) {
                        g_debug ("GdmSessionWorker: could not set pid '%u' as leader of new session and process group: %s",
                                 (guint) getpid (), g_strerror (errno));
                        _exit (EXIT_FAILURE);
                }

                /* Take control of the tty
                 */
                if (needs_controlling_terminal) {
                        if (ioctl (STDIN_FILENO, TIOCSCTTY, 0) < 0) {
                                g_debug ("GdmSessionWorker: could not take control of tty: %m");
                        }
                }

#ifdef HAVE_LOGINCAP
                if (setusercontext (NULL, passwd_entry, passwd_entry->pw_uid, LOGIN_SETALL) < 0) {
                        g_debug ("GdmSessionWorker: setusercontext() failed for user %s: %s",
                                 passwd_entry->pw_name, g_strerror (errno));
                        _exit (EXIT_FAILURE);
                }
#else
                if (setuid (worker->priv->uid) < 0) {
                        g_debug ("GdmSessionWorker: could not reset uid: %s", g_strerror (errno));
                        _exit (EXIT_FAILURE);
                }
#endif

                if (!worker->priv->is_program_session) {
                        gdm_session_worker_load_env_d (worker);
                }

                environment = gdm_session_worker_get_environment (worker);

                g_assert (geteuid () == getuid ());

                home_dir = gdm_session_worker_get_environment_variable (worker, "HOME");
                if ((home_dir == NULL) || g_chdir (home_dir) < 0) {
                        g_chdir ("/");
                }

#ifdef ENABLE_SYSTEMD_JOURNAL
                if (has_journald) {
                        stdout_fd = sd_journal_stream_fd (worker->priv->arguments[0], LOG_INFO, FALSE);
                        stderr_fd = sd_journal_stream_fd (worker->priv->arguments[0], LOG_WARNING, FALSE);

                        /* Unset the CLOEXEC flags, because sd_journal_stream_fd
                         * gives it to us by default.
                         */
                        gdm_clear_close_on_exec_flag (stdout_fd);
                        gdm_clear_close_on_exec_flag (stderr_fd);
                }
#endif
                if (!has_journald && !worker->priv->is_program_session) {
                        if (home_dir != NULL && home_dir[0] != '\0') {
                                char *cache_dir;
                                char *log_dir;

                                cache_dir = gdm_session_worker_get_environment_variable (worker, "XDG_CACHE_HOME");
                                if (cache_dir == NULL || cache_dir[0] == '\0') {
                                        cache_dir = g_build_filename (home_dir, ".cache", NULL);
                                }

                                log_dir = g_build_filename (cache_dir, "gdm", NULL);
                                g_free (cache_dir);

                                if (g_mkdir_with_parents (log_dir, S_IRWXU) == 0) {
                                        stdout_fd = _open_user_session_log (log_dir);
                                        stderr_fd = dup (stdout_fd);
                                } else {
                                        stdout_fd = open ("/dev/null", O_RDWR);
                                        stderr_fd = dup (stdout_fd);
                                }
                                g_free (log_dir);
                        } else {
                                stdout_fd = open ("/dev/null", O_RDWR);
                                stderr_fd = dup (stdout_fd);
                        }
                }
                g_free (home_dir);

                if (stdout_fd != -1) {
                        dup2 (stdout_fd, STDOUT_FILENO);
                        close (stdout_fd);
                }

                if (stderr_fd != -1) {
                        dup2 (stderr_fd, STDERR_FILENO);
                        close (stderr_fd);
                }

                gdm_log_shutdown ();

                /*
                 * Reset SIGPIPE to default so that any process in the user
                 * session get the default SIGPIPE behavior instead of ignoring
                 * SIGPIPE.
                 */
                signal (SIGPIPE, SIG_DFL);

                gdm_session_execute (worker->priv->arguments[0],
                                     worker->priv->arguments,
                                     (char **)
                                     environment,
                                     TRUE);

                gdm_log_init ();
                g_debug ("GdmSessionWorker: child '%s' could not be started: %s",
                         worker->priv->arguments[0],
                         g_strerror (errno));

                _exit (EXIT_FAILURE);
        }

        if (worker->priv->session_tty_fd > 0) {
                close (worker->priv->session_tty_fd);
                worker->priv->session_tty_fd = -1;
        }

        /* If we end up execing again, make sure we don't use the executable context set up
         * by pam_selinux durin pam_open_session
         */
#ifdef HAVE_SELINUX
        setexeccon (NULL);
#endif

        worker->priv->child_pid = session_pid;

        g_debug ("GdmSessionWorker: session opened creating reply...");
        g_assert (sizeof (GPid) <= sizeof (int));

        g_debug ("GdmSessionWorker: state SESSION_STARTED");
        gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SESSION_STARTED);

        gdm_session_worker_watch_child (worker);

 out:
        if (error_code != PAM_SUCCESS) {
                gdm_session_worker_uninitialize_pam (worker, error_code);
                return FALSE;
        }

        return TRUE;
}