diff options
author | Joe Marcus Clarke <marcus@FreeBSD.org> | 2009-08-18 13:14:40 -0400 |
---|---|---|
committer | Joe Marcus Clarke <marcus@FreeBSD.org> | 2009-08-18 13:14:40 -0400 |
commit | 4043c559e75d24735d4b25170fa7d03f7c949148 (patch) | |
tree | 3f04d271b5c821a27442b2be9fb7a940806d97fc | |
parent | 47a90727d13549fa1eea34dc9d13f75784bd3fb6 (diff) | |
download | hal-4043c559e75d24735d4b25170fa7d03f7c949148.tar.gz |
add locking support to the storage addon
Sync a lot of ideas from the Linux backend so that the FreeBSD storage
addon honors device locking. Additionally, add the ability to forcibly
unmount a volume if its media is removed (but only on recent -CURRENT).
-rw-r--r-- | hald/freebsd/addons/addon-storage.c | 246 |
1 files changed, 211 insertions, 35 deletions
diff --git a/hald/freebsd/addons/addon-storage.c b/hald/freebsd/addons/addon-storage.c index dcd9cdc9..31250373 100644 --- a/hald/freebsd/addons/addon-storage.c +++ b/hald/freebsd/addons/addon-storage.c @@ -36,17 +36,24 @@ #include "../libprobe/hfp.h" #include "../libprobe/hfp-cdrom.h" +static boolean is_locked_by_hal = FALSE; +static boolean check_lock_state = TRUE; +static boolean polling_disabled = FALSE; + static struct { - const struct timeval update_interval; + const struct timespec update_interval; char *device_file; char *parent; boolean is_cdrom; boolean is_scsi_removable; boolean had_media; - struct timeval next_update; + struct timespec next_update; } addon = { { 2, 0 } }; +static void update_proc_title (const char *device); +static void unmount_volumes (void); + /* see MMC-3 Working Draft Revision 10 */ static boolean hf_addon_storage_cdrom_eject_pressed (HFPCDROM *cdrom) @@ -144,18 +151,148 @@ hf_addon_storage_update (void) } } - hfp_gettimeofday(&addon.next_update); - hfp_timevaladd(&addon.next_update, &addon.update_interval); - return has_media; } +static void +unmount_volumes (void) +{ + int num_volumes; + char **volumes; + + if ((volumes = libhal_manager_find_device_string_match(hfp_ctx, + "block.storage_device", + hfp_udi, + &num_volumes, + &hfp_error)) != NULL) + { + int i; + + dbus_error_free(&hfp_error); + + for (i = 0; i < num_volumes; i++) + { + char *vol_udi; + + vol_udi = volumes[i]; + + if (libhal_device_get_property_bool(hfp_ctx, vol_udi, "volume.is_mounted", &hfp_error)) + { + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + DBusConnection *dbus_connection; + unsigned int num_options = 0; + char **options = NULL; + char *devfile; + + dbus_error_free(&hfp_error); + hfp_info("Forcing unmount of volume '%s'", vol_udi); + + dbus_connection = libhal_ctx_get_dbus_connection(hfp_ctx); + msg = dbus_message_new_method_call("org.freedesktop.Hal", vol_udi, + "org.freedesktop.Hal.Device.Volume", + "Unmount"); + if (msg == NULL) + { + hfp_warning("Could not create dbus message for %s", vol_udi); + continue; + } + + options = calloc(1, sizeof (char *)); + if (options == NULL) + { + hfp_warning("Could not allocation memory for options"); + dbus_message_unref(msg); + continue; + } + + options[0] = "force"; + num_options = 1; + + devfile = libhal_device_get_property_string(hfp_ctx, vol_udi, "block.device", NULL); + if (devfile != NULL) + { + hfp_info("Forcibly attempting to unmount %s as media was removed", devfile); + libhal_free_string(devfile); + } + + if (! dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options, DBUS_TYPE_INVALID)) + { + hfp_warning("Could not append args to dbus message for %s", vol_udi); + free(options); + dbus_message_unref(msg); + continue; + } + + if (! (reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, -1, &hfp_error))) + { + hfp_warning("Unmount failed for %s: %s: %s", vol_udi, hfp_error.name, hfp_error.message); + dbus_error_free(&hfp_error); + free(options); + dbus_message_unref(msg); + continue; + } + + if (dbus_error_is_set(&hfp_error)) + { + hfp_warning("Unmount failed for %s: %s : %s", vol_udi, hfp_error.name, hfp_error.message); + dbus_error_free(&hfp_error); + free(options); + dbus_message_unref(msg); + dbus_message_unref(reply); + continue; + } + + hfp_info("Successfully unmounted udi '%s'", vol_udi); + free(options); + dbus_message_unref(msg); + dbus_message_unref(reply); + } + } + libhal_free_string_array(volumes); + } +} + static boolean -poll_for_media (void) +poll_for_media (boolean check_only, boolean force) { boolean has_media; + if (check_lock_state) + { + boolean should_poll; + + check_lock_state = FALSE; + + hfp_info("Checking whether device %s is locked by HAL", addon.device_file); + if (libhal_device_is_locked_by_others(hfp_ctx, hfp_udi, "org.freedesktop.Hal.Device.Storage", &hfp_error)) + { + hfp_info("... device %s is locked by HAL", addon.device_file); + dbus_error_free(&hfp_error); + is_locked_by_hal = TRUE; + update_proc_title(addon.device_file); + goto skip_check; + } + else + { + hfp_info("... device %s is not locked by HAL", addon.device_file); + is_locked_by_hal = FALSE; + } + dbus_error_free(&hfp_error); + + should_poll = libhal_device_get_property_bool(hfp_ctx, hfp_udi, "storage.media_check_enabled", &hfp_error); + dbus_error_free(&hfp_error); + polling_disabled = ! should_poll; + update_proc_title(addon.device_file); + } + + if (! force && polling_disabled) + goto skip_check; + has_media = hf_addon_storage_update(); + if (check_only) + return has_media; + if (has_media != addon.had_media) { /* @@ -168,6 +305,14 @@ poll_for_media (void) * then hung while rebooting and did not unmount my other * filesystems. */ +#if __FreeBSD_version >= 800066 + /* + * With newusb, it is safe to force unmount volumes. This may be + * safe on newer versions of the old USB stack, but we'll be + * extra cautious. + */ + unmount_volumes(); +#endif libhal_device_rescan(hfp_ctx, hfp_udi, &hfp_error); dbus_error_free(&hfp_error); @@ -175,20 +320,33 @@ poll_for_media (void) return TRUE; } + +skip_check: + return FALSE; } static void -update_proc_title (const char *device, boolean polling_enabled) +update_proc_title (const char *device) { - if (polling_enabled) - setproctitle("%s", device); - else + if (polling_disabled) setproctitle("no polling on %s because it is explicitly disabled", device); + else if (is_locked_by_hal) + setproctitle("no polling on %s because it is locked by HAL", device); + else + setproctitle("%s", device); } static DBusHandlerResult -filter_function (DBusConnection *connection, DBusMessage *message, void *user_data) +dbus_filter_function (DBusConnection *connection, DBusMessage *message, void *user_data) +{ + check_lock_state = TRUE; + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +direct_filter_function (DBusConnection *connection, DBusMessage *message, void *user_data) { if (dbus_message_is_method_call(message, "org.freedesktop.Hal.Device.Storage.Removable", @@ -199,7 +357,7 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da hfp_info("Forcing poll for media becusse CheckForMedia() was called"); - had_effect = poll_for_media(); + had_effect = poll_for_media(FALSE, TRUE); reply = dbus_message_new_method_return (message); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &had_effect, DBUS_TYPE_INVALID); @@ -217,8 +375,9 @@ main (int argc, char **argv) char *removable; char *bus; char *driver; - boolean should_poll; + char *filter_str; DBusConnection *connection; + DBusConnection *syscon; if (! hfp_init(argc, argv)) goto end; @@ -251,16 +410,41 @@ main (int argc, char **argv) addon.is_scsi_removable = (! strcmp(bus, "scsi") || (! strcmp(bus, "usb") && (! strcmp(driver, "da") || ! strcmp(driver, "sa") || ! strcmp(driver, "cd")))) && ! strcmp(removable, "true"); - addon.had_media = hf_addon_storage_update(); + addon.had_media = poll_for_media(TRUE, FALSE); if (! libhal_device_addon_is_ready(hfp_ctx, hfp_udi, &hfp_error)) goto end; dbus_error_free(&hfp_error); + syscon = dbus_bus_get(DBUS_BUS_SYSTEM, &hfp_error); + dbus_error_free(&hfp_error); + assert(syscon != NULL); + dbus_connection_set_exit_on_disconnect(syscon, 0); + + dbus_bus_add_match(syscon, + "type='signal'" + ",interface='org.freedesktop.Hal.Manager'" + ",sender='org.freedesktop.Hal'", + NULL); + dbus_bus_add_match(syscon, + "type='signal'" + ",interface='org.freedesktop.Hal.Manager'" + ",sender='org.freedesktop.Hal'", + NULL); + filter_str = hfp_strdup_printf("type='signal'" + ",interface='org.freedesktop.Hal.Device'" + ",sender='org.freedesktop.Hal'" + ",path='%s'", + hfp_udi); + dbus_bus_add_match(syscon, filter_str, NULL); + hfp_free(filter_str); + + dbus_connection_add_filter(syscon, dbus_filter_function, NULL, NULL); + connection = libhal_ctx_get_dbus_connection(hfp_ctx); assert(connection != NULL); dbus_connection_set_exit_on_disconnect(connection, 0); - dbus_connection_add_filter(connection, filter_function, NULL, NULL); + dbus_connection_add_filter(connection, direct_filter_function, NULL, NULL); if (! libhal_device_claim_interface(hfp_ctx, hfp_udi, @@ -280,40 +464,32 @@ main (int argc, char **argv) /* process dbus traffic until update interval has elapsed */ while (TRUE) { - struct timeval now; + struct timespec now; - hfp_gettimeofday(&now); - if (hfp_timevalcmp(&now, &addon.next_update, <)) + hfp_clock_gettime(&now); + if (hfp_timespeccmp(&now, &addon.next_update, <)) { - struct timeval timeout; + struct timespec timeout; timeout = addon.next_update; - hfp_timevalsub(&timeout, &now); + hfp_timespecsub(&timeout, &now); if (timeout.tv_sec < 0) /* current time went backwards */ timeout = addon.update_interval; - dbus_connection_read_write_dispatch(connection, timeout.tv_sec * 1000 + timeout.tv_usec / 1000); - if (! dbus_connection_get_is_connected(connection)) + dbus_connection_read_write_dispatch(connection, (int) ((timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000) / 2)); + dbus_connection_read_write_dispatch(syscon, (int) ((timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000) / 2)); + if (! dbus_connection_get_is_connected(connection) || + ! dbus_connection_get_is_connected(syscon)) goto end; } else break; } - should_poll = libhal_device_get_property_bool(hfp_ctx, hfp_udi, "storage.media_check_enabled", &hfp_error); - dbus_error_free(&hfp_error); - update_proc_title(addon.device_file, should_poll); - - if (should_poll) - { - poll_for_media(); - } - else - { - hfp_gettimeofday(&addon.next_update); - hfp_timevaladd(&addon.next_update, &addon.update_interval); - } + poll_for_media(FALSE, FALSE); + hfp_clock_gettime(&addon.next_update); + hfp_timespecadd(&addon.next_update, &addon.update_interval); } end: |