explorer: Work around the latest HAL binary compatibility breakage.
[wine/wine-kai.git] / programs / explorer / hal.c
blobbcb1d983ee207aa417278ecaa0ae34cf6bbd7f69
1 /*
2 * HAL devices support
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
21 #include "config.h"
22 #include "wine/port.h"
24 #include <assert.h>
25 #include <errno.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <sys/time.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winuser.h"
34 #include "dbt.h"
35 #include "excpt.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);
44 #ifdef HAVE_LIBHAL
46 #include <dbus/dbus.h>
47 #include <hal/libhal.h>
49 struct dos_drive
51 struct list entry;
52 char *udi;
53 int drive;
56 static struct list drives_list = LIST_INIT(drives_list);
59 #define DBUS_FUNCS \
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)
67 #define HAL_FUNCS \
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
85 DBUS_FUNCS;
86 HAL_FUNCS;
87 #undef DO_FUNC
89 static BOOL load_functions(void)
91 void *hal_handle;
92 char error[128];
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))))
99 goto failed;
101 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( RTLD_DEFAULT, #f, error, sizeof(error) ))) goto failed
102 DBUS_FUNCS;
103 #undef DO_FUNC
105 #define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed
106 HAL_FUNCS;
107 #undef DO_FUNC
109 return TRUE;
111 failed:
112 WINE_WARN( "failed to load HAL support: %s\n", error );
113 return FALSE;
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 )
125 DWORD_PTR result;
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 );
142 if (path)
144 strcpy( path, config_dir );
145 strcat( path, "/dosdevices/a::" );
147 return path;
150 /* find or create a DOS drive for the corresponding device */
151 static int add_drive( const char *device, const char *type )
153 char *path, *p;
154 char in_use[26];
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) );
165 first = 2;
166 last = 26;
167 if (type && !strcmp( type, "floppy" ))
169 first = 0;
170 last = 2;
173 while (avail != -1)
175 avail = -1;
176 for (drive = first; drive < last; drive++)
178 if (in_use[drive]) continue; /* already checked */
179 *p = 'a' + drive;
180 if (stat( path, &drive_st ) == -1)
182 if (lstat( path, &drive_st ) == -1 && errno == ENOENT) /* this is a candidate */
184 if (avail == -1)
186 p[2] = 0;
187 /* if mount point symlink doesn't exist either, it's available */
188 if (lstat( path, &drive_st ) == -1 && errno == ENOENT) avail = drive;
189 p[2] = ':';
192 else in_use[drive] = 1;
194 else
196 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;
201 if (avail != -1)
203 /* try to use the one we found */
204 drive = avail;
205 *p = 'a' + drive;
206 if (symlink( device, path ) != -1) goto done;
207 /* failed, retry the search */
210 drive = -1;
212 done:
213 HeapFree( GetProcessHeap(), 0, path );
214 return drive;
217 static void set_mount_point( struct dos_drive *drive, const char *mount_point )
219 char *path, *p;
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;
226 p[2] = 0;
228 if (mount_point[0])
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)
234 unlink( path );
235 symlink( mount_point, path );
238 else unlink( 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 );
258 return;
260 strcpy( drive->udi, udi );
261 list_add_tail( &drives_list, &drive->entry );
263 found:
264 drive->drive = add_drive( device, type );
265 if (drive->drive != -1)
267 HKEY hkey;
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 ))
278 char name[3] = "a:";
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 );
282 RegCloseKey( hkey );
285 send_notify( drive->drive, DBT_DEVICEARRIVAL );
289 static void remove_dos_device( struct dos_drive *drive )
291 HKEY hkey;
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 ))
300 char name[3] = "a:";
301 name[0] += drive->drive;
302 RegDeleteValueA( hkey, name );
303 RegCloseKey( hkey );
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 )
317 DBusError error;
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 )))
323 goto done;
325 if (!(mount_point = p_libhal_device_get_property_string( ctx, udi, "volume.mount_point", &error )))
326 goto done;
328 if (!(parent = p_libhal_device_get_property_string( ctx, udi, "info.parent", &error )))
329 goto done;
331 if (!p_libhal_device_get_property_bool( ctx, parent, "storage.removable", &error ))
332 goto done;
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 );
347 done:
348 p_dbus_error_free( &error );
351 /* HAL callback for removed device */
352 static void removed_device( LibHalContext *ctx, const char *udi )
354 DBusError error;
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 );
366 return;
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 )
383 DBusError error;
384 DBusConnection *dbc;
385 LibHalContext *ctx;
386 int i, num;
387 char **list;
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 );
396 return 1;
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 );
408 return 1;
411 /* retrieve all existing devices */
412 if (!(list = p_libhal_get_all_devices( ctx, &num, &error ))) p_dbus_error_free( &error );
413 else
415 for (i = 0; i < num; i++) new_device( ctx, list[i] );
416 p_libhal_free_string_array( list );
419 __TRY
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" );
426 return 1;
428 __ENDTRY;
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 );
434 return 0;
437 void initialize_hal(void)
439 HANDLE handle;
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 */