dmusic: Set instrument stream position where the instrument begins, not at the beginn...
[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                     const char *uuid_str;
450                     if (p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_ARRAY)
451                         uuid_str = udisks2_string_from_array( &variant );
452                     else
453                         p_dbus_message_iter_get_basic( &variant, &uuid_str );
454                     guid_ptr = parse_uuid( &guid, uuid_str );
455                 }
456             }
457         }
458     }
459
460     TRACE( "udi %s device %s mount point %s uuid %s type %s removable %u\n",
461            debugstr_a(udi), debugstr_a(device), debugstr_a(mount_point),
462            debugstr_guid(guid_ptr), debugstr_a(type), removable );
463
464     if (type)
465     {
466         if (!strcmp( type, "iso9660" ))
467         {
468             removable = TRUE;
469             drive_type = DEVICE_CDROM;
470         }
471         else if (!strcmp( type, "udf" ))
472         {
473             removable = TRUE;
474             drive_type = DEVICE_DVD;
475         }
476     }
477     if (device)
478     {
479         if (removable) add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
480         else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
481     }
482 }
483
484 /* UDisks2 is almost, but not quite, entirely unlike UDisks.
485  * It would have been easy to make it backwards compatible, but where would be the fun in that?
486  */
487 static BOOL udisks2_add_devices( const char *changed )
488 {
489     DBusMessage *request, *reply;
490     DBusMessageIter dict, iter, block;
491     DBusError error;
492     const char *udi;
493
494     request = p_dbus_message_new_method_call( "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2",
495                                               "org.freedesktop.DBus.ObjectManager", "GetManagedObjects" );
496     if (!request) return FALSE;
497
498     p_dbus_error_init( &error );
499     reply = p_dbus_connection_send_with_reply_and_block( connection, request, udisks_timeout, &error );
500     p_dbus_message_unref( request );
501     if (!reply)
502     {
503         WARN( "failed: %s\n", error.message );
504         p_dbus_error_free( &error );
505         return FALSE;
506     }
507     p_dbus_error_free( &error );
508
509     p_dbus_message_iter_init( reply, &dict );
510     if (p_dbus_message_iter_get_arg_type( &dict ) == DBUS_TYPE_ARRAY)
511     {
512         p_dbus_message_iter_recurse( &dict, &iter );
513         while ((udi = udisks_next_dict_entry( &iter, &block )))
514         {
515             if (!starts_with( udi, "/org/freedesktop/UDisks2/block_devices/" )) continue;
516             if (changed && strcmp( changed, udi )) continue;
517             udisks2_add_device( udi, &dict, &block );
518         }
519     }
520     else WARN( "unexpected args in GetManagedObjects reply\n" );
521
522     p_dbus_message_unref( reply );
523     return TRUE;
524 }
525
526 static DBusHandlerResult udisks_filter( DBusConnection *ctx, DBusMessage *msg, void *user_data )
527 {
528     char *path;
529     DBusError error;
530
531     p_dbus_error_init( &error );
532
533     /* udisks signals */
534     if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceAdded" ) &&
535         p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
536     {
537         udisks_new_device( path );
538     }
539     else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceRemoved" ) &&
540              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
541     {
542         udisks_removed_device( path );
543     }
544     else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceChanged" ) &&
545              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
546     {
547         udisks_changed_device( path );
548     }
549     /* udisks2 signals */
550     else if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded" ) &&
551              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
552     {
553         TRACE( "added %s\n", wine_dbgstr_a(path) );
554         udisks2_add_devices( path );
555     }
556     else if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved" ) &&
557              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
558     {
559         udisks_removed_device( path );
560     }
561     else if (p_dbus_message_is_signal( msg, "org.freedesktop.DBus.Properties", "PropertiesChanged" ))
562     {
563         const char *udi = p_dbus_message_get_path( msg );
564         TRACE( "changed %s\n", wine_dbgstr_a(udi) );
565         udisks2_add_devices( udi );
566     }
567     else TRACE( "ignoring message type=%d path=%s interface=%s method=%s\n",
568                 p_dbus_message_get_type( msg ), p_dbus_message_get_path( msg ),
569                 p_dbus_message_get_interface( msg ), p_dbus_message_get_member( msg ) );
570
571     p_dbus_error_free( &error );
572     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
573 }
574
575 #ifdef SONAME_LIBHAL
576
577 /* HAL callback for new device */
578 static void hal_new_device( LibHalContext *ctx, const char *udi )
579 {
580     DBusError error;
581     char *parent = NULL;
582     char *mount_point = NULL;
583     char *device = NULL;
584     char *type = NULL;
585     char *uuid_str = NULL;
586     GUID guid, *guid_ptr = NULL;
587     enum device_type drive_type;
588
589     p_dbus_error_init( &error );
590
591     if (!(device = p_libhal_device_get_property_string( ctx, udi, "block.device", &error )))
592         goto done;
593
594     if (!(mount_point = p_libhal_device_get_property_string( ctx, udi, "volume.mount_point", &error )))
595         goto done;
596
597     if (!(parent = p_libhal_device_get_property_string( ctx, udi, "info.parent", &error )))
598         goto done;
599
600     if (!(uuid_str = p_libhal_device_get_property_string( ctx, udi, "volume.uuid", &error )))
601         p_dbus_error_free( &error );  /* ignore error */
602     else
603         guid_ptr = parse_uuid( &guid, uuid_str );
604
605     if (!(type = p_libhal_device_get_property_string( ctx, parent, "storage.drive_type", &error )))
606         p_dbus_error_free( &error );  /* ignore error */
607
608     if (type && !strcmp( type, "cdrom" )) drive_type = DEVICE_CDROM;
609     else if (type && !strcmp( type, "floppy" )) drive_type = DEVICE_FLOPPY;
610     else drive_type = DEVICE_UNKNOWN;
611
612     if (p_libhal_device_get_property_bool( ctx, parent, "storage.removable", &error ))
613     {
614         add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
615         /* add property watch for mount point */
616         p_libhal_device_add_property_watch( ctx, udi, &error );
617     }
618     else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
619
620 done:
621     if (type) p_libhal_free_string( type );
622     if (parent) p_libhal_free_string( parent );
623     if (device) p_libhal_free_string( device );
624     if (uuid_str) p_libhal_free_string( uuid_str );
625     if (mount_point) p_libhal_free_string( mount_point );
626     p_dbus_error_free( &error );
627 }
628
629 /* HAL callback for removed device */
630 static void hal_removed_device( LibHalContext *ctx, const char *udi )
631 {
632     DBusError error;
633
634     TRACE( "removed %s\n", wine_dbgstr_a(udi) );
635
636     if (!remove_dos_device( -1, udi ))
637     {
638         p_dbus_error_init( &error );
639         p_libhal_device_remove_property_watch( ctx, udi, &error );
640         p_dbus_error_free( &error );
641     }
642     else remove_volume( udi );
643 }
644
645 /* HAL callback for property changes */
646 static void hal_property_modified (LibHalContext *ctx, const char *udi,
647                                    const char *key, dbus_bool_t is_removed, dbus_bool_t is_added)
648 {
649     TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi), wine_dbgstr_a(key),
650            is_added ? "added" : is_removed ? "removed" : "modified" );
651
652     if (!strcmp( key, "volume.mount_point" )) hal_new_device( ctx, udi );
653 }
654
655 static BOOL hal_enumerate_devices(void)
656 {
657     LibHalContext *ctx;
658     DBusError error;
659     int i, num;
660     char **list;
661
662     if (!p_libhal_ctx_new) return FALSE;
663     if (!(ctx = p_libhal_ctx_new())) return FALSE;
664
665     p_libhal_ctx_set_dbus_connection( ctx, connection );
666     p_libhal_ctx_set_device_added( ctx, hal_new_device );
667     p_libhal_ctx_set_device_removed( ctx, hal_removed_device );
668     p_libhal_ctx_set_device_property_modified( ctx, hal_property_modified );
669
670     p_dbus_error_init( &error );
671     if (!p_libhal_ctx_init( ctx, &error ))
672     {
673         WARN( "HAL context init failed: %s\n", error.message );
674         p_dbus_error_free( &error );
675         return FALSE;
676     }
677
678     /* retrieve all existing devices */
679     if (!(list = p_libhal_get_all_devices( ctx, &num, &error ))) p_dbus_error_free( &error );
680     else
681     {
682         for (i = 0; i < num; i++) hal_new_device( ctx, list[i] );
683         p_libhal_free_string_array( list );
684     }
685     return TRUE;
686 }
687
688 #endif /* SONAME_LIBHAL */
689
690 static DWORD WINAPI dbus_thread( void *arg )
691 {
692     static const char udisks_match[] = "type='signal',"
693                                        "interface='org.freedesktop.UDisks',"
694                                        "sender='org.freedesktop.UDisks'";
695     static const char udisks2_match_interfaces[] = "type='signal',"
696                                                    "interface='org.freedesktop.DBus.ObjectManager',"
697                                                    "path='/org/freedesktop/UDisks2'";
698     static const char udisks2_match_properties[] = "type='signal',"
699                                                    "interface='org.freedesktop.DBus.Properties'";
700
701
702     DBusError error;
703
704     p_dbus_error_init( &error );
705     if (!(connection = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error )))
706     {
707         WARN( "failed to get system dbus connection: %s\n", error.message );
708         p_dbus_error_free( &error );
709         return 1;
710     }
711
712     /* first try UDisks2 */
713
714     p_dbus_connection_add_filter( connection, udisks_filter, NULL, NULL );
715     p_dbus_bus_add_match( connection, udisks2_match_interfaces, &error );
716     p_dbus_bus_add_match( connection, udisks2_match_properties, &error );
717     if (udisks2_add_devices( NULL )) goto found;
718     p_dbus_bus_remove_match( connection, udisks2_match_interfaces, &error );
719     p_dbus_bus_remove_match( connection, udisks2_match_properties, &error );
720
721     /* then try UDisks */
722
723     p_dbus_bus_add_match( connection, udisks_match, &error );
724     if (udisks_enumerate_devices()) goto found;
725     p_dbus_bus_remove_match( connection, udisks_match, &error );
726     p_dbus_connection_remove_filter( connection, udisks_filter, NULL );
727
728     /* then finally HAL */
729
730 #ifdef SONAME_LIBHAL
731     if (!hal_enumerate_devices())
732     {
733         p_dbus_error_free( &error );
734         return 1;
735     }
736 #endif
737
738 found:
739     __TRY
740     {
741         while (p_dbus_connection_read_write_dispatch( connection, -1 )) /* nothing */ ;
742     }
743     __EXCEPT( assert_fault )
744     {
745         WARN( "dbus assertion failure, disabling support\n" );
746         return 1;
747     }
748     __ENDTRY;
749
750     return 0;
751 }
752
753 void initialize_dbus(void)
754 {
755     HANDLE handle;
756
757 #ifdef SONAME_LIBHAL
758     if (!load_hal_functions())
759 #endif
760         if (!load_dbus_functions()) return;
761     if (!(handle = CreateThread( NULL, 0, dbus_thread, NULL, 0, NULL ))) return;
762     CloseHandle( handle );
763 }
764
765 #else  /* SONAME_LIBDBUS_1 */
766
767 void initialize_dbus(void)
768 {
769     TRACE( "Skipping, DBUS support not compiled in\n" );
770 }
771
772 #endif  /* SONAME_LIBDBUS_1 */