--- /dev/null
+/********************************************************************
+ *
+ * L I N U X E J E C T C O M M A N D
+ *
+ * by Jeff Tranter (tranter@pobox.com)
+ *
+ ********************************************************************
+ *
+ * Copyright (C) 1994-2001 Jeff Tranter (tranter@pobox.com)
+ * Copyright (C) 2004, 2005 Frank Lichtenheld (djpig@debian.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ********************************************************************
+ *
+ * See the man page for a description of what this program does and what
+ * the requirements to run it are.
+ *
+ */
+
+#include "i18n.h"
+
+#ifndef DEFAULTDEVICE
+#error DEFAULTDEVICE not set, check Makefile
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <dirent.h>
+
+#ifdef GETOPTLONG
+#include <getopt.h>
+#endif /* GETOPTLONG */
+#include <errno.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+//#include <sys/mtio.h>
+#include <sys/mount.h>
+
+#if defined(__linux__)
+#include <linux/version.h>
+/* handy macro found in 2.1 kernels, but not in older ones */
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+#include <linux/mtio.h>
+#include <linux/types.h>
+#include <linux/cdrom.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#include <linux/ucdrom.h>
+#endif
+#include <linux/fd.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+#include <sys/time.h>
+
+/* Used by the ToggleTray() function. If ejecting the tray takes this
+ * time or less, the tray was probably already ejected, so we close it
+ * again.
+ */
+#define TRAY_WAS_ALREADY_OPEN_USECS 200000 /* about 0.2 seconds */
+
+
+#define CLOSE(fd) if (close(fd)==-1) { \
+ perror(programName); \
+ exit(1); \
+}
+
+#define FCLOSE(fd) if (fclose(fd)==-1) { \
+ perror(programName); \
+ exit(1); \
+}
+
+#define HAVE_EJECT_SCSI
+#define HAVE_EJECT_FLOPPY
+#define HAVE_EJECT_TAPE
+
+#elif defined(__FreeBSD_kernel__)
+#include <sys/cdio.h>
+#endif /* if defined(__linux__) */
+
+
+#define CLOSE(fd) if (close(fd)==-1) { \
+ perror(programName); \
+ exit(1); \
+}
+
+#define FCLOSE(fd) if (fclose(fd)==-1) { \
+ perror(programName); \
+ exit(1); \
+}
+
+/* Global Variables */
+static const char *version = VERSION; /* program version */
+int a_option = 0; /* command flags and arguments */
+int c_option = 0;
+int d_option = 0;
+int f_option = 0;
+int h_option = 0;
+int n_option = 0;
+int i_option = 0;
+int q_option = 0;
+int r_option = 0;
+int s_option = 0;
+int t_option = 0;
+int T_option = 0;
+int X_option = 0;
+int v_option = 0;
+int x_option = 0;
+#ifdef ANDROID
+int p_option = 1;
+#else
+int p_option = 0;
+#endif
+int m_option = 0;
+int a_arg = 0;
+int c_arg = 0;
+int x_arg = 0;
+int i_arg = 0;
+static char *programName; /* used in error messages */
+
+/*
+ * These are the basenames of devices which can have multiple
+ * partitions per device.
+ */
+static const char *partitionDevice[] = {
+ "hd",
+ "sd",
+ "xd",
+ "dos_hd",
+ "mfm",
+ "ad",
+ "ed",
+ "ftl",
+ "pd",
+ 0};
+
+
+/* Display command usage on standard error and exit. */
+static void usage()
+{
+// perror(_("%s: device is `%s'\n"));
+ fprintf(stderr,_(
+"Eject version %s by Jeff Tranter (tranter@pobox.com)\n"
+"Usage:\n"
+" eject -h -- display command usage and exit\n"
+" eject -V -- display program version and exit\n"
+" eject [-vnrsfqpm] [<name>] -- eject device\n"
+" eject [-vn] -d -- display default device\n"
+" eject [-vn] -a on|off|1|0 [<name>] -- turn auto-eject feature on or off\n"
+" eject [-vn] -c <slot> [<name>] -- switch discs on a CD-ROM changer\n"
+" eject [-vn] -t [<name>] -- close tray\n"
+" eject [-vn] -T [<name>] -- toggle tray\n"
+" eject [-vn] -i on|off|1|0 [<name>] -- toggle manual eject protection on/off\n"
+" eject [-vn] -x <speed> [<name>] -- set CD-ROM max speed\n"
+" eject [-vn] -X [<name>] -- list CD-ROM available speeds\n"
+"Options:\n"
+" -v\t-- enable verbose output\n"
+" -n\t-- don't eject, just show device found\n"
+" -r\t-- eject CD-ROM\n"
+#ifdef HAVE_EJECT_SCSI
+" -s\t-- eject SCSI device\n"
+#endif
+#ifdef HAVE_EJECT_FLOPPY
+" -f\t-- eject floppy\n"
+#endif
+#ifdef HAVE_EJECT_TAPE
+" -q\t-- eject tape\n"
+#endif
+" -p\t-- use /proc/mounts instead of /etc/mtab\n"
+" -m\t-- do not unmount device even if it is mounted\n"
+)
+, version);
+#ifdef GETOPTLONG
+ fprintf(stderr,_(
+"Long options:\n"
+" -h --help -v --verbose -d --default\n"
+" -a --auto -c --changerslot -t --trayclose -x --cdspeed\n"
+" -r --cdrom"
+#ifdef HAVE_EJECT_SCSI
+" -s --scsi"
+#endif
+#ifdef HAVE_EJECT_FLOPPY
+" -f --floppy"
+#endif
+" -X --listspeed"
+#ifdef HAVE_EJECT_TAPE
+" -q --tape"
+#endif
+"\n"
+" -n --noop -V --version\n"
+" -p --proc -m --no-unmount -T --traytoggle -i --manualeject\n"));
+#endif /* GETOPTLONG */
+ fprintf(stderr,_(
+"Parameter <name> can be a device file or a mount point.\n"
+"If omitted, name defaults to `%s'.\n"
+"By default tries -r, -s, -f, and -q in order until success.\n"),
+ DEFAULTDEVICE);
+ exit(1);
+}
+
+
+/* Handle command line options. */
+static void parse_args(int argc, char **argv, char **device)
+{
+ const char *flags = "a:c:x:i:dfhnqrstTXvVpm";
+#ifdef GETOPTLONG
+ static struct option long_options[] =
+ {
+ {"help", no_argument, NULL, 'h'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"default", no_argument, NULL, 'd'},
+ {"auto", required_argument, NULL, 'a'},
+ {"changerslot", required_argument, NULL, 'c'},
+ {"manualeject", required_argument, NULL, 'i'},
+ {"trayclose", no_argument, NULL, 't'},
+ {"traytoggle", no_argument, NULL, 'T'},
+ {"cdspeed", required_argument, NULL, 'x'},
+ {"listspeed", no_argument, NULL, 'X'},
+ {"noop", no_argument, NULL, 'n'},
+ {"cdrom", no_argument, NULL, 'r'},
+ {"scsi", no_argument, NULL, 's'},
+ {"floppy", no_argument, NULL, 'f'},
+ {"tape", no_argument, NULL, 'q'},
+ {"version", no_argument, NULL, 'V'},
+ {"proc", no_argument, NULL, 'p'},
+ {"no-unmount", no_argument, NULL, 'm'},
+ {0, 0, 0, 0}
+ };
+ int option_index;
+#endif /* GETOPTLONG */
+ int c;
+
+#ifdef GETOPTLONG
+ while ((c = getopt_long(argc, argv, flags, long_options, &option_index)) != EOF) {
+#else
+ while ((c = getopt(argc, argv, flags)) != EOF) {
+#endif /* GETOPTLONG */
+ switch (c) {
+ case 'a':
+ a_option = 1;
+ if (!strcmp(optarg, "0"))
+ a_arg = 0;
+ else if (!strcmp(optarg, "off"))
+ a_arg = 0;
+ else if (!strcmp(optarg, "1"))
+ a_arg = 1;
+ else if (!strcmp(optarg, "on"))
+ a_arg = 1;
+ else {
+ fprintf(stderr, _("%s: invalid argument to --auto/-a option\n"), programName);
+ exit(1);
+ }
+ break;
+ case 'c':
+ c_option = 1;
+ /* atoi() returns 0 on error, so "0" must be parsed separately */
+ if (!strcmp(optarg, "0"))
+ c_arg = 0;
+ else {
+ c_arg = atoi(optarg);
+ if (c_arg <= 0) {
+ fprintf(stderr, _("%s: invalid argument to --changerslot/-c option\n"), programName);
+ exit(1);
+ }
+ }
+ break;
+ case 'x':
+ x_option = 1;
+ if (!strcmp(optarg, "0"))
+ x_arg = 0;
+ else {
+ x_arg = atoi(optarg);
+ if (x_arg <= 0) {
+ fprintf(stderr, _("%s: invalid argument to --cdspeed/-x option\n"), programName);
+ exit(1);
+ }
+ }
+ break;
+ case 'd':
+ d_option = 1;
+ break;
+ case 'f':
+ f_option = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ break;
+ case 'i':
+ i_option = 1;
+ if (!strcmp(optarg, "0"))
+ i_arg = 0;
+ else if (!strcmp(optarg, "off"))
+ i_arg = 0;
+ else if (!strcmp(optarg, "1"))
+ i_arg = 1;
+ else if (!strcmp(optarg, "on"))
+ i_arg = 1;
+ else {
+ fprintf(stderr, _("%s: invalid argument to -i option\n"), programName);
+ exit(1);
+ }
+ break;
+ case 'm':
+ m_option = 1;
+ break;
+ case 'n':
+ n_option = 1;
+ break;
+ case 'p':
+ p_option = 1;
+ break;
+ case 'q':
+ q_option = 1;
+ break;
+ case 'r':
+ r_option = 1;
+ break;
+ case 's':
+ s_option = 1;
+ break;
+ case 't':
+ t_option = 1;
+ break;
+ case 'X':
+ X_option = 1;
+ break;
+ case 'T':
+ T_option = 1;
+ break;
+ case 'v':
+ v_option = 1;
+ break;
+ case 'V':
+ printf(_("eject version %s by Jeff Tranter (tranter@pobox.com)\n"), version);
+ exit(0);
+ break;
+ case '?':
+ exit(1);
+ break;
+ }
+ }
+ /* check for a single additional argument */
+ if ((argc - optind) > 1) {
+ fprintf(stderr, _("%s: too many arguments\n"), programName);
+ exit(1);
+ }
+ if ((argc - optind) == 1) { /* one argument */
+ *device = strdup(argv[optind]);
+ }
+}
+
+
+/* Return 1 if file/device exists, 0 otherwise. */
+static int FileExists(const char *name, const int try, int *found)
+{
+
+ if (!found) return -1;
+ /*
+ * access() uses the UID, not the EUID. This way a normal user
+ * cannot find out if a file (say, /root/fubar) exists or not, even
+ * if eject is SUID root
+ */
+ if (access (name, F_OK) == 0) {
+ (*found)++;
+ if (try <= (*found))
+ return 1;
+ else
+ return 0;
+ } else {
+ return 0;
+ }
+}
+
+
+/*
+ * Linux mangles spaces in mount points by changing them to an octal string
+ * of '\040'. So lets scan the mount point and fix it up by replacing all
+ * occurrences off '\0##' with the ASCII value of 0##. Requires a writable
+ * string as input as we mangle in place. Some of this was taken from the
+ * util-linux package.
+ */
+#define octalify(a) ((a) & 7)
+#define tooctal(s) (64*octalify(s[1]) + 8*octalify(s[2]) + octalify(s[3]))
+#define isoctal(a) (((a) & ~7) == '0')
+static char *DeMangleMount(char *s)
+{
+ char *tmp = s;
+ while ((tmp = strchr(tmp, '\\')) != NULL) {
+ if (isoctal(tmp[1]) && isoctal(tmp[2]) && isoctal(tmp[3])) {
+ tmp[0] = tooctal(tmp);
+ memmove(tmp+1, tmp+4, strlen(tmp)-3);
+ }
+ ++tmp;
+ }
+ return s;
+}
+
+
+/*
+ * Given name, such as foo, see if any of the following exist:
+ *
+ * foo (if foo starts with '.' or '/')
+ * /dev/foo
+ * /media/foo
+ * /mnt/foo
+ * /dev/cdroms/foo
+ * /dev/cdroms/foo0
+ * /dev/dsk/foo
+ * /dev/rdsk/foo
+ * ./foo
+ *
+ * If found, return the full path. If not found, return 0.
+ * Returns pointer to dynamically allocated string.
+ */
+static char *FindDevice(const char *name)
+{
+ char *buf;
+ static int try = 0;
+ int found = 0;
+
+ buf = (char *) malloc(strlen(name)+14); /* to allow for "/dev/cdroms/ + "0" + null */
+ if (buf==NULL) {
+ fprintf(stderr, _("%s: could not allocate memory\n"), programName);
+ exit(1);
+ }
+
+ if (try == INT_MAX) {
+ fprintf(stderr, _("%s: FindDevice called too often\n"), programName );;
+ exit(1);
+ } else
+ try++;
+
+ if ((name[0] == '.') || (name[0] == '/')) {
+ strcpy(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+ }
+
+ strcpy(buf, "/dev/");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ strcpy(buf, "/media/");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ strcpy(buf, "/mnt/");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ /* for devfs under Linux */
+ strcpy(buf, "/dev/cdroms/");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ strcpy(buf, "/dev/cdroms/");
+ strcat(buf, name);
+ strcat(buf, "0");
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ /* for devfs under Solaris */
+ strcpy(buf, "/dev/rdsk/");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ strcpy(buf, "/dev/dsk/");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ strcpy(buf, "./");
+ strcat(buf, name);
+ if (FileExists(buf, try, &found))
+ return buf;
+
+ free(buf);
+ buf = 0;
+ return 0;
+}
+
+
+/*
+ * Stops CDROM from opening on manual eject pressing the button.
+ * This can be useful when you carry your laptop
+ * in your bag while it's on and no CD inserted in it's drive.
+ * Implemented as found in Documentation/ioctl/cdrom.txt
+ *
+ * TODO: Maybe we should check this also:
+ * EDRIVE_CANT_DO_THIS Door lock function not supported.
+ * EBUSY Attempt to unlock when multiple users
+ * have the drive open and not CAP_SYS_ADMIN
+ */
+static void ManualEject(int fd, int onOff)
+{
+ if (ioctl(fd, CDROM_LOCKDOOR, onOff) < 0) {
+ perror("ioctl on CDROM_LOCKDOOR");
+ } else {
+ if (onOff)
+ printf("CD-Drive may NOT be ejected with device button\n");
+ else
+ printf("CD-Drive may be ejected with device button\n");
+ }
+}
+
+
+/* Set or clear auto-eject mode. */
+static void AutoEject(int fd, int onOff)
+{
+ int status = -1;
+
+#if defined(CDROM_SET_OPTIONS) && defined(CDROM_CLEAR_OPTIONS)
+ if (onOff)
+ status = ioctl(fd, CDROM_SET_OPTIONS, CDO_AUTO_EJECT);
+ else
+ status = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_AUTO_EJECT);
+#else
+ errno = ENOSYS;
+#endif
+ if (status < 0) {
+ fprintf(stderr, _("%s: CD-ROM auto-eject command failed: %s\n"), programName, strerror(errno));
+ exit(1);
+ }
+}
+
+
+/*
+ * Changer select. CDROM_SELECT_DISC is preferred, older kernels used
+ * CDROMLOADFROMSLOT.
+ */
+static void ChangerSelect(int fd, int slot)
+{
+ int status;
+
+#ifdef CDROM_SELECT_DISC
+ status = ioctl(fd, CDROM_SELECT_DISC, slot);
+ if (status < 0) {
+ fprintf(stderr, _("%s: CD-ROM select disc command failed: %s\n"), programName, strerror(errno));
+ exit(1);
+ }
+#elif defined CDROMLOADFROMSLOT
+ status = ioctl(fd, CDROMLOADFROMSLOT, slot);
+ if (status != 0) {
+ fprintf(stderr, _("%s: CD-ROM load from slot command failed: %s\n"), programName, strerror(errno));
+ exit(1);
+ }
+#else
+ fprintf(stderr, _("%s: IDE/ATAPI CD-ROM changer not supported by this kernel\n"), programName);
+#endif
+}
+
+
+/*
+ * Close tray. Not supported by older kernels.
+ */
+static void CloseTray(int fd)
+{
+ int status;
+
+#if defined(CDROMCLOSETRAY) || defined(CDIOCCLOSE)
+#if defined(CDROMCLOSETRAY)
+ status = ioctl(fd, CDROMCLOSETRAY);
+#elif defined(CDIOCCLOSE)
+ status = ioctl(fd, CDIOCCLOSE);
+#endif
+ if (status != 0) {
+ fprintf(stderr, _("%s: CD-ROM tray close command failed: %s\n"), programName, strerror(errno));
+ exit(1);
+ }
+#else
+ fprintf(stderr, _("%s: CD-ROM tray close command not supported by this kernel\n"), programName);
+#endif
+}
+
+/*
+ * Toggle tray.
+ *
+ * Written by Benjamin Schwenk <benjaminschwenk@yahoo.de> and
+ * Sybren Stuvel <sybren@thirdtower.com>
+ *
+ * Not supported by older kernels because it might use
+ * CloseTray().
+ *
+ */
+static void ToggleTray(int fd)
+{
+ struct timeval time_start, time_stop;
+ int time_elapsed;
+
+#ifdef CDROMCLOSETRAY
+
+ /* Try to open the CDROM tray and measure the time needed.
+ * In my experience the function needs less than 0.05
+ * seconds if the tray was already open, and at least 1.5 seconds
+ * if it was closed.
+ */
+ gettimeofday(&time_start, NULL);
+
+ /* Send the CDROMEJECT command to the device. */
+ if (ioctl(fd, CDROMEJECT, 0) < 0) {
+ perror("ioctl");
+ exit(1);
+ }
+
+ /* Get the second timestamp, to measure the time needed to open
+ * the tray. */
+ gettimeofday(&time_stop, NULL);
+
+ time_elapsed = (time_stop.tv_sec * 1000000 + time_stop.tv_usec) -
+ (time_start.tv_sec * 1000000 + time_start.tv_usec);
+
+ /* If the tray "opened" too fast, we can be nearly sure, that it
+ * was already open. In this case, close it now. Else the tray was
+ * closed before. This would mean that we are done. */
+ if (time_elapsed < TRAY_WAS_ALREADY_OPEN_USECS)
+ CloseTray(fd);
+
+#else
+ fprintf(stderr, _("%s: CD-ROM tray toggle command not supported by this kernel\n"), programName);
+#endif
+
+}
+
+/*
+ * Select Speed of CD-ROM drive.
+ * Thanks to Roland Krivanek (krivanek@fmph.uniba.sk)
+ * http://dmpc.dbp.fmph.uniba.sk/~krivanek/cdrom_speed/
+ */
+static void SelectSpeedCdrom(int fd, int speed)
+{
+ int status;
+
+#ifdef CDROM_SELECT_SPEED
+ status = ioctl(fd, CDROM_SELECT_SPEED, speed);
+ if (status != 0) {
+ fprintf(stderr, _("%s: CD-ROM select speed command failed: %s\n"), programName, strerror(errno));
+ exit(1);
+ }
+#else
+ fprintf(stderr, _("%s: CD-ROM select speed command not supported by this kernel\n"), programName);
+#endif
+}
+
+/*
+ * Read Speed of CD-ROM drive. From Linux 2.6.13, the current speed is correctly reported
+ */
+static int ReadSpeedCdrom(const char *shortName)
+{
+ char line[512];
+ char *str_speed, *str_name;
+ int drive_number = -1, i;
+ FILE *f = fopen("/proc/sys/dev/cdrom/info", "r");
+
+ if (f == NULL) {
+ fprintf(stderr, _("%s: unable to read the speed from /proc/sys/dev/cdrom/info\n"), programName);
+ exit(1);
+ }
+
+ while (!feof(f)) {
+ fgets(line, sizeof(line), f);
+
+ /* find drive number from shortName in line "drive name" */
+ if (drive_number == -1) {
+ if (strncmp(line, "drive name:", 11) == 0) {
+ str_name = strtok(&line[11], "\t ");
+ drive_number = 0;
+ while (strncmp(shortName, str_name, strlen(shortName)) != 0) {
+ drive_number++;
+ str_name = strtok(NULL, "\t ");
+ if (str_name == NULL) {
+ fprintf(stderr, _("%s: error while finding CD-ROM name\n"), programName);
+ exit(1);
+ }
+ }
+ }
+ /* find line "drive speed" and read the correct speed */
+ } else {
+ if (strncmp(line, "drive speed:", 12) == 0) {
+ str_speed = strtok(&line[12], "\t ");
+ for (i = 1; i < drive_number; i++)
+ str_speed = strtok(NULL, "\t ");
+
+ if (str_speed == NULL) {
+ fprintf(stderr, _("%s: error while reading speed\n"), programName);
+ exit(1);
+ }
+ return atoi(str_speed);
+ }
+ }
+ }
+
+ fprintf(stderr, _("%s: error while reading speed\n"), programName);
+ exit(1);
+ return -1;
+}
+
+
+/*
+ * List Speed of CD-ROM drive.
+ */
+static void ListSpeedCdrom(const char *fullName, int fd)
+{
+#ifdef CDROM_SELECT_SPEED
+ int max_speed, curr_speed = 0, prev_speed;
+ char *shortName = strrchr(fullName, '/') + 1;
+
+ SelectSpeedCdrom(fd, 0);
+ max_speed = ReadSpeedCdrom(shortName);
+ while (curr_speed < max_speed) {
+ prev_speed = curr_speed;
+ SelectSpeedCdrom(fd, prev_speed + 1);
+ curr_speed = ReadSpeedCdrom(shortName);
+ if (curr_speed > prev_speed)
+ printf("%d ", curr_speed);
+ else
+ curr_speed = prev_speed + 1;
+ }
+
+ printf("\n");
+#else
+ fprintf(stderr, _("%s: CD-ROM select speed command not supported by this kernel\n"), programName);
+#endif
+}
+
+/*
+ * Eject using CDROMEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectCdrom(int fd)
+{
+ int status = -1;
+
+#if defined(CDROMEJECT)
+ status = ioctl(fd, CDROMEJECT);
+#elif defined(CDIOCEJECT)
+ status = ioctl(fd, CDIOCEJECT);
+#else
+/* Some kernels implement cdrom-eject only, but I don't think any kernel in the
+ world would implement eject only for non-cdrom drives. Let's die. */
+# error
+#endif
+ return (status == 0);
+}
+
+#ifdef HAVE_EJECT_SCSI
+/*
+ * Eject using SCSI SG_IO commands. Return 1 if successful, 0 otherwise.
+ */
+static int EjectScsi(int fd)
+{
+ int status, k;
+ sg_io_hdr_t io_hdr;
+ unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
+ unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0};
+ unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0};
+ unsigned char inqBuff[2];
+ unsigned char sense_buffer[32];
+
+ if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+ if (v_option) {
+ printf(_("not an sg device, or old sg driver\n"));
+ }
+ return 0;
+ }
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = 6;
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_NONE;
+ io_hdr.dxfer_len = 0;
+ io_hdr.dxferp = inqBuff;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 10000;
+
+ io_hdr.cmdp = allowRmBlk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return 0;
+
+ io_hdr.cmdp = startStop1Blk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return 0;
+
+ io_hdr.cmdp = startStop2Blk;
+ status = ioctl(fd, SG_IO, (void *)&io_hdr);
+ if (status < 0)
+ return 0;
+
+ /* force kernel to reread partition table when new disc inserted */
+ status = ioctl(fd, BLKRRPART);
+ return 1;
+}
+#endif
+
+
+#ifdef HAVE_EJECT_FLOPPY
+/*
+ * Eject using FDEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectFloppy(int fd)
+{
+ int status;
+
+ status = ioctl(fd, FDEJECT);
+ return (status >= 0);
+}
+#endif
+
+
+#ifdef HAVE_EJECT_TAPE
+/*
+ * Eject using tape ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectTape(int fd)
+{
+ int status;
+ struct mtop op;
+
+ op.mt_op = MTOFFL; /* rewind and eject */
+ op.mt_count = 0; /* not used */
+ status = ioctl(fd, MTIOCTOP, &op);
+ return (status >= 0);
+}
+#endif
+
+
+/* Unmount a device. */
+static void Unmount(const char *fullName)
+{
+ int status;
+
+ switch (fork()) {
+ case 0: /* child */
+ setuid(getuid()); /* reduce likelyhood of security holes when running setuid */
+ if(p_option) {
+ execlp("pumount", "pumount", fullName, "-n", NULL);
+ execlp("umount", "umount", fullName, "-n", NULL);
+ } else {
+ execlp("pumount", "pumount", fullName, NULL);
+ execlp("umount", "umount", fullName, NULL);
+ }
+ fprintf(stderr, _("%s: unable to exec umount of `%s': %s\n"),
+ programName, fullName, strerror(errno));
+ exit(1);
+ break;
+ case -1:
+ fprintf(stderr, _("%s: unable to fork: %s\n"), programName, strerror(errno));
+ break;
+ default: /* parent */
+ wait(&status);
+ if (WIFEXITED(status) == 0) {
+ fprintf(stderr, _("%s: unmount of `%s' did not exit normally\n"), programName, fullName);
+ exit(1);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ fprintf(stderr, _("%s: unmount of `%s' failed\n"), programName, fullName);
+ exit(1);
+ }
+ break;
+ }
+}
+
+
+/* Open a device file. Try opening first read/write, and if that fails then read only. */
+static int OpenDevice(const char *fullName)
+{
+ int fd;
+
+ fd = open(fullName, O_RDWR|O_NONBLOCK);
+ if (fd != -1) {
+ return fd;
+ }
+
+ fd = open(fullName, O_RDONLY|O_NONBLOCK);
+ if (fd == -1) {
+ fprintf(stderr, _("%s: unable to open `%s'\n"), programName, fullName);
+ exit(1);
+ }
+ return fd;
+}
+
+
+/*
+ * Get major and minor device numbers for a device file name, so we
+ * can check for duplicate devices.
+ */
+static int GetMajorMinor(const char *name, int *maj, int *min)
+{
+ struct stat sstat;
+ if (maj) *maj = -1;
+ if (min) *min = -1;
+ if (stat(name, &sstat) == -1)
+ return -1;
+ if (! S_ISBLK(sstat.st_mode) && ! S_ISCHR(sstat.st_mode))
+ return -1;
+ if (maj) *maj = major(sstat.st_rdev);
+ if (min) *min = minor(sstat.st_rdev);
+ return 0;
+}
+
+
+/*
+ * See if device has been mounted by looking in mount table. If so, set
+ * device name and mount point name, and return 1, otherwise return 0.
+ */
+static int MountedDevice(const char *name, char **mountName, char **deviceName)
+{
+ FILE *fp;
+ char line[1024];
+ char s1[1024];
+ char s2[1024];
+ int rc;
+
+ int maj;
+ int min;
+
+ GetMajorMinor(name, &maj, &min);
+
+ fp = fopen((p_option ? "/proc/mounts" : "/etc/mtab"), "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, _("unable to open %s: %s\n"), (p_option ? "/proc/mounts" : "/etc/mtab"), strerror(errno));
+ exit(1);
+ }
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ rc = sscanf(line, "%1023s %1023s", s1, s2);
+ if (rc >= 2) {
+ int mtabmaj, mtabmin;
+ DeMangleMount(s1);
+ DeMangleMount(s2);
+ GetMajorMinor(s1, &mtabmaj, &mtabmin);
+ if (((strcmp(s1, name) == 0) || (strcmp(s2, name) == 0)) ||
+ ((maj != -1) && (maj == mtabmaj) && (min == mtabmin))) {
+ FCLOSE(fp);
+ *deviceName = strdup(s1);
+ *mountName = strdup(s2);
+ return 1;
+ }
+ }
+ }
+ *deviceName = 0;
+ *mountName = 0;
+ FCLOSE(fp);
+ return 0;
+}
+
+
+/*
+ * See if device can be mounted by looking in /etc/fstab.
+ * If so, set device name and mount point name, and return 1,
+ * otherwise return 0.
+ */
+static int MountableDevice(const char *name, char **mountName, char **deviceName)
+{
+ FILE *fp;
+ char line[1024];
+ char s1[1024];
+ char s2[1024];
+ int rc;
+
+ fp = fopen("/etc/fstab", "r");
+ if (fp == NULL) {
+/*
+ * /etc/fstab may be unreadable in some situations due to passwords in the
+ * file.
+ */
+/* fprintf(stderr, _("%s: unable to open /etc/fstab: %s\n"), programName, strerror(errno));
+ exit(1);*/
+ if (v_option) {
+ printf( _("%s: unable to open /etc/fstab: %s\n"), programName, strerror(errno));
+ }
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ rc = sscanf(line, "%1023s %1023s", s1, s2);
+ DeMangleMount(s1);
+ DeMangleMount(s2);
+ if (rc >= 2 && s1[0] != '#' && strcmp(s2, name) == 0) {
+ FCLOSE(fp);
+ *deviceName = strdup(s1);
+ *mountName = strdup(s2);
+ return 1;
+ }
+ }
+ FCLOSE(fp);
+ return 0;
+}
+
+
+#ifndef ANDROID
+/*
+ * Step through mount table and unmount all devices that match a regular
+ * expression.
+ */
+static void UnmountDevices(const char *pattern)
+{
+ regex_t preg;
+ FILE *fp;
+ char s1[1024];
+ char s2[1024];
+ char line[1024];
+ int status;
+
+ if (regcomp(&preg, pattern, REG_EXTENDED)!=0) {
+ perror(programName);
+ exit(1);
+ }
+
+ fp = fopen((p_option ? "/proc/mounts" : "/etc/mtab"), "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, _("unable to open %s: %s\n"),(p_option ? "/proc/mounts" : "/etc/mtab"), strerror(errno));
+ exit(1);
+ }
+
+ while (fgets(line, sizeof(line), fp) != 0) {
+ status = sscanf(line, "%1023s %1023s", s1, s2);
+ if (status >= 2) {
+ status = regexec(&preg, s1, 0, 0, 0);
+ if (status == 0) {
+ if (v_option)
+ printf(_("%s: unmounting `%s'\n"), programName, s2);
+ Unmount(s2);
+ }
+ }
+ }
+ regfree(&preg);
+ FCLOSE(fp);
+}
+#endif
+
+
+/* Check if name is a symbolic link. If so, return what it points to. */
+static char *SymLink(const char *name)
+{
+ int status;
+ char s1[PATH_MAX];
+ char s2[PATH_MAX];
+ char s4[PATH_MAX];
+ char result[PATH_MAX];
+ char *s3;
+
+ memset(s1, 0, sizeof(s1));
+ memset(s2, 0, sizeof(s2));
+ memset(s4, 0, sizeof(s4));
+ memset(result, 0, sizeof(result));
+
+ status = readlink(name, s1, sizeof(s1) - 1);
+
+ if (status == -1)
+ return 0;
+
+ s1[status] = 0;
+ if (s1[0] == '/') { /* absolute link */
+ return strdup(s1);
+ } else { /* relative link, add base name */
+ strncpy(s2, name, sizeof(s2)-1);
+ s3 = strrchr(s2, '/');
+ if (s3 != 0) {
+ s3[1] = 0;
+ snprintf(result, sizeof(result)-1, "%s%s", s2, s1);
+ }
+ }
+ realpath(result, s4);
+ return strdup(s4);
+}
+
+
+#ifndef ANDROID
+/*
+ * Given a name, see if it matches a pattern for a device that can have
+ * multiple partitions. If so, return a regular expression that matches
+ * partitions for that device, otherwise return 0.
+ */
+static char *MultiplePartitions(const char *name)
+{
+ int i = 0;
+ int status;
+ regex_t preg;
+ char pattern[256];
+ char *result = 0;
+
+ for (i = 0; partitionDevice[i] != 0; i++) {
+ /* look for ^/dev/foo[a-z]([0-9]?[0-9])?$, e.g. /dev/hda1 */
+ strcpy(pattern, "^/dev/");
+ strcat(pattern, partitionDevice[i]);
+ strcat(pattern, "[a-z]([0-9]?[0-9])?$");
+ if (regcomp(&preg, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
+ perror(programName);
+ exit(1);
+ }
+ status = regexec(&preg, name, 1, 0, 0);
+ regfree(&preg);
+ if (status == 0) {
+ result = (char *) malloc(strlen(name) + 25);
+ if (result == NULL) {
+ fprintf(stderr, _("%s: could not allocate memory\n"), programName);
+ exit(1);
+ }
+ strcpy(result, name);
+ result[strlen(partitionDevice[i]) + 6] = 0;
+ strcat(result, "([0-9]?[0-9])?$");
+ if (v_option)
+ printf(_("%s: `%s' is a multipartition device\n"), programName, name);
+ return result;
+ }
+ }
+ if (v_option)
+ printf(_("%s: `%s' is not a multipartition device\n"), programName, name);
+ return 0;
+}
+#endif
+
+/*
+ * Find device name in /sys/block/. Returns NULL if not
+ * found. The returned pointer must be free()'d.
+ */
+static char* FindDeviceSysBlock(const char* deviceName)
+{
+ DIR *dir = opendir("/sys/block");
+ struct dirent *d;
+ const char *baseName = strrchr(deviceName, '/');
+ char *device;
+ int len;
+
+ baseName = baseName ? baseName + 1 : deviceName;
+ if (!dir) {
+ fprintf(stderr, _("%s: can not open directory /sys/block/"), programName);
+ return NULL;
+ }
+ while ((d = readdir(dir)) != NULL) {
+ if (d->d_type != DT_DIR && d->d_type != DT_LNK && d->d_type != DT_UNKNOWN)
+ continue;
+ len = strlen(d->d_name);
+ if (!strncmp(baseName, d->d_name, len)) {
+ if ((*(baseName+len) >= '0' &&
+ *(baseName+len) <= '9') ||
+ *(baseName+len) == '\0') {
+ device = strdup(d->d_name);
+ closedir(dir);
+ return device;
+ }
+ }
+ }
+ closedir(dir);
+ return NULL;
+}
+
+/*
+ * From given path gets a subsystem. Returns subsystem if any found
+ * otherwise returns NULL. Returned value must not be free()'d
+ */
+static char *GetSubSystem(const char *sysfspath)
+{
+ static char subsystem[PATH_MAX];
+ char link_subsystem[PATH_MAX];
+ struct stat buf;
+ char *pos;
+
+ snprintf(link_subsystem, sizeof(link_subsystem), "%s/subsystem", sysfspath);
+
+ if (lstat(link_subsystem, &buf) == -1)
+ return NULL;
+ if (!S_ISLNK(buf.st_mode))
+ return NULL;
+ if (readlink(link_subsystem, subsystem, sizeof(subsystem)) == -1)
+ return NULL;
+ if ((pos = strrchr(subsystem, '/')) == NULL)
+ return NULL;
+ strncpy(subsystem, pos+1, sizeof(subsystem));
+
+ return subsystem;
+}
+
+/*
+ * Check content of /sys/block/<dev>/removable. Returns 1 if the file
+ * contains '1' otherwise returns 0.
+ */
+static int CheckRemovable(const char* deviceName)
+{
+ FILE *fp;
+ int removable = 0;
+ char *device;
+ char path[PATH_MAX];
+
+ if ((device = FindDeviceSysBlock(deviceName)) == NULL) {
+ fprintf(stderr,
+ _("%s: did not find a device %s in /sys/block/\n"),
+ programName, deviceName);
+ exit(1);
+ }
+ snprintf(path, sizeof(path), "/sys/block/%s/removable", device);
+ free(device);
+ if((fp = fopen(path, "r")) == NULL)
+ return removable;
+ if (fgetc(fp) == '1')
+ removable = 1;
+
+ fclose(fp);
+ return removable;
+}
+
+/* Check if a device is on hotpluggable subsystem. Returns 1 if is
+ * otherwise returns 0.
+ */
+static int CheckHotpluggable(const char* deviceName)
+{
+ int hotpluggable = 0;
+ char *device;
+ char path[PATH_MAX];
+ char *device_chain;
+ struct stat buf;
+ char *subsystem;
+ char *pos;
+
+ if ((device = FindDeviceSysBlock(deviceName)) == NULL) {
+ fprintf(stderr, _("%s: did not find a device %s in /sys/block/\n"),
+ programName, deviceName);
+ exit(1);
+ }
+ snprintf(path, sizeof(path), "/sys/block/%s/device", device);
+ free(device);
+
+ if (lstat(path, &buf) == -1)
+ return hotpluggable;
+ if (!S_ISLNK(buf.st_mode))
+ return hotpluggable;
+ if ((device_chain = SymLink(path)) == NULL)
+ return hotpluggable;
+ while ( strncmp(device_chain, "", sizeof(device_chain) != 0)) {
+ subsystem = GetSubSystem(device_chain);
+ if (subsystem) {
+ /* as hotpluggable we assume devices on these buses */
+ if (strncmp("usb", subsystem, sizeof("usb")) == 0 ||
+ strncmp("ieee1394", subsystem, sizeof("ieee1394")) == 0 ||
+ strncmp("pcmcia", subsystem, sizeof("pcmcia")) == 0 ||
+ strncmp("mmc", subsystem, sizeof("mmc")) == 0 ||
+ strncmp("ccw", subsystem, sizeof("ccw")) == 0) {
+ hotpluggable = 1;
+ break;
+ }
+ }
+ /* remove one member from devicechain */
+ pos = strrchr(device_chain, '/');
+ if (pos)
+ pos[0] = '\0';
+ else
+ device_chain[0] = '\0';
+ }
+
+ return hotpluggable;
+}
+
+/* handle -x option */
+static void HandleXOption(char *deviceName)
+{
+ int fd; /* file descriptor for device */
+ if (x_option) {
+ if (v_option)
+ {
+ if (x_arg == 0)
+ printf(_("%s: setting CD-ROM speed to auto\n"), programName);
+ else
+ printf(_("%s: setting CD-ROM speed to %dX\n"), programName, x_arg);
+ }
+ fd = OpenDevice(deviceName);
+ SelectSpeedCdrom(fd, x_arg);
+ exit(0);
+ }
+}
+
+
+/* main program */
+int main(int argc, char **argv)
+{
+ const char *defaultDevice = DEFAULTDEVICE; /* default if no name passed by user */
+ int worked = 0; /* set to 1 when successfully ejected */
+ char *device = 0; /* name passed from user */
+ char *fullName; /* expanded name */
+ char *fullNameOrig;/* expanded name (links not resolved) */
+ char *deviceName; /* name of device */
+ char *linkName; /* name of device's symbolic link */
+ char *mountName; /* name of device's mount point */
+ int fd; /* file descriptor for device */
+ int mounted = 0; /* true if device is mounted */
+ int mountable = 0; /* true if device is in /etc/fstab */
+ int result = 0; /* store the result of a operation */
+ char *pattern = 0; /* regex for device if multiple partitions */
+ int ld = 6; /* symbolic link max depth */
+
+ I18NCODE
+
+ /* program name is global variable used by other procedures */
+ programName = strdup(argv[0]);
+
+ /* parse the command line arguments */
+ parse_args(argc, argv, &device);
+
+
+ /* handle -d option */
+ if (d_option) {
+ printf(_("%s: default device: `%s'\n"), programName, defaultDevice);
+ exit(0);
+ }
+
+ /* if no device, use default */
+ if (device == 0) {
+ device = strdup(defaultDevice);
+ if (v_option)
+ printf(_("%s: using default device `%s'\n"), programName, device);
+ }
+
+ /* Strip any trailing slash from name in case user used bash/tcsh
+ style filename completion (e.g. /mnt/cdrom/) */
+ if (device[strlen(device)-1] == '/')
+ device[strlen(device)-1] = 0;
+
+ if (v_option)
+ printf(_("%s: device name is `%s'\n"), programName, device);
+
+ do {
+ /* figure out full device or mount point name */
+ fullName = FindDevice(device);
+ if (fullName == 0) {
+ fprintf(stderr, _("%s: unable to find or open device for: `%s'\n"),
+ programName, device);
+ exit(1);
+ }
+ if (v_option)
+ printf(_("%s: expanded name is `%s'\n"), programName, fullName);
+
+ /* check for a symbolic link */
+ /* /proc/mounts doesn't resolve symbolic links */
+ fullNameOrig = strdup(fullName);
+ linkName = strdup(fullName); /* ensure linkName is initialized */
+ if (!p_option) {
+ while ((linkName = SymLink(fullName)) && (ld > 0)) {
+ if (v_option)
+ printf(_("%s: `%s' is a link to `%s'\n"), programName,
+ fullName, linkName);
+ free(fullName);
+ fullName = strdup(linkName);
+ free(linkName);
+ linkName = 0;
+ ld--;
+ }
+ }
+ /* handle max depth exceeded option */
+ if (ld <= 0) {
+ printf(_("%s: maximum symbolic link depth exceeded: `%s'\n"), programName, fullName);
+ exit(1);
+ }
+
+ /* if mount point, get device name */
+ mounted = MountedDevice(fullName, &mountName, &deviceName);
+ if (v_option) {
+ if (mounted)
+ printf(_("%s: `%s' is mounted at `%s'\n"), programName,
+ deviceName, mountName);
+ else
+ printf(_("%s: `%s' is not mounted\n"), programName, fullName);
+ }
+ if (!mounted) {
+ deviceName = strdup(fullName);
+ }
+
+ /* if not currently mounted, see if it is a possible mount point */
+ if (!mounted) {
+ mountable = MountableDevice(fullName, &mountName, &deviceName);
+ /* if return value -1 then fstab could not be read */
+ if (v_option && mountable >= 0) {
+ if (mountable)
+ printf(_("%s: `%s' can be mounted at `%s'\n"), programName, deviceName, mountName);
+ else
+ printf(_("%s: `%s' is not a mount point\n"), programName, fullName);
+ }
+ }
+
+ result = GetMajorMinor(deviceName, NULL, NULL);
+ if (result == -1) {
+ fprintf(stderr,
+ _("%s: tried to use `%s' as device name but it is no block device\n"),
+ programName, deviceName);
+ }
+
+ } while (result == -1);
+
+ /* handle -n option */
+ if (n_option) {
+ printf(_("%s: device is `%s'\n"), programName, deviceName);
+ if (v_option)
+ printf(_("%s: exiting due to -n/--noop option\n"), programName);
+ exit(0);
+ }
+
+ /* Check if device has removable flag*/
+ if (v_option)
+ printf(_("%s: checking if device \"%s\" has a removable or hotpluggable flag\n"),
+ programName, deviceName);
+ if (!CheckRemovable(deviceName) && !CheckHotpluggable(deviceName))
+ {
+ fprintf(stderr, _("%s: device \"%s\" doesn't have a removable or hotpluggable flag\n"),
+ programName, deviceName);
+ exit(1);
+ }
+
+ /* handle -i option */
+ if (i_option) {
+ fd = OpenDevice(deviceName);
+ ManualEject(fd, i_arg);
+ exit(0);
+ }
+
+ /* handle -a option */
+ if (a_option) {
+ if (v_option) {
+ if (a_arg)
+ printf(_("%s: enabling auto-eject mode for `%s'\n"), programName, deviceName);
+ else
+ printf(_("%s: disabling auto-eject mode for `%s'\n"), programName, deviceName);
+ }
+ fd = OpenDevice(deviceName);
+ AutoEject(fd, a_arg);
+ exit(0);
+ }
+
+ /* handle -t option */
+ if (t_option) {
+ if (v_option)
+ printf(_("%s: closing tray\n"), programName);
+ fd = OpenDevice(deviceName);
+ CloseTray(fd);
+ HandleXOption(deviceName);
+ exit(0);
+ }
+
+ /* handle -X option */
+ if (X_option) {
+ if (v_option)
+ printf(_("%s: listing CD-ROM speed\n"), programName);
+ fd = OpenDevice(deviceName);
+ ListSpeedCdrom(deviceName, fd);
+ exit(0);
+ }
+
+ /* handle -x option only */
+ if (!c_option) HandleXOption(deviceName);
+
+ /* unmount device if mounted */
+ if ((m_option != 1) && mounted) {
+ if (v_option)
+ printf(_("%s: unmounting device `%s' from `%s'\n"), programName, deviceName, mountName);
+ Unmount(mountName);
+ }
+
+#ifndef ANDROID
+ /* if it is a multipartition device, unmount any other partitions on
+ the device */
+ pattern = MultiplePartitions(deviceName);
+ if ((m_option != 1) && (pattern != 0))
+ UnmountDevices(pattern);
+#endif
+
+ /* handle -T option */
+ if (T_option) {
+ if (v_option)
+ printf(_("%s: toggling tray\n"), programName);
+ fd = OpenDevice(deviceName);
+ ToggleTray(fd);
+ HandleXOption(deviceName);
+ exit(0);
+ }
+
+ /* handle -c option */
+ if (c_option) {
+ if (v_option)
+ printf(_("%s: selecting CD-ROM disc #%d\n"), programName, c_arg);
+ fd = OpenDevice(deviceName);
+ ChangerSelect(fd, c_arg);
+ HandleXOption(deviceName);
+ exit(0);
+ }
+
+ /* if user did not specify type of eject, try all four methods */
+ if ((r_option + s_option + f_option + q_option) == 0) {
+ r_option = s_option = f_option = q_option = 1;
+ }
+
+ /* open device */
+ fd = OpenDevice(deviceName);
+
+ /* try various methods of ejecting until it works */
+ if (r_option) {
+ if (v_option)
+ printf(_("%s: trying to eject `%s' using CD-ROM eject command\n"), programName, deviceName);
+ worked = EjectCdrom(fd);
+ if (v_option) {
+ if (worked)
+ printf(_("%s: CD-ROM eject command succeeded\n"), programName);
+ else
+ printf(_("%s: CD-ROM eject command failed\n"), programName);
+ }
+ }
+
+#ifdef HAVE_EJECT_SCSI
+ if (s_option && !worked) {
+ if (v_option)
+ printf(_("%s: trying to eject `%s' using SCSI commands\n"), programName, deviceName);
+ worked = EjectScsi(fd);
+ if (v_option) {
+ if (worked)
+ printf(_("%s: SCSI eject succeeded\n"), programName);
+ else
+ printf(_("%s: SCSI eject failed\n"), programName);
+ }
+ }
+#endif
+
+#ifdef HAVE_EJECT_FLOPPY
+ if (f_option && !worked) {
+ if (v_option)
+ printf(_("%s: trying to eject `%s' using floppy eject command\n"), programName, deviceName);
+ worked = EjectFloppy(fd);
+ if (v_option) {
+ if (worked)
+ printf(_("%s: floppy eject command succeeded\n"), programName);
+ else
+ printf(_("%s: floppy eject command failed\n"), programName);
+ }
+ }
+#endif
+
+#ifdef HAVE_EJECT_TAPE
+ if (q_option && !worked) {
+ if (v_option)
+ printf(_("%s: trying to eject `%s' using tape offline command\n"), programName, deviceName);
+ worked = EjectTape(fd);
+ if (v_option) {
+ if (worked)
+ printf(_("%s: tape offline command succeeded\n"), programName);
+ else
+ printf(_("%s: tape offline command failed\n"), programName);
+ }
+ }
+#endif
+
+ if (!worked) {
+ fprintf(stderr, _("%s: unable to eject, last error: %s\n"), programName, strerror(errno));
+ exit(1);
+ }
+
+ /* cleanup */
+ CLOSE(fd);
+ free(device);
+ free(deviceName);
+ free(fullName);
+ free(fullNameOrig);
+ free(linkName);
+ free(mountName);
+ free(pattern);
+ exit(0);
+}