4 * Copyright 2006 Alexandre Julliard
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.
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.
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
22 #include "wine/port.h"
37 #include "wine/library.h"
38 #include "wine/list.h"
39 #include "wine/exception.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(explorer
);
46 #include <dbus/dbus.h>
47 #include <hal/libhal.h>
56 static struct list drives_list
= LIST_INIT(drives_list
);
60 DO_FUNC(dbus_bus_get); \
61 DO_FUNC(dbus_connection_close); \
62 DO_FUNC(dbus_connection_read_write_dispatch); \
63 DO_FUNC(dbus_error_init); \
64 DO_FUNC(dbus_error_free); \
65 DO_FUNC(dbus_error_is_set)
68 DO_FUNC(libhal_ctx_free); \
69 DO_FUNC(libhal_ctx_init); \
70 DO_FUNC(libhal_ctx_new); \
71 DO_FUNC(libhal_ctx_set_dbus_connection); \
72 DO_FUNC(libhal_ctx_set_device_added); \
73 DO_FUNC(libhal_ctx_set_device_property_modified); \
74 DO_FUNC(libhal_ctx_set_device_removed); \
75 DO_FUNC(libhal_ctx_shutdown); \
76 DO_FUNC(libhal_device_get_property_bool); \
77 DO_FUNC(libhal_device_get_property_string); \
78 DO_FUNC(libhal_device_add_property_watch); \
79 DO_FUNC(libhal_device_remove_property_watch); \
80 DO_FUNC(libhal_free_string); \
81 DO_FUNC(libhal_free_string_array); \
82 DO_FUNC(libhal_get_all_devices)
84 #define DO_FUNC(f) static typeof(f) * p_##f
89 static BOOL
load_functions(void)
94 /* Load libhal with RTLD_GLOBAL so that the dbus symbols are available.
95 * We can't load libdbus directly since libhal may have been built against a
96 * different version but with the same soname. Binary compatibility is for wimps. */
98 if (!(hal_handle
= wine_dlopen(SONAME_LIBHAL
, RTLD_NOW
|RTLD_GLOBAL
, error
, sizeof(error
))))
101 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( RTLD_DEFAULT, #f, error, sizeof(error) ))) goto failed
105 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed
112 WINE_WARN( "failed to load HAL support: %s\n", error
);
116 static WINE_EXCEPTION_FILTER(assert_fault
)
118 if (GetExceptionCode() == EXCEPTION_WINE_ASSERTION
) return EXCEPTION_EXECUTE_HANDLER
;
119 return EXCEPTION_CONTINUE_SEARCH
;
122 /* send notification about a change to a given drive */
123 static void send_notify( int drive
, int code
)
126 DEV_BROADCAST_VOLUME info
;
128 info
.dbcv_size
= sizeof(info
);
129 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
130 info
.dbcv_reserved
= 0;
131 info
.dbcv_unitmask
= 1 << drive
;
132 info
.dbcv_flags
= DBTF_MEDIA
;
133 SendMessageTimeoutW( HWND_BROADCAST
, WM_DEVICECHANGE
, code
, (LPARAM
)&info
,
134 SMTO_ABORTIFHUNG
, 0, &result
);
137 static char *get_dosdevices_path(void)
139 const char *config_dir
= wine_get_config_dir();
140 size_t len
= strlen(config_dir
) + sizeof("/dosdevices/a::");
141 char *path
= HeapAlloc( GetProcessHeap(), 0, len
);
144 strcpy( path
, config_dir
);
145 strcat( path
, "/dosdevices/a::" );
150 /* find or create a DOS drive for the corresponding device */
151 static int add_drive( const char *device
, const char *type
)
155 struct stat dev_st
, drive_st
;
156 int drive
, first
, last
, avail
= 0;
158 if (stat( device
, &dev_st
) == -1 || !S_ISBLK( dev_st
.st_mode
)) return -1;
160 if (!(path
= get_dosdevices_path())) return -1;
161 p
= path
+ strlen(path
) - 3;
163 memset( in_use
, 0, sizeof(in_use
) );
167 if (type
&& !strcmp( type
, "floppy" ))
176 for (drive
= first
; drive
< last
; drive
++)
178 if (in_use
[drive
]) continue; /* already checked */
180 if (stat( path
, &drive_st
) == -1)
182 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
187 /* if mount point symlink doesn't exist either, it's available */
188 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
192 else in_use
[drive
] = 1;
197 if (!S_ISBLK( drive_st
.st_mode
)) continue;
198 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
203 /* try to use the one we found */
206 if (symlink( device
, path
) != -1) goto done
;
207 /* failed, retry the search */
213 HeapFree( GetProcessHeap(), 0, path
);
217 static void set_mount_point( struct dos_drive
*drive
, const char *mount_point
)
220 struct stat path_st
, mnt_st
;
222 if (drive
->drive
== -1) return;
223 if (!(path
= get_dosdevices_path())) return;
224 p
= path
+ strlen(path
) - 3;
225 *p
= 'a' + drive
->drive
;
230 /* try to avoid unlinking if already set correctly */
231 if (stat( path
, &path_st
) == -1 || stat( mount_point
, &mnt_st
) == -1 ||
232 path_st
.st_dev
!= mnt_st
.st_dev
|| path_st
.st_ino
!= mnt_st
.st_ino
)
235 symlink( mount_point
, path
);
240 HeapFree( GetProcessHeap(), 0, path
);
243 static void add_dos_device( const char *udi
, const char *device
,
244 const char *mount_point
, const char *type
)
246 struct dos_drive
*drive
;
248 /* first check if it already exists */
249 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
251 if (!strcmp( udi
, drive
->udi
)) goto found
;
254 if (!(drive
= HeapAlloc( GetProcessHeap(), 0, sizeof(*drive
) ))) return;
255 if (!(drive
->udi
= HeapAlloc( GetProcessHeap(), 0, strlen(udi
)+1 )))
257 HeapFree( GetProcessHeap(), 0, drive
);
260 strcpy( drive
->udi
, udi
);
261 list_add_tail( &drives_list
, &drive
->entry
);
264 drive
->drive
= add_drive( device
, type
);
265 if (drive
->drive
!= -1)
269 set_mount_point( drive
, mount_point
);
271 WINE_TRACE( "added device %c: udi %s for %s on %s type %s\n",
272 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
273 wine_dbgstr_a(mount_point
), wine_dbgstr_a(type
) );
275 /* hack: force the drive type in the registry */
276 if (!RegCreateKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
279 name
[0] += drive
->drive
;
280 if (!type
|| strcmp( type
, "cdrom" )) type
= "floppy"; /* FIXME: default to floppy */
281 RegSetValueExA( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type
, strlen(type
) + 1 );
285 send_notify( drive
->drive
, DBT_DEVICEARRIVAL
);
289 static void remove_dos_device( struct dos_drive
*drive
)
293 if (drive
->drive
!= -1)
295 set_mount_point( drive
, "" );
297 /* clear the registry key too */
298 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
301 name
[0] += drive
->drive
;
302 RegDeleteValueA( hkey
, name
);
306 send_notify( drive
->drive
, DBT_DEVICEREMOVECOMPLETE
);
309 list_remove( &drive
->entry
);
310 HeapFree( GetProcessHeap(), 0, drive
->udi
);
311 HeapFree( GetProcessHeap(), 0, drive
);
314 /* HAL callback for new device */
315 static void new_device( LibHalContext
*ctx
, const char *udi
)
318 char *parent
, *mount_point
, *device
, *type
;
320 p_dbus_error_init( &error
);
322 if (!(device
= p_libhal_device_get_property_string( ctx
, udi
, "block.device", &error
)))
325 if (!(mount_point
= p_libhal_device_get_property_string( ctx
, udi
, "volume.mount_point", &error
)))
328 if (!(parent
= p_libhal_device_get_property_string( ctx
, udi
, "info.parent", &error
)))
331 if (!p_libhal_device_get_property_bool( ctx
, parent
, "storage.removable", &error
))
334 if (!(type
= p_libhal_device_get_property_string( ctx
, parent
, "storage.drive_type", &error
)))
335 p_dbus_error_free( &error
); /* ignore error */
337 add_dos_device( udi
, device
, mount_point
, type
);
339 if (type
) p_libhal_free_string( type
);
340 p_libhal_free_string( parent
);
341 p_libhal_free_string( device
);
342 p_libhal_free_string( mount_point
);
344 /* add property watch for mount point */
345 p_libhal_device_add_property_watch( ctx
, udi
, &error
);
348 p_dbus_error_free( &error
);
351 /* HAL callback for removed device */
352 static void removed_device( LibHalContext
*ctx
, const char *udi
)
355 struct dos_drive
*drive
;
357 WINE_TRACE( "removed %s\n", wine_dbgstr_a(udi
) );
359 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
361 if (strcmp( udi
, drive
->udi
)) continue;
362 p_dbus_error_init( &error
);
363 p_libhal_device_remove_property_watch( ctx
, udi
, &error
);
364 remove_dos_device( drive
);
365 p_dbus_error_free( &error
);
370 /* HAL callback for property changes */
371 static void property_modified (LibHalContext
*ctx
, const char *udi
,
372 const char *key
, dbus_bool_t is_removed
, dbus_bool_t is_added
)
374 WINE_TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi
), wine_dbgstr_a(key
),
375 is_added
? "added" : is_removed
? "removed" : "modified" );
377 if (!strcmp( key
, "volume.mount_point" )) new_device( ctx
, udi
);
381 static DWORD WINAPI
hal_thread( void *arg
)
389 if (!(ctx
= p_libhal_ctx_new())) return 1;
391 p_dbus_error_init( &error
);
392 if (!(dbc
= p_dbus_bus_get( DBUS_BUS_SYSTEM
, &error
)))
394 WINE_WARN( "failed to get system dbus connection: %s\n", error
.message
);
395 p_dbus_error_free( &error
);
399 p_libhal_ctx_set_dbus_connection( ctx
, dbc
);
400 p_libhal_ctx_set_device_added( ctx
, new_device
);
401 p_libhal_ctx_set_device_removed( ctx
, removed_device
);
402 p_libhal_ctx_set_device_property_modified( ctx
, property_modified
);
404 if (!p_libhal_ctx_init( ctx
, &error
))
406 WINE_WARN( "HAL context init failed: %s\n", error
.message
);
407 p_dbus_error_free( &error
);
411 /* retrieve all existing devices */
412 if (!(list
= p_libhal_get_all_devices( ctx
, &num
, &error
))) p_dbus_error_free( &error
);
415 for (i
= 0; i
< num
; i
++) new_device( ctx
, list
[i
] );
416 p_libhal_free_string_array( list
);
421 while (p_dbus_connection_read_write_dispatch( dbc
, -1 )) /* nothing */ ;
423 __EXCEPT( assert_fault
)
425 WINE_WARN( "dbus assertion failure, disabling HAL support\n" );
430 p_libhal_ctx_shutdown( ctx
, &error
);
431 p_dbus_error_free( &error
); /* just in case */
432 p_dbus_connection_close( dbc
);
433 p_libhal_ctx_free( ctx
);
437 void initialize_hal(void)
441 if (!load_functions()) return;
442 if (!(handle
= CreateThread( NULL
, 0, hal_thread
, NULL
, 0, NULL
))) return;
443 CloseHandle( handle
);
446 #else /* HAVE_LIBHAL */
448 void initialize_hal(void)
450 WINE_WARN( "HAL support not compiled in\n" );
453 #endif /* HAVE_LIBHAL */