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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 */