wbemprox: Add support for enumerating class properties.
[wine] / dlls / mountmgr.sys / dbus.c
1 /*
2  * DBus devices support
3  *
4  * Copyright 2006, 2011 Alexandre Julliard
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.1 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 Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <sys/time.h>
29 #ifdef SONAME_LIBDBUS_1
30 # include <dbus/dbus.h>
31 #endif
32 #ifdef SONAME_LIBHAL
33 # include <hal/libhal.h>
34 #endif
35
36 #include "mountmgr.h"
37 #include "winnls.h"
38 #include "excpt.h"
39
40 #include "wine/library.h"
41 #include "wine/exception.h"
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
45
46 #ifdef SONAME_LIBDBUS_1
47
48 #define DBUS_FUNCS \
49     DO_FUNC(dbus_bus_add_match); \
50     DO_FUNC(dbus_bus_get); \
51     DO_FUNC(dbus_bus_remove_match); \
52     DO_FUNC(dbus_connection_add_filter); \
53     DO_FUNC(dbus_connection_read_write_dispatch); \
54     DO_FUNC(dbus_connection_remove_filter); \
55     DO_FUNC(dbus_connection_send_with_reply_and_block); \
56     DO_FUNC(dbus_error_free); \
57     DO_FUNC(dbus_error_init); \
58     DO_FUNC(dbus_error_is_set); \
59     DO_FUNC(dbus_free_string_array); \
60     DO_FUNC(dbus_message_get_args); \
61     DO_FUNC(dbus_message_get_interface); \
62     DO_FUNC(dbus_message_get_member); \
63     DO_FUNC(dbus_message_get_path); \
64     DO_FUNC(dbus_message_get_type); \
65     DO_FUNC(dbus_message_is_signal); \
66     DO_FUNC(dbus_message_iter_append_basic); \
67     DO_FUNC(dbus_message_iter_get_arg_type); \
68     DO_FUNC(dbus_message_iter_get_basic); \
69     DO_FUNC(dbus_message_iter_get_fixed_array); \
70     DO_FUNC(dbus_message_iter_init); \
71     DO_FUNC(dbus_message_iter_init_append); \
72     DO_FUNC(dbus_message_iter_next); \
73     DO_FUNC(dbus_message_iter_recurse); \
74     DO_FUNC(dbus_message_new_method_call); \
75     DO_FUNC(dbus_message_unref);
76
77 #define DO_FUNC(f) static typeof(f) * p_##f
78 DBUS_FUNCS;
79 #undef DO_FUNC
80
81 static int udisks_timeout = -1;
82 static DBusConnection *connection;
83
84 #ifdef SONAME_LIBHAL
85
86 #define HAL_FUNCS \
87     DO_FUNC(libhal_ctx_free); \
88     DO_FUNC(libhal_ctx_init); \
89     DO_FUNC(libhal_ctx_new); \
90     DO_FUNC(libhal_ctx_set_dbus_connection); \
91     DO_FUNC(libhal_ctx_set_device_added); \
92     DO_FUNC(libhal_ctx_set_device_property_modified); \
93     DO_FUNC(libhal_ctx_set_device_removed); \
94     DO_FUNC(libhal_ctx_shutdown); \
95     DO_FUNC(libhal_device_get_property_bool); \
96     DO_FUNC(libhal_device_get_property_string); \
97     DO_FUNC(libhal_device_add_property_watch); \
98     DO_FUNC(libhal_device_remove_property_watch); \
99     DO_FUNC(libhal_free_string); \
100     DO_FUNC(libhal_free_string_array); \
101     DO_FUNC(libhal_get_all_devices)
102
103 #define DO_FUNC(f) static typeof(f) * p_##f
104 HAL_FUNCS;
105 #undef DO_FUNC
106
107 static BOOL load_hal_functions(void)
108 {
109     void *hal_handle;
110     char error[128];
111
112     /* Load libhal with RTLD_GLOBAL so that the dbus symbols are available.
113      * We can't load libdbus directly since libhal may have been built against a
114      * different version but with the same soname. Binary compatibility is for wimps. */
115
116     if (!(hal_handle = wine_dlopen(SONAME_LIBHAL, RTLD_NOW|RTLD_GLOBAL, error, sizeof(error))))
117         goto failed;
118
119 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( RTLD_DEFAULT, #f, error, sizeof(error) ))) goto failed
120     DBUS_FUNCS;
121 #undef DO_FUNC
122
123 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed
124     HAL_FUNCS;
125 #undef DO_FUNC
126
127     udisks_timeout = 3000;  /* don't try for too long if we can fall back to HAL */
128     return TRUE;
129
130 failed:
131     WARN( "failed to load HAL support: %s\n", error );
132     return FALSE;
133 }
134
135 #endif /* SONAME_LIBHAL */
136
137 static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr)
138 {
139     if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_ASSERTION)
140         return EXCEPTION_EXECUTE_HANDLER;
141     return EXCEPTION_CONTINUE_SEARCH;
142 }
143
144 static inline int starts_with( const char *str, const char *prefix )
145 {
146     return !strncmp( str, prefix, strlen(prefix) );
147 }
148
149 static GUID *parse_uuid( GUID *guid, const char *str )
150 {
151     /* standard uuid format */
152     if (strlen(str) == 36)
153     {
154         UNICODE_STRING strW;
155         WCHAR buffer[39];
156
157         if (MultiByteToWideChar( CP_UNIXCP, 0, str, 36, buffer + 1, 36 ))
158         {
159             buffer[0] = '{';
160             buffer[37] = '}';
161             buffer[38] = 0;
162             RtlInitUnicodeString( &strW, buffer );
163             if (!RtlGUIDFromString( &strW, guid )) return guid;
164         }
165     }
166
167     /* check for xxxx-xxxx format (FAT serial number) */
168     if (strlen(str) == 9 && str[4] == '-')
169     {
170         memset( guid, 0, sizeof(*guid) );
171         if (sscanf( str, "%hx-%hx", &guid->Data2, &guid->Data3 ) == 2) return guid;
172     }
173     return NULL;
174 }
175
176 static BOOL load_dbus_functions(void)
177 {
178     void *handle;
179     char error[128];
180
181     if (!(handle = wine_dlopen(SONAME_LIBDBUS_1, RTLD_NOW, error, sizeof(error))))
182         goto failed;
183
184 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( handle, #f, error, sizeof(error) ))) goto failed
185     DBUS_FUNCS;
186 #undef DO_FUNC
187     return TRUE;
188
189 failed:
190     WARN( "failed to load DBUS support: %s\n", error );
191     return FALSE;
192 }
193
194 static const char *udisks_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant )
195 {
196     DBusMessageIter sub;
197     const char *name;
198
199     if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY) return NULL;
200     p_dbus_message_iter_recurse( iter, &sub );
201     p_dbus_message_iter_next( iter );
202     p_dbus_message_iter_get_basic( &sub, &name );
203     p_dbus_message_iter_next( &sub );
204     p_dbus_message_iter_recurse( &sub, variant );
205     return name;
206 }
207
208 static enum device_type udisks_parse_media_compatibility( DBusMessageIter *iter )
209 {
210     DBusMessageIter media;
211     enum device_type drive_type = DEVICE_UNKNOWN;
212
213     p_dbus_message_iter_recurse( iter, &media );
214     while (p_dbus_message_iter_get_arg_type( &media ) == DBUS_TYPE_STRING)
215     {
216         const char *media_type;
217         p_dbus_message_iter_get_basic( &media, &media_type );
218         if (starts_with( media_type, "optical_dvd" ))
219             drive_type = DEVICE_DVD;
220         if (starts_with( media_type, "floppy" ))
221             drive_type = DEVICE_FLOPPY;
222         else if (starts_with( media_type, "optical_" ) && drive_type == DEVICE_UNKNOWN)
223             drive_type = DEVICE_CDROM;
224         p_dbus_message_iter_next( &media );
225     }
226     return drive_type;
227 }
228
229 /* UDisks callback for new device */
230 static void udisks_new_device( const char *udi )
231 {
232     static const char *dev_name = "org.freedesktop.UDisks.Device";
233     DBusMessage *request, *reply;
234     DBusMessageIter iter, variant;
235     DBusError error;
236     const char *device = NULL;
237     const char *mount_point = NULL;
238     const char *type = NULL;
239     GUID guid, *guid_ptr = NULL;
240     int removable = FALSE;
241     enum device_type drive_type = DEVICE_UNKNOWN;
242
243     request = p_dbus_message_new_method_call( "org.freedesktop.UDisks", udi,
244                                               "org.freedesktop.DBus.Properties", "GetAll" );
245     if (!request) return;
246
247     p_dbus_message_iter_init_append( request, &iter );
248     p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &dev_name );
249
250     p_dbus_error_init( &error );
251     reply = p_dbus_connection_send_with_reply_and_block( connection, request, -1, &error );
252     p_dbus_message_unref( request );
253     if (!reply)
254     {
255         WARN( "failed: %s\n", error.message );
256         p_dbus_error_free( &error );
257         return;
258     }
259     p_dbus_error_free( &error );
260
261     p_dbus_message_iter_init( reply, &iter );
262     if (p_dbus_message_iter_get_arg_type( &iter ) == DBUS_TYPE_ARRAY)
263     {
264         const char *name;
265
266         p_dbus_message_iter_recurse( &iter, &iter );
267         while ((name = udisks_next_dict_entry( &iter, &variant )))
268         {
269             if (!strcmp( name, "DeviceFile" ))
270                 p_dbus_message_iter_get_basic( &variant, &device );
271             else if (!strcmp( name, "DeviceIsRemovable" ))
272                 p_dbus_message_iter_get_basic( &variant, &removable );
273             else if (!strcmp( name, "IdType" ))
274                 p_dbus_message_iter_get_basic( &variant, &type );
275             else if (!strcmp( name, "DriveMediaCompatibility" ))
276                 drive_type = udisks_parse_media_compatibility( &variant );
277             else if (!strcmp( name, "DeviceMountPaths" ))
278             {
279                 DBusMessageIter paths;
280                 p_dbus_message_iter_recurse( &variant, &paths );
281                 if (p_dbus_message_iter_get_arg_type( &paths ) == DBUS_TYPE_STRING)
282                     p_dbus_message_iter_get_basic( &paths, &mount_point );
283             }
284             else if (!strcmp( name, "IdUuid" ))
285             {
286                 char *uuid_str;
287                 p_dbus_message_iter_get_basic( &variant, &uuid_str );
288                 guid_ptr = parse_uuid( &guid, uuid_str );
289             }
290         }
291     }
292
293     TRACE( "udi %s device %s mount point %s uuid %s type %s removable %u\n",
294            debugstr_a(udi), debugstr_a(device), debugstr_a(mount_point),
295            debugstr_guid(guid_ptr), debugstr_a(type), removable );
296
297     if (type)
298     {
299         if (!strcmp( type, "iso9660" ))
300         {
301             removable = TRUE;
302             drive_type = DEVICE_CDROM;
303         }
304         else if (!strcmp( type, "udf" ))
305         {
306             removable = TRUE;
307             drive_type = DEVICE_DVD;
308         }
309     }
310
311     if (device)
312     {
313         if (removable) add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
314         else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
315     }
316
317     p_dbus_message_unref( reply );
318 }
319
320 /* UDisks callback for removed device */
321 static void udisks_removed_device( const char *udi )
322 {
323     TRACE( "removed %s\n", wine_dbgstr_a(udi) );
324
325     if (!remove_dos_device( -1, udi )) remove_volume( udi );
326 }
327
328 /* UDisks callback for changed device */
329 static void udisks_changed_device( const char *udi )
330 {
331     udisks_new_device( udi );
332 }
333
334 static BOOL udisks_enumerate_devices(void)
335 {
336     DBusMessage *request, *reply;
337     DBusError error;
338     char **paths;
339     int i, count;
340
341     request = p_dbus_message_new_method_call( "org.freedesktop.UDisks", "/org/freedesktop/UDisks",
342                                               "org.freedesktop.UDisks", "EnumerateDevices" );
343     if (!request) return FALSE;
344
345     p_dbus_error_init( &error );
346     reply = p_dbus_connection_send_with_reply_and_block( connection, request, udisks_timeout, &error );
347     p_dbus_message_unref( request );
348     if (!reply)
349     {
350         WARN( "failed: %s\n", error.message );
351         p_dbus_error_free( &error );
352         return FALSE;
353     }
354     p_dbus_error_free( &error );
355
356     if (p_dbus_message_get_args( reply, &error, DBUS_TYPE_ARRAY,
357                                  DBUS_TYPE_OBJECT_PATH, &paths, &count, DBUS_TYPE_INVALID ))
358     {
359         for (i = 0; i < count; i++) udisks_new_device( paths[i] );
360         p_dbus_free_string_array( paths );
361     }
362     else WARN( "unexpected args in EnumerateDevices reply\n" );
363
364     p_dbus_message_unref( reply );
365     return TRUE;
366 }
367
368 /* to make things easier, UDisks2 stores strings as array of bytes instead of strings... */
369 static const char *udisks2_string_from_array( DBusMessageIter *iter )
370 {
371     DBusMessageIter string;
372     const char *array;
373     int size;
374
375     p_dbus_message_iter_recurse( iter, &string );
376     p_dbus_message_iter_get_fixed_array( &string, &array, &size );
377     return array;
378 }
379
380 /* find the drive entry in the dictionary and get its parameters */
381 static void udisks2_get_drive_info( const char *drive_name, DBusMessageIter *dict,
382                                     enum device_type *drive_type, int *removable )
383 {
384     DBusMessageIter iter, drive, variant;
385     const char *name;
386
387     p_dbus_message_iter_recurse( dict, &iter );
388     while ((name = udisks_next_dict_entry( &iter, &drive )))
389     {
390         if (strcmp( name, drive_name )) continue;
391         while ((name = udisks_next_dict_entry( &drive, &iter )))
392         {
393             if (strcmp( name, "org.freedesktop.UDisks2.Drive" )) continue;
394             while ((name = udisks_next_dict_entry( &iter, &variant )))
395             {
396                 if (!strcmp( name, "Removable" ))
397                     p_dbus_message_iter_get_basic( &variant, removable );
398                 else if (!strcmp( name, "MediaCompatibility" ))
399                     *drive_type = udisks_parse_media_compatibility( &variant );
400             }
401         }
402     }
403 }
404
405 static void udisks2_add_device( const char *udi, DBusMessageIter *dict, DBusMessageIter *block )
406 {
407     DBusMessageIter iter, variant, paths, string;
408     const char *device = NULL;
409     const char *mount_point = NULL;
410     const char *type = NULL;
411     const char *drive = NULL;
412     GUID guid, *guid_ptr = NULL;
413     const char *iface, *name;
414     int removable = FALSE;
415     enum device_type drive_type = DEVICE_UNKNOWN;
416
417     while ((iface = udisks_next_dict_entry( block, &iter )))
418     {
419         if (!strcmp( iface, "org.freedesktop.UDisks2.Filesystem" ))
420         {
421             while ((name = udisks_next_dict_entry( &iter, &variant )))
422             {
423                 if (!strcmp( name, "MountPoints" ))
424                 {
425                     p_dbus_message_iter_recurse( &variant, &paths );
426                     if (p_dbus_message_iter_get_arg_type( &paths ) == DBUS_TYPE_ARRAY)
427                     {
428                         p_dbus_message_iter_recurse( &variant, &string );
429                         mount_point = udisks2_string_from_array( &string );
430                     }
431                 }
432             }
433         }
434         if (!strcmp( iface, "org.freedesktop.UDisks2.Block" ))
435         {
436             while ((name = udisks_next_dict_entry( &iter, &variant )))
437             {
438                 if (!strcmp( name, "Device" ))
439                     device = udisks2_string_from_array( &variant );
440                 else if (!strcmp( name, "IdType" ))
441                     p_dbus_message_iter_get_basic( &variant, &type );
442                 else if (!strcmp( name, "Drive" ))
443                 {
444                     p_dbus_message_iter_get_basic( &variant, &drive );
445                     udisks2_get_drive_info( drive, dict, &drive_type, &removable );
446                 }
447                 else if (!strcmp( name, "IdUUID" ))
448                 {
449                     char *uuid_str;
450                     p_dbus_message_iter_get_basic( &variant, &uuid_str );
451                     guid_ptr = parse_uuid( &guid, uuid_str );
452                 }
453             }
454         }
455     }
456
457     TRACE( "udi %s device %s mount point %s uuid %s type %s removable %u\n",
458            debugstr_a(udi), debugstr_a(device), debugstr_a(mount_point),
459            debugstr_guid(guid_ptr), debugstr_a(type), removable );
460
461     if (type)
462     {
463         if (!strcmp( type, "iso9660" ))
464         {
465             removable = TRUE;
466             drive_type = DEVICE_CDROM;
467         }
468         else if (!strcmp( type, "udf" ))
469         {
470             removable = TRUE;
471             drive_type = DEVICE_DVD;
472         }
473     }
474     if (device)
475     {
476         if (removable) add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
477         else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
478     }
479 }
480
481 /* UDisks2 is almost, but not quite, entirely unlike UDisks.
482  * It would have been easy to make it backwards compatible, but where would be the fun in that?
483  */
484 static BOOL udisks2_add_devices( const char *changed )
485 {
486     DBusMessage *request, *reply;
487     DBusMessageIter dict, iter, block;
488     DBusError error;
489     const char *udi;
490
491     request = p_dbus_message_new_method_call( "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2",
492                                               "org.freedesktop.DBus.ObjectManager", "GetManagedObjects" );
493     if (!request) return FALSE;
494
495     p_dbus_error_init( &error );
496     reply = p_dbus_connection_send_with_reply_and_block( connection, request, udisks_timeout, &error );
497     p_dbus_message_unref( request );
498     if (!reply)
499     {
500         WARN( "failed: %s\n", error.message );
501         p_dbus_error_free( &error );
502         return FALSE;
503     }
504     p_dbus_error_free( &error );
505
506     p_dbus_message_iter_init( reply, &dict );
507     if (p_dbus_message_iter_get_arg_type( &dict ) == DBUS_TYPE_ARRAY)
508     {
509         p_dbus_message_iter_recurse( &dict, &iter );
510         while ((udi = udisks_next_dict_entry( &iter, &block )))
511         {
512             if (!starts_with( udi, "/org/freedesktop/UDisks2/block_devices/" )) continue;
513             if (changed && strcmp( changed, udi )) continue;
514             udisks2_add_device( udi, &dict, &block );
515         }
516     }
517     else WARN( "unexpected args in GetManagedObjects reply\n" );
518
519     p_dbus_message_unref( reply );
520     return TRUE;
521 }
522
523 static DBusHandlerResult udisks_filter( DBusConnection *ctx, DBusMessage *msg, void *user_data )
524 {
525     char *path;
526     DBusError error;
527
528     p_dbus_error_init( &error );
529
530     /* udisks signals */
531     if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceAdded" ) &&
532         p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
533     {
534         udisks_new_device( path );
535     }
536     else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceRemoved" ) &&
537              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
538     {
539         udisks_removed_device( path );
540     }
541     else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceChanged" ) &&
542              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
543     {
544         udisks_changed_device( path );
545     }
546     /* udisks2 signals */
547     else if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded" ) &&
548              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
549     {
550         TRACE( "added %s\n", wine_dbgstr_a(path) );
551         udisks2_add_devices( path );
552     }
553     else if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved" ) &&
554              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
555     {
556         udisks_removed_device( path );
557     }
558     else if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus.Properties", "PropertiesChanged" ))
559     {
560         const char *udi = p_dbus_message_get_path( msg );
561         TRACE( "changed %s\n", wine_dbgstr_a(udi) );
562         udisks2_add_devices( udi );
563     }
564     else TRACE( "ignoring message type=%d path=%s interface=%s method=%s\n",
565                 p_dbus_message_get_type( msg ), p_dbus_message_get_path( msg ),
566                 p_dbus_message_get_interface( msg ), p_dbus_message_get_member( msg ) );
567
568     p_dbus_error_free( &error );
569     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
570 }
571
572 #ifdef SONAME_LIBHAL
573
574 /* HAL callback for new device */
575 static void hal_new_device( LibHalContext *ctx, const char *udi )
576 {
577     DBusError error;
578     char *parent = NULL;
579     char *mount_point = NULL;
580     char *device = NULL;
581     char *type = NULL;
582     char *uuid_str = NULL;
583     GUID guid, *guid_ptr = NULL;
584     enum device_type drive_type;
585
586     p_dbus_error_init( &error );
587
588     if (!(device = p_libhal_device_get_property_string( ctx, udi, "block.device", &error )))
589         goto done;
590
591     if (!(mount_point = p_libhal_device_get_property_string( ctx, udi, "volume.mount_point", &error )))
592         goto done;
593
594     if (!(parent = p_libhal_device_get_property_string( ctx, udi, "info.parent", &error )))
595         goto done;
596
597     if (!(uuid_str = p_libhal_device_get_property_string( ctx, udi, "volume.uuid", &error )))
598         p_dbus_error_free( &error );  /* ignore error */
599     else
600         guid_ptr = parse_uuid( &guid, uuid_str );
601
602     if (!(type = p_libhal_device_get_property_string( ctx, parent, "storage.drive_type", &error )))
603         p_dbus_error_free( &error );  /* ignore error */
604
605     if (type && !strcmp( type, "cdrom" )) drive_type = DEVICE_CDROM;
606     else if (type && !strcmp( type, "floppy" )) drive_type = DEVICE_FLOPPY;
607     else drive_type = DEVICE_UNKNOWN;
608
609     if (p_libhal_device_get_property_bool( ctx, parent, "storage.removable", &error ))
610     {
611         add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
612         /* add property watch for mount point */
613         p_libhal_device_add_property_watch( ctx, udi, &error );
614     }
615     else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
616
617 done:
618     if (type) p_libhal_free_string( type );
619     if (parent) p_libhal_free_string( parent );
620     if (device) p_libhal_free_string( device );
621     if (uuid_str) p_libhal_free_string( uuid_str );
622     if (mount_point) p_libhal_free_string( mount_point );
623     p_dbus_error_free( &error );
624 }
625
626 /* HAL callback for removed device */
627 static void hal_removed_device( LibHalContext *ctx, const char *udi )
628 {
629     DBusError error;
630
631     TRACE( "removed %s\n", wine_dbgstr_a(udi) );
632
633     if (!remove_dos_device( -1, udi ))
634     {
635         p_dbus_error_init( &error );
636         p_libhal_device_remove_property_watch( ctx, udi, &error );
637         p_dbus_error_free( &error );
638     }
639     else remove_volume( udi );
640 }
641
642 /* HAL callback for property changes */
643 static void hal_property_modified (LibHalContext *ctx, const char *udi,
644                                    const char *key, dbus_bool_t is_removed, dbus_bool_t is_added)
645 {
646     TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi), wine_dbgstr_a(key),
647            is_added ? "added" : is_removed ? "removed" : "modified" );
648
649     if (!strcmp( key, "volume.mount_point" )) hal_new_device( ctx, udi );
650 }
651
652 static BOOL hal_enumerate_devices(void)
653 {
654     LibHalContext *ctx;
655     DBusError error;
656     int i, num;
657     char **list;
658
659     if (!p_libhal_ctx_new) return FALSE;
660     if (!(ctx = p_libhal_ctx_new())) return FALSE;
661
662     p_libhal_ctx_set_dbus_connection( ctx, connection );
663     p_libhal_ctx_set_device_added( ctx, hal_new_device );
664     p_libhal_ctx_set_device_removed( ctx, hal_removed_device );
665     p_libhal_ctx_set_device_property_modified( ctx, hal_property_modified );
666
667     p_dbus_error_init( &error );
668     if (!p_libhal_ctx_init( ctx, &error ))
669     {
670         WARN( "HAL context init failed: %s\n", error.message );
671         p_dbus_error_free( &error );
672         return FALSE;
673     }
674
675     /* retrieve all existing devices */
676     if (!(list = p_libhal_get_all_devices( ctx, &num, &error ))) p_dbus_error_free( &error );
677     else
678     {
679         for (i = 0; i < num; i++) hal_new_device( ctx, list[i] );
680         p_libhal_free_string_array( list );
681     }
682     return TRUE;
683 }
684
685 #endif /* SONAME_LIBHAL */
686
687 static DWORD WINAPI dbus_thread( void *arg )
688 {
689     static const char udisks_match[] = "type='signal',"
690                                        "interface='org.freedesktop.UDisks',"
691                                        "sender='org.freedesktop.UDisks'";
692     static const char udisks2_match_interfaces[] = "type='signal',"
693                                                    "interface='org.freedesktop.DBus.ObjectManager',"
694                                                    "path='/org/freedesktop/UDisks2'";
695     static const char udisks2_match_properties[] = "type='signal',"
696                                                    "interface='org.freedesktop.DBus.Properties'";
697
698
699     DBusError error;
700
701     p_dbus_error_init( &error );
702     if (!(connection = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error )))
703     {
704         WARN( "failed to get system dbus connection: %s\n", error.message );
705         p_dbus_error_free( &error );
706         return 1;
707     }
708
709     /* first try UDisks2 */
710
711     p_dbus_connection_add_filter( connection, udisks_filter, NULL, NULL );
712     p_dbus_bus_add_match( connection, udisks2_match_interfaces, &error );
713     p_dbus_bus_add_match( connection, udisks2_match_properties, &error );
714     if (udisks2_add_devices( NULL )) goto found;
715     p_dbus_bus_remove_match( connection, udisks2_match_interfaces, &error );
716     p_dbus_bus_remove_match( connection, udisks2_match_properties, &error );
717
718     /* then try UDisks */
719
720     p_dbus_bus_add_match( connection, udisks_match, &error );
721     if (udisks_enumerate_devices()) goto found;
722     p_dbus_bus_remove_match( connection, udisks_match, &error );
723     p_dbus_connection_remove_filter( connection, udisks_filter, NULL );
724
725     /* then finally HAL */
726
727 #ifdef SONAME_LIBHAL
728     if (!hal_enumerate_devices())
729     {
730         p_dbus_error_free( &error );
731         return 1;
732     }
733 #endif
734
735 found:
736     __TRY
737     {
738         while (p_dbus_connection_read_write_dispatch( connection, -1 )) /* nothing */ ;
739     }
740     __EXCEPT( assert_fault )
741     {
742         WARN( "dbus assertion failure, disabling support\n" );
743         return 1;
744     }
745     __ENDTRY;
746
747     return 0;
748 }
749
750 void initialize_dbus(void)
751 {
752     HANDLE handle;
753
754 #ifdef SONAME_LIBHAL
755     if (!load_hal_functions())
756 #endif
757         if (!load_dbus_functions()) return;
758     if (!(handle = CreateThread( NULL, 0, dbus_thread, NULL, 0, NULL ))) return;
759     CloseHandle( handle );
760 }
761
762 #else  /* SONAME_LIBDBUS_1 */
763
764 void initialize_dbus(void)
765 {
766     TRACE( "Skipping, DBUS support not compiled in\n" );
767 }
768
769 #endif  /* SONAME_LIBDBUS_1 */