Here's a patch that I posted also to savannah.gnu.org a couple of days
ago. However, I'm not sure if that is currently actively checked, so I
wanted to take the opportunity of posting to this mailing list as well:
Previously get_media_changed under linux only indicated a media change
for one of all concurrently requesting processes due to a shortcoming of
the kernels CDROM_MEDIA_CHANGED ioctl request.
The new CDROM_TIMED_MEDIA_CHANGE ioctl introduced with Linux v5.16
now allows media change detection to work robustly for multiple processes.
This patch adapts libcdio to use this new ioctl request if compiled
against kernel v5.16 or newer. It retains the old behavior for older
kernels.
Link to the patch posting on savannah.gnu.org:
https://savannah.gnu.org/patch/index.php?10169
Best,
Lukas
From 35f9fa8e2b63f1f4e14be432079e2dea2c47e619 Mon Sep 17 00:00:00 2001
From: Lukas Prediger <lu...@lumip.de>
Date: Sun, 10 Oct 2021 13:33:08 +0300
Subject: [PATCH] Multiprocess robust cd change detection
The new CDROM_TIMED_MEDIA_CHANGE ioctl introduced with Linux v5.16
allows media change detection to work robustly for multiple processes.
This commit adapts libcdio to use this new ioctl request if compiled
against kernel v5.16 or newer. It retains the old behavior for older
kernels.
---
example/cdchange.c | 14 ++++++++----
lib/driver/gnu_linux.c | 50 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 58 insertions(+), 6 deletions(-)
diff --git a/example/cdchange.c b/example/cdchange.c
index 37b1b1fb..e236750b 100644
--- a/example/cdchange.c
+++ b/example/cdchange.c
@@ -56,6 +56,8 @@ main(int argc, const char *argv[])
{
CdIo_t *p_cdio;
long int i_sleep = 30;
+ int media_changed_ret;
+
if (argc > 1) {
p_cdio = cdio_open (argv[1], DRIVER_DEVICE);
if (argc > 2) {
@@ -76,18 +78,22 @@ main(int argc, const char *argv[])
return 1;
}
- if (cdio_get_media_changed(p_cdio))
+ if ((media_changed_ret = cdio_get_media_changed(p_cdio)) == 1)
printf("Initial media status: changed\n");
- else
+ else if (media_changed_ret == 0)
printf("Initial media status: not changed\n");
+ else
+ printf("Error while invoking media changed request!\n");
printf("Giving you %ld seconds to change CD if you want to do so.\n",
i_sleep);
sleep(i_sleep);
- if (cdio_get_media_changed(p_cdio))
+ if ((media_changed_ret = cdio_get_media_changed(p_cdio)) == 1)
printf("Media status: changed\n");
- else
+ else if (media_changed_ret == 0)
printf("Media status: not changed\n");
+ else
+ printf("Error while invoking media changed request!\n");
cdio_destroy(p_cdio);
return 0;
diff --git a/lib/driver/gnu_linux.c b/lib/driver/gnu_linux.c
index fb189772..e6ad582f 100644
--- a/lib/driver/gnu_linux.c
+++ b/lib/driver/gnu_linux.c
@@ -52,6 +52,9 @@
# else
# error "You need a kernel greater than 2.2.16 to have CDROM support"
# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0)
+# define __CDIO_LINUXCD_USE_TIMED_MEDIA_CHANGED
+# endif
#else
# error "You need <linux/version.h> to have CDROM support"
#endif
@@ -99,6 +102,16 @@ typedef struct {
struct cdrom_tochdr tochdr;
+#if defined(__CDIO_LINUXCD_USE_TIMED_MEDIA_CHANGED)
+ /* The new TIMED_MEDIA_CHANGED ioctl requires us
+ to store the timestamp of our last check. Since
+ cdio_media_changed takes user data through a constant
+ pointer, we cannot use a direct field, as that would
+ not be writable in that method. Therefore, make this
+ a pointer. */
+ __s64 *last_changed_timestamp;
+#endif
+
} _img_private_t;
/* Some ioctl() errno values which occur when the tray is empty */
@@ -505,7 +518,19 @@ get_last_session_linux (void *p_user_data,
static int
get_media_changed_linux (const void *p_user_data) {
const _img_private_t *p_env = p_user_data;
+#if defined(__CDIO_LINUXCD_USE_TIMED_MEDIA_CHANGED)
+ struct cdrom_timed_media_change_info info = {
+ .last_media_change = *(p_env->last_changed_timestamp),
+ .media_flags = 0
+ };
+ if (ioctl(p_env->gen.fd, CDROM_TIMED_MEDIA_CHANGE, &info)) {
+ return DRIVER_OP_ERROR;
+ }
+ *(p_env->last_changed_timestamp) = info.last_media_change;
+ return info.media_flags & MEDIA_CHANGED_FLAG;
+#else
return ioctl(p_env->gen.fd, CDROM_MEDIA_CHANGED, 0);
+#endif
}
/*!
@@ -1610,6 +1635,23 @@ no_tuple:;
}
#endif
+/*!
+ Release and free resources associated with cd for linux driver.
+ */
+static void
+free_linux (void *p_user_data)
+{
+#ifdef __CDIO_LINUXCD_USE_TIMED_MEDIA_CHANGED
+ _img_private_t *p_env = p_user_data;
+ if (p_env->last_changed_timestamp) {
+ free(p_env->last_changed_timestamp);
+ p_env->last_changed_timestamp = 0;
+ }
+#endif
+
+ cdio_generic_free(p_user_data);
+}
+
/*!
Initialization routine. This is the only thing that doesn't
get called via a function pointer. In fact *we* are the
@@ -1654,7 +1696,7 @@ cdio_open_am_linux (const char *psz_orig_source, const char *access_mode)
.audio_set_volume = audio_set_volume_linux,
.audio_stop = audio_stop_linux,
.eject_media = eject_media_linux,
- .free = cdio_generic_free,
+ .free = free_linux,
.get_arg = get_arg_linux,
.get_blocksize = get_blocksize_mmc,
.get_cdtext = get_cdtext_generic,
@@ -1712,6 +1754,10 @@ cdio_open_am_linux (const char *psz_orig_source, const char *access_mode)
_data->gen.toc_init = false;
_data->gen.fd = -1;
_data->gen.b_cdtext_error = false;
+#ifdef __CDIO_LINUXCD_USE_TIMED_MEDIA_CHANGED
+ _data->last_changed_timestamp = calloc(1, sizeof (__s64));
+ *(_data->last_changed_timestamp) = 0;
+#endif
if (NULL == psz_orig_source) {
psz_source=cdio_get_default_device_linux();
@@ -1754,7 +1800,7 @@ cdio_open_am_linux (const char *psz_orig_source, const char *access_mode)
free(ret);
err_exit:
- cdio_generic_free(_data);
+ free_linux(_data);
return NULL;
#else
--
2.25.1