static gboolean do_console_raw_local_read(GObject *stream,
                                          gpointer opaque)
{
    GUnixInputStream *localStdin = G_UNIX_INPUT_STREAM(stream);
    GVirSandboxConsoleRaw *console = GVIR_SANDBOX_CONSOLE_RAW(opaque);
    GVirSandboxConsoleRawPrivate *priv = console->priv;
    GError *err = NULL;

    gssize ret = g_pollable_input_stream_read_nonblocking
        (G_POLLABLE_INPUT_STREAM(localStdin),
         priv->localToConsole + priv->localToConsoleOffset,
         priv->localToConsoleLength - priv->localToConsoleOffset,
         NULL, &err);
    if (ret < 0) {
        g_debug("Error from local read %s", err ? err->message : "");
        do_console_raw_close(console, err);
        g_error_free(err);
        goto cleanup;
    }

    if (ret == 0)
        priv->localEOF = TRUE;
    else if (priv->localToConsole[priv->localToConsoleOffset] ==
             CONTROL(gvir_sandbox_console_get_escape(GVIR_SANDBOX_CONSOLE(console)))) {
        do_console_raw_close(console, err);
        goto cleanup;
    }

    priv->localToConsoleOffset += ret;

    priv->localStdinSource = NULL;
    do_console_raw_update_events(console);
 cleanup:
    return FALSE;
}
static void gvir_sandbox_console_get_property(GObject *object,
                                              guint prop_id,
                                              GValue *value,
                                              GParamSpec *pspec)
{
    GVirSandboxConsole *console = GVIR_SANDBOX_CONSOLE(object);
    GVirSandboxConsolePrivate *priv = console->priv;

    switch (prop_id) {
    case PROP_CONNECTION:
        g_value_set_object(value, priv->connection);
        break;

    case PROP_DOMAIN:
        g_value_set_object(value, priv->domain);
        break;

    case PROP_DEVNAME:
        g_value_set_string(value, priv->devname);
        break;

    case PROP_ESCAPE:
        g_value_set_schar(value, priv->escape);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}
static void gvir_sandbox_console_raw_finalize(GObject *object)
{
    GVirSandboxConsoleRaw *console = GVIR_SANDBOX_CONSOLE_RAW(object);
    GVirSandboxConsoleRawPrivate *priv = console->priv;

    if (priv->attached)
        gvir_sandbox_console_detach(GVIR_SANDBOX_CONSOLE(console), NULL);

    /* All other private fields are free'd by the detach call */

    G_OBJECT_CLASS(gvir_sandbox_console_raw_parent_class)->finalize(object);
}
static void gvir_sandbox_console_finalize(GObject *object)
{
    GVirSandboxConsole *console = GVIR_SANDBOX_CONSOLE(object);
    GVirSandboxConsolePrivate *priv = console->priv;

    if (priv->domain)
        g_object_unref(priv->domain);
    if (priv->connection)
        g_object_unref(priv->connection);

    g_free(priv->devname);

    G_OBJECT_CLASS(gvir_sandbox_console_parent_class)->finalize(object);
}
static gboolean gvir_sandbox_console_open_remote(GVirSandboxConsoleRaw *console,
                                                 GError **error)
{
    GVirSandboxConsoleRawPrivate *priv = console->priv;
    GVirConnection *conn = NULL;
    GVirDomain *dom = NULL;
    gchar *devname = NULL;
    gboolean ret = FALSE;
    GVirConfigDomain *conf = NULL;
    GList *devices = NULL, *tmp;

    g_object_get(console,
                 "connection", &conn,
                 "domain", &dom,
                 "devname", &devname,
                 NULL);

    if (!gvir_sandbox_console_get_direct(GVIR_SANDBOX_CONSOLE(console))) {
        priv->console = gvir_connection_get_stream(conn, 0);

        if (!gvir_domain_open_console(dom, priv->console,
                                      devname, 0, error)) {
            g_object_unref(priv->console);
            priv->console = NULL;
            goto cleanup;
        }
    } else {
        const gchar *pty = NULL;

        if (!(conf = gvir_domain_get_config(dom, 0, error)))
            goto cleanup;

        if (!(devices = gvir_config_domain_get_devices(conf))) {
            g_set_error(error, GVIR_SANDBOX_CONSOLE_RAW_ERROR, 0, "%s",
                        _("No devices found for domain"));
            goto cleanup;
        }

        tmp = devices;
        while (tmp && !pty) {
            GVirConfigDomainDevice *dev = tmp->data;
            const gchar *alias = gvir_config_domain_device_get_alias(dev);

            if (alias && g_str_equal(alias, devname) &&
                GVIR_CONFIG_IS_DOMAIN_CHARDEV(dev)) {
                GVirConfigDomainChardev *cdev = GVIR_CONFIG_DOMAIN_CHARDEV(dev);
                GVirConfigDomainChardevSource *csrc =
                    gvir_config_domain_chardev_get_source(cdev);

                if (GVIR_CONFIG_IS_DOMAIN_CHARDEV_SOURCE_PTY(csrc)) {
                    GVirConfigDomainChardevSourcePty *csrcpty =
                        GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE_PTY(csrc);

                    pty = gvir_config_domain_chardev_source_pty_get_path(csrcpty);
                    break;
                }

            }

            tmp = tmp->next;
        }

        if (!pty) {
            g_set_error(error, GVIR_SANDBOX_CONSOLE_RAW_ERROR, 0,
                        _("No device %s found for domain"), devname);
            goto cleanup;
        }

        if ((priv->consolePty = open(pty, O_NOCTTY|O_RDWR)) < 0) {
            g_set_error(error, GVIR_SANDBOX_CONSOLE_RAW_ERROR, 0,
                        _("Unable to open console %s"), pty);
            goto cleanup;
        }

        priv->consoleInput = G_UNIX_INPUT_STREAM(g_unix_input_stream_new(priv->consolePty, FALSE));
        priv->consoleOutput = G_UNIX_OUTPUT_STREAM(g_unix_output_stream_new(priv->consolePty, FALSE));
    }

    ret = TRUE;

 cleanup:
    if (conf)
        g_object_unref(conf);
    if (conn)
        g_object_unref(conn);
    if (dom)
        g_object_unref(dom);
    g_free(devname);

    return ret;
}
static void do_console_raw_close(GVirSandboxConsoleRaw *console,
                                 GError *error)
{
    gvir_sandbox_console_detach(GVIR_SANDBOX_CONSOLE(console), NULL);
    g_signal_emit_by_name(console, "closed", error != NULL);
}