summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac10
-rw-r--r--misc-utils/Makefile.am6
-rw-r--r--misc-utils/eject.1316
-rw-r--r--misc-utils/eject.c1138
-rw-r--r--misc-utils/i18n.h22
5 files changed, 1492 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 2140bc596..4e9b5f4ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -862,6 +862,14 @@ ruman1dir='${mandir}/ru/man1'
AC_SUBST([ruman1dir])
+AC_ARG_ENABLE([eject],
+ AS_HELP_STRING([--disable-eject], [do not build eject]),
+ [], enable_eject=yes
+)
+UL_BUILD_INIT([eject])
+UL_REQUIRES_LINUX([eject])
+
+
AC_ARG_ENABLE([agetty],
AS_HELP_STRING([--disable-agetty], [do not build agetty]),
[], enable_agetty=yes
@@ -870,6 +878,8 @@ UL_BUILD_INIT([agetty])
AM_CONDITIONAL(BUILD_AGETTY, test "x$build_agetty" = xyes)
+
+
AC_ARG_ENABLE([cramfs],
AS_HELP_STRING([--disable-cramfs], [do not build fsck.cramfs, mkfs.cramfs]),
[], enable_cramfs=check
diff --git a/misc-utils/Makefile.am b/misc-utils/Makefile.am
index acf2c9d1a..097e1f969 100644
--- a/misc-utils/Makefile.am
+++ b/misc-utils/Makefile.am
@@ -32,6 +32,12 @@ dist_man_MANS = \
namei_SOURCES = namei.c $(top_srcdir)/lib/strutils.c
+
+usrbin_exec_PROGRAMS += eject
+eject_SOURCES = eject.c
+dist_man_MANS += eject.1
+
+
if BUILD_DDATE
usrbin_exec_PROGRAMS += ddate
dist_man_MANS += ddate.1
diff --git a/misc-utils/eject.1 b/misc-utils/eject.1
new file mode 100644
index 000000000..ae92b7fc5
--- /dev/null
+++ b/misc-utils/eject.1
@@ -0,0 +1,316 @@
+.\" This file Copyright (C) 1994-2005 Jeff Tranter
+.\" (tranter@pobox.com)
+.\" It may be distributed under the GNU Public License, version 2, or
+.\" any higher version. See section COPYING of the GNU Public license
+.\" for conditions under which this file may be redistributed.
+.TH EJECT 1 "18 May 2001" "Linux" "User Commands"
+.SH NAME
+eject \- eject removable media
+.SH SYNOPSIS
+eject \-h
+.br
+eject [\-vnrsfqp] [<name>]
+.br
+eject [\-vn] \-d
+.br
+eject [\-vn] \-a on|off|1|0 [<name>]
+.br
+eject [\-vn] \-c slot [<name>]
+.br
+eject [\-vn] \-t [<name>]
+.br
+eject [-vn] -T [<name>]
+.br
+eject [\-vn] \-x <speed> [<name>]
+.br
+eject \-V
+
+.SH DESCRIPTION
+
+.B Eject
+allows removable media (typically a CD-ROM, floppy disk, tape, or JAZ
+or ZIP disk) to be ejected under software control. The command can
+also control some multi-disc CD-ROM changers, the auto-eject feature
+supported by some devices, and close the disc tray of some CD-ROM
+drives.
+
+The device corresponding to <name> is ejected. The name can be a
+device file or mount point, either a full path or with the leading
+"/dev", "/media" or "/mnt" omitted. If no name is specified, the default name
+"cdrom" is used.
+
+There are four different methods of ejecting, depending on whether the
+device is a CD-ROM, SCSI device, removable floppy, or tape. By default
+eject tries all four methods in order until it succeeds.
+
+If the device is currently mounted, it is unmounted before ejecting.
+
+.PP
+.SH "COMMAND\-LINE OPTIONS"
+.TP 0.5i
+.B \-h
+This option causes
+.B eject
+to display a brief description of the command options.
+
+.TP 0.5i
+.B \-v
+This makes
+.B eject
+run in verbose mode; more information is displayed about what the
+command is doing.
+
+.TP 0.5i
+.B \-d
+If invoked with this option,
+.B eject
+lists the default device name.
+
+.TP 0.5i
+.B \-a on|1|off|0
+This option controls the auto-eject mode, supported by some devices.
+When enabled, the drive automatically ejects when the device is
+closed.
+
+.TP 0.5i
+.B \-c <slot>
+With this option a CD slot can be selected from an ATAPI/IDE CD-ROM
+changer. Linux 2.0 or higher is required to use this feature. The
+CD-ROM drive can not be in use (mounted data CD or playing a music CD)
+for a change request to work. Please also note that the first slot of
+the changer is referred to as 0, not 1.
+
+.TP 0.5i
+.B \-t
+With this option the drive is given a CD-ROM tray close command. Not
+all devices support this command.
+
+.TP 0.5i
+.B \-T
+With this option the drive is given a CD-ROM tray close command if
+it's opened, and a CD-ROM tray eject command if it's closed. Not all
+devices support this command, because it uses the above CD-ROM tray
+close command.
+
+.TP 0.5i
+.B \-x <speed>
+With this option the drive is given a CD-ROM select speed command.
+The speed argument is a number indicating the desired speed (e.g. 8
+for 8X speed), or 0 for maximum data rate. Not all devices support
+this command and you can only specify speeds that the drive is capable
+of. Every time the media is changed this option is cleared. This
+option can be used alone, or with the \-t and \-c options.
+
+.TP 0.5i
+.B \-n
+With this option the selected device is displayed but no action is
+performed.
+
+.TP 0.5i
+.B \-r
+This option specifies that the drive should be ejected using a
+CDROM eject command.
+.TP 0.5i
+
+.B \-s
+This option specifies that the drive should be ejected using
+SCSI commands.
+
+.TP 0.5i
+.B \-f
+This option specifies that the drive should be ejected using a
+removable floppy disk eject command.
+
+.TP 0.5i
+.B \-q
+This option specifies that the drive should be ejected using a
+tape drive offline command.
+
+.TP 0.5i
+.B \-p
+This option allow you to use /proc/mounts instead /etc/mtab. It
+also passes the \-n option to umount(1).
+
+.TP 0.5i
+.B \-m
+This option allows eject to work with device drivers which automatically
+mount removable media and therefore must be always mount()ed.
+The option tells eject to not try to unmount the given device,
+even if it is mounted according to /etc/mtab or /proc/mounts.
+
+.TP 0.5i
+.B \-V
+This option causes
+.B eject
+to display the program version and exit.
+
+.SH LONG OPTIONS
+All options have corresponding long names, as listed below. The long
+names can be abbreviated as long as they are unique.
+
+.br
+\-h \-\-help
+.br
+\-v \-\-verbose
+.br
+\-d \-\-default
+.br
+\-a \-\-auto
+.br
+\-c \-\-changerslot
+.br
+\-t \-\-trayclose
+.br
+\-t \-\-traytoggle
+.br
+\-x \-\-cdspeed
+.br
+\-n \-\-noop
+.br
+\-r \-\-cdrom
+.br
+\-s \-\-scsi
+.br
+\-f \-\-floppy
+.br
+\-q \-\-tape
+.br
+\-V \-\-version
+.br
+\-p \-\-proc
+.br
+\-m \-\-no-unmount
+.br
+
+.SH EXAMPLES
+.PP
+Eject the default device:
+.IP
+eject
+.PP
+Eject a device or mount point named cdrom:
+.IP
+eject cdrom
+.PP
+Eject using device name:
+.IP
+eject /dev/cdrom
+.PP
+Eject using mount point:
+.IP
+eject /mnt/cdrom/
+.PP
+Eject 4th IDE device:
+.IP
+eject hdd
+.PP
+Eject first SCSI device:
+.IP
+eject sda
+.PP
+Eject using SCSI partition name (e.g. a ZIP drive):
+.IP
+eject sda4
+.PP
+Select 5th disc on multi-disc changer:
+.IP
+eject \-v \-c4 /dev/cdrom
+.PP
+Turn on auto-eject on a SoundBlaster CD-ROM drive:
+.IP
+eject \-a on /dev/sbpcd
+
+.SH EXIT STATUS
+.PP
+
+Returns 0 if operation was successful, 1 if operation failed or command
+syntax was not valid.
+
+.SH NOTES
+.PP
+
+.B Eject
+only works with devices that support one or more of the four methods
+of ejecting. This includes most CD-ROM drives (IDE, SCSI, and
+proprietary), some SCSI tape drives, JAZ drives, ZIP drives (parallel
+port, SCSI, and IDE versions), and LS120 removable floppies. Users
+have also reported success with floppy drives on Sun SPARC and Apple
+Macintosh systems. If
+.B eject
+does not work, it is most likely a limitation of the kernel driver
+for the device and not the
+.B eject
+program itself.
+
+The \-r, \-s, \-f, and \-q options allow controlling which methods are
+used to eject. More than one method can be specified. If none of these
+options are specified, it tries all four (this works fine in most
+cases).
+
+.B Eject
+may not always be able to determine if the device is mounted (e.g. if
+it has several names). If the device name is a symbolic link,
+.B eject
+will follow the link and use the device that it points to.
+
+If
+.B eject
+determines that the device can have multiple partitions, it will
+attempt to unmount all mounted partitions of the device before
+ejecting. If an unmount fails, the program will not attempt to eject
+the media.
+
+You can eject an audio CD. Some CD-ROM drives will refuse to open the
+tray if the drive is empty. Some devices do not support the tray close
+command.
+
+If the auto-eject feature is enabled, then the drive will always be
+ejected after running this command. Not all Linux kernel CD-ROM
+drivers support the auto-eject mode. There is no way to find out the
+state of the auto-eject mode.
+
+You need appropriate privileges to access the device files. Running as
+root or setuid root is required to eject some devices (e.g. SCSI
+devices).
+
+The heuristic used to find a device, given a name, is as follows. If
+the name ends in a trailing slash, it is removed (this is to support
+filenames generated using shell file name completion). If the name
+starts with '.' or '/', it tries to open it as a device file or mount
+point. If that fails, it tries prepending '/dev/', '/media/' ,'/mnt/',
+\&'/dev/cdroms', '/dev/rdsk/', '/dev/dsk/', and finally './' to the name,
+until a
+device file or mount point is found that can be opened. The program
+checks /etc/mtab for mounted devices. If that fails, it also checks
+/etc/fstab for mount points of currently unmounted devices.
+
+Creating symbolic links such as /dev/cdrom or /dev/zip is recommended
+so that
+.B eject
+can determine the appropriate devices using easily remembered names.
+
+To save typing you can create a shell alias for the eject options that
+work for your particular setup.
+
+.SH AUTHOR
+.B Eject
+was written by Jeff Tranter (tranter@pobox.com) and is released
+under the conditions of the GNU General Public License. See the file
+COPYING and notes in the source code for details.
+
+The \-x option was added by Nobuyuki Tsuchimura (tutimura@nn.iij4u.or.jp),
+with thanks to Roland Krivanek (krivanek@fmph.uniba.sk) and his
+cdrom_speed command.
+
+The -T option was added by Sybren Stuvel (sybren@thirdtower.com), with
+big thanks to Benjamin Schwenk (benjaminschwenk@yahoo.de).
+
+ .SH SEE ALSO
+
+ mount(2), umount(2), mount(8), umount(8)
+
+.SH SEE ALSO
+
+mount(2), umount(2), mount(8), umount(8)
+.br
+/usr/src/linux/Documentation/cdrom/
diff --git a/misc-utils/eject.c b/misc-utils/eject.c
new file mode 100644
index 000000000..172278e02
--- /dev/null
+++ b/misc-utils/eject.c
@@ -0,0 +1,1138 @@
+/********************************************************************
+ *
+ * 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-2005 Jeff Tranter (tranter@pobox.com)
+ *
+ * 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"
+
+
+#define DEFAULTDEVICE "/dev/sr0"
+
+
+#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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.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 <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 <sys/mount.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); \
+}
+
+/* Global Variables */
+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 q_option = 0;
+int r_option = 0;
+int s_option = 0;
+int t_option = 0;
+int T_option = 0;
+int v_option = 0;
+int x_option = 0;
+int p_option = 0;
+int m_option = 0;
+int a_arg = 0;
+int c_arg = 0;
+int x_arg = 0;
+char *programName; /* used in error messages */
+
+/*
+ * These are the basenames of devices which can have multiple
+ * partitions per device.
+ */
+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 [-vnrsfq] [<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] -x <speed> [<name>] -- set CD-ROM max speed\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"
+" -s\t-- eject SCSI device\n"
+" -f\t-- eject floppy\n"
+" -q\t-- eject tape\n"
+" -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 -s --scsi -f --floppy\n"
+" -q --tape -n --noop -V --version\n"
+" -p --proc -m --no-unmount -T --traytoggle\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:dfhnqrstTvVpm";
+#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'},
+ {"trayclose", no_argument, NULL, 't'},
+ {"traytoggle", no_argument, NULL, 'T'},
+ {"cdspeed", required_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 '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 '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)
+{
+
+ /*
+ * 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) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/*
+ * 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;
+
+ 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 ((name[0] == '.') || (name[0] == '/')) {
+ strcpy(buf, name);
+ if (FileExists(buf))
+ return buf;
+ }
+
+ strcpy(buf, "/dev/");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ strcpy(buf, "/media/");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ strcpy(buf, "/mnt/");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ /* for devfs under Linux */
+ strcpy(buf, "/dev/cdroms/");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ strcpy(buf, "/dev/cdroms/");
+ strcat(buf, name);
+ strcat(buf, "0");
+ if (FileExists(buf))
+ return buf;
+
+ /* for devfs under Solaris */
+ strcpy(buf, "/dev/rdsk/");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ strcpy(buf, "/dev/dsk/");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ strcpy(buf, "./");
+ strcat(buf, name);
+ if (FileExists(buf))
+ return buf;
+
+ free(buf);
+ buf = 0;
+ return 0;
+}
+
+
+/* Set or clear auto-eject mode. */
+static void AutoEject(int fd, int onOff)
+{
+ int status;
+
+ status = ioctl(fd, CDROMEJECT_SW, onOff);
+ 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;
+
+#ifdef CDROMCLOSETRAY
+ status = ioctl(fd, CDROMCLOSETRAY);
+ 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 therefor
+ * 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
+}
+
+
+/*
+ * Eject using CDROMEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectCdrom(int fd)
+{
+ int status;
+
+ status = ioctl(fd, CDROMEJECT);
+ return (status == 0);
+}
+
+
+/*
+ * Eject using SCSI commands. Return 1 if successful, 0 otherwise.
+ */
+static int EjectScsi(int fd)
+{
+ int status;
+ struct sdata {
+ int inlen;
+ int outlen;
+ char cmd[256];
+ } scsi_cmd;
+
+ scsi_cmd.inlen = 0;
+ scsi_cmd.outlen = 0;
+ scsi_cmd.cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd.cmd[1] = 0;
+ scsi_cmd.cmd[2] = 0;
+ scsi_cmd.cmd[3] = 0;
+ scsi_cmd.cmd[4] = 0;
+ scsi_cmd.cmd[5] = 0;
+ status = ioctl(fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd);
+ if (status != 0)
+ return 0;
+
+ scsi_cmd.inlen = 0;
+ scsi_cmd.outlen = 0;
+ scsi_cmd.cmd[0] = START_STOP;
+ scsi_cmd.cmd[1] = 0;
+ scsi_cmd.cmd[2] = 0;
+ scsi_cmd.cmd[3] = 0;
+ scsi_cmd.cmd[4] = 1;
+ scsi_cmd.cmd[5] = 0;
+ status = ioctl(fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd);
+ if (status != 0)
+ return 0;
+
+ scsi_cmd.inlen = 0;
+ scsi_cmd.outlen = 0;
+ scsi_cmd.cmd[0] = START_STOP;
+ scsi_cmd.cmd[1] = 0;
+ scsi_cmd.cmd[2] = 0;
+ scsi_cmd.cmd[3] = 0;
+ scsi_cmd.cmd[4] = 2;
+ scsi_cmd.cmd[5] = 0;
+ status = ioctl(fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd);
+ if (status != 0)
+ return 0;
+
+ /* force kernel to reread partition table when new disc inserted */
+ status = ioctl(fd, BLKRRPART);
+ return (status == 0);
+}
+
+
+/*
+ * Eject using FDEJECT ioctl. Return 1 if successful, 0 otherwise.
+ */
+static int EjectFloppy(int fd)
+{
+ int status;
+
+ status = ioctl(fd, FDEJECT);
+ return (status == 0);
+}
+
+
+/*
+ * 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);
+}
+
+
+/* Unmount a device. */
+static void Unmount(const char *fullName)
+{
+ int status;
+
+ switch (fork()) {
+ case 0: /* child */
+ seteuid(getuid()); /* reduce likelyhood of security holes when running setuid */
+ if(p_option)
+ execl("/bin/umount", "/bin/umount", fullName, "-n", NULL);
+ else
+ execl("/bin/umount", "/bin/umount", fullName, NULL);
+ fprintf(stderr, _("%s: unable to exec /bin/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. */
+static int OpenDevice(const char *fullName)
+{
+ int 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;
+ *maj = *min = -1;
+ if (stat(name, &sstat) == -1)
+ return -1;
+ if (! S_ISBLK(sstat.st_mode))
+ return -1;
+ *maj = major(sstat.st_rdev);
+ *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;
+ 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);
+ if (rc >= 2 && s1[0] != '#' && strcmp(s2, name) == 0) {
+ FCLOSE(fp);
+ *deviceName = strdup(s1);
+ *mountName = strdup(s2);
+ return 1;
+ }
+ }
+ FCLOSE(fp);
+ return 0;
+}
+
+
+/*
+ * 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, s1);
+ Unmount(s1);
+ regfree(&preg);
+ }
+ }
+ }
+ FCLOSE(fp);
+}
+
+
+/* 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);
+}
+
+
+/*
+ * 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])?$");
+ regcomp(&preg, pattern, REG_EXTENDED|REG_NOSUB);
+ status = regexec(&preg, name, 1, 0, 0);
+ regfree(&preg);
+ if (status == 0) {
+ result = (char *) malloc(strlen(name) + 25);
+ 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;
+}
+
+
+/* handle -x option */
+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 *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 */
+ char *pattern; /* 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);
+
+ /* 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 */
+ 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);
+ }
+ }
+
+ /* 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);
+ }
+
+ /* 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 -T option */
+ if (T_option) {
+ if (v_option)
+ printf(_("%s: toggling tray\n"), programName);
+ fd = OpenDevice(deviceName);
+ ToggleTray(fd);
+ HandleXOption(deviceName);
+ 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 `%s'\n"), programName, deviceName);
+ Unmount(deviceName);
+ }
+
+ /* if it is a multipartition device, unmount any other partitions on
+ the device */
+ pattern = MultiplePartitions(deviceName);
+ if ((m_option != 1) && (pattern != 0))
+ UnmountDevices(pattern);
+
+ /* 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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(linkName);
+ free(mountName);
+ free(pattern);
+ exit(0);
+}
diff --git a/misc-utils/i18n.h b/misc-utils/i18n.h
new file mode 100644
index 000000000..e4c821b8a
--- /dev/null
+++ b/misc-utils/i18n.h
@@ -0,0 +1,22 @@
+/*
+ i18nized by: KUN-CHUNG, HSIEH <linuxer@coventive.com>
+ Taiwan
+
+ Homepage: http://www.geocities.com/linux4tw/
+
+ 程式國際化設計: 謝崑中
+*/
+
+#ifndef __i18n__
+ #define __i18n__
+ #define PKG "eject"
+ #define LOCALEDIR "/usr/share/locale"
+
+ #include <locale.h>
+ #include <libintl.h>
+ #define _(str) gettext (str)
+ #define N_(str) (str)
+ #define I18NCODE setlocale(LC_ALL,""); textdomain(PKG); bindtextdomain(PKG,LOCALEDIR);
+
+ void i18n_init (void);
+#endif