virtual-device-listener: Add a device socket handler class

Instead of repeating the same code in both the virtual-image and the
virtual-device drivers, implement a class to handle the socket listening
an data reading.

Co-authored-by: Marco Trevisan (Treviño) <mail@3v1n0.net>
This commit is contained in:
Benjamin Berg 2021-01-05 15:54:12 +01:00
parent 5df14206d8
commit 253750ec08
4 changed files with 524 additions and 187 deletions

View file

@ -0,0 +1,355 @@
/*
* Socket utilities for "simple" device debugging
*
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "virtual_device_connection"
#include "fpi-log.h"
#include <glib/gstdio.h>
#include <gio/gunixsocketaddress.h>
#include "virtual-device-private.h"
struct _FpDeviceVirtualListener
{
GSocketListener parent_instance;
GSocketConnection *connection;
GCancellable *cancellable;
guint cancellable_id;
FpDeviceVirtualListenerConnectionCb ready_cb;
gpointer ready_cb_data;
gint socket_fd;
gint client_fd;
};
G_DEFINE_TYPE (FpDeviceVirtualListener, fp_device_virtual_listener, G_TYPE_SOCKET_LISTENER)
static void start_listen (FpDeviceVirtualListener *self);
FpDeviceVirtualListener *
fp_device_virtual_listener_new (void)
{
return g_object_new (fp_device_virtual_listener_get_type (), NULL);
}
static void
fp_device_virtual_listener_dispose (GObject *object)
{
FpDeviceVirtualListener *self = FP_DEVICE_VIRTUAL_LISTENER (object);
if (self->cancellable_id)
{
g_cancellable_disconnect (self->cancellable, self->cancellable_id);
self->cancellable_id = 0;
}
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
g_clear_object (&self->connection);
self->ready_cb = NULL;
G_OBJECT_CLASS (fp_device_virtual_listener_parent_class)->dispose (object);
}
static void
fp_device_virtual_listener_class_init (FpDeviceVirtualListenerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = fp_device_virtual_listener_dispose;
}
static void
fp_device_virtual_listener_init (FpDeviceVirtualListener *self)
{
}
static void
new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GError) error = NULL;
FpDeviceVirtualListener *self = user_data;
GSocketConnection *connection;
connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (source_object),
res,
NULL,
&error);
if (!connection)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
g_warning ("Error accepting a new connection: %s", error->message);
start_listen (self);
return;
}
/* Always allow further connections.
* If we get a new one, we generally just close the old connection. */
start_listen (self);
if (self->connection)
{
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
g_clear_object (&self->connection);
}
self->connection = connection;
fp_dbg ("Got a new connection!");
self->ready_cb (self, self->ready_cb_data);
}
static void
start_listen (FpDeviceVirtualListener *self)
{
g_socket_listener_accept_async (G_SOCKET_LISTENER (self),
self->cancellable,
new_connection_cb,
self);
}
static void
on_cancelled (GCancellable *cancellable,
FpDeviceVirtualListener *self)
{
fp_device_virtual_listener_connection_close (self);
g_socket_listener_close (G_SOCKET_LISTENER (self));
g_clear_object (&self->cancellable);
self->ready_cb = NULL;
}
gboolean
fp_device_virtual_listener_start (FpDeviceVirtualListener *self,
const char *address,
GCancellable *cancellable,
FpDeviceVirtualListenerConnectionCb cb,
gpointer user_data,
GError **error)
{
g_autoptr(GSocketAddress) addr = NULL;
G_DEBUG_HERE ();
g_return_val_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self), FALSE);
g_return_val_if_fail (cb != NULL, FALSE);
g_return_val_if_fail (self->ready_cb == NULL, FALSE);
self->client_fd = -1;
g_socket_listener_set_backlog (G_SOCKET_LISTENER (self), 1);
/* Remove any left over socket. */
g_unlink (address);
addr = g_unix_socket_address_new (address);
if (!g_socket_listener_add_address (G_SOCKET_LISTENER (self),
addr,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
NULL,
NULL,
error))
{
g_warning ("Could not listen on unix socket: %s", (*error)->message);
return FALSE;
}
self->ready_cb = cb;
self->ready_cb_data = user_data;
self->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
if (self->cancellable)
self->cancellable_id = g_cancellable_connect (self->cancellable,
G_CALLBACK (on_cancelled), self, NULL);
start_listen (self);
return TRUE;
}
gboolean
fp_device_virtual_listener_connection_close (FpDeviceVirtualListener *self)
{
g_return_val_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self), FALSE);
if (!self->connection)
return FALSE;
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
g_clear_object (&self->connection);
return TRUE;
}
static void
on_stream_read_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
FpDeviceVirtualListener *self = g_task_get_source_object (task);
gboolean all;
gboolean success;
gsize bytes;
all = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "all"));
if (all)
{
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
}
else
{
gssize sbytes;
sbytes = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
bytes = sbytes;
success = (sbytes >= 0);
}
if (g_task_return_error_if_cancelled (task))
return;
/* If we are cancelled, just return immediately. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED))
{
g_task_return_int (task, 0);
return;
}
/* If this error is for an old connection (that should be closed already),
* then just give up immediately with a CLOSED error.
*/
if (self->connection &&
g_io_stream_get_input_stream (G_IO_STREAM (self->connection)) != G_INPUT_STREAM (source_object))
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_CLOSED,
"Error on old connection, ignoring.");
return;
}
if (!success || bytes == 0)
{
/* We accept it if someone tries to read twice and just return that error. */
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
{
if (self->connection)
{
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
g_clear_object (&self->connection);
}
}
if (error)
{
g_task_return_error (task, g_steal_pointer (&error));
return;
}
else
{
// g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Got empty data");
return;
}
}
g_task_return_int (task, bytes);
}
void
fp_device_virtual_listener_read (FpDeviceVirtualListener *self,
gboolean all,
void *buffer,
gsize count,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
GInputStream *stream;
g_return_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self));
task = g_task_new (self, self->cancellable, callback, user_data);
g_object_set_data (G_OBJECT (task), "all", GINT_TO_POINTER (all));
if (!self->connection || g_io_stream_is_closed (G_IO_STREAM (self->connection)))
{
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
"Listener not connected to any stream");
return;
}
stream = g_io_stream_get_input_stream (G_IO_STREAM (self->connection));
if (all)
{
g_input_stream_read_all_async (stream, buffer, count,
G_PRIORITY_DEFAULT,
self->cancellable,
on_stream_read_cb,
g_steal_pointer (&task));
}
else
{
g_input_stream_read_async (stream, buffer, count,
G_PRIORITY_DEFAULT,
self->cancellable,
on_stream_read_cb,
g_steal_pointer (&task));
}
}
gsize
fp_device_virtual_listener_read_finish (FpDeviceVirtualListener *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, self), 0);
return g_task_propagate_int (G_TASK (result), error);
}
gboolean
fp_device_virtual_listener_write_sync (FpDeviceVirtualListener *self,
const char *buffer,
gsize count,
GError **error)
{
if (!self->connection || g_io_stream_is_closed (G_IO_STREAM (self->connection)))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
"Listener not connected to any stream");
return FALSE;
}
return g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (self->connection)),
buffer,
count,
NULL,
self->cancellable,
error);
}

View file

@ -0,0 +1,93 @@
/*
* Virtual driver for "simple" device debugging
*
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This is a virtual driver to debug the non-image based drivers. A small
* python script is provided to connect to it via a socket, allowing
* prints to registered programmatically.
* Using this, it is possible to test libfprint and fprintd.
*/
#include <gio/gio.h>
#include "fpi-device.h"
#define MAX_LINE_LEN 1024
G_DECLARE_FINAL_TYPE (FpDeviceVirtualListener, fp_device_virtual_listener, FP, DEVICE_VIRTUAL_LISTENER, GSocketListener)
typedef void (*FpDeviceVirtualListenerConnectionCb) (FpDeviceVirtualListener *listener,
gpointer user_data);
FpDeviceVirtualListener * fp_device_virtual_listener_new (void);
gboolean fp_device_virtual_listener_start (FpDeviceVirtualListener *listener,
const char *address,
GCancellable *cancellable,
FpDeviceVirtualListenerConnectionCb cb,
gpointer user_data,
GError **error);
gboolean fp_device_virtual_listener_connection_close (FpDeviceVirtualListener *listener);
void fp_device_virtual_listener_read (FpDeviceVirtualListener *listener,
gboolean all,
void *buffer,
gsize count,
GAsyncReadyCallback callback,
gpointer user_data);
gsize fp_device_virtual_listener_read_finish (FpDeviceVirtualListener *listener,
GAsyncResult *result,
GError **error);
gboolean fp_device_virtual_listener_write_sync (FpDeviceVirtualListener *self,
const char *buffer,
gsize count,
GError **error);
struct _FpDeviceVirtualDevice
{
FpDevice parent;
FpDeviceVirtualListener *listener;
GCancellable *cancellable;
char recv_buf[MAX_LINE_LEN];
GPtrArray *pending_commands;
GHashTable *prints_storage;
};
/* Not really final here, but we can do this to share the FpDeviceVirtualDevice
* contents without having to use a shared private struct instead. */
G_DECLARE_FINAL_TYPE (FpDeviceVirtualDevice, fpi_device_virtual_device, FP, DEVICE_VIRTUAL_DEVICE, FpDevice)
struct _FpDeviceVirtualDeviceStorage
{
FpDeviceVirtualDevice parent;
};
G_DECLARE_FINAL_TYPE (FpDeviceVirtualDeviceStorage, fpi_device_virtual_device_storage, FP, DEVICE_VIRTUAL_DEVICE_STORAGE, FpDeviceVirtualDevice)
char * process_cmds (FpDeviceVirtualDevice * self, gboolean scan, GError **error);

View file

@ -29,36 +29,27 @@
#include "fpi-log.h" #include "fpi-log.h"
#include "virtual-device-private.h"
#include "../fpi-image.h" #include "../fpi-image.h"
#include "../fpi-image-device.h" #include "../fpi-image-device.h"
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <gio/gunixsocketaddress.h>
struct _FpDeviceVirtualImage struct _FpDeviceVirtualImage
{ {
FpImageDevice parent; FpImageDevice parent;
GSocketListener *listener; FpDeviceVirtualListener *listener;
GSocketConnection *connection; GCancellable *cancellable;
GCancellable *listen_cancellable;
GCancellable *cancellable;
gint socket_fd; gboolean automatic_finger;
gint client_fd; FpImage *recv_img;
gint recv_img_hdr[2];
gboolean automatic_finger;
FpImage *recv_img;
gint recv_img_hdr[2];
}; };
G_DECLARE_FINAL_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FPI, DEVICE_VIRTUAL_IMAGE, FpImageDevice) G_DECLARE_FINAL_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FPI, DEVICE_VIRTUAL_IMAGE, FpImageDevice)
G_DEFINE_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FP_TYPE_IMAGE_DEVICE) G_DEFINE_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FP_TYPE_IMAGE_DEVICE)
static void start_listen (FpDeviceVirtualImage *dev); static void recv_image (FpDeviceVirtualImage *self);
static void recv_image (FpDeviceVirtualImage *dev,
GInputStream *stream);
static void static void
recv_image_img_recv_cb (GObject *source_object, recv_image_img_recv_cb (GObject *source_object,
@ -66,35 +57,20 @@ recv_image_img_recv_cb (GObject *source_object,
gpointer user_data) gpointer user_data)
{ {
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object);
FpDeviceVirtualImage *self; FpDeviceVirtualImage *self;
FpImageDevice *device; FpImageDevice *device;
gboolean success; gsize bytes;
gsize bytes = 0;
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); bytes = fp_device_virtual_listener_read_finish (listener, res, &error);
/* Can't use self if the operation was cancelled. */ if (!bytes || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
return; return;
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
device = FP_IMAGE_DEVICE (self); device = FP_IMAGE_DEVICE (self);
/* Consider success if we received the right amount of data, otherwise
* an error must have happened. */
if (bytes < self->recv_img->width * self->recv_img->height)
{
if (!success)
g_warning ("Error receiving image data: %s", error->message);
else
g_warning ("Error receiving image data: end of stream before all data was read");
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
g_clear_object (&self->connection);
return;
}
if (self->automatic_finger) if (self->automatic_finger)
fpi_image_device_report_finger_status (device, TRUE); fpi_image_device_report_finger_status (device, TRUE);
fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img)); fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img));
@ -102,7 +78,7 @@ recv_image_img_recv_cb (GObject *source_object,
fpi_image_device_report_finger_status (device, FALSE); fpi_image_device_report_finger_status (device, FALSE);
/* And, listen for more images from the same client. */ /* And, listen for more images from the same client. */
recv_image (self, G_INPUT_STREAM (source_object)); recv_image (self);
} }
static void static void
@ -112,37 +88,30 @@ recv_image_hdr_recv_cb (GObject *source_object,
{ {
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
FpDeviceVirtualImage *self; FpDeviceVirtualImage *self;
gboolean success; FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object);
gsize bytes; gsize bytes;
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); bytes = fp_device_virtual_listener_read_finish (listener, res, &error);
if (!success || bytes != sizeof (self->recv_img_hdr)) if (error)
{ {
if (!success) if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
{ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) ||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) return;
return;
g_warning ("Error receiving header for image data: %s", error->message);
}
else if (bytes != 0)
{
g_warning ("Received incomplete header before end of stream.");
}
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); g_warning ("Error receiving header for image data: %s", error->message);
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
g_clear_object (&self->connection);
return; return;
} }
if (!bytes)
return;
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
if (self->recv_img_hdr[0] > 5000 || self->recv_img_hdr[1] > 5000) if (self->recv_img_hdr[0] > 5000 || self->recv_img_hdr[1] > 5000)
{ {
g_warning ("Image header suggests an unrealistically large image, disconnecting client."); g_warning ("Image header suggests an unrealistically large image, disconnecting client.");
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); fp_device_virtual_listener_connection_close (listener);
g_clear_object (&self->connection);
} }
if (self->recv_img_hdr[0] < 0 || self->recv_img_hdr[1] < 0) if (self->recv_img_hdr[0] < 0 || self->recv_img_hdr[1] < 0)
@ -178,153 +147,80 @@ recv_image_hdr_recv_cb (GObject *source_object,
default: default:
/* disconnect client, it didn't play fair */ /* disconnect client, it didn't play fair */
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); fp_device_virtual_listener_connection_close (listener);
g_clear_object (&self->connection);
} }
/* And, listen for more images from the same client. */ /* And, listen for more images from the same client. */
recv_image (self, G_INPUT_STREAM (source_object)); recv_image (self);
return; return;
} }
self->recv_img = fp_image_new (self->recv_img_hdr[0], self->recv_img_hdr[1]); self->recv_img = fp_image_new (self->recv_img_hdr[0], self->recv_img_hdr[1]);
g_debug ("image data: %p", self->recv_img->data); g_debug ("image data: %p", self->recv_img->data);
g_input_stream_read_all_async (G_INPUT_STREAM (source_object), fp_device_virtual_listener_read (listener,
(guint8 *) self->recv_img->data, TRUE,
self->recv_img->width * self->recv_img->height, (guint8 *) self->recv_img->data,
G_PRIORITY_DEFAULT, self->recv_img->width * self->recv_img->height,
self->cancellable, recv_image_img_recv_cb,
recv_image_img_recv_cb, self);
self);
} }
static void static void
recv_image (FpDeviceVirtualImage *self, GInputStream *stream) recv_image (FpDeviceVirtualImage *self)
{ {
fp_device_virtual_listener_read (self->listener,
TRUE,
self->recv_img_hdr,
sizeof (self->recv_img_hdr),
recv_image_hdr_recv_cb,
self);
}
static void
on_listener_connected (FpDeviceVirtualListener *listener,
gpointer user_data)
{
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
FpiImageDeviceState state; FpiImageDeviceState state;
g_object_get (self, "fpi-image-device-state", &state, NULL); self->automatic_finger = TRUE;
g_debug ("Starting image receive (if active), state is: %i", state); g_object_get (self,
"fpi-image-device-state", &state,
NULL);
/* Only read if we are in AWAIT_FINGER_* or CAPTURE states */
if (state <= FPI_IMAGE_DEVICE_STATE_DEACTIVATING)
return;
/* Only register if the state is active. */ recv_image (self);
if (state >= FPI_IMAGE_DEVICE_STATE_IDLE)
{
g_input_stream_read_all_async (stream,
self->recv_img_hdr,
sizeof (self->recv_img_hdr),
G_PRIORITY_DEFAULT,
self->cancellable,
recv_image_hdr_recv_cb,
self);
}
}
static void
new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
g_autoptr(GError) error = NULL;
GSocketConnection *connection;
GInputStream *stream;
FpDeviceVirtualImage *dev = user_data;
connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (source_object),
res,
NULL,
&error);
if (!connection)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
g_warning ("Error accepting a new connection: %s", error->message);
start_listen (dev);
}
/* Always accept further connections (but we disconnect them immediately
* if we already have a connection). */
start_listen (dev);
if (dev->connection)
{
/* We may not have noticed that the stream was closed,
* if the device is deactivated.
* Cancel any ongoing operation on the old connection. */
g_cancellable_cancel (dev->cancellable);
g_clear_object (&dev->cancellable);
dev->cancellable = g_cancellable_new ();
g_clear_object (&dev->connection);
}
if (dev->connection)
{
g_warning ("Rejecting new connection");
g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
g_object_unref (connection);
return;
}
dev->connection = connection;
dev->automatic_finger = TRUE;
stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
fp_dbg ("Got a new connection!");
recv_image (dev, stream);
}
static void
start_listen (FpDeviceVirtualImage *dev)
{
g_socket_listener_accept_async (dev->listener,
dev->listen_cancellable,
new_connection_cb,
dev);
} }
static void static void
dev_init (FpImageDevice *dev) dev_init (FpImageDevice *dev)
{ {
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_autoptr(GSocketListener) listener = NULL; g_autoptr(FpDeviceVirtualListener) listener = NULL;
g_autoptr(GCancellable) cancellable = NULL;
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
const char *env;
g_autoptr(GSocketAddress) addr = NULL;
G_DEBUG_HERE (); G_DEBUG_HERE ();
self->client_fd = -1; listener = fp_device_virtual_listener_new ();
cancellable = g_cancellable_new ();
env = fpi_device_get_virtual_env (FP_DEVICE (self)); if (!fp_device_virtual_listener_start (listener,
fpi_device_get_virtual_env (FP_DEVICE (self)),
listener = g_socket_listener_new (); cancellable,
g_socket_listener_set_backlog (listener, 1); on_listener_connected,
self,
/* Remove any left over socket. */ &error))
g_unlink (env);
addr = g_unix_socket_address_new (env);
if (!g_socket_listener_add_address (listener,
addr,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
NULL,
NULL,
&error))
{ {
g_warning ("Could not listen on unix socket: %s", error->message); fpi_image_device_open_complete (dev, g_steal_pointer (&error));
fpi_image_device_open_complete (FP_IMAGE_DEVICE (dev), g_steal_pointer (&error));
return; return;
} }
self->listener = g_steal_pointer (&listener); self->listener = g_steal_pointer (&listener);
self->cancellable = g_cancellable_new (); self->cancellable = g_steal_pointer (&cancellable);
self->listen_cancellable = g_cancellable_new ();
start_listen (self);
/* Delay result to open up the possibility of testing race conditions. */ /* Delay result to open up the possibility of testing race conditions. */
fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_open_complete, NULL, NULL); fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_open_complete, NULL, NULL);
@ -338,11 +234,8 @@ dev_deinit (FpImageDevice *dev)
G_DEBUG_HERE (); G_DEBUG_HERE ();
g_cancellable_cancel (self->cancellable); g_cancellable_cancel (self->cancellable);
g_cancellable_cancel (self->listen_cancellable);
g_clear_object (&self->cancellable); g_clear_object (&self->cancellable);
g_clear_object (&self->listen_cancellable);
g_clear_object (&self->listener); g_clear_object (&self->listener);
g_clear_object (&self->connection);
/* Delay result to open up the possibility of testing race conditions. */ /* Delay result to open up the possibility of testing race conditions. */
fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_close_complete, NULL, NULL); fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_close_complete, NULL, NULL);
@ -353,23 +246,16 @@ dev_activate (FpImageDevice *dev)
{ {
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
fpi_image_device_activate_complete (dev, NULL); /* Start reading (again). */
recv_image (self);
if (self->connection) fpi_image_device_activate_complete (dev, NULL);
recv_image (self, g_io_stream_get_input_stream (G_IO_STREAM (self->connection)));
} }
static void static void
dev_deactivate (FpImageDevice *dev) dev_deactivate (FpImageDevice *dev)
{ {
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); fpi_image_device_deactivate_complete (dev, NULL);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
self->cancellable = g_cancellable_new ();
/* XXX: Need to wait for the operation to be cancelled. */
fpi_device_add_timeout (FP_DEVICE (dev), 10, (FpTimeoutFunc) fpi_image_device_deactivate_complete, NULL, NULL);
} }
static void static void

View file

@ -155,6 +155,9 @@ foreach driver: drivers
if driver == 'virtual_image' if driver == 'virtual_image'
drivers_sources += [ 'drivers/virtual-image.c' ] drivers_sources += [ 'drivers/virtual-image.c' ]
endif endif
if driver.startswith('virtual_')
drivers_sources += [ 'drivers/virtual-device-listener.c' ]
endif
if driver == 'synaptics' if driver == 'synaptics'
drivers_sources += [ drivers_sources += [
'drivers/synaptics/synaptics.c', 'drivers/synaptics/synaptics.c',