OSDN Git Service

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