OSDN Git Service

Add the missing include <sys/sysmacros.h>
[android-x86/external-eject.git] / eject.c
1 /********************************************************************
2  *
3  *              L I N U X   E J E C T   C O M M A N D
4  *
5  *                by Jeff Tranter (tranter@pobox.com)
6  *
7  ********************************************************************
8  *
9  * Copyright (C) 1994-2001 Jeff Tranter (tranter@pobox.com)
10  * Copyright (C) 2004, 2005 Frank Lichtenheld (djpig@debian.org)
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  *
26  ********************************************************************
27  *
28  * See the man page for a description of what this program does and what
29  * the requirements to run it are.
30  *
31  */
32
33 #include "i18n.h"
34
35 #ifndef DEFAULTDEVICE
36 #error DEFAULTDEVICE not set, check Makefile
37 #endif
38
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <fcntl.h>
44 #include <limits.h>
45 #include <dirent.h>
46
47 #ifdef GETOPTLONG
48 #include <getopt.h>
49 #endif /* GETOPTLONG */
50 #include <errno.h>
51 #ifndef ANDROID
52 #include <regex.h>
53 #endif
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <sys/ioctl.h>
57 #include <sys/wait.h>
58 //#include <sys/mtio.h>
59 #include <sys/mount.h>
60
61 #if defined(__linux__)
62 #include <linux/version.h>
63 /* handy macro found in 2.1 kernels, but not in older ones */
64 #ifndef KERNEL_VERSION
65 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
66 #endif
67 #include <linux/mtio.h>
68 #include <linux/types.h>
69 #include <linux/cdrom.h>
70 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
71 #include <linux/ucdrom.h>
72 #endif
73 #include <linux/fd.h>
74 #ifndef ANDROID
75 #include <scsi/scsi.h>
76 #include <scsi/sg.h>
77 #include <scsi/scsi_ioctl.h>
78 #endif
79 #include <sys/time.h>
80 #include <sys/sysmacros.h>
81
82 /* Used by the ToggleTray() function. If ejecting the tray takes this
83  * time or less, the tray was probably already ejected, so we close it
84  * again.
85  */
86 #define TRAY_WAS_ALREADY_OPEN_USECS  200000     /* about 0.2 seconds */
87
88
89 #define CLOSE(fd) if (close(fd)==-1) { \
90     perror(programName); \
91     exit(1); \
92 }
93
94 #define FCLOSE(fd) if (fclose(fd)==-1) { \
95     perror(programName); \
96     exit(1); \
97 }
98
99 #ifndef ANDROID
100 #define HAVE_EJECT_SCSI
101 #endif
102 #define HAVE_EJECT_FLOPPY
103 #define HAVE_EJECT_TAPE
104
105 #elif defined(__FreeBSD_kernel__)
106 #include <sys/cdio.h>
107 #endif /* if defined(__linux__) */
108
109
110 #define CLOSE(fd) if (close(fd)==-1) { \
111     perror(programName); \
112     exit(1); \
113 }
114
115 #define FCLOSE(fd) if (fclose(fd)==-1) { \
116     perror(programName); \
117     exit(1); \
118 }
119
120 /* Global Variables */
121 static const char *version = VERSION; /* program version */
122 int a_option = 0; /* command flags and arguments */
123 int c_option = 0;
124 int d_option = 0;
125 int f_option = 0;
126 int h_option = 0;
127 int n_option = 0;
128 int i_option = 0;
129 int q_option = 0;
130 int r_option = 0;
131 int s_option = 0;
132 int t_option = 0;
133 int T_option = 0;
134 int X_option = 0;
135 int v_option = 0;
136 int x_option = 0;
137 #ifdef ANDROID
138 int p_option = 1;
139 #else
140 int p_option = 0;
141 #endif
142 int m_option = 0;
143 int a_arg = 0;
144 int c_arg = 0;
145 int x_arg = 0;
146 int i_arg = 0;
147 static char *programName; /* used in error messages */
148
149 /*
150  * These are the basenames of devices which can have multiple
151  * partitions per device.
152  */
153 static const char *partitionDevice[] = {
154         "hd",
155         "sd",
156         "xd",
157         "dos_hd",
158         "mfm",
159         "ad",
160         "ed",
161         "ftl",
162         "pd",
163         0};
164
165
166 /* Display command usage on standard error and exit. */
167 static void usage()
168 {
169 //    perror(_("%s: device is `%s'\n"));
170         fprintf(stderr,_(
171 "Eject version %s by Jeff Tranter (tranter@pobox.com)\n"
172 "Usage:\n"
173 "  eject -h                             -- display command usage and exit\n"
174 "  eject -V                             -- display program version and exit\n"
175 "  eject [-vnrsfqpm] [<name>]           -- eject device\n"
176 "  eject [-vn] -d                       -- display default device\n"
177 "  eject [-vn] -a on|off|1|0 [<name>]   -- turn auto-eject feature on or off\n"
178 "  eject [-vn] -c <slot> [<name>]       -- switch discs on a CD-ROM changer\n"
179 "  eject [-vn] -t [<name>]              -- close tray\n"
180 "  eject [-vn] -T [<name>]              -- toggle tray\n"
181 "  eject [-vn] -i on|off|1|0 [<name>]   -- toggle manual eject protection on/off\n"
182 "  eject [-vn] -x <speed> [<name>]      -- set CD-ROM max speed\n"
183 "  eject [-vn] -X [<name>]              -- list CD-ROM available speeds\n"
184 "Options:\n"
185 "  -v\t-- enable verbose output\n"
186 "  -n\t-- don't eject, just show device found\n"
187 "  -r\t-- eject CD-ROM\n"
188 #ifdef HAVE_EJECT_SCSI
189 "  -s\t-- eject SCSI device\n"
190 #endif
191 #ifdef HAVE_EJECT_FLOPPY
192 "  -f\t-- eject floppy\n"
193 #endif
194 #ifdef HAVE_EJECT_TAPE
195 "  -q\t-- eject tape\n"
196 #endif
197 "  -p\t-- use /proc/mounts instead of /etc/mtab\n"
198 "  -m\t-- do not unmount device even if it is mounted\n"
199 )
200 , version);
201 #ifdef GETOPTLONG
202         fprintf(stderr,_(
203 "Long options:\n"
204 "  -h --help   -v --verbose      -d --default\n"
205 "  -a --auto   -c --changerslot  -t --trayclose  -x --cdspeed\n"
206 "  -r --cdrom"
207 #ifdef HAVE_EJECT_SCSI
208 "  -s --scsi"
209 #endif
210 #ifdef HAVE_EJECT_FLOPPY
211 "         -f --floppy"
212 #endif
213 "     -X --listspeed"
214 #ifdef HAVE_EJECT_TAPE
215 "     -q --tape"
216 #endif
217 "\n"
218 "  -n --noop   -V --version\n"
219 "  -p --proc   -m --no-unmount   -T --traytoggle -i --manualeject\n"));
220 #endif /* GETOPTLONG */
221         fprintf(stderr,_(
222 "Parameter <name> can be a device file or a mount point.\n"
223 "If omitted, name defaults to `%s'.\n"
224 "By default tries -r, -s, -f, and -q in order until success.\n"),
225                         DEFAULTDEVICE);
226   exit(1);
227 }
228
229
230 /* Handle command line options. */
231 static void parse_args(int argc, char **argv, char **device)
232 {
233         const char *flags = "a:c:x:i:dfhnqrstTXvVpm";
234 #ifdef GETOPTLONG
235         static struct option long_options[] =
236         {
237                 {"help",        no_argument,       NULL, 'h'},
238                 {"verbose",     no_argument,       NULL, 'v'},
239                 {"default",     no_argument,       NULL, 'd'},
240                 {"auto",        required_argument, NULL, 'a'},
241                 {"changerslot", required_argument, NULL, 'c'},
242                 {"manualeject", required_argument, NULL, 'i'},
243                 {"trayclose",   no_argument,       NULL, 't'},
244                 {"traytoggle",  no_argument,       NULL, 'T'},
245                 {"cdspeed",     required_argument, NULL, 'x'},
246                 {"listspeed",   no_argument,       NULL, 'X'},
247                 {"noop",        no_argument,       NULL, 'n'},
248                 {"cdrom",       no_argument,       NULL, 'r'},
249                 {"scsi",        no_argument,       NULL, 's'},
250                 {"floppy",      no_argument,       NULL, 'f'},
251                 {"tape",        no_argument,       NULL, 'q'},
252                 {"version",     no_argument,       NULL, 'V'},
253                 {"proc",        no_argument,       NULL, 'p'},
254                 {"no-unmount",  no_argument,       NULL, 'm'},
255                 {0, 0, 0, 0}
256         };
257         int option_index;
258 #endif /* GETOPTLONG */
259         int c;
260
261 #ifdef GETOPTLONG
262         while ((c = getopt_long(argc, argv, flags, long_options, &option_index)) != EOF) {
263 #else
264         while ((c = getopt(argc, argv, flags)) != EOF) {
265 #endif /* GETOPTLONG */
266                 switch (c) {
267                   case 'a':
268                           a_option = 1;
269                           if (!strcmp(optarg, "0"))
270                                   a_arg = 0;
271                           else if (!strcmp(optarg, "off"))
272                                   a_arg = 0;
273                           else if (!strcmp(optarg, "1"))
274                                   a_arg = 1;
275                           else if (!strcmp(optarg, "on"))
276                                   a_arg = 1;
277                           else {
278                                   fprintf(stderr, _("%s: invalid argument to --auto/-a option\n"), programName);
279                                   exit(1);
280                           }
281                           break;
282                   case 'c':
283                           c_option = 1;
284                           /* atoi() returns 0 on error, so "0" must be parsed separately */
285                           if (!strcmp(optarg, "0"))
286                                   c_arg = 0;
287                           else {
288                                   c_arg = atoi(optarg);
289                                   if (c_arg <= 0) {
290                                           fprintf(stderr, _("%s: invalid argument to --changerslot/-c option\n"), programName);
291                                           exit(1);
292                                   }
293                           }
294                           break;
295                   case 'x':
296                           x_option = 1;
297                           if (!strcmp(optarg, "0"))
298                                   x_arg = 0;
299                           else {
300                                   x_arg = atoi(optarg);
301                                   if (x_arg <= 0) {
302                                           fprintf(stderr, _("%s: invalid argument to --cdspeed/-x option\n"), programName);
303                                           exit(1);
304                                   }
305                           }
306                           break;
307                   case 'd':
308                           d_option = 1;
309                           break;
310                   case 'f':
311                           f_option = 1;
312                           break;
313                   case 'h':
314                           usage();
315                           exit(0);
316                           break;
317                   case 'i':
318                           i_option = 1;
319                           if (!strcmp(optarg, "0"))
320                                   i_arg = 0;
321                           else if (!strcmp(optarg, "off"))
322                                   i_arg = 0;
323                           else if (!strcmp(optarg, "1"))
324                                   i_arg = 1;
325                           else if (!strcmp(optarg, "on"))
326                                   i_arg = 1;
327                           else {
328                                   fprintf(stderr, _("%s: invalid argument to -i option\n"), programName);
329                                   exit(1);
330                           }
331                           break;
332                   case 'm':
333                           m_option = 1;
334                           break;
335                   case 'n':
336                           n_option = 1;
337                           break;
338                   case 'p':
339                           p_option = 1;
340                           break;
341                   case 'q':
342                           q_option = 1;
343                           break;
344                   case 'r':
345                           r_option = 1;
346                           break;
347                   case 's':
348                           s_option = 1;
349                           break;
350                   case 't':
351                           t_option = 1;
352                           break;
353                   case 'X':
354                           X_option = 1;
355                           break;
356                   case 'T':
357                           T_option = 1;
358                           break;
359                   case 'v':
360                           v_option = 1;
361                           break;
362                   case 'V':
363                           printf(_("eject version %s by Jeff Tranter (tranter@pobox.com)\n"), version);
364                           exit(0);
365                           break;
366                   case '?':
367                           exit(1);
368                           break;
369                 }
370         }
371         /* check for a single additional argument */
372         if ((argc - optind) > 1) {
373                 fprintf(stderr, _("%s: too many arguments\n"), programName);
374                 exit(1);
375         }
376         if ((argc - optind) == 1) { /* one argument */
377                 *device = strdup(argv[optind]);
378         }
379 }
380
381
382 /* Return 1 if file/device exists, 0 otherwise. */
383 static int FileExists(const char *name, const int try, int *found)
384 {
385
386         if (!found) return -1;
387         /*
388          * access() uses the UID, not the EUID. This way a normal user
389          * cannot find out if a file (say, /root/fubar) exists or not, even
390          * if eject is SUID root
391          */
392         if (access (name, F_OK) == 0) {
393           (*found)++;
394           if (try <= (*found))
395             return 1;
396           else
397             return 0;
398         } else {
399           return 0;
400         }
401 }
402
403
404 /*
405  * Linux mangles spaces in mount points by changing them to an octal string
406  * of '\040'.  So lets scan the mount point and fix it up by replacing all
407  * occurrences off '\0##' with the ASCII value of 0##.  Requires a writable
408  * string as input as we mangle in place.  Some of this was taken from the
409  * util-linux package.
410  */
411 #define octalify(a) ((a) & 7)
412 #define tooctal(s) (64*octalify(s[1]) + 8*octalify(s[2]) + octalify(s[3]))
413 #define isoctal(a) (((a) & ~7) == '0')
414 static char *DeMangleMount(char *s)
415 {
416         char *tmp = s;
417         while ((tmp = strchr(tmp, '\\')) != NULL) {
418                 if (isoctal(tmp[1]) && isoctal(tmp[2]) && isoctal(tmp[3])) {
419                         tmp[0] = tooctal(tmp);
420                         memmove(tmp+1, tmp+4, strlen(tmp)-3);
421                 }
422                 ++tmp;
423         }
424         return s;
425 }
426
427
428 /*
429  * Given name, such as foo, see if any of the following exist:
430  *
431  * foo (if foo starts with '.' or '/')
432  * /dev/foo
433  * /media/foo
434  * /mnt/foo
435  * /dev/cdroms/foo
436  * /dev/cdroms/foo0
437  * /dev/dsk/foo
438  * /dev/rdsk/foo
439  * ./foo
440  *
441  * If found, return the full path. If not found, return 0.
442  * Returns pointer to dynamically allocated string.
443  */
444 static char *FindDevice(const char *name)
445 {
446         char *buf;
447         static int try = 0;
448         int found = 0;
449
450         buf = (char *) malloc(strlen(name)+14); /* to allow for "/dev/cdroms/ + "0" + null */
451         if (buf==NULL) {
452                 fprintf(stderr, _("%s: could not allocate memory\n"), programName);
453                 exit(1);
454         }
455
456         if (try == INT_MAX) {
457           fprintf(stderr, _("%s: FindDevice called too often\n"), programName );;
458           exit(1);
459         } else
460           try++;
461
462         if ((name[0] == '.') || (name[0] == '/')) {
463                 strcpy(buf, name);
464                 if (FileExists(buf, try, &found))
465                         return buf;
466         }
467
468         strcpy(buf, "/dev/");
469         strcat(buf, name);
470         if (FileExists(buf, try, &found))
471                 return buf;
472
473         strcpy(buf, "/media/");
474         strcat(buf, name);
475         if (FileExists(buf, try, &found))
476                 return buf;
477
478         strcpy(buf, "/mnt/");
479         strcat(buf, name);
480         if (FileExists(buf, try, &found))
481                 return buf;
482
483         /* for devfs under Linux */
484         strcpy(buf, "/dev/cdroms/");
485         strcat(buf, name);
486         if (FileExists(buf, try, &found))
487                 return buf;
488
489         strcpy(buf, "/dev/cdroms/");
490         strcat(buf, name);
491         strcat(buf, "0");
492         if (FileExists(buf, try, &found))
493                 return buf;
494
495         /* for devfs under Solaris */
496         strcpy(buf, "/dev/rdsk/");
497         strcat(buf, name);
498         if (FileExists(buf, try, &found))
499                 return buf;
500
501         strcpy(buf, "/dev/dsk/");
502         strcat(buf, name);
503         if (FileExists(buf, try, &found))
504                 return buf;
505
506         strcpy(buf, "./");
507         strcat(buf, name);
508         if (FileExists(buf, try, &found))
509                 return buf;
510
511         free(buf);
512         buf = 0;
513         return 0;
514 }
515
516
517 /*
518  * Stops CDROM from opening on manual eject pressing the button.
519  * This can be useful when you carry your laptop
520  * in your bag while it's on and no CD inserted in it's drive.
521  * Implemented as found in Documentation/ioctl/cdrom.txt
522  *
523  * TODO: Maybe we should check this also:
524  * EDRIVE_CANT_DO_THIS   Door lock function not supported.
525  * EBUSY                 Attempt to unlock when multiple users
526  *                       have the drive open and not CAP_SYS_ADMIN
527  */
528 static void ManualEject(int fd, int onOff)
529 {
530         if (ioctl(fd, CDROM_LOCKDOOR, onOff) < 0) {
531                 perror("ioctl on CDROM_LOCKDOOR");
532         } else {
533             if (onOff)
534                 printf("CD-Drive may NOT be ejected with device button\n");
535             else
536                 printf("CD-Drive may be ejected with device button\n");
537         }
538 }
539
540
541 /* Set or clear auto-eject mode. */
542 static void AutoEject(int fd, int onOff)
543 {
544         int status = -1;
545
546 #if defined(CDROM_SET_OPTIONS) && defined(CDROM_CLEAR_OPTIONS)
547         if (onOff)
548                 status = ioctl(fd, CDROM_SET_OPTIONS, CDO_AUTO_EJECT);
549         else
550                 status = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_AUTO_EJECT);
551 #else
552         errno = ENOSYS;
553 #endif
554         if (status < 0) {
555                 fprintf(stderr, _("%s: CD-ROM auto-eject command failed: %s\n"), programName, strerror(errno));
556                 exit(1);
557         }
558 }
559
560
561 /*
562  * Changer select. CDROM_SELECT_DISC is preferred, older kernels used
563  * CDROMLOADFROMSLOT.
564  */
565 static void ChangerSelect(int fd, int slot)
566 {
567         int status;
568
569 #ifdef CDROM_SELECT_DISC
570         status = ioctl(fd, CDROM_SELECT_DISC, slot);
571         if (status < 0) {
572                 fprintf(stderr, _("%s: CD-ROM select disc command failed: %s\n"), programName, strerror(errno));
573                 exit(1);
574         }
575 #elif defined CDROMLOADFROMSLOT
576         status = ioctl(fd, CDROMLOADFROMSLOT, slot);
577         if (status != 0) {
578                 fprintf(stderr, _("%s: CD-ROM load from slot command failed: %s\n"), programName, strerror(errno));
579                 exit(1);
580         }
581 #else
582     fprintf(stderr, _("%s: IDE/ATAPI CD-ROM changer not supported by this kernel\n"), programName);
583 #endif
584 }
585
586
587 /*
588  * Close tray. Not supported by older kernels.
589  */
590 static void CloseTray(int fd)
591 {
592         int status;
593
594 #if defined(CDROMCLOSETRAY) || defined(CDIOCCLOSE)
595 #if defined(CDROMCLOSETRAY)
596         status = ioctl(fd, CDROMCLOSETRAY);
597 #elif defined(CDIOCCLOSE)
598         status = ioctl(fd, CDIOCCLOSE);
599 #endif
600         if (status != 0) {
601                 fprintf(stderr, _("%s: CD-ROM tray close command failed: %s\n"), programName, strerror(errno));
602                 exit(1);
603         }
604 #else
605     fprintf(stderr, _("%s: CD-ROM tray close command not supported by this kernel\n"), programName);
606 #endif
607 }
608
609 /*
610  * Toggle tray.
611  *
612  * Written by Benjamin Schwenk <benjaminschwenk@yahoo.de> and
613  * Sybren Stuvel <sybren@thirdtower.com>
614  *
615  * Not supported by older kernels because it might use
616  * CloseTray().
617  *
618  */
619 static void ToggleTray(int fd)
620 {
621         struct timeval time_start, time_stop;
622         int time_elapsed;
623
624 #ifdef CDROMCLOSETRAY
625
626         /* Try to open the CDROM tray and measure the time needed.
627          * In my experience the function needs less than 0.05
628          * seconds if the tray was already open, and at least 1.5 seconds
629          * if it was closed.
630          */
631         gettimeofday(&time_start, NULL);
632
633         /* Send the CDROMEJECT command to the device. */
634         if (ioctl(fd, CDROMEJECT, 0) < 0) {
635                 perror("ioctl");
636                 exit(1);
637         }
638
639         /* Get the second timestamp, to measure the time needed to open
640          * the tray.  */
641         gettimeofday(&time_stop, NULL);
642
643         time_elapsed = (time_stop.tv_sec * 1000000 + time_stop.tv_usec) -
644                 (time_start.tv_sec * 1000000 + time_start.tv_usec);
645
646         /* If the tray "opened" too fast, we can be nearly sure, that it
647          * was already open. In this case, close it now. Else the tray was
648          * closed before. This would mean that we are done.  */
649         if (time_elapsed < TRAY_WAS_ALREADY_OPEN_USECS)
650                 CloseTray(fd);
651
652 #else
653     fprintf(stderr, _("%s: CD-ROM tray toggle command not supported by this kernel\n"), programName);
654 #endif
655
656 }
657
658 /*
659  * Select Speed of CD-ROM drive.
660  * Thanks to Roland Krivanek (krivanek@fmph.uniba.sk)
661  * http://dmpc.dbp.fmph.uniba.sk/~krivanek/cdrom_speed/
662  */
663 static void SelectSpeedCdrom(int fd, int speed)
664 {
665         int status;
666
667 #ifdef CDROM_SELECT_SPEED
668         status = ioctl(fd, CDROM_SELECT_SPEED, speed);
669         if (status != 0) {
670                 fprintf(stderr, _("%s: CD-ROM select speed command failed: %s\n"), programName, strerror(errno));
671                 exit(1);
672         }
673 #else
674     fprintf(stderr, _("%s: CD-ROM select speed command not supported by this kernel\n"), programName);
675 #endif
676 }
677
678 /*
679  * Read Speed of CD-ROM drive. From Linux 2.6.13, the current speed is correctly reported
680  */
681 static int ReadSpeedCdrom(const char *shortName)
682 {
683         char line[512];
684         char *str_speed, *str_name;
685         int drive_number = -1, i;
686         FILE *f = fopen("/proc/sys/dev/cdrom/info", "r");
687
688         if (f == NULL) {
689                 fprintf(stderr, _("%s: unable to read the speed from /proc/sys/dev/cdrom/info\n"), programName);
690                 exit(1);
691         }
692
693         while (!feof(f)) {
694                 fgets(line, sizeof(line), f);
695
696                 /* find drive number from shortName in line "drive name" */
697                 if (drive_number == -1) {
698                         if (strncmp(line, "drive name:", 11) == 0) {
699                                 str_name = strtok(&line[11], "\t ");
700                                 drive_number = 0;
701                                 while (strncmp(shortName, str_name, strlen(shortName)) != 0) {
702                                         drive_number++;
703                                         str_name = strtok(NULL, "\t ");
704                                         if (str_name == NULL) {
705                                                 fprintf(stderr, _("%s: error while finding CD-ROM name\n"), programName);
706                                                 exit(1);
707                                         }
708                                 }
709                         }
710                 /* find line "drive speed" and read the correct speed */
711                 } else {
712                         if (strncmp(line, "drive speed:", 12) == 0) {
713                                 str_speed = strtok(&line[12], "\t ");
714                                 for (i = 1; i < drive_number; i++)
715                                         str_speed = strtok(NULL, "\t ");
716
717                                 if (str_speed == NULL) {
718                                         fprintf(stderr, _("%s: error while reading speed\n"), programName);
719                                         exit(1);
720                                 }
721                                 return atoi(str_speed);
722                         }
723                 }
724         }
725
726         fprintf(stderr, _("%s: error while reading speed\n"), programName);
727         exit(1);
728         return -1;
729 }
730
731
732 /*
733  * List Speed of CD-ROM drive.
734  */
735 static void ListSpeedCdrom(const char *fullName, int fd)
736 {
737 #ifdef CDROM_SELECT_SPEED
738         int max_speed, curr_speed = 0, prev_speed;
739         char *shortName = strrchr(fullName, '/') + 1;
740
741         SelectSpeedCdrom(fd, 0);
742         max_speed = ReadSpeedCdrom(shortName);
743         while (curr_speed < max_speed) {
744                 prev_speed = curr_speed;
745                 SelectSpeedCdrom(fd, prev_speed + 1);
746                 curr_speed = ReadSpeedCdrom(shortName);
747                 if (curr_speed > prev_speed)
748                         printf("%d ", curr_speed);
749                 else
750                         curr_speed = prev_speed + 1;
751         }
752
753         printf("\n");
754 #else
755         fprintf(stderr, _("%s: CD-ROM select speed command not supported by this kernel\n"), programName);
756 #endif
757 }
758
759 /*
760  * Eject using CDROMEJECT ioctl. Return 1 if successful, 0 otherwise.
761  */
762 static int EjectCdrom(int fd)
763 {
764         int status = -1;
765
766 #if defined(CDROMEJECT)
767         status = ioctl(fd, CDROMEJECT);
768 #elif defined(CDIOCEJECT)
769         status = ioctl(fd, CDIOCEJECT);
770 #else
771 /* Some kernels implement cdrom-eject only, but I don't think any kernel in the
772    world would implement eject only for non-cdrom drives.  Let's die. */
773 # error
774 #endif
775         return (status == 0);
776 }
777
778 #ifdef HAVE_EJECT_SCSI
779 /*
780  * Eject using SCSI SG_IO commands. Return 1 if successful, 0 otherwise.
781  */
782 static int EjectScsi(int fd)
783 {
784         int status, k;
785         sg_io_hdr_t io_hdr;
786         unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
787         unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0};
788         unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0};
789         unsigned char inqBuff[2];
790         unsigned char sense_buffer[32];
791
792         if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
793           if (v_option) {
794                   printf(_("not an sg device, or old sg driver\n"));
795           }
796           return 0;
797         }
798
799         memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
800         io_hdr.interface_id = 'S';
801         io_hdr.cmd_len = 6;
802         io_hdr.mx_sb_len = sizeof(sense_buffer);
803         io_hdr.dxfer_direction = SG_DXFER_NONE;
804         io_hdr.dxfer_len = 0;
805         io_hdr.dxferp = inqBuff;
806         io_hdr.sbp = sense_buffer;
807         io_hdr.timeout = 10000;
808
809         io_hdr.cmdp = allowRmBlk;
810         status = ioctl(fd, SG_IO, (void *)&io_hdr);
811         if (status < 0)
812                 return 0;
813
814         io_hdr.cmdp = startStop1Blk;
815         status = ioctl(fd, SG_IO, (void *)&io_hdr);
816         if (status < 0)
817                 return 0;
818
819         io_hdr.cmdp = startStop2Blk;
820         status = ioctl(fd, SG_IO, (void *)&io_hdr);
821         if (status < 0)
822                 return 0;
823
824         /* force kernel to reread partition table when new disc inserted */
825         status = ioctl(fd, BLKRRPART);
826         return 1;
827 }
828 #endif
829
830
831 #ifdef HAVE_EJECT_FLOPPY
832 /*
833  * Eject using FDEJECT ioctl. Return 1 if successful, 0 otherwise.
834  */
835 static int EjectFloppy(int fd)
836 {
837         int status;
838
839         status = ioctl(fd, FDEJECT);
840         return (status >= 0);
841 }
842 #endif
843
844
845 #ifdef HAVE_EJECT_TAPE
846 /*
847  * Eject using tape ioctl. Return 1 if successful, 0 otherwise.
848  */
849 static int EjectTape(int fd)
850 {
851         int status;
852         struct mtop op;
853
854         op.mt_op = MTOFFL; /* rewind and eject */
855         op.mt_count = 0;   /* not used */
856         status = ioctl(fd, MTIOCTOP, &op);
857         return (status >= 0);
858 }
859 #endif
860
861
862 /* Unmount a device. */
863 static void Unmount(const char *fullName)
864 {
865         int status;
866
867         switch (fork()) {
868           case 0: /* child */
869                   setuid(getuid()); /* reduce likelyhood of security holes when running setuid */
870                   if(p_option) {
871                           execlp("pumount", "pumount", fullName, "-n", NULL);
872                           execlp("umount", "umount", fullName, "-n", NULL);
873                   } else {
874                           execlp("pumount", "pumount", fullName, NULL);
875                           execlp("umount", "umount", fullName, NULL);
876                   }
877                   fprintf(stderr, _("%s: unable to exec umount of `%s': %s\n"),
878                                   programName, fullName, strerror(errno));
879                   exit(1);
880                   break;
881           case -1:
882                   fprintf(stderr, _("%s: unable to fork: %s\n"), programName, strerror(errno));
883                   break;
884           default: /* parent */
885                   wait(&status);
886                   if (WIFEXITED(status) == 0) {
887                           fprintf(stderr, _("%s: unmount of `%s' did not exit normally\n"), programName, fullName);
888                           exit(1);
889                   }
890                   if (WEXITSTATUS(status) != 0) {
891                           fprintf(stderr, _("%s: unmount of `%s' failed\n"), programName, fullName);
892                           exit(1);
893                   }
894                   break;
895         }
896 }
897
898
899 /* Open a device file. Try opening first read/write, and if that fails then read only. */
900 static int OpenDevice(const char *fullName)
901 {
902         int fd;
903
904         fd = open(fullName, O_RDWR|O_NONBLOCK);
905         if (fd != -1) {
906                 return fd;
907         }
908
909         fd = open(fullName, O_RDONLY|O_NONBLOCK);
910         if (fd == -1) {
911                 fprintf(stderr, _("%s: unable to open `%s'\n"), programName, fullName);
912                 exit(1);
913         }
914         return fd;
915 }
916
917
918 /*
919  * Get major and minor device numbers for a device file name, so we
920  * can check for duplicate devices.
921  */
922 static int GetMajorMinor(const char *name, int *maj, int *min)
923 {
924         struct stat sstat;
925         if (maj) *maj = -1;
926         if (min) *min = -1;
927         if (stat(name, &sstat) == -1)
928                 return -1;
929         if (! S_ISBLK(sstat.st_mode) && ! S_ISCHR(sstat.st_mode))
930                 return -1;
931         if (maj) *maj = major(sstat.st_rdev);
932         if (min) *min = minor(sstat.st_rdev);
933         return 0;
934 }
935
936
937 /*
938  * See if device has been mounted by looking in mount table.  If so, set
939  * device name and mount point name, and return 1, otherwise return 0.
940  */
941 static int MountedDevice(const char *name, char **mountName, char **deviceName)
942 {
943         FILE *fp;
944         char line[1024];
945         char s1[1024];
946         char s2[1024];
947         int rc;
948
949         int maj;
950         int min;
951
952         GetMajorMinor(name, &maj, &min);
953
954         fp = fopen((p_option ? "/proc/mounts" : "/etc/mtab"), "r");
955         if (fp == NULL)
956         {
957                 fprintf(stderr, _("unable to open %s: %s\n"), (p_option ? "/proc/mounts" : "/etc/mtab"), strerror(errno));
958                 exit(1);
959         }
960
961         while (fgets(line, sizeof(line), fp) != 0) {
962                 rc = sscanf(line, "%1023s %1023s", s1, s2);
963                 if (rc >= 2) {
964                         int mtabmaj, mtabmin;
965                         DeMangleMount(s1);
966                         DeMangleMount(s2);
967                         GetMajorMinor(s1, &mtabmaj, &mtabmin);
968                         if (((strcmp(s1, name) == 0) || (strcmp(s2, name) == 0)) ||
969                                 ((maj != -1) && (maj == mtabmaj) && (min == mtabmin))) {
970                                 FCLOSE(fp);
971                                 *deviceName = strdup(s1);
972                                 *mountName = strdup(s2);
973                                 return 1;
974                         }
975                 }
976         }
977         *deviceName = 0;
978         *mountName = 0;
979         FCLOSE(fp);
980         return 0;
981 }
982
983
984 /*
985  * See if device can be mounted by looking in /etc/fstab.
986  * If so, set device name and mount point name, and return 1,
987  * otherwise return 0.
988  */
989 static int MountableDevice(const char *name, char **mountName, char **deviceName)
990 {
991         FILE *fp;
992         char line[1024];
993         char s1[1024];
994         char s2[1024];
995         int rc;
996
997         fp = fopen("/etc/fstab", "r");
998         if (fp == NULL) {
999 /*
1000  * /etc/fstab may be unreadable in some situations due to passwords in the
1001  * file.
1002  */
1003 /*              fprintf(stderr, _("%s: unable to open /etc/fstab: %s\n"), programName, strerror(errno));
1004                 exit(1);*/
1005                 if (v_option) {
1006                         printf( _("%s: unable to open /etc/fstab: %s\n"), programName, strerror(errno));
1007                 }
1008                 return -1;
1009         }
1010
1011         while (fgets(line, sizeof(line), fp) != 0) {
1012                 rc = sscanf(line, "%1023s %1023s", s1, s2);
1013                 DeMangleMount(s1);
1014                 DeMangleMount(s2);
1015                 if (rc >= 2 && s1[0] != '#' && strcmp(s2, name) == 0) {
1016                         FCLOSE(fp);
1017                         *deviceName = strdup(s1);
1018                         *mountName = strdup(s2);
1019                         return 1;
1020                 }
1021         }
1022         FCLOSE(fp);
1023         return 0;
1024 }
1025
1026
1027 #ifndef ANDROID
1028 /*
1029  * Step through mount table and unmount all devices that match a regular
1030  * expression.
1031  */
1032 static void UnmountDevices(const char *pattern)
1033 {
1034         regex_t preg;
1035         FILE *fp;
1036         char s1[1024];
1037         char s2[1024];
1038         char line[1024];
1039         int status;
1040
1041         if (regcomp(&preg, pattern, REG_EXTENDED)!=0) {
1042                 perror(programName);
1043                 exit(1);
1044         }
1045
1046         fp = fopen((p_option ? "/proc/mounts" : "/etc/mtab"), "r");
1047         if (fp == NULL)
1048         {
1049                 fprintf(stderr, _("unable to open %s: %s\n"),(p_option ? "/proc/mounts" : "/etc/mtab"), strerror(errno));
1050                 exit(1);
1051         }
1052
1053         while (fgets(line, sizeof(line), fp) != 0) {
1054                 status = sscanf(line, "%1023s %1023s", s1, s2);
1055                 if (status >= 2) {
1056                         status = regexec(&preg, s1, 0, 0, 0);
1057                         if (status == 0) {
1058                                 if (v_option)
1059                                         printf(_("%s: unmounting `%s'\n"), programName, s2);
1060                                 Unmount(s2);
1061                         }
1062                 }
1063         }
1064         regfree(&preg);
1065         FCLOSE(fp);
1066 }
1067 #endif
1068
1069
1070 /* Check if name is a symbolic link. If so, return what it points to. */
1071 static char *SymLink(const char *name)
1072 {
1073         int status;
1074         char s1[PATH_MAX];
1075         char s2[PATH_MAX];
1076         char s4[PATH_MAX];
1077         char result[PATH_MAX];
1078         char *s3;
1079
1080         memset(s1, 0, sizeof(s1));
1081         memset(s2, 0, sizeof(s2));
1082         memset(s4, 0, sizeof(s4));
1083         memset(result, 0, sizeof(result));
1084
1085         status = readlink(name, s1, sizeof(s1) - 1);
1086
1087         if (status == -1)
1088                 return 0;
1089
1090         s1[status] = 0;
1091         if (s1[0] == '/') { /* absolute link */
1092                 return strdup(s1);
1093         } else { /* relative link, add base name */
1094                 strncpy(s2, name, sizeof(s2)-1);
1095                 s3 = strrchr(s2, '/');
1096                 if (s3 != 0) {
1097                         s3[1] = 0;
1098                         snprintf(result, sizeof(result)-1, "%s%s", s2, s1);
1099                 }
1100         }
1101         realpath(result, s4);
1102         return strdup(s4);
1103 }
1104
1105
1106 #ifndef ANDROID
1107 /*
1108  * Given a name, see if it matches a pattern for a device that can have
1109  * multiple partitions.  If so, return a regular expression that matches
1110  * partitions for that device, otherwise return 0.
1111  */
1112 static char *MultiplePartitions(const char *name)
1113 {
1114         int i = 0;
1115         int status;
1116         regex_t preg;
1117         char pattern[256];
1118         char *result = 0;
1119
1120         for (i = 0; partitionDevice[i] != 0; i++) {
1121                 /* look for ^/dev/foo[a-z]([0-9]?[0-9])?$, e.g. /dev/hda1 */
1122                 strcpy(pattern, "^/dev/");
1123                 strcat(pattern, partitionDevice[i]);
1124                 strcat(pattern, "[a-z]([0-9]?[0-9])?$");
1125                 if (regcomp(&preg, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
1126                         perror(programName);
1127                         exit(1);
1128                 }
1129                 status = regexec(&preg, name, 1, 0, 0);
1130                 regfree(&preg);
1131                 if (status == 0) {
1132                         result = (char *) malloc(strlen(name) + 25);
1133                         if (result == NULL) {
1134                                 fprintf(stderr, _("%s: could not allocate memory\n"), programName);
1135                                 exit(1);
1136                         }
1137                         strcpy(result, name);
1138                         result[strlen(partitionDevice[i]) + 6] = 0;
1139                         strcat(result, "([0-9]?[0-9])?$");
1140                         if (v_option)
1141                                 printf(_("%s: `%s' is a multipartition device\n"), programName, name);
1142                         return result;
1143                 }
1144         }
1145         if (v_option)
1146                 printf(_("%s: `%s' is not a multipartition device\n"), programName, name);
1147         return 0;
1148 }
1149 #endif
1150
1151 /*
1152  * Find device name in /sys/block/. Returns NULL if not
1153  * found. The returned pointer must be free()'d.
1154  */
1155 static char* FindDeviceSysBlock(const char* deviceName)
1156 {
1157         DIR *dir = opendir("/sys/block");
1158         struct dirent *d;
1159         const char *baseName = strrchr(deviceName, '/');
1160         char *device;
1161         int len;
1162
1163         baseName = baseName ? baseName + 1 : deviceName;
1164         if (!dir) {
1165                 fprintf(stderr, _("%s: can not open directory /sys/block/"), programName);
1166                 return NULL;
1167         }
1168         while ((d = readdir(dir)) != NULL) {
1169                 if (d->d_type != DT_DIR && d->d_type != DT_LNK && d->d_type != DT_UNKNOWN)
1170                         continue;
1171                 len = strlen(d->d_name);
1172                 if (!strncmp(baseName, d->d_name, len)) {
1173                         if ((*(baseName+len) >= '0' &&
1174                              *(baseName+len) <= '9') ||
1175                              *(baseName+len) == '\0') {
1176                                 device = strdup(d->d_name);
1177                                 closedir(dir);
1178                                 return device;
1179                         }
1180                 }
1181         }
1182         closedir(dir);
1183         return NULL;
1184 }
1185
1186 /*
1187  * From given path gets a subsystem. Returns subsystem if any found
1188  * otherwise returns NULL. Returned value must not be free()'d
1189  */
1190 static char *GetSubSystem(const char *sysfspath)
1191 {
1192         static char subsystem[PATH_MAX];
1193         char link_subsystem[PATH_MAX];
1194         struct stat buf;
1195         char *pos;
1196
1197         snprintf(link_subsystem, sizeof(link_subsystem), "%s/subsystem", sysfspath);
1198
1199         if (lstat(link_subsystem, &buf) == -1)
1200                 return NULL;
1201         if (!S_ISLNK(buf.st_mode))
1202                 return NULL;
1203         if (readlink(link_subsystem, subsystem, sizeof(subsystem)) == -1)
1204                 return NULL;
1205         if ((pos = strrchr(subsystem, '/')) == NULL)
1206                 return NULL;
1207         strncpy(subsystem, pos+1, sizeof(subsystem));
1208
1209         return subsystem;
1210 }
1211
1212 /*
1213  * Check content of /sys/block/<dev>/removable. Returns 1 if the file
1214  * contains '1' otherwise returns 0.
1215  */
1216 static int CheckRemovable(const char* deviceName)
1217 {
1218         FILE *fp;
1219         int removable = 0;
1220         char *device;
1221         char path[PATH_MAX];
1222
1223         if ((device = FindDeviceSysBlock(deviceName)) == NULL) {
1224                 fprintf(stderr,
1225                                 _("%s: did not find a device %s in /sys/block/\n"),
1226                                 programName, deviceName);
1227                 exit(1);
1228         }
1229         snprintf(path, sizeof(path), "/sys/block/%s/removable", device);
1230         free(device);
1231         if((fp = fopen(path, "r")) == NULL)
1232                 return removable;
1233         if (fgetc(fp) == '1')
1234                 removable = 1;
1235
1236         fclose(fp);
1237         return removable;
1238 }
1239
1240 /* Check if a device is on hotpluggable subsystem. Returns 1 if is
1241  * otherwise returns 0.
1242  */
1243 static int CheckHotpluggable(const char* deviceName)
1244 {
1245         int hotpluggable = 0;
1246         char *device;
1247         char path[PATH_MAX];
1248         char *device_chain;
1249         struct stat buf;
1250         char *subsystem;
1251         char *pos;
1252
1253         if ((device = FindDeviceSysBlock(deviceName)) == NULL) {
1254                 fprintf(stderr, _("%s: did not find a device %s in /sys/block/\n"),
1255                                 programName, deviceName);
1256                 exit(1);
1257         }
1258         snprintf(path, sizeof(path), "/sys/block/%s/device", device);
1259         free(device);
1260
1261         if (lstat(path, &buf) == -1)
1262                 return hotpluggable;
1263         if (!S_ISLNK(buf.st_mode))
1264                 return hotpluggable;
1265         if ((device_chain = SymLink(path)) == NULL)
1266                 return hotpluggable;
1267         while ( strncmp(device_chain, "", sizeof(device_chain) != 0)) {
1268                 subsystem = GetSubSystem(device_chain);
1269                 if (subsystem) {
1270                         /* as hotpluggable we assume devices on these buses  */
1271                         if (strncmp("usb", subsystem, sizeof("usb")) == 0 ||
1272                             strncmp("ieee1394", subsystem, sizeof("ieee1394")) == 0 ||
1273                             strncmp("pcmcia", subsystem, sizeof("pcmcia")) == 0 ||
1274                             strncmp("mmc", subsystem, sizeof("mmc")) == 0 ||
1275                             strncmp("ccw", subsystem, sizeof("ccw")) == 0) {
1276                                 hotpluggable = 1;
1277                                 break;
1278                         }
1279                 }
1280                 /* remove one member from devicechain */
1281                 pos = strrchr(device_chain, '/');
1282                 if (pos)
1283                         pos[0] = '\0';
1284                 else
1285                         device_chain[0] = '\0';
1286         }
1287
1288         return hotpluggable;
1289 }
1290
1291 /* handle -x option */
1292 static void HandleXOption(char *deviceName)
1293 {
1294         int fd;            /* file descriptor for device */
1295         if (x_option) {
1296                 if (v_option)
1297                 {
1298                         if (x_arg == 0)
1299                                 printf(_("%s: setting CD-ROM speed to auto\n"), programName);
1300                         else
1301                                 printf(_("%s: setting CD-ROM speed to %dX\n"), programName, x_arg);
1302                 }
1303                 fd = OpenDevice(deviceName);
1304                 SelectSpeedCdrom(fd, x_arg);
1305                 exit(0);
1306         }
1307 }
1308
1309
1310 /* main program */
1311 int main(int argc, char **argv)
1312 {
1313         const char *defaultDevice = DEFAULTDEVICE;  /* default if no name passed by user */
1314         int worked = 0;    /* set to 1 when successfully ejected */
1315         char *device = 0;  /* name passed from user */
1316         char *fullName;    /* expanded name */
1317         char *fullNameOrig;/* expanded name (links not resolved) */
1318         char *deviceName;  /* name of device */
1319         char *linkName;    /* name of device's symbolic link */
1320         char *mountName;   /* name of device's mount point */
1321         int fd;            /* file descriptor for device */
1322         int mounted = 0;   /* true if device is mounted */
1323         int mountable = 0; /* true if device is in /etc/fstab */
1324         int result = 0;    /* store the result of a operation */
1325         char *pattern = 0; /* regex for device if multiple partitions */
1326         int ld = 6;        /* symbolic link max depth */
1327
1328         I18NCODE
1329
1330         /* program name is global variable used by other procedures */
1331         programName = strdup(argv[0]);
1332
1333         /* parse the command line arguments */
1334         parse_args(argc, argv, &device);
1335
1336
1337         /* handle -d option */
1338         if (d_option) {
1339                 printf(_("%s: default device: `%s'\n"), programName, defaultDevice);
1340                 exit(0);
1341         }
1342
1343         /* if no device, use default */
1344         if (device == 0) {
1345                 device = strdup(defaultDevice);
1346                 if (v_option)
1347                         printf(_("%s: using default device `%s'\n"), programName, device);
1348         }
1349
1350         /* Strip any trailing slash from name in case user used bash/tcsh
1351            style filename completion (e.g. /mnt/cdrom/) */
1352         if (device[strlen(device)-1] == '/')
1353                 device[strlen(device)-1] = 0;
1354
1355         if (v_option)
1356                 printf(_("%s: device name is `%s'\n"), programName, device);
1357
1358         do {
1359           /* figure out full device or mount point name */
1360           fullName = FindDevice(device);
1361           if (fullName == 0) {
1362             fprintf(stderr, _("%s: unable to find or open device for: `%s'\n"),
1363                     programName, device);
1364             exit(1);
1365           }
1366           if (v_option)
1367             printf(_("%s: expanded name is `%s'\n"), programName, fullName);
1368
1369           /* check for a symbolic link */
1370           /* /proc/mounts doesn't resolve symbolic links */
1371           fullNameOrig = strdup(fullName);
1372           linkName = strdup(fullName); /* ensure linkName is initialized */
1373           if (!p_option) {
1374             while ((linkName = SymLink(fullName)) && (ld > 0)) {
1375               if (v_option)
1376                 printf(_("%s: `%s' is a link to `%s'\n"), programName,
1377                        fullName, linkName);
1378               free(fullName);
1379               fullName = strdup(linkName);
1380               free(linkName);
1381               linkName = 0;
1382               ld--;
1383             }
1384           }
1385           /* handle max depth exceeded option */
1386           if (ld <= 0) {
1387             printf(_("%s: maximum symbolic link depth exceeded: `%s'\n"), programName, fullName);
1388             exit(1);
1389           }
1390
1391           /* if mount point, get device name */
1392           mounted = MountedDevice(fullName, &mountName, &deviceName);
1393           if (v_option) {
1394             if (mounted)
1395               printf(_("%s: `%s' is mounted at `%s'\n"), programName,
1396                      deviceName, mountName);
1397             else
1398               printf(_("%s: `%s' is not mounted\n"), programName, fullName);
1399           }
1400           if (!mounted) {
1401             deviceName = strdup(fullName);
1402           }
1403
1404           /* if not currently mounted, see if it is a possible mount point */
1405           if (!mounted) {
1406             mountable = MountableDevice(fullName, &mountName, &deviceName);
1407             /* if return value -1 then fstab could not be read */
1408             if (v_option && mountable >= 0) {
1409               if (mountable)
1410                 printf(_("%s: `%s' can be mounted at `%s'\n"), programName, deviceName, mountName);
1411               else
1412                 printf(_("%s: `%s' is not a mount point\n"), programName, fullName);
1413             }
1414           }
1415
1416           result = GetMajorMinor(deviceName, NULL, NULL);
1417           if (result == -1) {
1418               fprintf(stderr,
1419                       _("%s: tried to use `%s' as device name but it is no block device\n"),
1420                       programName, deviceName);
1421           }
1422
1423         } while (result == -1);
1424
1425         /* handle -n option */
1426         if (n_option) {
1427                 printf(_("%s: device is `%s'\n"), programName, deviceName);
1428                 if (v_option)
1429                         printf(_("%s: exiting due to -n/--noop option\n"), programName);
1430                 exit(0);
1431         }
1432
1433         /* Check if device has removable flag*/
1434         if (v_option)
1435                 printf(_("%s: checking if device \"%s\" has a removable or hotpluggable flag\n"),
1436                                 programName, deviceName);
1437         if (!CheckRemovable(deviceName) && !CheckHotpluggable(deviceName))
1438         {
1439                 fprintf(stderr, _("%s: device \"%s\" doesn't have a removable or hotpluggable flag\n"),
1440                                 programName, deviceName);
1441                 exit(1);
1442         }
1443
1444         /* handle -i option */
1445         if (i_option) {
1446                 fd = OpenDevice(deviceName);
1447                 ManualEject(fd, i_arg);
1448                 exit(0);
1449         }
1450
1451         /* handle -a option */
1452         if (a_option) {
1453                 if (v_option) {
1454                         if (a_arg)
1455                                 printf(_("%s: enabling auto-eject mode for `%s'\n"), programName, deviceName);
1456                         else
1457                                 printf(_("%s: disabling auto-eject mode for `%s'\n"), programName, deviceName);
1458                 }
1459                 fd = OpenDevice(deviceName);
1460                 AutoEject(fd, a_arg);
1461                 exit(0);
1462         }
1463
1464         /* handle -t option */
1465         if (t_option) {
1466                 if (v_option)
1467                         printf(_("%s: closing tray\n"), programName);
1468                 fd = OpenDevice(deviceName);
1469                 CloseTray(fd);
1470                 HandleXOption(deviceName);
1471                 exit(0);
1472         }
1473
1474         /* handle -X option */
1475         if (X_option) {
1476                 if (v_option)
1477                         printf(_("%s: listing CD-ROM speed\n"), programName);
1478                 fd = OpenDevice(deviceName);
1479                 ListSpeedCdrom(deviceName, fd);
1480                 exit(0);
1481         }
1482
1483         /* handle -x option only */
1484         if (!c_option) HandleXOption(deviceName);
1485
1486         /* unmount device if mounted */
1487         if ((m_option != 1) && mounted) {
1488                 if (v_option)
1489                         printf(_("%s: unmounting device `%s' from `%s'\n"), programName, deviceName, mountName);
1490                 Unmount(mountName);
1491         }
1492
1493 #ifndef ANDROID
1494         /* if it is a multipartition device, unmount any other partitions on
1495            the device */
1496         pattern = MultiplePartitions(deviceName);
1497         if ((m_option != 1) && (pattern != 0))
1498                 UnmountDevices(pattern);
1499 #endif
1500
1501         /* handle -T option */
1502         if (T_option) {
1503                 if (v_option)
1504                         printf(_("%s: toggling tray\n"), programName);
1505                 fd = OpenDevice(deviceName);
1506                 ToggleTray(fd);
1507                 HandleXOption(deviceName);
1508                 exit(0);
1509         }
1510
1511         /* handle -c option */
1512         if (c_option) {
1513                 if (v_option)
1514                         printf(_("%s: selecting CD-ROM disc #%d\n"), programName, c_arg);
1515                 fd = OpenDevice(deviceName);
1516                 ChangerSelect(fd, c_arg);
1517                 HandleXOption(deviceName);
1518                 exit(0);
1519         }
1520
1521         /* if user did not specify type of eject, try all four methods */
1522         if ((r_option + s_option + f_option + q_option) == 0) {
1523                 r_option = s_option = f_option = q_option = 1;
1524         }
1525
1526         /* open device */
1527         fd = OpenDevice(deviceName);
1528
1529         /* try various methods of ejecting until it works */
1530         if (r_option) {
1531                 if (v_option)
1532                         printf(_("%s: trying to eject `%s' using CD-ROM eject command\n"), programName, deviceName);
1533                 worked = EjectCdrom(fd);
1534                 if (v_option) {
1535                         if (worked)
1536                                 printf(_("%s: CD-ROM eject command succeeded\n"), programName);
1537                         else
1538                                 printf(_("%s: CD-ROM eject command failed\n"), programName);
1539                 }
1540         }
1541
1542 #ifdef HAVE_EJECT_SCSI
1543         if (s_option && !worked) {
1544                 if (v_option)
1545                         printf(_("%s: trying to eject `%s' using SCSI commands\n"), programName, deviceName);
1546                 worked = EjectScsi(fd);
1547                 if (v_option) {
1548                         if (worked)
1549                                 printf(_("%s: SCSI eject succeeded\n"), programName);
1550                         else
1551                                 printf(_("%s: SCSI eject failed\n"), programName);
1552                 }
1553         }
1554 #endif
1555
1556 #ifdef HAVE_EJECT_FLOPPY
1557         if (f_option && !worked) {
1558                 if (v_option)
1559                         printf(_("%s: trying to eject `%s' using floppy eject command\n"), programName, deviceName);
1560                 worked = EjectFloppy(fd);
1561                 if (v_option) {
1562                         if (worked)
1563                                 printf(_("%s: floppy eject command succeeded\n"), programName);
1564                         else
1565                                 printf(_("%s: floppy eject command failed\n"), programName);
1566                 }
1567         }
1568 #endif
1569
1570 #ifdef HAVE_EJECT_TAPE
1571         if (q_option && !worked) {
1572                 if (v_option)
1573                         printf(_("%s: trying to eject `%s' using tape offline command\n"), programName, deviceName);
1574                 worked = EjectTape(fd);
1575                 if (v_option) {
1576                         if (worked)
1577                                 printf(_("%s: tape offline command succeeded\n"), programName);
1578                         else
1579                                 printf(_("%s: tape offline command failed\n"), programName);
1580                 }
1581         }
1582 #endif
1583
1584         if (!worked) {
1585                 fprintf(stderr, _("%s: unable to eject, last error: %s\n"), programName, strerror(errno));
1586                 exit(1);
1587         }
1588
1589         /* cleanup */
1590         CLOSE(fd);
1591         free(device);
1592         free(deviceName);
1593         free(fullName);
1594         free(fullNameOrig);
1595         free(linkName);
1596         free(mountName);
1597         free(pattern);
1598         exit(0);
1599 }