From 31af515ef47da0be6b59e70d6b1992ed2adde032 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Mon, 4 Apr 2011 11:53:09 +0200 Subject: [PATCH 3/4] =?UTF-8?q?Bug=2034710=20=E2=80=94=20CD-ROM=20polling=20?= =?UTF-8?q?failed=20due=20to=20O=5FEXCL=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the CD tray door is locked and the hardware eject button is pressed, newer kernels (2.6.38+) will send out a change event with a DISK_EJECT_REQUEST==1 property. If this feature is available and used, lock the CD drive door immediately after media becomes available, and call DriveEject when the eject button is pressed. This properly cleans up existing mounts. As the ioctls sent to the CD drive might potentially block for a longer time, do them in the polling process, through the new poller_lock_cdtray() command. If the eject_request event is not supported, fall back to the previous method of unlocking the CD drive door right after mounting, to keep the hardware button working. --- src/daemon.c | 12 ++++++++ src/device.c | 19 ++++++++++++- src/poller.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++------------ src/poller.h | 3 ++ 4 files changed, 99 insertions(+), 18 deletions(-) diff --git a/src/daemon.c b/src/daemon.c index 6072502..0ba791d 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -1019,6 +1019,18 @@ block_device_changed (Daemon *daemon, { gboolean keep_device; + /* Pressing a drive's eject button causes a change event on the device + * with the DISK_EJECT_REQUEST property set. The event is only sent if + * the drive door is locked. Since this does not actually change anything + * on the medium, intercept it early. */ + if (g_udev_device_get_property (d, "DISK_EJECT_REQUEST")) + { + char *options[] = { "unmount", NULL }; + g_print ("**** Eject button pressed on %s\n", native_path); + device_drive_eject (device, options, NULL); + return; + } + g_print ("**** CHANGING %s\n", native_path); /* The device file (udev rules) and/or sysfs path ('move' uevent) may actually change so diff --git a/src/device.c b/src/device.c index b30fe1c..4b41d67 100644 --- a/src/device.c +++ b/src/device.c @@ -5207,6 +5207,18 @@ device_changed (Device *device, /* no, it's good .. keep it.. and always force a 'change' signal if the event isn't synthesized */ drain_pending_changes (device, !synthesized); + /* When using in-kernel polling on a drive which supports the + * "eject_request" uevent we MUST lock the tray as soon as we find + * a medium, otherwise we don't get ANY uevent not even for media + * changes. This is because of hardware limitations. + * + * See https://bugs.freedesktop.org/show_bug.cgi?id=34710 for more + * details. + */ + if (device->priv->supports_eject_request_event && device->priv->using_in_kernel_polling && + device->priv->device_is_media_available) + poller_lock_cdtray (device, TRUE); + /* Check if media was removed. If so, we possibly need to clean up * if the device itself is busy. This includes * @@ -6213,7 +6225,12 @@ filesystem_mount_completed_cb (DBusGMethodInvocation *context, update_info (device); drain_pending_changes (device, FALSE); - unlock_cd_tray (device); + /* If the kernel and device support sending EJECT_REQUEST change uevents + * and we use in-kernel polling, keep the door locked, as we'll call + * eject on pressing the button. Otherwise unlock it, to keep the + * hardware button working without userspace support */ + if (!(device->priv->supports_eject_request_event && device->priv->using_in_kernel_polling)) + unlock_cd_tray (device); dbus_g_method_return (context, data->mount_point); } diff --git a/src/poller.c b/src/poller.c index f518617..4fe7569 100644 --- a/src/poller.c +++ b/src/poller.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -149,6 +151,29 @@ poller_poll_device (const gchar *device_file) /* ---------------------------------------------------------------------------------------------------- */ +static void +poller_do_lock_cdtray (const char* device_file, int value) +{ + gint fd; + g_print ("**** Setting CD drive tray lock for %s to %i\n", device_file, value); + + fd = open (device_file, O_RDONLY); + if (fd > 0) + { + if (ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK) < 0) + g_warning ("Could not disable kernel auto-lock option: %s", strerror (errno)); + if (ioctl (fd, CDROM_LOCKDOOR, value) < 0) + g_warning ("Could not set CD-ROM tray lock status: %s", strerror (errno)); + close (fd); + } + else + { + g_warning ("Could not open CD-ROM device: %s", strerror (errno)); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + static gboolean poller_timeout_cb (gpointer user_data) { @@ -206,30 +231,36 @@ poller_have_data (GIOChannel *channel, { g_strfreev (poller_devices_to_poll); poller_devices_to_poll = g_strsplit (line + strlen ("set-poll:"), " ", 0); - } - else - { - g_printerr ("**** POLLER (%d): unknown command '%s'\n", getpid (), line); - } - if (g_strv_length (poller_devices_to_poll) == 0) - { - if (poller_timeout_id > 0) + if (g_strv_length (poller_devices_to_poll) == 0) { - g_source_remove (poller_timeout_id); - poller_timeout_id = 0; + if (poller_timeout_id > 0) + { + g_source_remove (poller_timeout_id); + poller_timeout_id = 0; + } + + set_proc_title ("udisks-daemon: not polling any devices"); } + else + { + set_proc_title ("udisks-daemon: polling %s", line + strlen ("set-poll:")); - set_proc_title ("udisks-daemon: not polling any devices"); + if (poller_timeout_id == 0) + { + poller_timeout_id = g_timeout_add_seconds (2, poller_timeout_cb, NULL); + } + } + } + else if (g_str_has_prefix (line, "lock-tray:")) + { + gchar **tokens = g_strsplit (line + strlen ("lock-tray:"), " ", 2); + poller_do_lock_cdtray (tokens[1], atoi (tokens[0])); + g_strfreev (tokens); } else { - set_proc_title ("udisks-daemon: polling %s", line + strlen ("set-poll:")); - - if (poller_timeout_id == 0) - { - poller_timeout_id = g_timeout_add_seconds (2, poller_timeout_cb, NULL); - } + g_printerr ("**** POLLER (%d): unknown command '%s'\n", getpid (), line); } g_free (line); @@ -429,3 +460,21 @@ poller_set_devices (GList *devices) } /* ---------------------------------------------------------------------------------------------------- */ + +void +poller_lock_cdtray (Device* device, gboolean lock) +{ + gchar *command; + + if (g_udev_device_has_property (device->priv->d, "ID_CDROM")) + { + command = g_strconcat ("lock-tray:", lock ? "1 " : "0 ", device->priv->device_file, "\n", NULL); +#ifdef POLL_SHOW_DEBUG + g_print ("**** POLLER (%d): Sending tray lock command: '%s'\n", getpid (), command); +#endif + if (write (poller_daemon_write_end_fd, command, strlen (command)) < 0) + g_error ("**** POLLER (%d): Failed to send tray lock command: %s", getpid (), g_strerror (errno)); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/src/poller.h b/src/poller.h index 21a7a25..67eb9fc 100644 --- a/src/poller.h +++ b/src/poller.h @@ -28,4 +28,7 @@ gboolean poller_setup (int argc, char *argv[]); /* media detection polling */ void poller_set_devices (GList *devices); +/* CD tray handling; moved to poller as it could block for a longer time */ +void poller_lock_cdtray (Device* device, gboolean lock); + #endif /* __POLLER_H */ -- 1.7.5.4