opj_image_t *pngtoimage(const char *read_idf, opj_cparameters_t * params)
{
    png_structp  png = NULL;
    png_infop    info = NULL;
    double gamma;
    int bit_depth, interlace_type, compression_type, filter_type;
    OPJ_UINT32 i;
    png_uint_32  width, height = 0U;
    int color_type;
    FILE *reader = NULL;
    OPJ_BYTE** rows = NULL;
    OPJ_INT32* row32s = NULL;
    /* j2k: */
    opj_image_t *image = NULL;
    opj_image_cmptparm_t cmptparm[4];
    OPJ_UINT32 nr_comp;
    OPJ_BYTE sigbuf[8];
    convert_XXx32s_C1R cvtXXTo32s = NULL;
    convert_32s_CXPX cvtCxToPx = NULL;
    OPJ_INT32* planes[4];

    if ((reader = fopen(read_idf, "rb")) == NULL) {
        fprintf(stderr, "pngtoimage: can not open %s\n", read_idf);
        return NULL;
    }

    if (fread(sigbuf, 1, MAGIC_SIZE, reader) != MAGIC_SIZE
            || memcmp(sigbuf, PNG_MAGIC, MAGIC_SIZE) != 0) {
        fprintf(stderr, "pngtoimage: %s is no valid PNG file\n", read_idf);
        goto fin;
    }

    if ((png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                      NULL, NULL, NULL)) == NULL) {
        goto fin;
    }
    if ((info = png_create_info_struct(png)) == NULL) {
        goto fin;
    }

    if (setjmp(png_jmpbuf(png))) {
        goto fin;
    }

    png_init_io(png, reader);
    png_set_sig_bytes(png, MAGIC_SIZE);

    png_read_info(png, info);

    if (png_get_IHDR(png, info, &width, &height,
                     &bit_depth, &color_type, &interlace_type,
                     &compression_type, &filter_type) == 0) {
        goto fin;
    }

    /* png_set_expand():
     * expand paletted images to RGB, expand grayscale images of
     * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
     * to alpha channels.
     */
    if (color_type == PNG_COLOR_TYPE_PALETTE) {
        png_set_expand(png);
    }

    if (png_get_valid(png, info, PNG_INFO_tRNS)) {
        png_set_expand(png);
    }
    /* We might wan't to expand background */
    /*
    if(png_get_valid(png, info, PNG_INFO_bKGD)) {
        png_color_16p bgnd;
        png_get_bKGD(png, info, &bgnd);
        png_set_background(png, bgnd, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
    }
    */

    if (!png_get_gAMA(png, info, &gamma)) {
        gamma = 1.0;
    }

    /* we're not displaying but converting, screen gamma == 1.0 */
    png_set_gamma(png, 1.0, gamma);

    png_read_update_info(png, info);

    color_type = png_get_color_type(png, info);

    switch (color_type) {
    case PNG_COLOR_TYPE_GRAY:
        nr_comp = 1;
        break;
    case PNG_COLOR_TYPE_GRAY_ALPHA:
        nr_comp = 2;
        break;
    case PNG_COLOR_TYPE_RGB:
        nr_comp = 3;
        break;
    case PNG_COLOR_TYPE_RGB_ALPHA:
        nr_comp = 4;
        break;
    default:
        fprintf(stderr, "pngtoimage: colortype %d is not supported\n", color_type);
        goto fin;
    }
    cvtCxToPx = convert_32s_CXPX_LUT[nr_comp];
    bit_depth = png_get_bit_depth(png, info);

    switch (bit_depth) {
    case 1:
    case 2:
    case 4:
    case 8:
        cvtXXTo32s = convert_XXu32s_C1R_LUT[bit_depth];
        break;
    case 16: /* 16 bpp is specific to PNG */
        cvtXXTo32s = convert_16u32s_C1R;
        break;
    default:
        fprintf(stderr, "pngtoimage: bit depth %d is not supported\n", bit_depth);
        goto fin;
    }

    rows = (OPJ_BYTE**)calloc(height + 1, sizeof(OPJ_BYTE*));
    if (rows == NULL) {
        fprintf(stderr, "pngtoimage: memory out\n");
        goto fin;
    }
    for (i = 0; i < height; ++i) {
        rows[i] = (OPJ_BYTE*)malloc(png_get_rowbytes(png, info));
        if (rows[i] == NULL) {
            fprintf(stderr, "pngtoimage: memory out\n");
            goto fin;
        }
    }
    png_read_image(png, rows);

    /* Create image */
    memset(cmptparm, 0, sizeof(cmptparm));
    for (i = 0; i < nr_comp; ++i) {
        cmptparm[i].prec = (OPJ_UINT32)bit_depth;
        /* bits_per_pixel: 8 or 16 */
        cmptparm[i].bpp = (OPJ_UINT32)bit_depth;
        cmptparm[i].sgnd = 0;
        cmptparm[i].dx = (OPJ_UINT32)params->subsampling_dx;
        cmptparm[i].dy = (OPJ_UINT32)params->subsampling_dy;
        cmptparm[i].w = (OPJ_UINT32)width;
        cmptparm[i].h = (OPJ_UINT32)height;
    }

    image = opj_image_create(nr_comp, &cmptparm[0],
                             (nr_comp > 2U) ? OPJ_CLRSPC_SRGB : OPJ_CLRSPC_GRAY);
    if (image == NULL) {
        goto fin;
    }
    image->x0 = (OPJ_UINT32)params->image_offset_x0;
    image->y0 = (OPJ_UINT32)params->image_offset_y0;
    image->x1 = (OPJ_UINT32)(image->x0 + (width  - 1) * (OPJ_UINT32)
                             params->subsampling_dx + 1);
    image->y1 = (OPJ_UINT32)(image->y0 + (height - 1) * (OPJ_UINT32)
                             params->subsampling_dy + 1);

    row32s = (OPJ_INT32 *)malloc((size_t)width * nr_comp * sizeof(OPJ_INT32));
    if (row32s == NULL) {
        goto fin;
    }

    /* Set alpha channel */
    image->comps[nr_comp - 1U].alpha = 1U - (nr_comp & 1U);

    for (i = 0; i < nr_comp; i++) {
        planes[i] = image->comps[i].data;
    }

    for (i = 0; i < height; ++i) {
        cvtXXTo32s(rows[i], row32s, (OPJ_SIZE_T)width * nr_comp);
        cvtCxToPx(row32s, planes, width);
        planes[0] += width;
        planes[1] += width;
        planes[2] += width;
        planes[3] += width;
    }
fin:
    if (rows) {
        for (i = 0; i < height; ++i)
            if (rows[i]) {
                free(rows[i]);
            }
        free(rows);
    }
    if (row32s) {
        free(row32s);
    }
    if (png) {
        png_destroy_read_struct(&png, &info, NULL);
    }

    fclose(reader);

    return image;

}/* pngtoimage() */


static void zynq_slcr_propagate_clocks(ZynqSLCRState *s)
{
    clock_propagate(s->uart0_ref_clk);
    clock_propagate(s->uart1_ref_clk);
}


static int __tty_fasync(int fd, struct file *filp, int on)
{
	struct tty_struct *tty = file_tty(filp);
	unsigned long flags;
	int retval = 0;

	if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
		goto out;

	retval = fasync_helper(fd, filp, on, &tty->fasync);
	if (retval <= 0)
		goto out;

	if (on) {
		enum pid_type type;
		struct pid *pid;

		spin_lock_irqsave(&tty->ctrl_lock, flags);
		if (tty->pgrp) {
			pid = tty->pgrp;
			type = PIDTYPE_PGID;
		} else {
			pid = task_pid(current);
			type = PIDTYPE_TGID;
		}
		get_pid(pid);
		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
		__f_setown(filp, pid, type, 0);
		put_pid(pid);
		retval = 0;
	}
out:
	return retval;
}


long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct tty_struct *tty = file_tty(file);
	struct tty_struct *real_tty;
	void __user *p = (void __user *)arg;
	int retval;
	struct tty_ldisc *ld;

	if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
		return -EINVAL;

	real_tty = tty_pair_get_tty(tty);

	/*
	 * Factor out some common prep work
	 */
	switch (cmd) {
	case TIOCSETD:
	case TIOCSBRK:
	case TIOCCBRK:
	case TCSBRK:
	case TCSBRKP:
		retval = tty_check_change(tty);
		if (retval)
			return retval;
		if (cmd != TIOCCBRK) {
			tty_wait_until_sent(tty, 0);
			if (signal_pending(current))
				return -EINTR;
		}
		break;
	}

	/*
	 *	Now do the stuff.
	 */
	switch (cmd) {
	case TIOCSTI:
		return tiocsti(tty, p);
	case TIOCGWINSZ:
		return tiocgwinsz(real_tty, p);
	case TIOCSWINSZ:
		return tiocswinsz(real_tty, p);
	case TIOCCONS:
		return real_tty != tty ? -EINVAL : tioccons(file);
	case TIOCEXCL:
		set_bit(TTY_EXCLUSIVE, &tty->flags);
		return 0;
	case TIOCNXCL:
		clear_bit(TTY_EXCLUSIVE, &tty->flags);
		return 0;
	case TIOCGEXCL:
	{
		int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
		return put_user(excl, (int __user *)p);
	}
	case TIOCGETD:
		return tiocgetd(tty, p);
	case TIOCSETD:
		return tiocsetd(tty, p);
	case TIOCVHANGUP:
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;
		tty_vhangup(tty);
		return 0;
	case TIOCGDEV:
	{
		unsigned int ret = new_encode_dev(tty_devnum(real_tty));
		return put_user(ret, (unsigned int __user *)p);
	}
	/*
	 * Break handling
	 */
	case TIOCSBRK:	/* Turn break on, unconditionally */
		if (tty->ops->break_ctl)
			return tty->ops->break_ctl(tty, -1);
		return 0;
	case TIOCCBRK:	/* Turn break off, unconditionally */
		if (tty->ops->break_ctl)
			return tty->ops->break_ctl(tty, 0);
		return 0;
	case TCSBRK:   /* SVID version: non-zero arg --> no break */
		/* non-zero arg means wait for all output data
		 * to be sent (performed above) but don't send break.
		 * This is used by the tcdrain() termios function.
		 */
		if (!arg)
			return send_break(tty, 250);
		return 0;
	case TCSBRKP:	/* support for POSIX tcsendbreak() */
		return send_break(tty, arg ? arg*100 : 250);

	case TIOCMGET:
		return tty_tiocmget(tty, p);
	case TIOCMSET:
	case TIOCMBIC:
	case TIOCMBIS:
		return tty_tiocmset(tty, cmd, p);
	case TIOCGICOUNT:
		return tty_tiocgicount(tty, p);
	case TCFLSH:
		switch (arg) {
		case TCIFLUSH:
		case TCIOFLUSH:
		/* flush tty buffer and allow ldisc to process ioctl */
			tty_buffer_flush(tty, NULL);
			break;
		}
		break;
	case TIOCSSERIAL:
		return tty_tiocsserial(tty, p);
	case TIOCGSERIAL:
		return tty_tiocgserial(tty, p);
	case TIOCGPTPEER:
		/* Special because the struct file is needed */
		return ptm_open_peer(file, tty, (int)arg);
	default:
		retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg);
		if (retval != -ENOIOCTLCMD)
			return retval;
	}
	if (tty->ops->ioctl) {
		retval = tty->ops->ioctl(tty, cmd, arg);
		if (retval != -ENOIOCTLCMD)
			return retval;
	}
	ld = tty_ldisc_ref_wait(tty);
	if (!ld)
		return hung_up_tty_ioctl(file, cmd, arg);
	retval = -EINVAL;
	if (ld->ops->ioctl) {
		retval = ld->ops->ioctl(tty, file, cmd, arg);
		if (retval == -ENOIOCTLCMD)
			retval = -ENOTTY;
	}
	tty_ldisc_deref(ld);
	return retval;
}


long tty_jobctrl_ioctl(struct tty_struct *tty, struct tty_struct *real_tty,
		       struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *p = (void __user *)arg;

	switch (cmd) {
	case TIOCNOTTY:
		if (current->signal->tty != tty)
			return -ENOTTY;
		no_tty();
		return 0;
	case TIOCSCTTY:
		return tiocsctty(real_tty, file, arg);
	case TIOCGPGRP:
		return tiocgpgrp(tty, real_tty, p);
	case TIOCSPGRP:
		return tiocspgrp(tty, real_tty, p);
	case TIOCGSID:
		return tiocgsid(tty, real_tty, p);
	}
	return -ENOIOCTLCMD;
}


static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
			loff_t *ppos)
{
	int i;
	struct inode *inode = file_inode(file);
	struct tty_struct *tty = file_tty(file);
	struct tty_ldisc *ld;

	if (tty_paranoia_check(tty, inode, "tty_read"))
		return -EIO;
	if (!tty || tty_io_error(tty))
		return -EIO;

	/* We want to wait for the line discipline to sort out in this
	   situation */
	ld = tty_ldisc_ref_wait(tty);
	if (!ld)
		return hung_up_tty_read(file, buf, count, ppos);
	if (ld->ops->read)
		i = ld->ops->read(tty, file, buf, count);
	else
		i = -EIO;
	tty_ldisc_deref(ld);

	if (i > 0)
		tty_update_time(&inode->i_atime);

	return i;
}


int __tty_check_change(struct tty_struct *tty, int sig)
{
	unsigned long flags;
	struct pid *pgrp, *tty_pgrp;
	int ret = 0;

	if (current->signal->tty != tty)
		return 0;

	rcu_read_lock();
	pgrp = task_pgrp(current);

	spin_lock_irqsave(&tty->ctrl_lock, flags);
	tty_pgrp = tty->pgrp;
	spin_unlock_irqrestore(&tty->ctrl_lock, flags);

	if (tty_pgrp && pgrp != tty_pgrp) {
		if (is_ignored(sig)) {
			if (sig == SIGTTIN)
				ret = -EIO;
		} else if (is_current_pgrp_orphaned())
			ret = -EIO;
		else {
			kill_pgrp(pgrp, sig, 1);
			set_thread_flag(TIF_SIGPENDING);
			ret = -ERESTARTSYS;
		}
	}
	rcu_read_unlock();

	if (!tty_pgrp)
		tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig);

	return ret;
}


static void free_tty_struct(struct tty_struct *tty)
{
	tty_ldisc_deinit(tty);
	put_device(tty->dev);
	kfree(tty->write_buf);
	tty->magic = 0xDEADDEAD;
	kfree(tty);
}


void tty_kclose(struct tty_struct *tty)
{
	/*
	 * Ask the line discipline code to release its structures
	 */
	tty_ldisc_release(tty);

	/* Wait for pending work before tty destruction commmences */
	tty_flush_works(tty);

	tty_debug_hangup(tty, "freeing structure\n");
	/*
	 * The release_tty function takes care of the details of clearing
	 * the slots and preserving the termios structure.
	 */
	mutex_lock(&tty_mutex);
	tty_port_set_kopened(tty->port, 0);
	release_tty(tty, tty->index);
	mutex_unlock(&tty_mutex);
}


static void tty_del_file(struct file *file)
{
	struct tty_file_private *priv = file->private_data;
	struct tty_struct *tty = priv->tty;

	spin_lock(&tty->files_lock);
	list_del(&priv->list);
	spin_unlock(&tty->files_lock);
	tty_free_file(file);
}


struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
{
	struct tty_struct *tty;

	tty = kzalloc(sizeof(*tty), GFP_KERNEL);
	if (!tty)
		return NULL;

	kref_init(&tty->kref);
	tty->magic = TTY_MAGIC;
	if (tty_ldisc_init(tty)) {
		kfree(tty);
		return NULL;
	}
	tty->session = NULL;
	tty->pgrp = NULL;
	mutex_init(&tty->legacy_mutex);
	mutex_init(&tty->throttle_mutex);
	init_rwsem(&tty->termios_rwsem);
	mutex_init(&tty->winsize_mutex);
	init_ldsem(&tty->ldisc_sem);
	init_waitqueue_head(&tty->write_wait);
	init_waitqueue_head(&tty->read_wait);
	INIT_WORK(&tty->hangup_work, do_tty_hangup);
	mutex_init(&tty->atomic_write_lock);
	spin_lock_init(&tty->ctrl_lock);
	spin_lock_init(&tty->flow_lock);
	spin_lock_init(&tty->files_lock);
	INIT_LIST_HEAD(&tty->tty_files);
	INIT_WORK(&tty->SAK_work, do_SAK_work);

	tty->driver = driver;
	tty->ops = driver->ops;
	tty->index = idx;
	tty_line_name(driver, idx, tty->name);
	tty->dev = tty_get_device(tty);

	return tty;
}


compare_str (gconstpointer  a,
             gconstpointer  b)
{
  return strcmp (*(const char **)a, *(const char **)b);
}


worker_interface_init (GdmDBusWorkerIface *interface)
{
        interface->handle_initialize = gdm_session_worker_handle_initialize;
        /* The next three are for backward compat only */
        interface->handle_setup = gdm_session_worker_handle_setup;
        interface->handle_setup_for_user = gdm_session_worker_handle_setup_for_user;
        interface->handle_setup_for_program = gdm_session_worker_handle_setup_for_program;
        interface->handle_authenticate = gdm_session_worker_handle_authenticate;
        interface->handle_authorize = gdm_session_worker_handle_authorize;
        interface->handle_establish_credentials = gdm_session_worker_handle_establish_credentials;
        interface->handle_open = gdm_session_worker_handle_open;
        interface->handle_set_language_name = gdm_session_worker_handle_set_language_name;
        interface->handle_set_session_name = gdm_session_worker_handle_set_session_name;
        interface->handle_set_session_display_mode = gdm_session_worker_handle_set_session_display_mode;
        interface->handle_set_environment_variable = gdm_session_worker_handle_set_environment_variable;
        interface->handle_start_program = gdm_session_worker_handle_start_program;
        interface->handle_start_reauthentication = gdm_session_worker_handle_start_reauthentication;
}


set_up_for_new_vt (GdmSessionWorker *worker)
{
        int initial_vt_fd;
        char vt_string[256], tty_string[256];
        int session_vt = 0;

        /* open the initial vt.  We need it for two scenarios:
         *
         * 1) display_is_initial is TRUE.  We need it directly.
         * 2) display_is_initial is FALSE. We need it to mark
         * the initial VT as "in use" so it doesn't get returned
         * by VT_OPENQRY
         * */
        g_snprintf (tty_string, sizeof (tty_string), "/dev/tty%d", GDM_INITIAL_VT);
        initial_vt_fd = open (tty_string, O_RDWR | O_NOCTTY);

        if (initial_vt_fd < 0) {
                g_debug ("GdmSessionWorker: couldn't open console of initial fd: %m");
                return FALSE;
        }

        if (worker->priv->display_is_initial) {
                session_vt = GDM_INITIAL_VT;
        } else {

                /* Typically VT_OPENQRY is called on /dev/tty0, but we already
                 * have /dev/tty1 open above, so might as well use it.
                 */
                if (ioctl (initial_vt_fd, VT_OPENQRY, &session_vt) < 0) {
                        g_debug ("GdmSessionWorker: couldn't open new VT: %m");
                        goto fail;
                }
        }

        worker->priv->session_vt = session_vt;

        g_assert (session_vt > 0);

        g_snprintf (vt_string, sizeof (vt_string), "%d", session_vt);

        /* Set the VTNR. This is used by logind to configure a session in
         * the logind-managed case, but it doesn't hurt to set it always.
         * When logind gains support for XDG_VTNR=auto, we can make the
         * OPENQRY and this whole path only used by the new VT code. */
        gdm_session_worker_set_environment_variable (worker,
                                                     "XDG_VTNR",
                                                     vt_string);

        if (worker->priv->display_is_initial) {
             worker->priv->session_tty_fd = initial_vt_fd;
        } else {
             g_snprintf (tty_string, sizeof (tty_string), "/dev/tty%d", session_vt);
             worker->priv->session_tty_fd = open (tty_string, O_RDWR | O_NOCTTY);
             close (initial_vt_fd);
        }

        pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string);

        return TRUE;

fail:
        close (initial_vt_fd);
        return FALSE;
}


wait_until_dbus_signal_emission_to_manager_finishes (GdmSessionWorker *worker)
{
        g_autoptr (GdmDBusPeer) peer_proxy = NULL;
        g_autoptr (GError) error = NULL;
        gboolean pinged;

        peer_proxy = gdm_dbus_peer_proxy_new_sync (worker->priv->connection,
                                                   G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
                                                   NULL,
                                                   "/org/freedesktop/DBus",
                                                   NULL,
                                                   &error);

        if (peer_proxy == NULL) {
                g_debug ("GdmSessionWorker: could not create peer proxy to daemon: %s",
                         error->message);
                return;
        }

        pinged = gdm_dbus_peer_call_ping_sync (peer_proxy, NULL, &error);

        if (!pinged) {
                g_debug ("GdmSessionWorker: could not ping daemon: %s",
                         error->message);
                return;
        }
}


gdm_session_worker_get_environment_variable (GdmSessionWorker *worker,
                                             const char       *key)
{
        return g_strdup (pam_getenv (worker->priv->pam_handle, key));
}


reauthentication_request_free (ReauthenticationRequest *request)
{

        g_signal_handlers_disconnect_by_func (request->session,
                                              G_CALLBACK (on_reauthentication_client_connected),
                                              request);
        g_signal_handlers_disconnect_by_func (request->session,
                                              G_CALLBACK (on_reauthentication_client_disconnected),
                                              request);
        g_signal_handlers_disconnect_by_func (request->session,
                                              G_CALLBACK (on_reauthentication_cancelled),
                                              request);
        g_signal_handlers_disconnect_by_func (request->session,
                                              G_CALLBACK (on_reauthentication_conversation_started),
                                              request);
        g_signal_handlers_disconnect_by_func (request->session,
                                              G_CALLBACK (on_reauthentication_conversation_stopped),
                                              request);
        g_signal_handlers_disconnect_by_func (request->session,
                                              G_CALLBACK (on_reauthentication_verification_complete),
                                              request);
        g_clear_object (&request->session);
        g_slice_free (ReauthenticationRequest, request);
}


jump_to_vt (GdmSessionWorker  *worker,
            int                vt_number)
{
        int fd;
        int active_vt_tty_fd;
        int active_vt = -1;
        struct vt_stat vt_state = { 0 };

        g_debug ("GdmSessionWorker: jumping to VT %d", vt_number);
        active_vt_tty_fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);

        if (worker->priv->session_tty_fd != -1) {
                static const char *clear_screen_escape_sequence = "\33[H\33[2J";

                /* let's make sure the new VT is clear */
                write (worker->priv->session_tty_fd,
                       clear_screen_escape_sequence,
                       sizeof (clear_screen_escape_sequence));

                fd = worker->priv->session_tty_fd;

                g_debug ("GdmSessionWorker: first setting graphics mode to prevent flicker");
                if (ioctl (fd, KDSETMODE, KD_GRAPHICS) < 0) {
                        g_debug ("GdmSessionWorker: couldn't set graphics mode: %m");
                }

                /* It's possible that the current VT was left in a broken
                 * combination of states (KD_GRAPHICS with VT_AUTO), that
                 * can't be switched away from.  This call makes sure things
                 * are set in a way that VT_ACTIVATE should work and
                 * VT_WAITACTIVE shouldn't hang.
                 */
                fix_terminal_vt_mode (worker, active_vt_tty_fd);
        } else {
                fd = active_vt_tty_fd;
        }

        handle_terminal_vt_switches (worker, fd);

        if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
                g_debug ("GdmSessionWorker: couldn't get current VT: %m");
        } else {
                active_vt = vt_state.v_active;
        }

        if (active_vt != vt_number) {
                if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) {
                        g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m",
                                 vt_number);
                } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) {
                        g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m",
                                 vt_number);
                }
        }

        close (active_vt_tty_fd);
}


do_accredit (GdmSessionWorker *worker)
{
        GError  *error;
        gboolean res;

        /* get kerberos tickets, setup group lists, etc
         */
        error = NULL;
        res = gdm_session_worker_accredit_user (worker, &error);

        if (res) {
                gdm_dbus_worker_complete_establish_credentials (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
        } else {
                g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
        }
        worker->priv->pending_invocation = NULL;
}


gdm_session_worker_handle_setup (GdmDBusWorker         *object,
                                 GDBusMethodInvocation *invocation,
                                 const char            *service,
                                 const char            *x11_display_name,
                                 const char            *x11_authority_file,
                                 const char            *console,
                                 const char            *seat_id,
                                 const char            *hostname,
                                 gboolean               display_is_local,
                                 gboolean               display_is_initial)
{
        GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
        validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);

        worker->priv->service = g_strdup (service);
        worker->priv->x11_display_name = g_strdup (x11_display_name);
        worker->priv->x11_authority_file = g_strdup (x11_authority_file);
        worker->priv->display_device = g_strdup (console);
        worker->priv->display_seat_id = g_strdup (seat_id);
        worker->priv->hostname = g_strdup (hostname);
        worker->priv->display_is_local = display_is_local;
        worker->priv->display_is_initial = display_is_initial;
        worker->priv->username = NULL;

        g_signal_connect_swapped (worker->priv->user_settings,
                                  "notify::language-name",
                                  G_CALLBACK (on_saved_language_name_read),
                                  worker);

        g_signal_connect_swapped (worker->priv->user_settings,
                                  "notify::session-name",
                                  G_CALLBACK (on_saved_session_name_read),
                                  worker);
        return TRUE;
}


validate_and_queue_state_change (GdmSessionWorker      *worker,
                                 GDBusMethodInvocation *invocation,
                                 int                    new_state)
{
        if (validate_state_change (worker, invocation, new_state)) {
                worker->priv->pending_invocation = invocation;
                queue_state_change (worker);
        }
}


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;
}


_open_program_session_log (const char *filename)
{
        int   fd;

        rotate_logs (filename, MAX_LOGS);

        fd = g_open (filename, O_WRONLY | O_APPEND | O_CREAT, 0600);

        if (fd < 0) {
                char *temp_name;

                temp_name = g_strdup_printf ("%s.XXXXXXXX", filename);

                fd = g_mkstemp (temp_name);

                if (fd < 0) {
                        g_free (temp_name);
                        goto out;
                }

                g_warning ("session log '%s' is not appendable, logging session to '%s' instead.\n", filename,
                           temp_name);
                g_free (temp_name);
        } else {
                if (ftruncate (fd, 0) < 0) {
                        close (fd);
                        fd = -1;
                        goto out;
                }
        }

        if (fchmod (fd, 0644) < 0) {
                close (fd);
                fd = -1;
                goto out;
        }

out:
        if (fd < 0) {
                g_warning ("unable to log program session");
                fd = g_open ("/dev/null", O_RDWR);
        }

        return fd;
}


run_script (GdmSessionWorker *worker,
            const char       *dir)
{
        /* scripts are for non-program sessions only */
        if (worker->priv->is_program_session) {
                return TRUE;
        }

        return gdm_run_script (dir,
                               worker->priv->username,
                               worker->priv->x11_display_name,
                               worker->priv->display_is_local? NULL : worker->priv->hostname,
                               worker->priv->x11_authority_file);
}


gdm_session_worker_handle_set_session_display_mode (GdmDBusWorker         *object,
                                                    GDBusMethodInvocation *invocation,
                                                    const char            *str)
{
        GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
        g_debug ("GdmSessionWorker: session display mode set to %s", str);
        worker->priv->display_mode = gdm_session_display_mode_from_string (str);
        gdm_dbus_worker_complete_set_session_display_mode (object, invocation);
        return TRUE;
}


do_start_session (GdmSessionWorker *worker)
{
        GError  *error;
        gboolean res;

        error = NULL;
        res = gdm_session_worker_start_session (worker, &error);
        if (res) {
                gdm_dbus_worker_complete_start_program (GDM_DBUS_WORKER (worker),
                                                        worker->priv->pending_invocation,
                                                        worker->priv->child_pid);
        } else {
                g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
        }
        worker->priv->pending_invocation = NULL;
}


on_reauthentication_verification_complete (GdmSession              *session,
                                           const char              *service_name,
                                           ReauthenticationRequest *request)
{
        GdmSessionWorker *worker;

        worker = request->worker;

        g_debug ("GdmSessionWorker: pid %d reauthenticated user %d with service '%s'",
                 (int) request->pid_of_caller,
                 (int) request->uid_of_caller,
                 service_name);
        gdm_session_reset (session);

        gdm_dbus_worker_emit_reauthenticated (GDM_DBUS_WORKER (worker), service_name);
}


gdm_session_worker_load_env_dir (GdmSessionWorker *worker,
                                 GFile *dir)
{
        GFileInfo *info = NULL;
        GFileEnumerator *enumerator = NULL;
        GPtrArray *names = NULL;
        GFile *file;
        const gchar *name;
        int i;

        enumerator = g_file_enumerate_children (dir,
                                                G_FILE_ATTRIBUTE_STANDARD_TYPE","
                                                G_FILE_ATTRIBUTE_STANDARD_NAME","
                                                G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN","
                                                G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
                                                G_FILE_QUERY_INFO_NONE,
                                                NULL, NULL);
        if (!enumerator) {
                goto out;
        }

        names = g_ptr_array_new_with_free_func (g_free);
        while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
                if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR &&
                    !g_file_info_get_is_hidden (info) &&
                    g_str_has_suffix (g_file_info_get_name (info), ".env"))
                  g_ptr_array_add (names, g_strdup (g_file_info_get_name (info)));

                g_clear_object (&info);
        }

        g_ptr_array_sort (names, compare_str);

        for (i = 0; i < names->len; i++) {
                name = g_ptr_array_index (names, i);
                file = g_file_get_child (dir, name);
                load_env_file (worker, file);
                g_object_unref (file);
        }

 out:
        g_clear_pointer (&names, g_ptr_array_unref);
        g_clear_object (&enumerator);
}