diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in index 6bdb5c4e312..3510becf11d 100644 --- a/dlls/mountmgr.sys/Makefile.in +++ b/dlls/mountmgr.sys/Makefile.in @@ -2,7 +2,7 @@ MODULE = mountmgr.sys IMPORTS = uuid advapi32 ntoskrnl.exe DELAYIMPORTS = user32 EXTRADLLFLAGS = -Wb,--subsystem,native -EXTRADEFS = @HALINCL@ +EXTRADEFS = @DBUSINCL@ @HALINCL@ EXTRALIBS = @DISKARBITRATIONLIB@ C_SRCS = \ diff --git a/dlls/mountmgr.sys/dbus.c b/dlls/mountmgr.sys/dbus.c index 42974b2b7b8..c7dd03dbe34 100644 --- a/dlls/mountmgr.sys/dbus.c +++ b/dlls/mountmgr.sys/dbus.c @@ -1,7 +1,7 @@ /* * DBus devices support * - * Copyright 2006 Alexandre Julliard + * Copyright 2006, 2011 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -43,15 +43,45 @@ WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); -#ifdef SONAME_LIBHAL +#ifdef SONAME_LIBDBUS_1 #define DBUS_FUNCS \ + DO_FUNC(dbus_bus_add_match); \ DO_FUNC(dbus_bus_get); \ + DO_FUNC(dbus_bus_remove_match); \ + DO_FUNC(dbus_connection_add_filter); \ DO_FUNC(dbus_connection_close); \ DO_FUNC(dbus_connection_read_write_dispatch); \ - DO_FUNC(dbus_error_init); \ + DO_FUNC(dbus_connection_remove_filter); \ + DO_FUNC(dbus_connection_send_with_reply_and_block); \ DO_FUNC(dbus_error_free); \ - DO_FUNC(dbus_error_is_set) + DO_FUNC(dbus_error_init); \ + DO_FUNC(dbus_error_is_set); \ + DO_FUNC(dbus_free_string_array); \ + DO_FUNC(dbus_message_get_args); \ + DO_FUNC(dbus_message_get_interface); \ + DO_FUNC(dbus_message_get_member); \ + DO_FUNC(dbus_message_get_path); \ + DO_FUNC(dbus_message_get_type); \ + DO_FUNC(dbus_message_is_signal); \ + DO_FUNC(dbus_message_iter_append_basic); \ + DO_FUNC(dbus_message_iter_get_arg_type); \ + DO_FUNC(dbus_message_iter_get_basic); \ + DO_FUNC(dbus_message_iter_init); \ + DO_FUNC(dbus_message_iter_init_append); \ + DO_FUNC(dbus_message_iter_next); \ + DO_FUNC(dbus_message_iter_recurse); \ + DO_FUNC(dbus_message_new_method_call); \ + DO_FUNC(dbus_message_unref); + +#define DO_FUNC(f) static typeof(f) * p_##f +DBUS_FUNCS; +#undef DO_FUNC + +static int udisks_timeout = -1; +static DBusConnection *connection; + +#ifdef SONAME_LIBHAL #define HAL_FUNCS \ DO_FUNC(libhal_ctx_free); \ @@ -71,11 +101,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); DO_FUNC(libhal_get_all_devices) #define DO_FUNC(f) static typeof(f) * p_##f -DBUS_FUNCS; HAL_FUNCS; #undef DO_FUNC -static BOOL load_functions(void) +static BOOL load_hal_functions(void) { void *hal_handle; char error[128]; @@ -95,6 +124,7 @@ static BOOL load_functions(void) HAL_FUNCS; #undef DO_FUNC + udisks_timeout = 3000; /* don't try for too long if we can fall back to HAL */ return TRUE; failed: @@ -102,6 +132,8 @@ failed: return FALSE; } +#endif /* SONAME_LIBHAL */ + static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr) { if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_ASSERTION) @@ -136,6 +168,224 @@ static GUID *parse_uuid( GUID *guid, const char *str ) return NULL; } +static BOOL load_dbus_functions(void) +{ + void *handle; + char error[128]; + + if (!(handle = wine_dlopen(SONAME_LIBDBUS_1, RTLD_NOW, error, sizeof(error)))) + goto failed; + +#define DO_FUNC(f) if (!(p_##f = wine_dlsym( handle, #f, error, sizeof(error) ))) goto failed + DBUS_FUNCS; +#undef DO_FUNC + return TRUE; + +failed: + WARN( "failed to load DBUS support: %s\n", error ); + return FALSE; +} + +static const char *udisks_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) +{ + DBusMessageIter sub; + const char *name; + + if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY) return NULL; + p_dbus_message_iter_recurse( iter, &sub ); + p_dbus_message_iter_next( iter ); + p_dbus_message_iter_get_basic( &sub, &name ); + p_dbus_message_iter_next( &sub ); + p_dbus_message_iter_recurse( &sub, variant ); + return name; +} + +/* UDisks callback for new device */ +static void udisks_new_device( const char *udi ) +{ + static const char *dev_name = "org.freedesktop.UDisks.Device"; + DBusMessage *request, *reply; + DBusMessageIter iter, variant; + DBusError error; + const char *device = NULL; + const char *mount_point = NULL; + const char *type = NULL; + GUID guid, *guid_ptr = NULL; + int removable = FALSE; + enum device_type drive_type = DEVICE_UNKNOWN; + + request = p_dbus_message_new_method_call( "org.freedesktop.UDisks", udi, + "org.freedesktop.DBus.Properties", "GetAll" ); + if (!request) return; + + p_dbus_message_iter_init_append( request, &iter ); + p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &dev_name ); + + p_dbus_error_init( &error ); + reply = p_dbus_connection_send_with_reply_and_block( connection, request, -1, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + WARN( "failed: %s", error.message ); + p_dbus_error_free( &error ); + return; + } + p_dbus_error_free( &error ); + + p_dbus_message_iter_init( reply, &iter ); + if (p_dbus_message_iter_get_arg_type( &iter ) == DBUS_TYPE_ARRAY) + { + const char *name; + + p_dbus_message_iter_recurse( &iter, &iter ); + while ((name = udisks_next_dict_entry( &iter, &variant ))) + { + if (!strcmp( name, "DeviceFile" )) + p_dbus_message_iter_get_basic( &variant, &device ); + else if (!strcmp( name, "DeviceIsRemovable" )) + p_dbus_message_iter_get_basic( &variant, &removable ); + else if (!strcmp( name, "IdType" )) + p_dbus_message_iter_get_basic( &variant, &type ); + else if (!strcmp( name, "DriveMediaCompatibility" )) + { + DBusMessageIter media; + p_dbus_message_iter_recurse( &variant, &media ); + while (p_dbus_message_iter_get_arg_type( &media ) == DBUS_TYPE_STRING) + { + const char *media_type; + p_dbus_message_iter_get_basic( &media, &media_type ); + if (!strncmp( media_type, "optical_dvd", 11 )) + drive_type = DEVICE_DVD; + if (!strncmp( media_type, "floppy", 6 )) + drive_type = DEVICE_FLOPPY; + else if (!strncmp( media_type, "optical_", 8 ) && drive_type == DEVICE_UNKNOWN) + drive_type = DEVICE_CDROM; + p_dbus_message_iter_next( &media ); + } + } + else if (!strcmp( name, "DeviceMountPaths" )) + { + DBusMessageIter paths; + p_dbus_message_iter_recurse( &variant, &paths ); + if (p_dbus_message_iter_get_arg_type( &paths ) == DBUS_TYPE_STRING) + p_dbus_message_iter_get_basic( &paths, &mount_point ); + } + else if (!strcmp( name, "IdUuid" )) + { + char *uuid_str; + p_dbus_message_iter_get_basic( &variant, &uuid_str ); + guid_ptr = parse_uuid( &guid, uuid_str ); + } + } + } + + TRACE( "udi %s device %s mount point %s uuid %s type %s removable %u\n", + debugstr_a(udi), debugstr_a(device), debugstr_a(mount_point), + debugstr_guid(guid_ptr), debugstr_a(type), removable ); + + if (type) + { + if (!strcmp( type, "iso9660" )) + { + removable = TRUE; + drive_type = DEVICE_CDROM; + } + else if (!strcmp( type, "udf" )) + { + removable = TRUE; + drive_type = DEVICE_DVD; + } + } + + if (device) + { + if (removable) add_dos_device( -1, udi, device, mount_point, drive_type, guid_ptr ); + else if (guid_ptr) add_volume( udi, device, mount_point, DEVICE_HARDDISK_VOL, guid_ptr ); + } + + p_dbus_message_unref( reply ); +} + +/* UDisks callback for removed device */ +static void udisks_removed_device( const char *udi ) +{ + TRACE( "removed %s\n", wine_dbgstr_a(udi) ); + + if (!remove_dos_device( -1, udi )) remove_volume( udi ); +} + +/* UDisks callback for changed device */ +static void udisks_changed_device( const char *udi ) +{ + udisks_new_device( udi ); +} + +static BOOL udisks_enumerate_devices(void) +{ + DBusMessage *request, *reply; + DBusError error; + char **paths; + int i, count; + + request = p_dbus_message_new_method_call( "org.freedesktop.UDisks", "/org/freedesktop/UDisks", + "org.freedesktop.UDisks", "EnumerateDevices" ); + if (!request) return FALSE; + + p_dbus_error_init( &error ); + reply = p_dbus_connection_send_with_reply_and_block( connection, request, udisks_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + WARN( "failed: %s\n", error.message ); + p_dbus_error_free( &error ); + return FALSE; + } + p_dbus_error_free( &error ); + + if (p_dbus_message_get_args( reply, &error, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, &paths, &count, DBUS_TYPE_INVALID )) + { + for (i = 0; i < count; i++) udisks_new_device( paths[i] ); + p_dbus_free_string_array( paths ); + } + else WARN( "unexpected args in EnumerateDevices reply\n" ); + + p_dbus_message_unref( reply ); + return TRUE; +} + +static DBusHandlerResult udisks_filter( DBusConnection *ctx, DBusMessage *msg, void *user_data ) +{ + char *path; + DBusError error; + + p_dbus_error_init( &error ); + + if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceAdded" ) && + p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID )) + { + udisks_new_device( path ); + } + else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceRemoved" ) && + p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID )) + { + udisks_removed_device( path ); + } + else if (p_dbus_message_is_signal( msg, "org.freedesktop.UDisks", "DeviceChanged" ) && + p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID )) + { + udisks_changed_device( path ); + } + else TRACE( "ignoring message type=%d path=%s interface=%s method=%s\n", + p_dbus_message_get_type( msg ), p_dbus_message_get_path( msg ), + p_dbus_message_get_interface( msg ), p_dbus_message_get_member( msg ) ); + + p_dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +#ifdef SONAME_LIBHAL + /* HAL callback for new device */ static void hal_new_device( LibHalContext *ctx, const char *udi ) { @@ -214,35 +464,27 @@ static void hal_property_modified (LibHalContext *ctx, const char *udi, if (!strcmp( key, "volume.mount_point" )) hal_new_device( ctx, udi ); } - -static DWORD WINAPI dbus_thread( void *arg ) +static BOOL hal_enumerate_devices(void) { - DBusError error; - DBusConnection *dbc; LibHalContext *ctx; + DBusError error; int i, num; char **list; - if (!(ctx = p_libhal_ctx_new())) return 1; + if (!p_libhal_ctx_new) return FALSE; + if (!(ctx = p_libhal_ctx_new())) return FALSE; - p_dbus_error_init( &error ); - if (!(dbc = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error ))) - { - WARN( "failed to get system dbus connection: %s\n", error.message ); - p_dbus_error_free( &error ); - return 1; - } - - p_libhal_ctx_set_dbus_connection( ctx, dbc ); + p_libhal_ctx_set_dbus_connection( ctx, connection ); p_libhal_ctx_set_device_added( ctx, hal_new_device ); p_libhal_ctx_set_device_removed( ctx, hal_removed_device ); p_libhal_ctx_set_device_property_modified( ctx, hal_property_modified ); + p_dbus_error_init( &error ); if (!p_libhal_ctx_init( ctx, &error )) { WARN( "HAL context init failed: %s\n", error.message ); p_dbus_error_free( &error ); - return 1; + return FALSE; } /* retrieve all existing devices */ @@ -252,22 +494,57 @@ static DWORD WINAPI dbus_thread( void *arg ) for (i = 0; i < num; i++) hal_new_device( ctx, list[i] ); p_libhal_free_string_array( list ); } + return TRUE; +} + +#endif /* SONAME_LIBHAL */ + +static DWORD WINAPI dbus_thread( void *arg ) +{ + static const char udisks_match[] = "type='signal'," + "interface='org.freedesktop.UDisks'," + "sender='org.freedesktop.UDisks'"; + + DBusError error; + + p_dbus_error_init( &error ); + if (!(connection = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error ))) + { + WARN( "failed to get system dbus connection: %s\n", error.message ); + p_dbus_error_free( &error ); + return 1; + } + + if (p_dbus_connection_add_filter( connection, udisks_filter, NULL, NULL )) + p_dbus_bus_add_match( connection, udisks_match, &error ); + + if (!udisks_enumerate_devices()) + { + p_dbus_bus_remove_match( connection, udisks_match, &error ); + p_dbus_connection_remove_filter( connection, udisks_filter, NULL ); + +#ifdef SONAME_LIBHAL + if (!hal_enumerate_devices()) + { + p_dbus_connection_close( connection ); + p_dbus_error_free( &error ); + return 1; + } +#endif + } __TRY { - while (p_dbus_connection_read_write_dispatch( dbc, -1 )) /* nothing */ ; + while (p_dbus_connection_read_write_dispatch( connection, -1 )) /* nothing */ ; } __EXCEPT( assert_fault ) { - WARN( "dbus assertion failure, disabling HAL support\n" ); + WARN( "dbus assertion failure, disabling support\n" ); return 1; } __ENDTRY; - p_libhal_ctx_shutdown( ctx, &error ); - p_dbus_error_free( &error ); /* just in case */ - p_dbus_connection_close( dbc ); - p_libhal_ctx_free( ctx ); + p_dbus_connection_close( connection ); return 0; } @@ -275,16 +552,19 @@ void initialize_dbus(void) { HANDLE handle; - if (!load_functions()) return; +#ifdef SONAME_LIBHAL + if (!load_hal_functions()) +#endif + if (!load_dbus_functions()) return; if (!(handle = CreateThread( NULL, 0, dbus_thread, NULL, 0, NULL ))) return; CloseHandle( handle ); } -#else /* SONAME_LIBHAL */ +#else /* SONAME_LIBDBUS_1 */ void initialize_dbus(void) { TRACE( "Skipping, DBUS support not compiled in\n" ); } -#endif /* SONAME_LIBHAL */ +#endif /* SONAME_LIBDBUS_1 */