manager.c 13.7 KB
Newer Older
Daniel Drake's avatar
Daniel Drake committed
1 2 3
/*
 * /net/reactivated/Fprint/Manager object implementation
 * Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
4
 * Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
Daniel Drake's avatar
Daniel Drake committed
5 6 7 8 9
 *
 * 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 2 of the License, or
 * (at your option) any later version.
10
 *
Daniel Drake's avatar
Daniel Drake committed
11 12 13 14
 * 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.
15
 *
Daniel Drake's avatar
Daniel Drake committed
16 17 18 19 20
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

21 22
#include <unistd.h>
#include <stdlib.h>
Daniel Drake's avatar
Daniel Drake committed
23
#include <glib.h>
24
#include <glib/gi18n.h>
Benjamin Berg's avatar
Benjamin Berg committed
25
#include <fprint.h>
Daniel Drake's avatar
Daniel Drake committed
26 27 28 29
#include <glib-object.h>

#include "fprintd.h"

30
static void fprint_manager_constructed (GObject *object);
31 32 33 34 35 36
static gboolean fprint_manager_get_devices (FprintManager *manager,
                                            GPtrArray    **devices,
                                            GError       **error);
static gboolean fprint_manager_get_default_device (FprintManager *manager,
                                                   const char   **device,
                                                   GError       **error);
Bastien Nocera's avatar
Bastien Nocera committed
37 38 39

typedef struct
{
40 41 42 43 44 45
  GDBusConnection    *connection;
  GDBusObjectManager *object_manager;
  FprintDBusManager  *dbus_manager;
  FpContext          *context;
  gboolean            no_timeout;
  guint               timeout_id;
Bastien Nocera's avatar
Bastien Nocera committed
46 47
} FprintManagerPrivate;

48
G_DEFINE_TYPE_WITH_CODE (FprintManager, fprint_manager, G_TYPE_OBJECT, G_ADD_PRIVATE (FprintManager))
Bastien Nocera's avatar
Bastien Nocera committed
49

50
enum {
51 52 53
  PROP_0,
  FPRINT_MANAGER_CONNECTION,
  N_PROPS
54 55 56 57
};

static GParamSpec *properties[N_PROPS];

58 59
static void
fprint_manager_finalize (GObject *object)
Daniel Drake's avatar
Daniel Drake committed
60
{
61
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
Bastien Nocera's avatar
Bastien Nocera committed
62

63 64 65 66
  g_clear_object (&priv->object_manager);
  g_clear_object (&priv->dbus_manager);
  g_clear_object (&priv->connection);
  g_clear_object (&priv->context);
Bastien Nocera's avatar
Bastien Nocera committed
67

68
  G_OBJECT_CLASS (fprint_manager_parent_class)->finalize (object);
Daniel Drake's avatar
Daniel Drake committed
69 70
}

71
static FprintDevice *
72 73 74
fprint_dbus_object_skeleton_get_device (FprintDBusObjectSkeleton *object)
{
  FprintDevice *rdev;
75

76 77
  g_object_get (object, "device", &rdev, NULL);
  return rdev;
78 79
}

80 81 82
static void
fprint_manager_set_property (GObject *object, guint property_id,
                             const GValue *value, GParamSpec *pspec)
83
{
84 85 86 87 88 89 90 91 92 93 94 95 96
  FprintManager *self = FPRINT_MANAGER (object);
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (self);

  switch (property_id)
    {
    case FPRINT_MANAGER_CONNECTION:
      priv->connection = g_value_dup_object (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
97 98
}

99 100 101
static void
fprint_manager_get_property (GObject *object, guint property_id,
                             GValue *value, GParamSpec *pspec)
Daniel Drake's avatar
Daniel Drake committed
102
{
103 104 105 106 107 108 109 110 111 112 113 114 115
  FprintManager *self = FPRINT_MANAGER (object);
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (self);

  switch (property_id)
    {
    case FPRINT_MANAGER_CONNECTION:
      g_value_set_object (value, priv->connection);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
116
}
Daniel Drake's avatar
Daniel Drake committed
117

118 119
static void
fprint_manager_class_init (FprintManagerClass *klass)
120
{
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed = fprint_manager_constructed;
  object_class->set_property = fprint_manager_set_property;
  object_class->get_property = fprint_manager_get_property;
  object_class->finalize = fprint_manager_finalize;

  properties[FPRINT_MANAGER_CONNECTION] =
    g_param_spec_object ("connection",
                         "Connection",
                         "Set GDBus connection property",
                         G_TYPE_DBUS_CONNECTION,
                         G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_READWRITE);

  g_object_class_install_properties (object_class, N_PROPS, properties);
Daniel Drake's avatar
Daniel Drake committed
137 138
}

139 140
static gchar *
get_device_path (FprintDevice *rdev)
Daniel Drake's avatar
Daniel Drake committed
141
{
142 143
  return g_strdup_printf (FPRINT_SERVICE_PATH "/Device/%d",
                          _fprint_device_get_id (rdev));
Daniel Drake's avatar
Daniel Drake committed
144 145
}

146 147 148
static gboolean
fprint_manager_timeout_cb (FprintManager *manager)
{
149 150 151
  //FIXME kill all the devices
  exit (0);
  return FALSE;
152 153 154
}

static void
155
fprint_manager_in_use_notified (FprintDevice *rdev, GParamSpec *spec, FprintManager *manager)
156
{
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
  guint num_devices_used = 0;

  g_autolist (GDBusObject) devices = NULL;
  GList *l;
  gboolean in_use;

  if (priv->timeout_id > 0)
    {
      g_source_remove (priv->timeout_id);
      priv->timeout_id = 0;
    }
  if (priv->no_timeout)
    return;

  devices = g_dbus_object_manager_get_objects (priv->object_manager);

  for (l = devices; l != NULL; l = l->next)
    {
      g_autoptr(FprintDevice) dev = NULL;
      FprintDBusObjectSkeleton *object = l->data;

      dev = fprint_dbus_object_skeleton_get_device (object);
      g_object_get (G_OBJECT (dev), "in-use", &in_use, NULL);
      if (in_use != FALSE)
        num_devices_used++;
    }

  if (num_devices_used == 0)
    priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, manager);
187 188
}

189 190
static gboolean
handle_get_devices (FprintManager *manager, GDBusMethodInvocation *invocation,
191
                    FprintDBusManager *skeleton)
192
{
193 194
  g_autoptr(GPtrArray) devices = NULL;
  g_autoptr(GError) error = NULL;
195

196 197 198 199 200
  if (!fprint_manager_get_devices (manager, &devices, &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      return TRUE;
    }
201

202 203 204
  fprint_dbus_manager_complete_get_devices (skeleton, invocation,
                                            (const gchar *const *)
                                            devices->pdata);
205

206
  return TRUE;
207 208 209
}

static gboolean
210 211 212
handle_get_default_device (FprintManager         *manager,
                           GDBusMethodInvocation *invocation,
                           FprintDBusManager     *skeleton)
213
{
214
  const gchar *device;
215

216
  g_autoptr(GError) error = NULL;
217

218 219 220 221 222
  if (!fprint_manager_get_default_device (manager, &device, &error))
    {
      g_dbus_method_invocation_return_gerror (invocation, error);
      return TRUE;
    }
223

224 225 226 227
  fprint_dbus_manager_complete_get_default_device (skeleton, invocation,
                                                   device);

  return TRUE;
228 229
}

Bastien Nocera's avatar
Bastien Nocera committed
230
static void
Benjamin Berg's avatar
Benjamin Berg committed
231
device_added_cb (FprintManager *manager, FpDevice *device, FpContext *context)
Daniel Drake's avatar
Daniel Drake committed
232
{
233 234 235 236 237
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);

  g_autoptr(FprintDBusObjectSkeleton) object = NULL;
  g_autoptr(FprintDevice) rdev = NULL;
  g_autofree gchar *path = NULL;
Daniel Drake's avatar
Daniel Drake committed
238

239
  rdev = fprint_device_new (device);
240

241 242
  g_signal_connect (G_OBJECT (rdev), "notify::in-use",
                    G_CALLBACK (fprint_manager_in_use_notified), manager);
Daniel Drake's avatar
Daniel Drake committed
243

244
  path = get_device_path (rdev);
245

246 247 248 249 250 251
  object = fprint_dbus_object_skeleton_new (path);
  fprint_dbus_object_skeleton_set_device (object,
                                          FPRINT_DBUS_DEVICE (rdev));
  g_dbus_object_manager_server_export (
    G_DBUS_OBJECT_MANAGER_SERVER (priv->object_manager),
    G_DBUS_OBJECT_SKELETON (object));
Benjamin Berg's avatar
Benjamin Berg committed
252
}
253

Benjamin Berg's avatar
Benjamin Berg committed
254 255 256
static void
device_removed_cb (FprintManager *manager, FpDevice *device, FpContext *context)
{
257
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
Daniel Drake's avatar
Daniel Drake committed
258

259 260
  g_autolist (FprintDBusObjectSkeleton) objects = NULL;
  GList *item;
261

262
  objects = g_dbus_object_manager_get_objects (priv->object_manager);
Benjamin Berg's avatar
Benjamin Berg committed
263

264 265 266 267 268
  for (item = objects; item; item = item->next)
    {
      g_autoptr(FprintDevice) rdev = NULL;
      g_autoptr(FpDevice) dev = NULL;
      FprintDBusObjectSkeleton *object = item->data;
Benjamin Berg's avatar
Benjamin Berg committed
269

270 271 272 273
      rdev = fprint_dbus_object_skeleton_get_device (object);
      g_object_get (rdev, "dev", &dev, NULL);
      if (dev != device)
        continue;
Benjamin Berg's avatar
Benjamin Berg committed
274

275 276 277
      g_dbus_object_manager_server_unexport (
        G_DBUS_OBJECT_MANAGER_SERVER (priv->object_manager),
        g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (rdev)));
Benjamin Berg's avatar
Benjamin Berg committed
278

279
      g_signal_handlers_disconnect_by_data (rdev, manager);
Benjamin Berg's avatar
Benjamin Berg committed
280

281 282 283 284 285 286 287
      /* We cannot continue to iterate at this point, but we don't need to either */
      break;
    }

  /* The device that disappeared might have been in-use.
   * Do we need to do anything else in this case to clean up more gracefully? */
  fprint_manager_in_use_notified (NULL, NULL, manager);
Benjamin Berg's avatar
Benjamin Berg committed
288 289
}

290 291
static void
fprint_manager_constructed (GObject *object)
Benjamin Berg's avatar
Benjamin Berg committed
292
{
293 294 295 296 297
  FprintManager *manager = FPRINT_MANAGER (object);
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);
  GDBusObjectManagerServer *object_manager_server;

  object_manager_server =
298
    g_dbus_object_manager_server_new (FPRINT_SERVICE_PATH);
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

  priv->object_manager = G_DBUS_OBJECT_MANAGER (object_manager_server);
  priv->dbus_manager = fprint_dbus_manager_skeleton_new ();
  priv->context = fp_context_new ();

  g_signal_connect_object (priv->dbus_manager,
                           "handle-get-devices",
                           G_CALLBACK (handle_get_devices),
                           manager,
                           G_CONNECT_SWAPPED);
  g_signal_connect_object (priv->dbus_manager,
                           "handle-get-default-device",
                           G_CALLBACK (handle_get_default_device),
                           manager,
                           G_CONNECT_SWAPPED);

  g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_manager),
                                    priv->connection,
                                    FPRINT_SERVICE_PATH "/Manager", NULL);

  g_dbus_object_manager_server_set_connection (object_manager_server,
                                               priv->connection);

  /* And register the signals for initial enumeration and hotplug. */
  g_signal_connect_object (priv->context,
                           "device-added",
                           (GCallback) device_added_cb,
                           manager,
                           G_CONNECT_SWAPPED);

  g_signal_connect_object (priv->context,
                           "device-removed",
                           (GCallback) device_removed_cb,
                           manager,
                           G_CONNECT_SWAPPED);

  /* Prepare everything by enumerating all devices.
   * This blocks the main loop until the existing devices are enumerated
   */
  fp_context_enumerate (priv->context);

  G_OBJECT_CLASS (fprint_manager_parent_class)->constructed (object);
Bastien Nocera's avatar
Bastien Nocera committed
341 342
}

343 344 345 346 347
static void
fprint_manager_init (FprintManager *manager)
{
}

348 349
FprintManager *
fprint_manager_new (GDBusConnection *connection, gboolean no_timeout)
Bastien Nocera's avatar
Bastien Nocera committed
350
{
351 352
  FprintManagerPrivate *priv;
  GObject *object;
353

354 355 356
  object = g_object_new (FPRINT_TYPE_MANAGER, "connection", connection, NULL);
  priv = fprint_manager_get_instance_private (FPRINT_MANAGER (object));
  priv->no_timeout = no_timeout;
357

358 359
  if (!priv->no_timeout)
    priv->timeout_id = g_timeout_add_seconds (TIMEOUT, (GSourceFunc) fprint_manager_timeout_cb, object);
Bastien Nocera's avatar
Bastien Nocera committed
360

361
  return FPRINT_MANAGER (object);
Bastien Nocera's avatar
Bastien Nocera committed
362 363
}

364 365 366
static gboolean
fprint_manager_get_devices (FprintManager *manager,
                            GPtrArray **devices, GError **error)
Daniel Drake's avatar
Daniel Drake committed
367
{
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);

  g_autolist (FprintDBusObjectSkeleton) objects = NULL;
  GList *l;
  int num_open;
  GPtrArray *devs;

  objects = g_dbus_object_manager_get_objects (priv->object_manager);
  objects = g_list_reverse (objects);

  num_open = g_list_length (objects);
  devs = g_ptr_array_sized_new (num_open);

  if (num_open > 0)
    {
      for (l = objects; l != NULL; l = l->next)
        {
          g_autoptr(FprintDevice) rdev = NULL;
          FprintDBusObjectSkeleton *object = l->data;
          const char *path;

          rdev = fprint_dbus_object_skeleton_get_device (object);
          path = g_dbus_interface_skeleton_get_object_path (
            G_DBUS_INTERFACE_SKELETON (rdev));
          g_ptr_array_add (devs, (char *) path);
        }
    }
  g_ptr_array_add (devs, NULL);

  *devices = devs;
  return TRUE;
Daniel Drake's avatar
Daniel Drake committed
399 400
}

401 402 403
static gboolean
fprint_manager_get_default_device (FprintManager *manager,
                                   const char **device, GError **error)
Bastien Nocera's avatar
Bastien Nocera committed
404
{
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
  FprintManagerPrivate *priv = fprint_manager_get_instance_private (manager);

  g_autolist (FprintDBusObjectSkeleton) objects = NULL;
  int num_open;

  objects = g_dbus_object_manager_get_objects (priv->object_manager);
  num_open = g_list_length (objects);

  if (num_open > 0)
    {
      g_autoptr(FprintDevice) rdev = NULL;
      FprintDBusObjectSkeleton *object = g_list_last (objects)->data;

      rdev = fprint_dbus_object_skeleton_get_device (object);
      *device = g_dbus_interface_skeleton_get_object_path (
        G_DBUS_INTERFACE_SKELETON (rdev));
      return TRUE;
    }
  else
    {
      g_set_error (error, FPRINT_ERROR, FPRINT_ERROR_NO_SUCH_DEVICE,
                   "No devices available");
      *device = NULL;
      return FALSE;
    }
Bastien Nocera's avatar
Bastien Nocera committed
430
}
431

432 433
GQuark
fprint_error_quark (void)
Bastien Nocera's avatar
Bastien Nocera committed
434
{
435
  static gsize quark = 0;
436 437 438 439 440 441

  if (g_once_init_enter (&quark))
    {
      g_autoptr(GEnumClass) errors_enum = NULL;
      GQuark domain;
      unsigned i;
442

443 444
      domain = g_quark_from_static_string ("fprintd-error-quark");
      errors_enum = g_type_class_ref (FPRINT_TYPE_ERROR);
445

446 447 448
      for (i = 0; i < errors_enum->n_values; ++i)
        {
          GEnumValue *value = &errors_enum->values[i];
449

450 451 452
          g_dbus_error_register_error (domain, value->value,
                                       value->value_nick);
        }
453

454 455 456
      g_once_init_leave (&quark, domain);
    }
  return (GQuark) quark;
Bastien Nocera's avatar
Bastien Nocera committed
457
}