From 4bc2e868a3948b8a8d93780b30cba3c76d9fbc42 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, 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 | 13 ++++++++- src/poller.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++------------ src/poller.h | 3 ++ 4 files changed, 93 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 abb2ff7..3cd8265 100644 --- a/src/device.c +++ b/src/device.c @@ -5207,6 +5207,12 @@ 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); + /* On newer kernels which support the "eject_request" uevent we need to + * lock the tray as soon as we find a medium, as the uevent is only being + * sent while the tray is locked. */ + if (device->priv->supports_eject_request_event && 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 +6219,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, + * 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) + unlock_cd_tray (device); dbus_g_method_return (context, data->mount_point); } diff --git a/src/poller.c b/src/poller.c index 1bf265b..a75bacc 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); @@ -413,3 +444,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.1