OSDN Git Service

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