d3drm: Handle texture associated with the material when loading a mesh.
[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_init); \
70     DO_FUNC(dbus_message_iter_init_append); \
71     DO_FUNC(dbus_message_iter_next); \
72     DO_FUNC(dbus_message_iter_recurse); \
73     DO_FUNC(dbus_message_new_method_call); \
74     DO_FUNC(dbus_message_unref);
75
76 #define DO_FUNC(f) static typeof(f) * p_##f
77 DBUS_FUNCS;
78 #undef DO_FUNC
79
80 static int udisks_timeout = -1;
81 static DBusConnection *connection;
82
83 #ifdef SONAME_LIBHAL
84
85 #define HAL_FUNCS \
86     DO_FUNC(libhal_ctx_free); \
87     DO_FUNC(libhal_ctx_init); \
88     DO_FUNC(libhal_ctx_new); \
89     DO_FUNC(libhal_ctx_set_dbus_connection); \
90     DO_FUNC(libhal_ctx_set_device_added); \
91     DO_FUNC(libhal_ctx_set_device_property_modified); \
92     DO_FUNC(libhal_ctx_set_device_removed); \
93     DO_FUNC(libhal_ctx_shutdown); \
94     DO_FUNC(libhal_device_get_property_bool); \
95     DO_FUNC(libhal_device_get_property_string); \
96     DO_FUNC(libhal_device_add_property_watch); \
97     DO_FUNC(libhal_device_remove_property_watch); \
98     DO_FUNC(libhal_free_string); \
99     DO_FUNC(libhal_free_string_array); \
100     DO_FUNC(libhal_get_all_devices)
101
102 #define DO_FUNC(f) static typeof(f) * p_##f
103 HAL_FUNCS;
104 #undef DO_FUNC
105
106 static BOOL load_hal_functions(void)
107 {
108     void *hal_handle;
109     char error[128];
110
111     /* Load libhal with RTLD_GLOBAL so that the dbus symbols are available.
112      * We can't load libdbus directly since libhal may have been built against a
113      * different version but with the same soname. Binary compatibility is for wimps. */
114
115     if (!(hal_handle = wine_dlopen(SONAME_LIBHAL, RTLD_NOW|RTLD_GLOBAL, error, sizeof(error))))
116         goto failed;
117
118 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( RTLD_DEFAULT, #f, error, sizeof(error) ))) goto failed
119     DBUS_FUNCS;
120 #undef DO_FUNC
121
122 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed
123     HAL_FUNCS;
124 #undef DO_FUNC
125
126     udisks_timeout = 3000;  /* don't try for too long if we can fall back to HAL */
127     return TRUE;
128
129 failed:
130     WARN( "failed to load HAL support: %s\n", error );
131     return FALSE;
132 }
133
134 #endif /* SONAME_LIBHAL */
135
136 static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr)
137 {
138     if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_ASSERTION)
139         return EXCEPTION_EXECUTE_HANDLER;
140     return EXCEPTION_CONTINUE_SEARCH;
141 }
142
143 static GUID *parse_uuid( GUID *guid, const char *str )
144 {
145     /* standard uuid format */
146     if (strlen(str) == 36)
147     {
148         UNICODE_STRING strW;
149         WCHAR buffer[39];
150
151         if (MultiByteToWideChar( CP_UNIXCP, 0, str, 36, buffer + 1, 36 ))
152         {
153             buffer[0] = '{';
154             buffer[37] = '}';
155             buffer[38] = 0;
156             RtlInitUnicodeString( &strW, buffer );
157             if (!RtlGUIDFromString( &strW, guid )) return guid;
158         }
159     }
160
161     /* check for xxxx-xxxx format (FAT serial number) */
162     if (strlen(str) == 9 && str[4] == '-')
163     {
164         memset( guid, 0, sizeof(*guid) );
165         if (sscanf( str, "%hx-%hx", &guid->Data2, &guid->Data3 ) == 2) return guid;
166     }
167     return NULL;
168 }
169
170 static BOOL load_dbus_functions(void)
171 {
172     void *handle;
173     char error[128];
174
175     if (!(handle = wine_dlopen(SONAME_LIBDBUS_1, RTLD_NOW, error, sizeof(error))))
176         goto failed;
177
178 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( handle, #f, error, sizeof(error) ))) goto failed
179     DBUS_FUNCS;
180 #undef DO_FUNC
181     return TRUE;
182
183 failed:
184     WARN( "failed to load DBUS support: %s\n", error );
185     return FALSE;
186 }
187
188 static const char *udisks_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant )
189 {
190     DBusMessageIter sub;
191     const char *name;
192
193     if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY) return NULL;
194     p_dbus_message_iter_recurse( iter, &sub );
195     p_dbus_message_iter_next( iter );
196     p_dbus_message_iter_get_basic( &sub, &name );
197     p_dbus_message_iter_next( &sub );
198     p_dbus_message_iter_recurse( &sub, variant );
199     return name;
200 }
201
202 /* UDisks callback for new device */
203 static void udisks_new_device( const char *udi )
204 {
205     static const char *dev_name = "org.freedesktop.UDisks.Device";
206     DBusMessage *request, *reply;
207     DBusMessageIter iter, variant;
208     DBusError error;
209     const char *device = NULL;
210     const char *mount_point = NULL;
211     const char *type = NULL;
212     GUID guid, *guid_ptr = NULL;
213     int removable = FALSE;
214     enum device_type drive_type = DEVICE_UNKNOWN;
215
216     request = p_dbus_message_new_method_call( "org.freedesktop.UDisks", udi,
217                                               "org.freedesktop.DBus.Properties", "GetAll" );
218     if (!request) return;
219
220     p_dbus_message_iter_init_append( request, &iter );
221     p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &dev_name );
222
223     p_dbus_error_init( &error );
224     reply = p_dbus_connection_send_with_reply_and_block( connection, request, -1, &error );
225     p_dbus_message_unref( request );
226     if (!reply)
227     {
228         WARN( "failed: %s\n", error.message );
229         p_dbus_error_free( &error );
230         return;
231     }
232     p_dbus_error_free( &error );
233
234     p_dbus_message_iter_init( reply, &iter );
235     if (p_dbus_message_iter_get_arg_type( &iter ) == DBUS_TYPE_ARRAY)
236     {
237         const char *name;
238
239         p_dbus_message_iter_recurse( &iter, &iter );
240         while ((name = udisks_next_dict_entry( &iter, &variant )))
241         {
242             if (!strcmp( name, "DeviceFile" ))
243                 p_dbus_message_iter_get_basic( &variant, &device );
244             else if (!strcmp( name, "DeviceIsRemovable" ))
245                 p_dbus_message_iter_get_basic( &variant, &removable );
246             else if (!strcmp( name, "IdType" ))
247                 p_dbus_message_iter_get_basic( &variant, &type );
248             else if (!strcmp( name, "DriveMediaCompatibility" ))
249             {
250                 DBusMessageIter media;
251                 p_dbus_message_iter_recurse( &variant, &media );
252                 while (p_dbus_message_iter_get_arg_type( &media ) == DBUS_TYPE_STRING)
253                 {
254                     const char *media_type;
255                     p_dbus_message_iter_get_basic( &media, &media_type );
256                     if (!strncmp( media_type, "optical_dvd", 11 ))
257                         drive_type = DEVICE_DVD;
258                     if (!strncmp( media_type, "floppy", 6 ))
259                         drive_type = DEVICE_FLOPPY;
260                     else if (!strncmp( media_type, "optical_", 8 ) && drive_type == DEVICE_UNKNOWN)
261                         drive_type = DEVICE_CDROM;
262                     p_dbus_message_iter_next( &media );
263                 }
264             }
265             else if (!strcmp( name, "DeviceMountPaths" ))
266             {
267                 DBusMessageIter paths;
268                 p_dbus_message_iter_recurse( &variant, &paths );
269                 if (p_dbus_message_iter_get_arg_type( &paths ) == DBUS_TYPE_STRING)
270                     p_dbus_message_iter_get_basic( &paths, &mount_point );
271             }
272             else if (!strcmp( name, "IdUuid" ))
273             {
274                 char *uuid_str;
275                 p_dbus_message_iter_get_basic( &variant, &uuid_str );
276                 guid_ptr = parse_uuid( &guid, uuid_str );
277             }
278         }
279     }
280
281     TRACE( "udi %s device %s mount point %s uuid %s type %s removable %u\n",
282            debugstr_a(udi), debugstr_a(device), debugstr_a(mount_point),
283            debugstr_guid(guid_ptr), debugstr_a(type), removable );
284
285     if (type)
286     {
287         if (!strcmp( type, "iso9660" ))
288         {
289             removable = TRUE;
290             drive_type = DEVICE_CDROM;
291         }
292         else if (!strcmp( type, "udf" ))
293         {
294             removable = TRUE;
295             drive_type = DEVICE_DVD;
296         }
297     }
298
299     if (device)
300     {
301         if (removable) add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
302         else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
303     }
304
305     p_dbus_message_unref( reply );
306 }
307
308 /* UDisks callback for removed device */
309 static void udisks_removed_device( const char *udi )
310 {
311     TRACE( "removed %s\n", wine_dbgstr_a(udi) );
312
313     if (!remove_dos_device( -1, udi )) remove_volume( udi );
314 }
315
316 /* UDisks callback for changed device */
317 static void udisks_changed_device( const char *udi )
318 {
319     udisks_new_device( udi );
320 }
321
322 static BOOL udisks_enumerate_devices(void)
323 {
324     DBusMessage *request, *reply;
325     DBusError error;
326     char **paths;
327     int i, count;
328
329     request = p_dbus_message_new_method_call( "org.freedesktop.UDisks", "/org/freedesktop/UDisks",
330                                               "org.freedesktop.UDisks", "EnumerateDevices" );
331     if (!request) return FALSE;
332
333     p_dbus_error_init( &error );
334     reply = p_dbus_connection_send_with_reply_and_block( connection, request, udisks_timeout, &error );
335     p_dbus_message_unref( request );
336     if (!reply)
337     {
338         WARN( "failed: %s\n", error.message );
339         p_dbus_error_free( &error );
340         return FALSE;
341     }
342     p_dbus_error_free( &error );
343
344     if (p_dbus_message_get_args( reply, &error, DBUS_TYPE_ARRAY,
345                                  DBUS_TYPE_OBJECT_PATH, &paths, &count, DBUS_TYPE_INVALID ))
346     {
347         for (i = 0; i < count; i++) udisks_new_device( paths[i] );
348         p_dbus_free_string_array( paths );
349     }
350     else WARN( "unexpected args in EnumerateDevices reply\n" );
351
352     p_dbus_message_unref( reply );
353     return TRUE;
354 }
355
356 static DBusHandlerResult udisks_filter( DBusConnection *ctx, DBusMessage *msg, void *user_data )
357 {
358     char *path;
359     DBusError error;
360
361     p_dbus_error_init( &error );
362
363     if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceAdded" ) &&
364         p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
365     {
366         udisks_new_device( path );
367     }
368     else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceRemoved" ) &&
369              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
370     {
371         udisks_removed_device( path );
372     }
373     else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceChanged" ) &&
374              p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID ))
375     {
376         udisks_changed_device( path );
377     }
378     else TRACE( "ignoring message type=%d path=%s interface=%s method=%s\n",
379                 p_dbus_message_get_type( msg ), p_dbus_message_get_path( msg ),
380                 p_dbus_message_get_interface( msg ), p_dbus_message_get_member( msg ) );
381
382     p_dbus_error_free( &error );
383     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
384 }
385
386 #ifdef SONAME_LIBHAL
387
388 /* HAL callback for new device */
389 static void hal_new_device( LibHalContext *ctx, const char *udi )
390 {
391     DBusError error;
392     char *parent = NULL;
393     char *mount_point = NULL;
394     char *device = NULL;
395     char *type = NULL;
396     char *uuid_str = NULL;
397     GUID guid, *guid_ptr = NULL;
398     enum device_type drive_type;
399
400     p_dbus_error_init( &error );
401
402     if (!(device = p_libhal_device_get_property_string( ctx, udi, "block.device", &error )))
403         goto done;
404
405     if (!(mount_point = p_libhal_device_get_property_string( ctx, udi, "volume.mount_point", &error )))
406         goto done;
407
408     if (!(parent = p_libhal_device_get_property_string( ctx, udi, "info.parent", &error )))
409         goto done;
410
411     if (!(uuid_str = p_libhal_device_get_property_string( ctx, udi, "volume.uuid", &error )))
412         p_dbus_error_free( &error );  /* ignore error */
413     else
414         guid_ptr = parse_uuid( &guid, uuid_str );
415
416     if (!(type = p_libhal_device_get_property_string( ctx, parent, "storage.drive_type", &error )))
417         p_dbus_error_free( &error );  /* ignore error */
418
419     if (type && !strcmp( type, "cdrom" )) drive_type = DEVICE_CDROM;
420     else if (type && !strcmp( type, "floppy" )) drive_type = DEVICE_FLOPPY;
421     else drive_type = DEVICE_UNKNOWN;
422
423     if (p_libhal_device_get_property_bool( ctx, parent, "storage.removable", &error ))
424     {
425         add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr );
426         /* add property watch for mount point */
427         p_libhal_device_add_property_watch( ctx, udi, &error );
428     }
429     else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr );
430
431 done:
432     if (type) p_libhal_free_string( type );
433     if (parent) p_libhal_free_string( parent );
434     if (device) p_libhal_free_string( device );
435     if (uuid_str) p_libhal_free_string( uuid_str );
436     if (mount_point) p_libhal_free_string( mount_point );
437     p_dbus_error_free( &error );
438 }
439
440 /* HAL callback for removed device */
441 static void hal_removed_device( LibHalContext *ctx, const char *udi )
442 {
443     DBusError error;
444
445     TRACE( "removed %s\n", wine_dbgstr_a(udi) );
446
447     if (!remove_dos_device( -1, udi ))
448     {
449         p_dbus_error_init( &error );
450         p_libhal_device_remove_property_watch( ctx, udi, &error );
451         p_dbus_error_free( &error );
452     }
453     else remove_volume( udi );
454 }
455
456 /* HAL callback for property changes */
457 static void hal_property_modified (LibHalContext *ctx, const char *udi,
458                                    const char *key, dbus_bool_t is_removed, dbus_bool_t is_added)
459 {
460     TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi), wine_dbgstr_a(key),
461            is_added ? "added" : is_removed ? "removed" : "modified" );
462
463     if (!strcmp( key, "volume.mount_point" )) hal_new_device( ctx, udi );
464 }
465
466 static BOOL hal_enumerate_devices(void)
467 {
468     LibHalContext *ctx;
469     DBusError error;
470     int i, num;
471     char **list;
472
473     if (!p_libhal_ctx_new) return FALSE;
474     if (!(ctx = p_libhal_ctx_new())) return FALSE;
475
476     p_libhal_ctx_set_dbus_connection( ctx, connection );
477     p_libhal_ctx_set_device_added( ctx, hal_new_device );
478     p_libhal_ctx_set_device_removed( ctx, hal_removed_device );
479     p_libhal_ctx_set_device_property_modified( ctx, hal_property_modified );
480
481     p_dbus_error_init( &error );
482     if (!p_libhal_ctx_init( ctx, &error ))
483     {
484         WARN( "HAL context init failed: %s\n", error.message );
485         p_dbus_error_free( &error );
486         return FALSE;
487     }
488
489     /* retrieve all existing devices */
490     if (!(list = p_libhal_get_all_devices( ctx, &num, &error ))) p_dbus_error_free( &error );
491     else
492     {
493         for (i = 0; i < num; i++) hal_new_device( ctx, list[i] );
494         p_libhal_free_string_array( list );
495     }
496     return TRUE;
497 }
498
499 #endif /* SONAME_LIBHAL */
500
501 static DWORD WINAPI dbus_thread( void *arg )
502 {
503     static const char udisks_match[] = "type='signal',"
504                                        "interface='org.freedesktop.UDisks',"
505                                        "sender='org.freedesktop.UDisks'";
506
507     DBusError error;
508
509     p_dbus_error_init( &error );
510     if (!(connection = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error )))
511     {
512         WARN( "failed to get system dbus connection: %s\n", error.message );
513         p_dbus_error_free( &error );
514         return 1;
515     }
516
517     if (p_dbus_connection_add_filter( connection, udisks_filter, NULL, NULL ))
518         p_dbus_bus_add_match( connection, udisks_match, &error );
519
520     if (!udisks_enumerate_devices())
521     {
522         p_dbus_bus_remove_match( connection, udisks_match, &error );
523         p_dbus_connection_remove_filter( connection, udisks_filter, NULL );
524
525 #ifdef SONAME_LIBHAL
526         if (!hal_enumerate_devices())
527         {
528             p_dbus_error_free( &error );
529             return 1;
530         }
531 #endif
532     }
533
534     __TRY
535     {
536         while (p_dbus_connection_read_write_dispatch( connection, -1 )) /* nothing */ ;
537     }
538     __EXCEPT( assert_fault )
539     {
540         WARN( "dbus assertion failure, disabling support\n" );
541         return 1;
542     }
543     __ENDTRY;
544
545     return 0;
546 }
547
548 void initialize_dbus(void)
549 {
550     HANDLE handle;
551
552 #ifdef SONAME_LIBHAL
553     if (!load_hal_functions())
554 #endif
555         if (!load_dbus_functions()) return;
556     if (!(handle = CreateThread( NULL, 0, dbus_thread, NULL, 0, NULL ))) return;
557     CloseHandle( handle );
558 }
559
560 #else  /* SONAME_LIBDBUS_1 */
561
562 void initialize_dbus(void)
563 {
564     TRACE( "Skipping, DBUS support not compiled in\n" );
565 }
566
567 #endif  /* SONAME_LIBDBUS_1 */