1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* GIO - GLib Input, Output and Streaming Library
3 *
4 * Copyright (C) 2006-2007 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 * Author: David Zeuthen <davidz@redhat.com>
22 */
23
24 #include <config.h>
25
26 #include <limits.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32 #include <gphoto2.h>
33 #include <gio/gio.h>
34
35 #include "ggphoto2volumemonitor.h"
36 #include "ggphoto2volume.h"
37
38 #include <gio/gunixmounts.h>
39
40 G_LOCK_DEFINE_STATIC(gphoto2_vm);
41
42 static GGPhoto2VolumeMonitor *the_volume_monitor = NULL;
43
44 struct _GGPhoto2VolumeMonitor {
45 GNativeVolumeMonitor parent;
46
47 GUnixMountMonitor *mount_monitor;
48
49 GUdevClient *gudev_client;
50
51 GList *last_camera_devices;
52
53 GList *camera_volumes;
54 };
55
56 static void on_uevent (GUdevClient *client,
57 gchar *action,
58 GUdevDevice *device,
59 gpointer user_data);
60
61 static GList* get_stores_for_camera (const char *bus_num, const char *device_num);
62
G_DEFINE_TYPE(GGPhoto2VolumeMonitor,g_gphoto2_volume_monitor,G_TYPE_VOLUME_MONITOR)63 G_DEFINE_TYPE (GGPhoto2VolumeMonitor, g_gphoto2_volume_monitor, G_TYPE_VOLUME_MONITOR)
64
65 static void
66 g_gphoto2_volume_monitor_dispose (GObject *object)
67 {
68 G_LOCK (gphoto2_vm);
69 the_volume_monitor = NULL;
70 G_UNLOCK (gphoto2_vm);
71
72 if (G_OBJECT_CLASS (g_gphoto2_volume_monitor_parent_class)->dispose)
73 (*G_OBJECT_CLASS (g_gphoto2_volume_monitor_parent_class)->dispose) (object);
74 }
75
76 static void
g_gphoto2_volume_monitor_finalize(GObject * object)77 g_gphoto2_volume_monitor_finalize (GObject *object)
78 {
79 GGPhoto2VolumeMonitor *monitor;
80
81 monitor = G_GPHOTO2_VOLUME_MONITOR (object);
82
83 g_signal_handlers_disconnect_by_func (monitor->gudev_client, on_uevent, monitor);
84
85 g_object_unref (monitor->gudev_client);
86
87 g_list_free_full (monitor->last_camera_devices, g_object_unref);
88 g_list_free_full (monitor->camera_volumes, g_object_unref);
89
90 if (G_OBJECT_CLASS (g_gphoto2_volume_monitor_parent_class)->finalize)
91 (*G_OBJECT_CLASS (g_gphoto2_volume_monitor_parent_class)->finalize) (object);
92 }
93
94 static GList *
get_mounts(GVolumeMonitor * volume_monitor)95 get_mounts (GVolumeMonitor *volume_monitor)
96 {
97 return NULL;
98 }
99
100 static GList *
get_volumes(GVolumeMonitor * volume_monitor)101 get_volumes (GVolumeMonitor *volume_monitor)
102 {
103 GGPhoto2VolumeMonitor *monitor;
104 GList *l;
105
106 monitor = G_GPHOTO2_VOLUME_MONITOR (volume_monitor);
107
108 G_LOCK (gphoto2_vm);
109
110 l = g_list_copy (monitor->camera_volumes);
111 g_list_foreach (l, (GFunc)g_object_ref, NULL);
112
113 G_UNLOCK (gphoto2_vm);
114
115 return l;
116 }
117
118 static GList *
get_connected_drives(GVolumeMonitor * volume_monitor)119 get_connected_drives (GVolumeMonitor *volume_monitor)
120 {
121 return NULL;
122 }
123
124 static GVolume *
get_volume_for_uuid(GVolumeMonitor * volume_monitor,const char * uuid)125 get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
126 {
127 return NULL;
128 }
129
130 static GMount *
get_mount_for_uuid(GVolumeMonitor * volume_monitor,const char * uuid)131 get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
132 {
133 return NULL;
134 }
135
136 static void
gudev_add_camera(GGPhoto2VolumeMonitor * monitor,GUdevDevice * device,gboolean do_emit)137 gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean do_emit)
138 {
139 GGPhoto2Volume *volume;
140 GList *store_heads, *l;
141 guint num_store_heads;
142 const char *usb_bus_num, *usb_device_num, *usb_serial_id, *device_path;
143 gchar *prefix, *usb_serial_id_escaped;
144 GFile *mount_prefix;
145 gboolean serial_conflict = FALSE;
146
147 device_path = g_udev_device_get_device_file (device);
148 if (!device_path)
149 {
150 g_debug ("Ignoring device '%s' without a device file",
151 g_udev_device_get_sysfs_path (device));
152 return;
153 }
154
155 #ifdef HAVE_LIBMTP
156 if (g_udev_device_get_property_as_boolean (device, "ID_MTP_DEVICE"))
157 {
158 g_debug ("gudev_add_camera: ignoring device, is MTP");
159 return;
160 }
161 #endif /* HAVE_LIBMTP */
162
163 /*
164 * We do not use ID_SERIAL_SHORT (the actualy device serial value) as
165 * this field is not populated when an ID_SERIAL has to be synthesized.
166 */
167 usb_serial_id = g_udev_device_get_property (device, "ID_SERIAL");
168 if (usb_serial_id == NULL)
169 {
170 g_warning ("device %s has no ID_SERIAL property, ignoring", device_path);
171 return;
172 }
173
174 usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
175 if (usb_bus_num == NULL)
176 {
177 g_warning ("device %s has no BUSNUM property, ignoring", device_path);
178 return;
179 }
180
181 usb_device_num = g_udev_device_get_property (device, "DEVNUM");
182 if (usb_device_num == NULL)
183 {
184 g_warning ("device %s has no DEVNUM property, ignoring", device_path);
185 return;
186 }
187
188 usb_serial_id_escaped = g_uri_escape_string (usb_serial_id, NULL, FALSE);
189 prefix = g_strdup_printf ("gphoto2://%s", usb_serial_id_escaped);
190 mount_prefix = g_file_new_for_uri (prefix);
191 g_free (prefix);
192
193 /*
194 * We do not support plugging in multiple devices that lack proper serial
195 * numbers. Linux will attempt to synthesize an ID based on the device
196 * product information, which will avoid collisions between different
197 * types of device, but two identical, serial-less, devices will still
198 * conflict.
199 */
200 for (l = monitor->camera_volumes; l != NULL; l = l->next)
201 {
202 GGPhoto2Volume *volume = G_GPHOTO2_VOLUME (l->data);
203
204 GFile *existing_root = g_volume_get_activation_root (G_VOLUME (volume));
205 if (g_file_equal (existing_root, mount_prefix) ||
206 g_file_has_prefix (existing_root, mount_prefix))
207 {
208 serial_conflict = TRUE;
209 }
210 g_object_unref (existing_root);
211 if (serial_conflict)
212 {
213 break;
214 }
215 }
216 g_object_unref (mount_prefix);
217 if (serial_conflict)
218 {
219 g_warning ("device %s has an identical ID_SERIAL value to an "
220 "existing device. Multiple devices are not supported.",
221 g_udev_device_get_device_file (device));
222 g_free (usb_serial_id_escaped);
223 return;
224 }
225
226 g_debug ("gudev_add_camera: camera device %s (id: %s)",
227 g_udev_device_get_device_file (device),
228 usb_serial_id);
229
230 store_heads = get_stores_for_camera (usb_bus_num, usb_device_num);
231 num_store_heads = g_list_length (store_heads);
232 for (l = store_heads ; l != NULL; l = l->next)
233 {
234 char *store_path = (char *) l->data;
235 GFile *activation_mount_root;
236 gchar *uri;
237
238 /* If we only have a single store, don't use the store name at all. The backend automatically
239 * prepend the storename; this is to work around bugs with devices (like the iPhone) for which
240 * the store name changes every time the camera is initialized (e.g. mounted).
241 */
242 if (num_store_heads == 1)
243 {
244 uri = g_strdup_printf ("gphoto2://%s", usb_serial_id_escaped);
245 }
246 else
247 {
248 uri = g_strdup_printf ("gphoto2://%s/%s", usb_serial_id_escaped,
249 store_path[0] == '/' ? store_path + 1 : store_path);
250 }
251 g_debug ("gudev_add_camera: ... adding URI for storage head: %s", uri);
252 activation_mount_root = g_file_new_for_uri (uri);
253 g_free (uri);
254
255 volume = g_gphoto2_volume_new (G_VOLUME_MONITOR (monitor),
256 device,
257 monitor->gudev_client,
258 activation_mount_root);
259 if (volume != NULL)
260 {
261 monitor->camera_volumes = g_list_prepend (monitor->camera_volumes, volume);
262 if (do_emit)
263 g_signal_emit_by_name (monitor, "volume_added", volume);
264 }
265
266 if (activation_mount_root != NULL)
267 g_object_unref (activation_mount_root);
268 }
269
270 g_list_free_full (store_heads, g_free);
271 g_free (usb_serial_id_escaped);
272 }
273
274 static void
gudev_remove_camera(GGPhoto2VolumeMonitor * monitor,GUdevDevice * device)275 gudev_remove_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device)
276 {
277 GList *l, *ll;
278 const gchar* sysfs_path;
279
280 sysfs_path = g_udev_device_get_sysfs_path (device);
281
282 g_debug ("gudev_remove_camera: %s", g_udev_device_get_device_file (device));
283
284 for (l = monitor->camera_volumes; l != NULL; l = ll)
285 {
286 GGPhoto2Volume *volume = G_GPHOTO2_VOLUME (l->data);
287
288 ll = l->next;
289
290 if (g_gphoto2_volume_has_path (volume, sysfs_path))
291 {
292 g_debug ("gudev_remove_camera: found volume %s, deleting", sysfs_path);
293 g_signal_emit_by_name (monitor, "volume_removed", volume);
294 g_signal_emit_by_name (volume, "removed");
295 g_gphoto2_volume_removed (volume);
296 monitor->camera_volumes = g_list_remove (monitor->camera_volumes, volume);
297 g_object_unref (volume);
298 }
299 }
300 }
301
302 static void
on_uevent(GUdevClient * client,gchar * action,GUdevDevice * device,gpointer user_data)303 on_uevent (GUdevClient *client,
304 gchar *action,
305 GUdevDevice *device,
306 gpointer user_data)
307 {
308 GGPhoto2VolumeMonitor *monitor = G_GPHOTO2_VOLUME_MONITOR (user_data);
309
310 g_debug ("on_uevent: action=%s, device=%s", action, g_udev_device_get_device_file (device));
311
312 if (g_strcmp0 (action, "add") == 0 && g_udev_device_has_property (device, "ID_GPHOTO2"))
313 gudev_add_camera (monitor, device, TRUE);
314 else if (g_strcmp0 (action, "remove") == 0)
315 gudev_remove_camera (monitor, device);
316 else
317 g_debug ("on_uevent: discarding");
318 }
319
320 /* Find all attached gphoto supported cameras; this is called on startup
321 * (coldplugging). */
322 static void
gudev_coldplug_cameras(GGPhoto2VolumeMonitor * monitor)323 gudev_coldplug_cameras (GGPhoto2VolumeMonitor *monitor)
324 {
325 GList *usb_devices, *l;
326
327 usb_devices = g_udev_client_query_by_subsystem (monitor->gudev_client, "usb");
328 for (l = usb_devices; l != NULL; l = l->next)
329 {
330 GUdevDevice *d = l->data;
331 if (g_udev_device_has_property (d, "ID_GPHOTO2"))
332 gudev_add_camera (monitor, d, FALSE);
333 }
334 }
335
336 static GObject *
g_gphoto2_volume_monitor_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)337 g_gphoto2_volume_monitor_constructor (GType type,
338 guint n_construct_properties,
339 GObjectConstructParam *construct_properties)
340 {
341 GObject *object;
342 GGPhoto2VolumeMonitor *monitor;
343 GGPhoto2VolumeMonitorClass *klass;
344 GObjectClass *parent_class;
345
346 G_LOCK (gphoto2_vm);
347 if (the_volume_monitor != NULL)
348 {
349 object = G_OBJECT (g_object_ref (the_volume_monitor));
350 G_UNLOCK (gphoto2_vm);
351 return object;
352 }
353 G_UNLOCK (gphoto2_vm);
354
355 object = NULL;
356
357 /* Invoke parent constructor. */
358 klass = G_GPHOTO2_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_GPHOTO2_VOLUME_MONITOR));
359 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
360 object = parent_class->constructor (type,
361 n_construct_properties,
362 construct_properties);
363
364 monitor = G_GPHOTO2_VOLUME_MONITOR (object);
365
366 const char *subsystems[] = {"usb", NULL};
367 monitor->gudev_client = g_udev_client_new (subsystems);
368
369 g_signal_connect (monitor->gudev_client,
370 "uevent", G_CALLBACK (on_uevent),
371 monitor);
372
373 gudev_coldplug_cameras (monitor);
374
375 G_LOCK (gphoto2_vm);
376 the_volume_monitor = monitor;
377 G_UNLOCK (gphoto2_vm);
378
379 return object;
380 }
381
382 static void
g_gphoto2_volume_monitor_init(GGPhoto2VolumeMonitor * monitor)383 g_gphoto2_volume_monitor_init (GGPhoto2VolumeMonitor *monitor)
384 {
385 }
386
387 static gboolean
is_supported(void)388 is_supported (void)
389 {
390 /* Today's Linux desktops pretty much need udev to have anything working, so
391 * assume it's there */
392 return TRUE;
393 }
394
395 static void
g_gphoto2_volume_monitor_class_init(GGPhoto2VolumeMonitorClass * klass)396 g_gphoto2_volume_monitor_class_init (GGPhoto2VolumeMonitorClass *klass)
397 {
398 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
399 GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
400
401 gobject_class->constructor = g_gphoto2_volume_monitor_constructor;
402 gobject_class->finalize = g_gphoto2_volume_monitor_finalize;
403 gobject_class->dispose = g_gphoto2_volume_monitor_dispose;
404
405 monitor_class->get_mounts = get_mounts;
406 monitor_class->get_volumes = get_volumes;
407 monitor_class->get_connected_drives = get_connected_drives;
408 monitor_class->get_volume_for_uuid = get_volume_for_uuid;
409 monitor_class->get_mount_for_uuid = get_mount_for_uuid;
410 monitor_class->is_supported = is_supported;
411 }
412
413 /**
414 * g_gphoto2_volume_monitor_new:
415 *
416 * Returns: a new #GVolumeMonitor.
417 **/
418 GVolumeMonitor *
g_gphoto2_volume_monitor_new(void)419 g_gphoto2_volume_monitor_new (void)
420 {
421 GGPhoto2VolumeMonitor *monitor;
422
423 monitor = g_object_new (G_TYPE_GPHOTO2_VOLUME_MONITOR, NULL);
424
425 return G_VOLUME_MONITOR (monitor);
426 }
427
428 static GList *
get_stores_for_camera(const char * bus_num,const char * device_num)429 get_stores_for_camera (const char *bus_num, const char *device_num)
430 {
431 GList *l;
432 CameraStorageInformation *storage_info;
433 GPContext *context;
434 GPPortInfo info;
435 GPPortInfoList *il;
436 int num_storage_info, n, rc;
437 Camera *camera;
438 char *port;
439 guint i;
440
441 il = NULL;
442 camera = NULL;
443 context = NULL;
444 l = NULL;
445 port = g_strdup_printf ("usb:%s,%s", bus_num, device_num);
446
447 /* Connect to the camera */
448 context = gp_context_new ();
449 if (gp_camera_new (&camera) != 0)
450 goto out;
451 if (gp_port_info_list_new (&il) != 0)
452 goto out;
453 if (gp_port_info_list_load (il) != 0)
454 goto out;
455 n = gp_port_info_list_lookup_path (il, port);
456 if (n == GP_ERROR_UNKNOWN_PORT)
457 goto out;
458 if (gp_port_info_list_get_info (il, n, &info) != 0)
459 goto out;
460 if (gp_camera_set_port_info (camera, info) != 0)
461 goto out;
462 gp_port_info_list_free (il);
463 il = NULL;
464 if (gp_camera_init (camera, context) != 0)
465 goto out;
466
467 /* Get information about the storage heads */
468 rc = gp_camera_get_storageinfo (camera, &storage_info, &num_storage_info, context);
469 if (rc != 0) {
470 /* Not all gphoto drivers implement get storage info (drivers for proprietary
471 protocols often don't) */
472 if (rc == GP_ERROR_NOT_SUPPORTED)
473 l = g_list_prepend (l, g_strdup ("/"));
474 goto out;
475 }
476
477 /* Append the data to the list */
478 for (i = 0; i < num_storage_info; i++)
479 {
480 const gchar *basedir;
481
482 /* Ignore storage with no capacity (see bug 570888) */
483 if ((storage_info[i].fields & GP_STORAGEINFO_MAXCAPACITY) &&
484 storage_info[i].capacitykbytes == 0)
485 continue;
486
487 /* Some cameras, such as the Canon 5D, won't report the basedir */
488 if (storage_info[i].fields & GP_STORAGEINFO_BASE)
489 basedir = storage_info[i].basedir;
490 else
491 basedir = "/";
492
493 l = g_list_prepend (l, g_strdup (basedir));
494 }
495
496 out:
497 /* Clean up */
498 if (il != NULL)
499 gp_port_info_list_free (il);
500 if (context != NULL)
501 gp_context_unref (context);
502 if (camera != NULL)
503 gp_camera_unref (camera);
504
505 g_free (port);
506
507 return l;
508 }
509