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