Description: Add support for ide-cd Add support for ide-cd cdroms in the devicekit poller. Poke the device with ioctl() and fake a "change" uevent whenever the state changes. Also make sure we run blkid for hd* type cd-roms. Author: Michael Biebl Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=550316 Index: devicekit-disks/data/95-devkit-disks.rules =================================================================== --- devicekit-disks.orig/data/95-devkit-disks.rules 2009-11-17 15:28:05.846859444 -0600 +++ devicekit-disks/data/95-devkit-disks.rules 2009-11-17 15:28:05.894861650 -0600 @@ -6,6 +6,21 @@ SUBSYSTEM!="block", GOTO="devkit_disks_end" KERNEL=="loop*|ram*", GOTO="devkit_disks_end" +############################################################################################################## + +# probe ide-cd drives + +# probe filesystem metadata of optical drives which have a media inserted +KERNEL=="hd*", ENV{ID_CDROM_MEDIA}=="?*", \ + ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ + IMPORT{program}="/sbin/blkid -o udev -p -u noraid -O $env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET} $tempnode" +# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET +KERNEL=="hd*", ENV{ID_CDROM_MEDIA}=="?*", \ + ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ + IMPORT{program}="/sbin/blkid -o udev -p -u noraid $tempnode" + +############################################################################################################## + # Mark devices that shouldn't be automounted / autoassembled etc. # # Deny all, then punch holes for what looks like physical drives @@ -37,7 +52,7 @@ KERNEL=="hd*[0-9]", ATTR{removable}=="1", GOTO="probe_parttable_end" # don't scan for partition tables on empty discs -KERNEL=="sr*", ENV{ID_CDROM_MEDIA}!="?*", GOTO="probe_parttable_end" +KERNEL=="sr*|hd*", ENV{ID_CDROM_MEDIA}!="?*", GOTO="probe_parttable_end" # scan for partition tables both on whole-disk and partitions # Index: devicekit-disks/src/devkit-disks-poller.c =================================================================== --- devicekit-disks.orig/src/devkit-disks-poller.c 2009-11-17 15:26:07.054860522 -0600 +++ devicekit-disks/src/devkit-disks-poller.c 2009-11-17 15:31:49.026863915 -0600 @@ -28,9 +28,15 @@ #include #include #include +#include +#include #include #include +#include + +#include + #include "devkit-disks-poller.h" #include "devkit-disks-device.h" #include "devkit-disks-device-private.h" @@ -99,37 +105,111 @@ /* ---------------------------------------------------------------------------------------------------- */ -static gchar **poller_devices_to_poll = NULL; + +typedef struct { + int state; /* for ide-cd devices we need to keep track of the state */ + gboolean is_libata_cdrom; + gboolean is_ide_cdrom; + gchar *dev_path; + gchar *uevent_path; /* only required for ide-cd */ +} PollerDevice; + +static GList *poller_devices_to_poll = NULL; static guint poller_timeout_id = 0; static void -devkit_disks_poller_poll_device (const gchar *device_file) +poller_check_ide_cdrom (int fd, PollerDevice *device) { - gboolean is_cdrom; - int fd, fd2; + int drive; + gboolean trigger_change_uevent = FALSE; + + drive = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + + switch (drive) { + case CDS_DISC_OK: + if (device->state != 1) { + trigger_change_uevent = TRUE; + device->state = 1; + } + break; + case CDS_NO_DISC: + if (device->state != 2) { + trigger_change_uevent = TRUE; + device->state = 2; + } + break; + case CDS_TRAY_OPEN: + if (device->state != 3) { + trigger_change_uevent = TRUE; + device->state = 3; + } + break; + case CDS_DRIVE_NOT_READY: + case CDS_NO_INFO: + case -1: + if (device->state != 4) device->state = 4; + break; + default: + if (device->state != 5) device->state = 5; + } + + if (trigger_change_uevent) { + int uevent_fd; + uevent_fd = open (device->uevent_path, O_RDWR); + if (uevent_fd != -1) { +#ifdef POLL_SHOW_DEBUG + g_print ("**** POLLER (%d): triggering change uevent for %s\n", getpid (), device->dev_path); +#endif + write (uevent_fd, "change", strlen("change")); + close (uevent_fd); + } + } +} - /* the device file is the canonical device file from udev */ - is_cdrom = (g_str_has_prefix (device_file, "/dev/sr") || g_str_has_prefix (device_file, "/dev/scd")); + +static void +devkit_disks_poller_poll_device (PollerDevice *device) +{ + int fd, fd2; #ifdef POLL_SHOW_DEBUG - g_print ("**** POLLER (%d): polling %s\n", getpid (), device_file); + g_print ("**** POLLER (%d): polling %s\n", getpid (), device->dev_path); #endif - if (is_cdrom) { + if (device->is_libata_cdrom | device->is_ide_cdrom) { /* optical drives need special care * * - use O_NONBLOCK to avoid closing the door * - use O_EXCL to avoid interferring with cd burning software / audio playback / etc */ - fd = open (device_file, O_RDONLY | O_NONBLOCK | O_EXCL); + fd = open (device->dev_path, O_RDONLY | O_NONBLOCK | O_EXCL); if (fd != -1) { + if (device->is_ide_cdrom) { +#ifdef POLL_SHOW_DEBUG + g_print ("**** POLLER (%d): open successful for %s\n", getpid (), device->dev_path); +#endif + poller_check_ide_cdrom (fd, device); + } close (fd); + } else if (errno == EBUSY && device->is_ide_cdrom) { + // TODO: check if media is mounted + // in that case mounting with O_EXCL should be safe +#ifdef POLL_SHOW_DEBUG + g_print ("**** POLLER (%d): open failed %s\n", getpid (), strerror (errno) ); +#endif + fd = open (device->dev_path, O_RDONLY | O_NONBLOCK); + if (fd != -1) { +#ifdef POLL_SHOW_DEBUG + g_print ("**** POLLER (%d): open successful for %s without O_EXCL\n", getpid (), device->dev_path); +#endif + poller_check_ide_cdrom (fd, device); + close (fd); + } } - } else { - fd = open (device_file, O_RDONLY); - fd2 = open (device_file, O_RDONLY | O_NONBLOCK); + fd = open (device->dev_path, O_RDONLY); + fd2 = open (device->dev_path, O_RDONLY | O_NONBLOCK); if (fd != -1) { close (fd); } @@ -144,13 +224,7 @@ static gboolean poller_timeout_cb (gpointer user_data) { - guint n; - - for (n = 0; poller_devices_to_poll != NULL && poller_devices_to_poll[n] != NULL; n++) { - const gchar *device_file = poller_devices_to_poll[n]; - - devkit_disks_poller_poll_device (device_file); - } + g_list_foreach (poller_devices_to_poll, (GFunc)devkit_disks_poller_poll_device, NULL); /* don't remove the source */ return TRUE; @@ -158,6 +232,74 @@ /* ---------------------------------------------------------------------------------------------------- */ +static PollerDevice* +poller_device_populate (const gchar *dev_path) +{ + PollerDevice *device; + + device = g_malloc (sizeof(PollerDevice)); + device->dev_path = g_strdup (dev_path); + device->uevent_path = NULL; + device->state = 0; + + device->is_libata_cdrom = g_str_has_prefix (device->dev_path, "/dev/sr") || g_str_has_prefix (device->dev_path, "/dev/scd"); + + if (device->is_libata_cdrom) { + device->is_ide_cdrom = 0; + } else { /* ask udev if device is a ide-cd cdrom */ + struct udev *udev; + struct stat statbuf; + struct udev_device *udevice; + const gchar *type; + const gchar *bus; + + udev = udev_new (); + if (udev == NULL) { + g_printerr ("Error initializing libudev: %m\n"); + goto out; + } + if (stat (device->dev_path, &statbuf) != 0) { + g_printerr ("Error stating %s: %m\n", device->dev_path); + goto out; + } + udevice = udev_device_new_from_devnum (udev, 'b', statbuf.st_rdev); + if (udevice == NULL) { + g_printerr ("Error getting udev device for %s: %m\n", device->dev_path); + goto out; + } + type = udev_device_get_property_value (udevice, "ID_TYPE"); + bus = udev_device_get_property_value (udevice, "ID_BUS"); + + device->is_ide_cdrom = (g_strcmp0 (type, "cd") == 0) && (g_strcmp0 (bus, "ata") == 0); + if (device->is_ide_cdrom) { + device->uevent_path = g_build_filename (udev_device_get_syspath (udevice), "uevent", NULL); + } + + out: + if (udevice != NULL) { + udev_device_unref (udevice); + } + if (udev != NULL) { + udev_unref (udev); + } + + } + return device; +} + +static void +poller_device_free (PollerDevice *device) +{ + if (device->dev_path != NULL) { + g_free (device->dev_path); + device->dev_path = NULL; + } + if (device->uevent_path != NULL) { + g_free (device->uevent_path); + device->uevent_path = NULL; + } +} + static gboolean poller_have_data (GIOChannel *channel, GIOCondition condition, @@ -196,13 +338,20 @@ g_print ("**** POLLER (%d): polling process read '%s'\n", getpid (), line); #endif if (g_str_has_prefix (line, "set-poll:")) { - g_strfreev (poller_devices_to_poll); - poller_devices_to_poll = g_strsplit (line + strlen ("set-poll:"), " ", 0); + gchar **tmp; + int n; + + g_list_foreach (poller_devices_to_poll, (GFunc)poller_device_free, NULL); + g_list_free (poller_devices_to_poll); + tmp = g_strsplit (line + strlen ("set-poll:"), " ", 0); + for (n = 0; tmp != NULL && tmp[n] != NULL; n++) { + poller_devices_to_poll = g_list_append (poller_devices_to_poll, poller_device_populate (tmp[n])); + } } else { g_printerr ("**** POLLER (%d): unknown command '%s'\n", getpid (), line); } - if (g_strv_length (poller_devices_to_poll) == 0) { + if (g_list_length (poller_devices_to_poll) == 0) { if (poller_timeout_id > 0) { g_source_remove (poller_timeout_id); poller_timeout_id = 0;