On Fri, Jul 05, 2013 at 01:48:41PM +0100, Daniel P. Berrange wrote:
> On Fri, Jul 05, 2013 at 07:16:03PM +0700, Alexey Dokuchaev wrote:
> > [...]
> > What I need to do, is first to reestablish new connection to newly run
> > devd, and then g_io_channel_shutdown(...)/g_object_unref(oldsocket).
> 
> IMHO the order you have here ought to be ok.

My thinking the reason for calling devd_init() first, then cleaning up is
because it takes sertain time to devd to reappear and start serving again.
In indication of this is a series of repeating failing connect() calls if
perror()'ing them...  But I might be wrong.

> Can you send the code you currently have, so I can see the bigger
> picture of how you now have it written.

Sure, attached is the current version of entangle-device-manager.c.

./danfe
/*
 *  Entangle: Tethered Camera Control & Capture
 *
 *  Copyright (C) 2009-2012 Daniel P. Berrange
 *
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#if defined(__FreeBSD__)
#include <gio/gio.h>
#include <gio/gunixsocketaddress.h>
#include <glib.h>
//#include <unistd.h>
#else
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#ifdef G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#endif
#endif
#include <string.h>
#include <stdio.h>

#include "entangle-debug.h"
#include "entangle-device-manager.h"

#define ENTANGLE_DEVICE_MANAGER_GET_PRIVATE(obj)                        \
    (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_DEVICE_MANAGER, 
EntangleDeviceManagerPrivate))

struct _EntangleDeviceManagerPrivate {
#if defined(__linux__)
    GUdevClient
#elif defined(__FreeBSD__)
    GSocket
#endif
    *ctx;
};

G_DEFINE_TYPE(EntangleDeviceManager, entangle_device_manager, G_TYPE_OBJECT);


static void entangle_device_manager_finalize (GObject *object)
{
    EntangleDeviceManager *manager = ENTANGLE_DEVICE_MANAGER(object);
    EntangleDeviceManagerPrivate *priv = manager->priv;
    ENTANGLE_DEBUG("Finalize manager");

    if (priv->ctx)
        g_object_unref(priv->ctx);

    G_OBJECT_CLASS (entangle_device_manager_parent_class)->finalize (object);
}


static void entangle_device_manager_class_init(EntangleDeviceManagerClass 
*klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->finalize = entangle_device_manager_finalize;

    g_signal_new("device-added",
                 G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_FIRST,
                 G_STRUCT_OFFSET(EntangleDeviceManagerClass, device_added),
                 NULL, NULL,
                 g_cclosure_marshal_VOID__STRING,
                 G_TYPE_NONE,
                 1,
                 G_TYPE_STRING);

    g_signal_new("device-removed",
                 G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_FIRST,
                 G_STRUCT_OFFSET(EntangleDeviceManagerClass, device_removed),
                 NULL, NULL,
                 g_cclosure_marshal_VOID__STRING,
                 G_TYPE_NONE,
                 1,
                 G_TYPE_STRING);


    g_type_class_add_private(klass, sizeof(EntangleDeviceManagerPrivate));
}


#if defined(__linux__)
static void do_udev_event(GUdevClient *client G_GNUC_UNUSED,
                          const char *action,
                          GUdevDevice *dev,
                          gpointer opaque)
{
    EntangleDeviceManager *manager = opaque;
    const gchar *sysfs;
    const gchar *usbbus, *usbdev;
    const gchar *devtype;
    gchar *port;

    if (strcmp(action, "add") != 0 &&
        strcmp(action, "remove") != 0)
        return;

    devtype = g_udev_device_get_devtype(dev);
    if ((devtype == NULL) ||
        strcmp(devtype, "usb_device") != 0)
        return;

    sysfs = g_udev_device_get_sysfs_path(dev);

    usbbus = g_udev_device_get_property(dev, "BUSNUM");
    usbdev = g_udev_device_get_property(dev, "DEVNUM");

    if (sysfs == NULL ||
        usbbus == NULL ||
        usbdev == NULL)
        return;

    port = g_strdup_printf("usb:%s,%s", usbbus, usbdev);

    ENTANGLE_DEBUG("%s device '%s' '%s'", action, sysfs, port);

    if (strcmp(action, "add") == 0) {
        g_signal_emit_by_name(manager, "device-added", port);
    } else {
        g_signal_emit_by_name(manager, "device-removed", port);
    }
    g_free(port);
}
#elif defined(__FreeBSD__)
static void devd_init(EntangleDeviceManager *manager);

static gboolean do_devd_event(GIOChannel *source,
                              GIOCondition condition,
                              gpointer user_data)
{
    EntangleDeviceManager *manager = user_data;
    char *event, *cutoff;
    gsize end;
    GIOStatus status;

    status = g_io_channel_read_line(source, &event, NULL, &end, NULL);

    switch (status) {
    case G_IO_STATUS_NORMAL:
        event[end] = '\0';
        if (strncmp(event + 1, "ugen", 4))
            break;
        if (!(cutoff = strchr(event + 5, ' ')))
            break;
        *cutoff = '\0';
        if (*event == '+') {
            g_signal_emit_by_name(manager, "device-added", event + 1);
        } else if (*event == '-') {
            g_signal_emit_by_name(manager, "device-removed", event + 1);
        }
        g_free(event);
        break;
    case G_IO_STATUS_EOF:
    case G_IO_STATUS_AGAIN:
        /*
         * Apparently, devd(8) was reinited (perhaps restarted?).
         * Teardown previous connection and allocate new channel.
         */
        if (manager->priv->ctx) {
            g_io_channel_shutdown(source, FALSE, NULL);
            g_object_unref(manager->priv->ctx);
        }
        devd_init(manager);
        return FALSE;
    case G_IO_STATUS_ERROR:
    default:
        /* XXX: what shall we do? */
        break;
    }
    return TRUE;
}


static void devd_init(EntangleDeviceManager *manager)
{
    EntangleDeviceManagerPrivate *priv = manager->priv;
    GSocketAddress *addr;
    GError *err = NULL;

    priv->ctx = g_socket_new(G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, 0, 
&err);
    if (priv->ctx == NULL) {
        ENTANGLE_DEBUG("g_socket_new: %s\n", err->message);
        g_error_free(err);
        return;
    }

    addr = g_unix_socket_address_new("/var/run/devd.pipe");

    if (g_socket_connect(priv->ctx, addr, NULL, &err) == TRUE) {
        GIOChannel *channel = g_io_channel_unix_new(g_socket_get_fd(priv->ctx));
        g_io_add_watch(channel, G_IO_IN, do_devd_event, manager);
        /*
         * We can reduce the refcount here so that when the watch is
         * removed, the channel will be destroyed.
         */
        g_io_channel_unref(channel);
    } else {
        ENTANGLE_DEBUG("g_socket_connect: %s\n", err->message);
        g_error_free(err);
        g_object_unref(priv->ctx);
        priv->ctx = NULL;
    }
}
#endif


EntangleDeviceManager *entangle_device_manager_new(void)
{
    return ENTANGLE_DEVICE_MANAGER(g_object_new(ENTANGLE_TYPE_DEVICE_MANAGER, 
NULL));
}


static void entangle_device_manager_init_devices(EntangleDeviceManager *manager)
{
    EntangleDeviceManagerPrivate *priv = manager->priv;
    GList *devs, *tmp;

#if defined(__linux__)
    const gchar *const subsys[] = {
        "usb/usb_device", NULL,
    };

    ENTANGLE_DEBUG("Init udev");

    priv->ctx = g_udev_client_new(subsys);

    g_signal_connect(priv->ctx, "uevent", G_CALLBACK(do_udev_event), manager);

    devs = g_udev_client_query_by_subsystem(priv->ctx, "usb");

    tmp = devs;
    while (tmp) {
        GUdevDevice *dev = tmp->data;

        do_udev_event(priv->ctx, "add", dev, manager);

        g_object_unref(dev);
        tmp = tmp->next;
    }

    g_list_free(devs);
#elif defined(__FreeBSD__)

    ENTANGLE_DEBUG("Init devd");

    devd_init(manager);
#endif
}


static void entangle_device_manager_init(EntangleDeviceManager *manager)
{
    manager->priv = ENTANGLE_DEVICE_MANAGER_GET_PRIVATE(manager);

    entangle_device_manager_init_devices(manager);
}


/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  indent-tabs-mode: nil
 *  tab-width: 8
 * End:
 */
_______________________________________________
Entangle-devel mailing list
Entangle-devel@gna.org
https://mail.gna.org/listinfo/entangle-devel

Reply via email to