Code :
/******************************************************************** * * 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> #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/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 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; int p_option = 0; int m_option = 0; int a_arg = 0; int c_arg = 0; int x_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] -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\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: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'}, {"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 '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; } } /* * 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; } /* 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 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) */ 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 = rindex(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) else curr_speed = prev_speed + 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 = -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)) { 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 = 2000; 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. */ 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; 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; 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, s2 ); Unmount(s2); } } } 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])?$" ); 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; } /* 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; /* 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); } /* 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); } /* 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 -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); }
|