From 63d8add85ef380f321d38b7e9ccd50111eafe463 Mon Sep 17 00:00:00 2001 From: gmerlin Date: Sat, 21 Oct 2006 10:55:18 +0000 Subject: [PATCH] * Umount before ejecting * Ultra simple eject command --- example/Makefile.am | 7 ++- example/cdio-eject.c | 59 ++++++++++++++++++ lib/driver/gnu_linux.c | 137 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 example/cdio-eject.c diff --git a/example/Makefile.am b/example/Makefile.am index 87635bd5..09573aff 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -1,4 +1,4 @@ -# $Id: Makefile.am,v 1.38 2006/10/11 12:38:18 rocky Exp $ +# $Id: Makefile.am,v 1.39 2006/10/21 10:55:18 gmerlin Exp $ # # Copyright (C) 2003, 2004, 2005, 2006 # Rocky Bernstein @@ -30,7 +30,7 @@ endif noinst_PROGRAMS = audio cdchange cdtext device drives eject \ isofile isofile2 isofuzzy isolist \ mmc1 mmc2 mmc2a mmc3 $(paranoia_progs) tracks \ - sample3 sample4 udf1 udffile + sample3 sample4 udf1 udffile cdio-eject INCLUDES = -I$(top_srcdir) $(LIBCDIO_CFLAGS) @@ -52,6 +52,9 @@ drives_LDADD = $(LIBCDIO_LIBS) eject_DEPENDENCIES = $(LIBCDIO_DEPS) eject_LDADD = $(LIBCDIO_LIBS) +cdio_eject_DEPENDENCIES = $(LIBCDIO_DEPS) +cdio_eject_LDADD = $(LIBCDIO_LIBS) + if BUILD_CD_PARANOIA paranoia_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_LIBS) paranoia2_LDADD = $(LIBCDIO_PARANOIA_LIBS) $(LIBCDIO_CDDA_LIBS) $(LIBCDIO_LIBS) diff --git a/example/cdio-eject.c b/example/cdio-eject.c new file mode 100644 index 00000000..deb48914 --- /dev/null +++ b/example/cdio-eject.c @@ -0,0 +1,59 @@ +#include +#include +#include + +static void usage(char * progname) + { + fprintf(stderr, "Usage: %s [-t] \n", progname); + } + +int main(int argc, char ** argv) + { + driver_return_code_t err; + int close_tray = 0; + const char * device = NULL; + + if(argc < 2 || argc > 3) + { + usage(argv[0]); + return -1; + } + + if((argc == 3) && strcmp(argv[1], "-t")) + { + usage(argv[0]); + return -1; + } + + if(argc == 2) + device = argv[1]; + else if(argc == 3) + { + close_tray = 1; + device = argv[2]; + } + + if(close_tray) + { + err = cdio_close_tray(device, NULL); + if(err) + { + fprintf(stderr, "Closing tray failed for device %s: %s\n", + device, cdio_driver_errmsg(err)); + return -1; + } + } + else + { + err = cdio_eject_media_drive(device); + if(err) + { + fprintf(stderr, "Ejecting failed for device %s: %s\n", + device, cdio_driver_errmsg(err)); + return -1; + } + } + + return 0; + + } diff --git a/lib/driver/gnu_linux.c b/lib/driver/gnu_linux.c index b39ea1af..462ff018 100644 --- a/lib/driver/gnu_linux.c +++ b/lib/driver/gnu_linux.c @@ -1,5 +1,5 @@ /* - $Id: gnu_linux.c,v 1.24 2006/08/02 11:00:31 rocky Exp $ + $Id: gnu_linux.c,v 1.25 2006/10/21 10:55:18 gmerlin Exp $ Copyright (C) 2001 Herbert Valerio Riedel Copyright (C) 2002, 2003, 2004, 2005, 2006 Rocky Bernstein @@ -28,9 +28,12 @@ # include "config.h" #endif -static const char _rcsid[] = "$Id: gnu_linux.c,v 1.24 2006/08/02 11:00:31 rocky Exp $"; +static const char _rcsid[] = "$Id: gnu_linux.c,v 1.25 2006/10/21 10:55:18 gmerlin Exp $"; #include +#include +#include +#include #include #include @@ -592,6 +595,107 @@ get_track_msf_linux(void *p_user_data, track_t i_track, msf_t *msf) } } +/*! + Follow symlinks until we have the real device file + (idea taken from libunieject). +*/ + +static void follow_symlink (const char * src, char * dst) { + char tmp_src[PATH_MAX]; + char tmp_dst[PATH_MAX]; + + int len; + + strcpy(tmp_src, src); + while(1) { + len = readlink(tmp_src, tmp_dst, PATH_MAX); + if(len < 0) { + strcpy(dst, tmp_src); + return; + } + else { + tmp_dst[len] = '\0'; + strcpy(tmp_src, tmp_dst); + } + } +} + +/*! + Check, if a device is mounted and return the target (=mountpoint) + needed for umounting (idea taken from libunieject). + */ + +static int is_mounted (const char * device, char * target) { + FILE * fp; + char real_device_1[PATH_MAX]; + char real_device_2[PATH_MAX]; + + char file_device[PATH_MAX]; + char file_target[PATH_MAX]; + + fp = fopen ( "/proc/mounts", "r"); + /* Older systems just have /etc/mtab */ + if(!fp) + fp = fopen ( "/etc/mtab", "r"); + + /* Neither /proc/mounts nor /etc/mtab could be opened, give up here */ + if(!fp) return 0; + + /* Get real device */ + follow_symlink(device, real_device_1); + + /* Read entries */ + + while ( fscanf(fp, "%s %s %*s %*s %*d %*d\n", file_device, file_target) != EOF ) { + follow_symlink(file_device, real_device_2); + if(!strcmp(real_device_1, real_device_2)) { + strcpy(target, file_target); + fclose(fp); + return 1; + } + + } + fclose(fp); + return 0; +} + +/*! + Umount a filesystem specified by it's mountpoint. We must do this + by forking and calling the umount command, because the raw umount + (or umount2) system calls will *always* trigger an EPERM even if + we are allowed to umount the filesystem. The umount command is + suid root. + + Code here is inspired by the standard linux eject command by + Jeff Tranter and Frank Lichtenheld. + */ + +static int do_umount(char * target) { + int status; + + switch (fork()) { + case 0: /* child */ + execlp("pumount", "pumount", target, NULL); + execlp("umount", "umount", target, NULL); + return -1; + break; + case -1: + return -1; + break; + default: /* parent */ + wait(&status); + if (WIFEXITED(status) == 0) { + return -1; + } + if (WEXITSTATUS(status) != 0) { + return -1; + } + break; + } + return 0; +} + + /*! Eject media in CD-ROM drive. Return DRIVER_OP_SUCCESS if successful, DRIVER_OP_ERROR on error. @@ -603,10 +707,15 @@ eject_media_linux (void *p_user_data) { _img_private_t *p_env = p_user_data; driver_return_code_t ret=DRIVER_OP_SUCCESS; int status; - + int was_open = 0; + char mount_target[PATH_MAX]; + if ( p_env->gen.fd <= -1 ) { p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK); } + else { + was_open = 1; + } if ( p_env->gen.fd <= -1 ) return DRIVER_OP_ERROR; @@ -622,6 +731,23 @@ eject_media_linux (void *p_user_data) { cdio_info ("Unknown state of CD-ROM (%d)\n", status); /* Fall through */ case CDS_DISC_OK: + /* Some systems automount the drive, so we must umount it. + We check if the drive is actually mounted */ + if(is_mounted (p_env->gen.source_name, mount_target)) { + /* Try to umount the drive */ + if(do_umount(mount_target)) { + cdio_log(CDIO_LOG_WARN, "Could not umount %s\n", + p_env->gen.source_name); + ret=DRIVER_OP_ERROR; + break; + } + /* For some reason, we must close and reopen the device after + it got umounted (at least the commandline eject program + opens the device just after umounting it) */ + close(p_env->gen.fd); + p_env->gen.fd = open (p_env->gen.source_name, O_RDONLY|O_NONBLOCK); + } + if((ret = ioctl(p_env->gen.fd, CDROMEJECT)) != 0) { int eject_error = errno; /* Try ejecting the MMC way... */ @@ -642,7 +768,10 @@ eject_media_linux (void *p_user_data) { cdio_warn ("CDROM_DRIVE_STATUS failed: %s\n", strerror(errno)); ret=DRIVER_OP_ERROR; } - p_env->gen.fd = -1; + if(!was_open) { + close(p_env->gen.fd); + p_env->gen.fd = -1; + } return ret; }