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)
91 void *dbus_handle
, *hal_handle
;
94 if (!(dbus_handle
= wine_dlopen(SONAME_LIBDBUS_1
, RTLD_NOW
, error
, sizeof(error
)))) goto failed
;
95 if (!(hal_handle
= wine_dlopen(SONAME_LIBHAL
, RTLD_NOW
, error
, sizeof(error
)))) goto failed
;
97 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( dbus_handle, #f, error, sizeof(error) ))) goto failed
101 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed
108 WINE_WARN( "failed to load HAL support: %s\n", error
);
112 static WINE_EXCEPTION_FILTER(assert_fault
)
114 if (GetExceptionCode() == EXCEPTION_WINE_ASSERTION
) return EXCEPTION_EXECUTE_HANDLER
;
115 return EXCEPTION_CONTINUE_SEARCH
;
118 /* send notification about a change to a given drive */
119 static void send_notify( int drive
, int code
)
122 DEV_BROADCAST_VOLUME info
;
124 info
.dbcv_size
= sizeof(info
);
125 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
126 info
.dbcv_reserved
= 0;
127 info
.dbcv_unitmask
= 1 << drive
;
128 info
.dbcv_flags
= DBTF_MEDIA
;
129 SendMessageTimeoutW( HWND_BROADCAST
, WM_DEVICECHANGE
, code
, (LPARAM
)&info
,
130 SMTO_ABORTIFHUNG
, 0, &result
);
133 static char *get_dosdevices_path(void)
135 const char *config_dir
= wine_get_config_dir();
136 size_t len
= strlen(config_dir
) + sizeof("/dosdevices/a::");
137 char *path
= HeapAlloc( GetProcessHeap(), 0, len
);
140 strcpy( path
, config_dir
);
141 strcat( path
, "/dosdevices/a::" );
146 /* find or create a DOS drive for the corresponding device */
147 static int add_drive( const char *device
, const char *type
)
151 struct stat dev_st
, drive_st
;
152 int drive
, first
, last
, avail
= 0;
154 if (stat( device
, &dev_st
) == -1 || !S_ISBLK( dev_st
.st_mode
)) return -1;
156 if (!(path
= get_dosdevices_path())) return -1;
157 p
= path
+ strlen(path
) - 3;
159 memset( in_use
, 0, sizeof(in_use
) );
163 if (type
&& !strcmp( type
, "floppy" ))
172 for (drive
= first
; drive
< last
; drive
++)
174 if (in_use
[drive
]) continue; /* already checked */
176 if (stat( path
, &drive_st
) == -1)
178 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
183 /* if mount point symlink doesn't exist either, it's available */
184 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
188 else in_use
[drive
] = 1;
193 if (!S_ISBLK( drive_st
.st_mode
)) continue;
194 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
199 /* try to use the one we found */
202 if (symlink( device
, path
) != -1) goto done
;
203 /* failed, retry the search */
209 HeapFree( GetProcessHeap(), 0, path
);
213 static void set_mount_point( struct dos_drive
*drive
, const char *mount_point
)
216 struct stat path_st
, mnt_st
;
218 if (drive
->drive
== -1) return;
219 if (!(path
= get_dosdevices_path())) return;
220 p
= path
+ strlen(path
) - 3;
221 *p
= 'a' + drive
->drive
;
226 /* try to avoid unlinking if already set correctly */
227 if (stat( path
, &path_st
) == -1 || stat( mount_point
, &mnt_st
) == -1 ||
228 path_st
.st_dev
!= mnt_st
.st_dev
|| path_st
.st_ino
!= mnt_st
.st_ino
)
231 symlink( mount_point
, path
);
236 HeapFree( GetProcessHeap(), 0, path
);
239 static void add_dos_device( const char *udi
, const char *device
,
240 const char *mount_point
, const char *type
)
242 struct dos_drive
*drive
;
244 /* first check if it already exists */
245 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
247 if (!strcmp( udi
, drive
->udi
)) goto found
;
250 if (!(drive
= HeapAlloc( GetProcessHeap(), 0, sizeof(*drive
) ))) return;
251 if (!(drive
->udi
= HeapAlloc( GetProcessHeap(), 0, strlen(udi
)+1 )))
253 HeapFree( GetProcessHeap(), 0, drive
);
256 strcpy( drive
->udi
, udi
);
257 list_add_tail( &drives_list
, &drive
->entry
);
260 drive
->drive
= add_drive( device
, type
);
261 if (drive
->drive
!= -1)
265 set_mount_point( drive
, mount_point
);
267 WINE_TRACE( "added device %c: udi %s for %s on %s type %s\n",
268 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
269 wine_dbgstr_a(mount_point
), wine_dbgstr_a(type
) );
271 /* hack: force the drive type in the registry */
272 if (!RegCreateKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
275 name
[0] += drive
->drive
;
276 if (!type
|| strcmp( type
, "cdrom" )) type
= "floppy"; /* FIXME: default to floppy */
277 RegSetValueExA( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type
, strlen(type
) + 1 );
281 send_notify( drive
->drive
, DBT_DEVICEARRIVAL
);
285 static void remove_dos_device( struct dos_drive
*drive
)
289 if (drive
->drive
!= -1)
291 set_mount_point( drive
, "" );
293 /* clear the registry key too */
294 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
297 name
[0] += drive
->drive
;
298 RegDeleteValueA( hkey
, name
);
302 send_notify( drive
->drive
, DBT_DEVICEREMOVECOMPLETE
);
305 list_remove( &drive
->entry
);
306 HeapFree( GetProcessHeap(), 0, drive
->udi
);
307 HeapFree( GetProcessHeap(), 0, drive
);
310 /* HAL callback for new device */
311 static void new_device( LibHalContext
*ctx
, const char *udi
)
314 char *parent
, *mount_point
, *device
, *type
;
316 p_dbus_error_init( &error
);
318 if (!(device
= p_libhal_device_get_property_string( ctx
, udi
, "block.device", &error
)))
321 if (!(mount_point
= p_libhal_device_get_property_string( ctx
, udi
, "volume.mount_point", &error
)))
324 if (!(parent
= p_libhal_device_get_property_string( ctx
, udi
, "info.parent", &error
)))
327 if (!p_libhal_device_get_property_bool( ctx
, parent
, "storage.removable", &error
))
330 if (!(type
= p_libhal_device_get_property_string( ctx
, parent
, "storage.drive_type", &error
)))
331 p_dbus_error_free( &error
); /* ignore error */
333 add_dos_device( udi
, device
, mount_point
, type
);
335 if (type
) p_libhal_free_string( type
);
336 p_libhal_free_string( parent
);
337 p_libhal_free_string( device
);
338 p_libhal_free_string( mount_point
);
340 /* add property watch for mount point */
341 p_libhal_device_add_property_watch( ctx
, udi
, &error
);
344 p_dbus_error_free( &error
);
347 /* HAL callback for removed device */
348 static void removed_device( LibHalContext
*ctx
, const char *udi
)
351 struct dos_drive
*drive
;
353 WINE_TRACE( "removed %s\n", wine_dbgstr_a(udi
) );
355 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
357 if (strcmp( udi
, drive
->udi
)) continue;
358 p_dbus_error_init( &error
);
359 p_libhal_device_remove_property_watch( ctx
, udi
, &error
);
360 remove_dos_device( drive
);
361 p_dbus_error_free( &error
);
366 /* HAL callback for property changes */
367 static void property_modified (LibHalContext
*ctx
, const char *udi
,
368 const char *key
, dbus_bool_t is_removed
, dbus_bool_t is_added
)
370 WINE_TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi
), wine_dbgstr_a(key
),
371 is_added
? "added" : is_removed
? "removed" : "modified" );
373 if (!strcmp( key
, "volume.mount_point" )) new_device( ctx
, udi
);
377 static DWORD WINAPI
hal_thread( void *arg
)
385 if (!(ctx
= p_libhal_ctx_new())) return 1;
387 p_dbus_error_init( &error
);
388 if (!(dbc
= p_dbus_bus_get( DBUS_BUS_SYSTEM
, &error
)))
390 WINE_WARN( "failed to get system dbus connection: %s\n", error
.message
);
391 p_dbus_error_free( &error
);
395 p_libhal_ctx_set_dbus_connection( ctx
, dbc
);
396 p_libhal_ctx_set_device_added( ctx
, new_device
);
397 p_libhal_ctx_set_device_removed( ctx
, removed_device
);
398 p_libhal_ctx_set_device_property_modified( ctx
, property_modified
);
400 if (!p_libhal_ctx_init( ctx
, &error
))
402 WINE_WARN( "HAL context init failed: %s\n", error
.message
);
403 p_dbus_error_free( &error
);
407 /* retrieve all existing devices */
408 if (!(list
= p_libhal_get_all_devices( ctx
, &num
, &error
))) p_dbus_error_free( &error
);
411 for (i
= 0; i
< num
; i
++) new_device( ctx
, list
[i
] );
412 p_libhal_free_string_array( list
);
417 while (p_dbus_connection_read_write_dispatch( dbc
, -1 )) /* nothing */ ;
419 __EXCEPT( assert_fault
)
421 WINE_WARN( "dbus assertion failure, disabling HAL support\n" );
426 p_libhal_ctx_shutdown( ctx
, &error
);
427 p_dbus_error_free( &error
); /* just in case */
428 p_dbus_connection_close( dbc
);
429 p_libhal_ctx_free( ctx
);
433 void initialize_hal(void)
437 if (!load_functions()) return;
438 if (!(handle
= CreateThread( NULL
, 0, hal_thread
, NULL
, 0, NULL
))) return;
439 CloseHandle( handle
);
442 #else /* HAVE_LIBHAL */
444 void initialize_hal(void)
446 WINE_WARN( "HAL support not compiled in\n" );
449 #endif /* HAVE_LIBHAL */