]>
Commit | Line | Data |
---|---|---|
aa8cb034 | 1 | /* |
539feec1 | 2 | * Copyright (C) 1994-2005 Jeff Tranter (tranter@pobox.com) |
aa8cb034 KZ |
3 | * Copyright (C) 2012 Karel Zak <kzak@redhat.com> |
4 | * Copyright (C) Michal Luscon <mluscon@redhat.com> | |
539feec1 ML |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
539feec1 ML |
19 | */ |
20 | ||
539feec1 ML |
21 | #include <unistd.h> |
22 | #include <stdlib.h> | |
23 | #include <stdio.h> | |
24 | #include <string.h> | |
25 | #include <fcntl.h> | |
26 | #include <limits.h> | |
b35b3b77 | 27 | #include <err.h> |
d9877823 | 28 | #include <stdarg.h> |
7f0cd302 | 29 | |
539feec1 | 30 | #include <getopt.h> |
539feec1 ML |
31 | #include <errno.h> |
32 | #include <regex.h> | |
33 | #include <sys/types.h> | |
34 | #include <sys/stat.h> | |
35 | #include <sys/ioctl.h> | |
36 | #include <sys/wait.h> | |
37 | #include <sys/mtio.h> | |
539feec1 | 38 | #include <linux/cdrom.h> |
539feec1 ML |
39 | #include <linux/fd.h> |
40 | #include <sys/mount.h> | |
41 | #include <scsi/scsi.h> | |
42 | #include <scsi/sg.h> | |
43 | #include <scsi/scsi_ioctl.h> | |
44 | #include <sys/time.h> | |
45 | ||
9a41136b KZ |
46 | #include <libmount.h> |
47 | ||
aa8cb034 | 48 | #include "c.h" |
9146de02 | 49 | #include "closestream.h" |
aa8cb034 KZ |
50 | #include "nls.h" |
51 | #include "strutils.h" | |
52 | #include "xalloc.h" | |
1abc3326 | 53 | #include "pathnames.h" |
9b439aa9 | 54 | #include "sysfs.h" |
cd2876d2 | 55 | #include "monotonic.h" |
aa8cb034 | 56 | |
90a0e97c KZ |
57 | /* |
58 | * sg_io_hdr_t driver_status -- see kernel include/scsi/scsi.h | |
59 | */ | |
60 | #ifndef DRIVER_SENSE | |
61 | # define DRIVER_SENSE 0x08 | |
62 | #endif | |
63 | ||
64 | ||
aa8cb034 KZ |
65 | #define EJECT_DEFAULT_DEVICE "/dev/cdrom" |
66 | ||
67 | ||
6154d238 | 68 | /* Used by the toggle_tray() function. If ejecting the tray takes this |
539feec1 ML |
69 | * time or less, the tray was probably already ejected, so we close it |
70 | * again. | |
71 | */ | |
72 | #define TRAY_WAS_ALREADY_OPEN_USECS 200000 /* about 0.2 seconds */ | |
73 | ||
257a0035 SK |
74 | struct eject_control { |
75 | struct libmnt_table *mtab; | |
76 | char *device; /* device or mount point to be ejected */ | |
77 | int fd; /* file descriptor for device */ | |
78 | unsigned int /* command flags and arguments */ | |
79 | a_option:1, | |
80 | c_option:1, | |
81 | d_option:1, | |
82 | F_option:1, | |
83 | f_option:1, | |
84 | i_option:1, | |
85 | M_option:1, | |
86 | m_option:1, | |
87 | n_option:1, | |
88 | p_option:1, | |
89 | q_option:1, | |
90 | r_option:1, | |
91 | s_option:1, | |
92 | T_option:1, | |
93 | t_option:1, | |
94 | v_option:1, | |
95 | X_option:1, | |
96 | x_option:1, | |
97 | a_arg:1, | |
98 | i_arg:1; | |
99 | long int c_arg; /* changer slot number */ | |
100 | long int x_arg; /* cd speed */ | |
101 | }; | |
9b439aa9 | 102 | |
f1989b79 KZ |
103 | static void vinfo(const char *fmt, va_list va) |
104 | { | |
105 | fprintf(stdout, "%s: ", program_invocation_short_name); | |
106 | vprintf(fmt, va); | |
107 | fputc('\n', stdout); | |
108 | } | |
109 | ||
257a0035 | 110 | static inline void verbose(const struct eject_control *ctl, const char *fmt, ...) |
f1989b79 KZ |
111 | { |
112 | va_list va; | |
113 | ||
257a0035 | 114 | if (!ctl->v_option) |
f1989b79 KZ |
115 | return; |
116 | ||
117 | va_start(va, fmt); | |
118 | vinfo(fmt, va); | |
119 | va_end(va); | |
120 | } | |
121 | ||
122 | static inline void info(const char *fmt, ...) | |
123 | { | |
124 | va_list va; | |
125 | va_start(va, fmt); | |
126 | vinfo(fmt, va); | |
127 | va_end(va); | |
128 | } | |
129 | ||
86be6a32 | 130 | static void __attribute__((__noreturn__)) usage(void) |
805424cd | 131 | { |
86be6a32 | 132 | FILE *out = stdout; |
805424cd | 133 | fputs(USAGE_HEADER, out); |
805424cd KZ |
134 | fprintf(out, |
135 | _(" %s [options] [<device>|<mountpoint>]\n"), program_invocation_short_name); | |
136 | ||
451dbcfa BS |
137 | fputs(USAGE_SEPARATOR, out); |
138 | fputs(_("Eject removable media.\n"), out); | |
139 | ||
805424cd | 140 | fputs(USAGE_OPTIONS, out); |
566cd92c KZ |
141 | fputs(_(" -a, --auto <on|off> turn auto-eject feature on or off\n" |
142 | " -c, --changerslot <slot> switch discs on a CD-ROM changer\n" | |
143 | " -d, --default display default device\n" | |
144 | " -f, --floppy eject floppy\n" | |
145 | " -F, --force don't care about device type\n" | |
146 | " -i, --manualeject <on|off> toggle manual eject protection on/off\n" | |
147 | " -m, --no-unmount do not unmount device even if it is mounted\n" | |
148 | " -M, --no-partitions-unmount do not unmount another partitions\n" | |
149 | " -n, --noop don't eject, just show device found\n" | |
150 | " -p, --proc use /proc/mounts instead of /etc/mtab\n" | |
151 | " -q, --tape eject tape\n" | |
152 | " -r, --cdrom eject CD-ROM\n" | |
153 | " -s, --scsi eject SCSI device\n" | |
154 | " -t, --trayclose close tray\n" | |
155 | " -T, --traytoggle toggle tray\n" | |
156 | " -v, --verbose enable verbose output\n" | |
157 | " -x, --cdspeed <speed> set CD-ROM max speed\n" | |
158 | " -X, --listspeed list CD-ROM available speeds\n"), | |
805424cd KZ |
159 | out); |
160 | ||
652ddc0b | 161 | fputs(USAGE_SEPARATOR, out); |
f45f3ec3 | 162 | printf(USAGE_HELP_OPTIONS(29)); |
652ddc0b | 163 | |
805424cd | 164 | fputs(_("\nBy default tries -r, -s, -f, and -q in order until success.\n"), out); |
f45f3ec3 | 165 | printf(USAGE_MAN_TAIL("eject(1)")); |
539feec1 | 166 | |
86be6a32 | 167 | exit(EXIT_SUCCESS); |
539feec1 ML |
168 | } |
169 | ||
170 | ||
171 | /* Handle command line options. */ | |
257a0035 | 172 | static void parse_args(struct eject_control *ctl, int argc, char **argv) |
539feec1 | 173 | { |
652ddc0b | 174 | static const struct option long_opts[] = |
539feec1 | 175 | { |
539feec1 | 176 | {"auto", required_argument, NULL, 'a'}, |
652ddc0b | 177 | {"cdrom", no_argument, NULL, 'r'}, |
539feec1 | 178 | {"cdspeed", required_argument, NULL, 'x'}, |
652ddc0b KZ |
179 | {"changerslot", required_argument, NULL, 'c'}, |
180 | {"default", no_argument, NULL, 'd'}, | |
181 | {"floppy", no_argument, NULL, 'f'}, | |
182 | {"force", no_argument, NULL, 'F'}, | |
183 | {"help", no_argument, NULL, 'h'}, | |
1abc3326 | 184 | {"listspeed", no_argument, NULL, 'X'}, |
652ddc0b | 185 | {"manualeject", required_argument, NULL, 'i'}, |
539feec1 | 186 | {"noop", no_argument, NULL, 'n'}, |
652ddc0b | 187 | {"no-unmount", no_argument, NULL, 'm'}, |
566cd92c | 188 | {"no-partitions-unmount", no_argument, NULL, 'M' }, |
652ddc0b | 189 | {"proc", no_argument, NULL, 'p'}, |
539feec1 | 190 | {"scsi", no_argument, NULL, 's'}, |
539feec1 | 191 | {"tape", no_argument, NULL, 'q'}, |
652ddc0b KZ |
192 | {"trayclose", no_argument, NULL, 't'}, |
193 | {"traytoggle", no_argument, NULL, 'T'}, | |
194 | {"verbose", no_argument, NULL, 'v'}, | |
539feec1 | 195 | {"version", no_argument, NULL, 'V'}, |
87918040 | 196 | {NULL, 0, NULL, 0} |
539feec1 | 197 | }; |
539feec1 ML |
198 | int c; |
199 | ||
e460d12d | 200 | while ((c = getopt_long(argc, argv, |
566cd92c | 201 | "a:c:i:x:dfFhnqrstTXvVpmM", long_opts, NULL)) != -1) { |
539feec1 | 202 | switch (c) { |
e460d12d | 203 | case 'a': |
257a0035 | 204 | ctl->a_option = 1; |
47d20536 SK |
205 | ctl->a_arg = parse_switch(optarg, _("argument error"), |
206 | "on", "off", "1", "0", NULL); | |
e460d12d KZ |
207 | break; |
208 | case 'c': | |
257a0035 SK |
209 | ctl->c_option = 1; |
210 | ctl->c_arg = strtoul_or_err(optarg, _("invalid argument to --changerslot/-c option")); | |
e460d12d KZ |
211 | break; |
212 | case 'x': | |
257a0035 SK |
213 | ctl->x_option = 1; |
214 | ctl->x_arg = strtoul_or_err(optarg, _("invalid argument to --cdspeed/-x option")); | |
e460d12d KZ |
215 | break; |
216 | case 'd': | |
257a0035 | 217 | ctl->d_option = 1; |
e460d12d KZ |
218 | break; |
219 | case 'f': | |
257a0035 | 220 | ctl->f_option = 1; |
e460d12d | 221 | break; |
8bb4a6cb | 222 | case 'F': |
257a0035 | 223 | ctl->F_option = 1; |
8bb4a6cb | 224 | break; |
0bcfb489 | 225 | case 'i': |
257a0035 | 226 | ctl->i_option = 1; |
47d20536 SK |
227 | ctl->i_arg = parse_switch(optarg, _("argument error"), |
228 | "on", "off", "1", "0", NULL); | |
0bcfb489 | 229 | break; |
e460d12d | 230 | case 'm': |
257a0035 | 231 | ctl->m_option = 1; |
e460d12d | 232 | break; |
566cd92c | 233 | case 'M': |
257a0035 | 234 | ctl->M_option = 1; |
566cd92c | 235 | break; |
e460d12d | 236 | case 'n': |
257a0035 | 237 | ctl->n_option = 1; |
e460d12d KZ |
238 | break; |
239 | case 'p': | |
257a0035 | 240 | ctl->p_option = 1; |
e460d12d KZ |
241 | break; |
242 | case 'q': | |
257a0035 | 243 | ctl->q_option = 1; |
e460d12d KZ |
244 | break; |
245 | case 'r': | |
257a0035 | 246 | ctl->r_option = 1; |
e460d12d KZ |
247 | break; |
248 | case 's': | |
257a0035 | 249 | ctl->s_option = 1; |
e460d12d KZ |
250 | break; |
251 | case 't': | |
257a0035 | 252 | ctl->t_option = 1; |
e460d12d KZ |
253 | break; |
254 | case 'T': | |
257a0035 | 255 | ctl->T_option = 1; |
e460d12d | 256 | break; |
1abc3326 | 257 | case 'X': |
257a0035 | 258 | ctl->X_option = 1; |
1abc3326 | 259 | break; |
e460d12d | 260 | case 'v': |
257a0035 | 261 | ctl->v_option = 1; |
e460d12d | 262 | break; |
2c308875 KZ |
263 | |
264 | case 'h': | |
265 | usage(); | |
e460d12d | 266 | case 'V': |
2c308875 | 267 | print_version(EXIT_SUCCESS); |
aa8cb034 | 268 | default: |
677ec86c | 269 | errtryhelp(EXIT_FAILURE); |
e460d12d | 270 | break; |
539feec1 ML |
271 | } |
272 | } | |
e460d12d | 273 | |
539feec1 | 274 | /* check for a single additional argument */ |
e460d12d KZ |
275 | if ((argc - optind) > 1) |
276 | errx(EXIT_FAILURE, _("too many arguments")); | |
277 | ||
278 | if ((argc - optind) == 1) | |
257a0035 | 279 | ctl->device = xstrdup(argv[optind]); |
539feec1 ML |
280 | } |
281 | ||
539feec1 ML |
282 | /* |
283 | * Given name, such as foo, see if any of the following exist: | |
284 | * | |
285 | * foo (if foo starts with '.' or '/') | |
286 | * /dev/foo | |
539feec1 ML |
287 | * |
288 | * If found, return the full path. If not found, return 0. | |
289 | * Returns pointer to dynamically allocated string. | |
290 | */ | |
dc87bd47 KZ |
291 | static char *find_device(const char *name) |
292 | { | |
9b439aa9 KZ |
293 | if (!name) |
294 | return NULL; | |
295 | ||
dc87bd47 KZ |
296 | if ((*name == '.' || *name == '/') && access(name, F_OK) == 0) |
297 | return xstrdup(name); | |
298 | else { | |
299 | char buf[PATH_MAX]; | |
300 | ||
301 | snprintf(buf, sizeof(buf), "/dev/%s", name); | |
7ec1eca8 | 302 | if (access(buf, F_OK) == 0) |
dc87bd47 | 303 | return xstrdup(buf); |
539feec1 ML |
304 | } |
305 | ||
dc87bd47 | 306 | return NULL; |
539feec1 ML |
307 | } |
308 | ||
539feec1 | 309 | /* Set or clear auto-eject mode. */ |
257a0035 | 310 | static void auto_eject(const struct eject_control *ctl) |
dc87bd47 | 311 | { |
d7b0a90e KZ |
312 | int status = -1; |
313 | ||
314 | #if defined(CDROM_SET_OPTIONS) && defined(CDROM_CLEAR_OPTIONS) | |
257a0035 SK |
315 | if (ctl->a_arg) |
316 | status = ioctl(ctl->fd, CDROM_SET_OPTIONS, CDO_AUTO_EJECT); | |
d7b0a90e | 317 | else |
257a0035 | 318 | status = ioctl(ctl->fd, CDROM_CLEAR_OPTIONS, CDO_AUTO_EJECT); |
d7b0a90e KZ |
319 | #else |
320 | errno = ENOSYS; | |
321 | #endif | |
322 | if (status < 0) | |
323 | err(EXIT_FAILURE,_("CD-ROM auto-eject command failed")); | |
539feec1 ML |
324 | } |
325 | ||
0bcfb489 | 326 | /* |
c4e8acb9 | 327 | * Stops CDROM from opening on manual eject button press. |
0bcfb489 KZ |
328 | * This can be useful when you carry your laptop |
329 | * in your bag while it's on and no CD inserted in it's drive. | |
330 | * Implemented as found in Documentation/ioctl/cdrom.txt | |
0bcfb489 | 331 | */ |
257a0035 | 332 | static void manual_eject(const struct eject_control *ctl) |
0bcfb489 | 333 | { |
c4e8acb9 SK |
334 | if (ioctl(ctl->fd, CDROM_LOCKDOOR, ctl->i_arg) < 0) { |
335 | switch (errno) { | |
336 | case EDRIVE_CANT_DO_THIS: | |
337 | errx(EXIT_FAILURE, _("CD-ROM door lock is not supported")); | |
338 | case EBUSY: | |
339 | errx(EXIT_FAILURE, _("other users have the drive open and not CAP_SYS_ADMIN")); | |
340 | default: | |
341 | err(EXIT_FAILURE, _("CD-ROM lock door command failed")); | |
342 | } | |
343 | } | |
0bcfb489 | 344 | |
257a0035 | 345 | if (ctl->i_arg) |
0bcfb489 KZ |
346 | info(_("CD-Drive may NOT be ejected with device button")); |
347 | else | |
348 | info(_("CD-Drive may be ejected with device button")); | |
349 | } | |
539feec1 ML |
350 | |
351 | /* | |
352 | * Changer select. CDROM_SELECT_DISC is preferred, older kernels used | |
353 | * CDROMLOADFROMSLOT. | |
354 | */ | |
257a0035 | 355 | static void changer_select(const struct eject_control *ctl) |
dc87bd47 | 356 | { |
539feec1 | 357 | #ifdef CDROM_SELECT_DISC |
257a0035 | 358 | if (ioctl(ctl->fd, CDROM_SELECT_DISC, ctl->c_arg) < 0) |
dc87bd47 KZ |
359 | err(EXIT_FAILURE, _("CD-ROM select disc command failed")); |
360 | ||
539feec1 | 361 | #elif defined CDROMLOADFROMSLOT |
257a0035 | 362 | if (ioctl(ctl->fd, CDROMLOADFROMSLOT, ctl->c_arg) != 0) |
dc87bd47 | 363 | err(EXIT_FAILURE, _("CD-ROM load from slot command failed")); |
539feec1 | 364 | #else |
dc87bd47 | 365 | warnx(_("IDE/ATAPI CD-ROM changer not supported by this kernel\n") ); |
539feec1 ML |
366 | #endif |
367 | } | |
368 | ||
539feec1 ML |
369 | /* |
370 | * Close tray. Not supported by older kernels. | |
371 | */ | |
dc87bd47 KZ |
372 | static void close_tray(int fd) |
373 | { | |
48a2130c KZ |
374 | int status; |
375 | ||
376 | #if defined(CDROMCLOSETRAY) || defined(CDIOCCLOSE) | |
377 | #if defined(CDROMCLOSETRAY) | |
378 | status = ioctl(fd, CDROMCLOSETRAY); | |
379 | #elif defined(CDIOCCLOSE) | |
380 | status = ioctl(fd, CDIOCCLOSE); | |
381 | #endif | |
382 | if (status != 0) | |
dc87bd47 | 383 | err(EXIT_FAILURE, _("CD-ROM tray close command failed")); |
539feec1 | 384 | #else |
dc87bd47 | 385 | warnx(_("CD-ROM tray close command not supported by this kernel\n")); |
539feec1 ML |
386 | #endif |
387 | } | |
388 | ||
df3449d5 MF |
389 | /* |
390 | * Eject using CDROMEJECT ioctl. | |
391 | */ | |
392 | static int eject_cdrom(int fd) | |
393 | { | |
394 | #if defined(CDROMEJECT) | |
12272030 AL |
395 | int ret = ioctl(fd, CDROM_LOCKDOOR, 0); |
396 | if (ret < 0) | |
397 | return 0; | |
3514cc3a | 398 | return ioctl(fd, CDROMEJECT) >= 0; |
df3449d5 | 399 | #elif defined(CDIOCEJECT) |
3514cc3a | 400 | return ioctl(fd, CDIOCEJECT) >= 0; |
df3449d5 MF |
401 | #else |
402 | warnx(_("CD-ROM eject unsupported")); | |
403 | errno = ENOSYS; | |
3514cc3a | 404 | return 0; |
df3449d5 MF |
405 | #endif |
406 | } | |
407 | ||
539feec1 ML |
408 | /* |
409 | * Toggle tray. | |
410 | * | |
411 | * Written by Benjamin Schwenk <benjaminschwenk@yahoo.de> and | |
412 | * Sybren Stuvel <sybren@thirdtower.com> | |
413 | * | |
414 | * Not supported by older kernels because it might use | |
415 | * CloseTray(). | |
416 | * | |
417 | */ | |
dc87bd47 KZ |
418 | static void toggle_tray(int fd) |
419 | { | |
df3449d5 MF |
420 | #ifdef CDROM_DRIVE_STATUS |
421 | /* First ask the CDROM for info, otherwise fall back to manual. */ | |
422 | switch (ioctl(fd, CDROM_DRIVE_STATUS)) { | |
423 | case CDS_TRAY_OPEN: | |
424 | close_tray(fd); | |
425 | return; | |
426 | ||
427 | case CDS_NO_DISC: | |
428 | case CDS_DISC_OK: | |
3514cc3a | 429 | if (!eject_cdrom(fd)) |
df3449d5 MF |
430 | err(EXIT_FAILURE, _("CD-ROM eject command failed")); |
431 | return; | |
f31d041a SK |
432 | case CDS_NO_INFO: |
433 | warnx(_("no CD-ROM information available")); | |
434 | return; | |
435 | case CDS_DRIVE_NOT_READY: | |
436 | warnx(_("CD-ROM drive is not ready")); | |
437 | return; | |
438 | default: | |
a6f99be4 | 439 | err(EXIT_FAILURE, _("CD-ROM status command failed")); |
df3449d5 | 440 | } |
6f7f155c SK |
441 | #else |
442 | struct timeval time_start, time_stop; | |
443 | int time_elapsed; | |
df3449d5 | 444 | |
11026083 | 445 | /* Try to open the CDROM tray and measure the time therefore |
539feec1 ML |
446 | * needed. In my experience the function needs less than 0.05 |
447 | * seconds if the tray was already open, and at least 1.5 seconds | |
448 | * if it was closed. */ | |
700031ad | 449 | gettime_monotonic(&time_start); |
539feec1 ML |
450 | |
451 | /* Send the CDROMEJECT command to the device. */ | |
3514cc3a | 452 | if (!eject_cdrom(fd)) |
dc87bd47 | 453 | err(EXIT_FAILURE, _("CD-ROM eject command failed")); |
539feec1 ML |
454 | |
455 | /* Get the second timestamp, to measure the time needed to open | |
456 | * the tray. */ | |
700031ad | 457 | gettime_monotonic(&time_stop); |
539feec1 ML |
458 | |
459 | time_elapsed = (time_stop.tv_sec * 1000000 + time_stop.tv_usec) - | |
460 | (time_start.tv_sec * 1000000 + time_start.tv_usec); | |
461 | ||
462 | /* If the tray "opened" too fast, we can be nearly sure, that it | |
463 | * was already open. In this case, close it now. Else the tray was | |
464 | * closed before. This would mean that we are done. */ | |
465 | if (time_elapsed < TRAY_WAS_ALREADY_OPEN_USECS) | |
6154d238 | 466 | close_tray(fd); |
6f7f155c | 467 | #endif |
539feec1 ML |
468 | } |
469 | ||
470 | /* | |
471 | * Select Speed of CD-ROM drive. | |
472 | * Thanks to Roland Krivanek (krivanek@fmph.uniba.sk) | |
473 | * http://dmpc.dbp.fmph.uniba.sk/~krivanek/cdrom_speed/ | |
474 | */ | |
25a3fa81 | 475 | static void select_speed(const struct eject_control *ctl) |
dc87bd47 | 476 | { |
539feec1 | 477 | #ifdef CDROM_SELECT_SPEED |
25a3fa81 | 478 | if (ioctl(ctl->fd, CDROM_SELECT_SPEED, ctl->x_arg) != 0) |
dc87bd47 | 479 | err(EXIT_FAILURE, _("CD-ROM select speed command failed")); |
539feec1 | 480 | #else |
dc87bd47 | 481 | warnx(_("CD-ROM select speed command not supported by this kernel")); |
539feec1 ML |
482 | #endif |
483 | } | |
484 | ||
1abc3326 KZ |
485 | /* |
486 | * Read Speed of CD-ROM drive. From Linux 2.6.13, the current speed | |
487 | * is correctly reported | |
488 | */ | |
489 | static int read_speed(const char *devname) | |
490 | { | |
491 | int drive_number = -1; | |
492 | char *name; | |
493 | FILE *f; | |
494 | ||
495 | f = fopen(_PATH_PROC_CDROMINFO, "r"); | |
496 | if (!f) | |
289dcc90 | 497 | err(EXIT_FAILURE, _("cannot open %s"), _PATH_PROC_CDROMINFO); |
1abc3326 | 498 | |
913e43b8 | 499 | name = strrchr(devname, '/') + 1; |
1abc3326 | 500 | |
92fe6e96 | 501 | while (name && !feof(f)) { |
1abc3326 KZ |
502 | char line[512]; |
503 | char *str; | |
504 | ||
505 | if (!fgets(line, sizeof(line), f)) | |
506 | break; | |
507 | ||
508 | /* find drive number in line "drive name" */ | |
509 | if (drive_number == -1) { | |
510 | if (strncmp(line, "drive name:", 11) == 0) { | |
511 | str = strtok(&line[11], "\t "); | |
512 | drive_number = 0; | |
92fe6e96 | 513 | while (str && strncmp(name, str, strlen(name)) != 0) { |
1abc3326 KZ |
514 | drive_number++; |
515 | str = strtok(NULL, "\t "); | |
516 | if (!str) | |
517 | errx(EXIT_FAILURE, | |
518 | _("%s: failed to finding CD-ROM name"), | |
519 | _PATH_PROC_CDROMINFO); | |
520 | } | |
521 | } | |
522 | /* find line "drive speed" and read the correct speed */ | |
523 | } else { | |
524 | if (strncmp(line, "drive speed:", 12) == 0) { | |
525 | int i; | |
1abc3326 KZ |
526 | |
527 | str = strtok(&line[12], "\t "); | |
528 | for (i = 1; i < drive_number; i++) | |
529 | str = strtok(NULL, "\t "); | |
530 | ||
531 | if (!str) | |
532 | errx(EXIT_FAILURE, | |
533 | _("%s: failed to read speed"), | |
534 | _PATH_PROC_CDROMINFO); | |
92091356 | 535 | fclose(f); |
1abc3326 KZ |
536 | return atoi(str); |
537 | } | |
538 | } | |
539 | } | |
540 | ||
541 | errx(EXIT_FAILURE, _("failed to read speed")); | |
542 | } | |
543 | ||
544 | /* | |
545 | * List Speed of CD-ROM drive. | |
546 | */ | |
25a3fa81 | 547 | static void list_speeds(struct eject_control *ctl) |
1abc3326 | 548 | { |
25a3fa81 | 549 | int max_speed, curr_speed = 0; |
1abc3326 | 550 | |
25a3fa81 | 551 | select_speed(ctl); |
257a0035 | 552 | max_speed = read_speed(ctl->device); |
1abc3326 KZ |
553 | |
554 | while (curr_speed < max_speed) { | |
25a3fa81 SK |
555 | ctl->x_arg = curr_speed + 1; |
556 | select_speed(ctl); | |
257a0035 | 557 | curr_speed = read_speed(ctl->device); |
25a3fa81 | 558 | if (ctl->x_arg < curr_speed) |
1abc3326 KZ |
559 | printf("%d ", curr_speed); |
560 | else | |
25a3fa81 | 561 | curr_speed = ctl->x_arg + 1; |
1abc3326 KZ |
562 | } |
563 | ||
564 | printf("\n"); | |
1abc3326 KZ |
565 | } |
566 | ||
539feec1 | 567 | /* |
6ef30f29 | 568 | * Eject using SCSI SG_IO commands. Return 1 if successful, 0 otherwise. |
539feec1 | 569 | */ |
257a0035 | 570 | static int eject_scsi(const struct eject_control *ctl) |
dc87bd47 | 571 | { |
6ef30f29 KZ |
572 | int status, k; |
573 | sg_io_hdr_t io_hdr; | |
574 | unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0}; | |
575 | unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0}; | |
576 | unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0}; | |
577 | unsigned char inqBuff[2]; | |
578 | unsigned char sense_buffer[32]; | |
579 | ||
257a0035 SK |
580 | if ((ioctl(ctl->fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { |
581 | verbose(ctl, _("not an sg device, or old sg driver")); | |
539feec1 | 582 | return 0; |
6ef30f29 | 583 | } |
539feec1 | 584 | |
6ef30f29 KZ |
585 | memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); |
586 | io_hdr.interface_id = 'S'; | |
587 | io_hdr.cmd_len = 6; | |
588 | io_hdr.mx_sb_len = sizeof(sense_buffer); | |
589 | io_hdr.dxfer_direction = SG_DXFER_NONE; | |
590 | io_hdr.dxfer_len = 0; | |
591 | io_hdr.dxferp = inqBuff; | |
592 | io_hdr.sbp = sense_buffer; | |
593 | io_hdr.timeout = 10000; | |
594 | ||
595 | io_hdr.cmdp = allowRmBlk; | |
257a0035 | 596 | status = ioctl(ctl->fd, SG_IO, (void *)&io_hdr); |
90a0e97c | 597 | if (status < 0 || io_hdr.host_status || io_hdr.driver_status) |
539feec1 ML |
598 | return 0; |
599 | ||
6ef30f29 | 600 | io_hdr.cmdp = startStop1Blk; |
257a0035 | 601 | status = ioctl(ctl->fd, SG_IO, (void *)&io_hdr); |
90a0e97c KZ |
602 | if (status < 0 || io_hdr.host_status) |
603 | return 0; | |
604 | ||
605 | /* Ignore errors when there is not medium -- in this case driver sense | |
606 | * buffer sets MEDIUM NOT PRESENT (3a) bit. For more details see: | |
607 | * http://www.tldp.org/HOWTO/archived/SCSI-Programming-HOWTO/SCSI-Programming-HOWTO-22.html#sec-sensecodes | |
608 | * -- kzak Jun 2013 | |
609 | */ | |
610 | if (io_hdr.driver_status != 0 && | |
611 | !(io_hdr.driver_status == DRIVER_SENSE && io_hdr.sbp && | |
612 | io_hdr.sbp[12] == 0x3a)) | |
6ef30f29 KZ |
613 | return 0; |
614 | ||
615 | io_hdr.cmdp = startStop2Blk; | |
257a0035 | 616 | status = ioctl(ctl->fd, SG_IO, (void *)&io_hdr); |
90a0e97c | 617 | if (status < 0 || io_hdr.host_status || io_hdr.driver_status) |
539feec1 ML |
618 | return 0; |
619 | ||
620 | /* force kernel to reread partition table when new disc inserted */ | |
257a0035 | 621 | ioctl(ctl->fd, BLKRRPART); |
6ef30f29 | 622 | return 1; |
539feec1 ML |
623 | } |
624 | ||
539feec1 ML |
625 | /* |
626 | * Eject using FDEJECT ioctl. Return 1 if successful, 0 otherwise. | |
627 | */ | |
dc87bd47 KZ |
628 | static int eject_floppy(int fd) |
629 | { | |
5a13cf4a | 630 | return ioctl(fd, FDEJECT) >= 0; |
539feec1 ML |
631 | } |
632 | ||
633 | ||
634 | /* | |
dc87bd47 | 635 | * Rewind and eject using tape ioctl. Return 1 if successful, 0 otherwise. |
539feec1 | 636 | */ |
dc87bd47 KZ |
637 | static int eject_tape(int fd) |
638 | { | |
639 | struct mtop op = { .mt_op = MTOFFL, .mt_count = 0 }; | |
539feec1 | 640 | |
5a13cf4a | 641 | return ioctl(fd, MTIOCTOP, &op) >= 0; |
539feec1 ML |
642 | } |
643 | ||
644 | ||
25c30aa8 | 645 | /* umount a device. */ |
257a0035 | 646 | static void umount_one(const struct eject_control *ctl, const char *name) |
b3d62406 | 647 | { |
539feec1 ML |
648 | int status; |
649 | ||
9b439aa9 KZ |
650 | if (!name) |
651 | return; | |
652 | ||
257a0035 | 653 | verbose(ctl, _("%s: unmounting"), name); |
9b439aa9 | 654 | |
539feec1 | 655 | switch (fork()) { |
b3d62406 KZ |
656 | case 0: /* child */ |
657 | if (setgid(getgid()) < 0) | |
658 | err(EXIT_FAILURE, _("cannot set group id")); | |
659 | ||
660 | if (setuid(getuid()) < 0) | |
291af4bc | 661 | err(EXIT_FAILURE, _("cannot set user id")); |
b3d62406 | 662 | |
257a0035 | 663 | if (ctl->p_option) |
b3d62406 KZ |
664 | execl("/bin/umount", "/bin/umount", name, "-n", NULL); |
665 | else | |
666 | execl("/bin/umount", "/bin/umount", name, NULL); | |
667 | ||
fd777151 | 668 | errexec("/bin/umount"); |
b3d62406 KZ |
669 | |
670 | case -1: | |
671 | warn( _("unable to fork")); | |
672 | break; | |
673 | ||
674 | default: /* parent */ | |
675 | wait(&status); | |
676 | if (WIFEXITED(status) == 0) | |
677 | errx(EXIT_FAILURE, | |
678 | _("unmount of `%s' did not exit normally"), name); | |
679 | ||
680 | if (WEXITSTATUS(status) != 0) | |
681 | errx(EXIT_FAILURE, _("unmount of `%s' failed\n"), name); | |
682 | break; | |
539feec1 ML |
683 | } |
684 | } | |
685 | ||
539feec1 | 686 | /* Open a device file. */ |
25a3fa81 | 687 | static void open_device(struct eject_control *ctl) |
b3d62406 | 688 | { |
25a3fa81 SK |
689 | ctl->fd = open(ctl->device, O_RDWR | O_NONBLOCK); |
690 | if (ctl->fd < 0) | |
691 | ctl->fd = open(ctl->device, O_RDONLY | O_NONBLOCK); | |
692 | if (ctl->fd == -1) | |
693 | err(EXIT_FAILURE, _("cannot open %s"), ctl->device); | |
539feec1 ML |
694 | } |
695 | ||
539feec1 ML |
696 | /* |
697 | * See if device has been mounted by looking in mount table. If so, set | |
698 | * device name and mount point name, and return 1, otherwise return 0. | |
699 | */ | |
257a0035 | 700 | static int device_get_mountpoint(struct eject_control *ctl, char **devname, char **mnt) |
3bd1b682 | 701 | { |
9a41136b | 702 | struct libmnt_fs *fs; |
9b439aa9 | 703 | int rc; |
539feec1 | 704 | |
9b439aa9 | 705 | *mnt = NULL; |
539feec1 | 706 | |
257a0035 | 707 | if (!ctl->mtab) { |
50fccba1 KZ |
708 | struct libmnt_cache *cache; |
709 | ||
257a0035 SK |
710 | ctl->mtab = mnt_new_table(); |
711 | if (!ctl->mtab) | |
9b439aa9 | 712 | err(EXIT_FAILURE, _("failed to initialize libmount table")); |
b3d62406 | 713 | |
50fccba1 | 714 | cache = mnt_new_cache(); |
257a0035 | 715 | mnt_table_set_cache(ctl->mtab, cache); |
50fccba1 | 716 | mnt_unref_cache(cache); |
9a41136b | 717 | |
257a0035 SK |
718 | if (ctl->p_option) |
719 | rc = mnt_table_parse_file(ctl->mtab, _PATH_PROC_MOUNTINFO); | |
9b439aa9 | 720 | else |
257a0035 | 721 | rc = mnt_table_parse_mtab(ctl->mtab, NULL); |
9b439aa9 KZ |
722 | if (rc) |
723 | err(EXIT_FAILURE, _("failed to parse mount table")); | |
724 | } | |
9a41136b | 725 | |
257a0035 | 726 | fs = mnt_table_find_source(ctl->mtab, *devname, MNT_ITER_BACKWARD); |
9a41136b KZ |
727 | if (!fs) { |
728 | /* maybe 'devname' is mountpoint rather than a real device */ | |
257a0035 | 729 | fs = mnt_table_find_target(ctl->mtab, *devname, MNT_ITER_BACKWARD); |
9a41136b KZ |
730 | if (fs) { |
731 | free(*devname); | |
732 | *devname = xstrdup(mnt_fs_get_source(fs)); | |
539feec1 ML |
733 | } |
734 | } | |
9b439aa9 | 735 | |
8cd48d67 | 736 | if (fs) |
9a41136b | 737 | *mnt = xstrdup(mnt_fs_get_target(fs)); |
9b439aa9 | 738 | return *mnt ? 0 : -1; |
539feec1 ML |
739 | } |
740 | ||
9b439aa9 | 741 | static char *get_disk_devname(const char *device) |
3bd1b682 | 742 | { |
9b439aa9 KZ |
743 | struct stat st; |
744 | dev_t diskno = 0; | |
745 | char diskname[128]; | |
746 | ||
747 | if (stat(device, &st) != 0) | |
748 | return NULL; | |
749 | ||
750 | /* get whole-disk devno */ | |
751 | if (sysfs_devno_to_wholedisk(st.st_rdev, diskname, | |
752 | sizeof(diskname), &diskno) != 0) | |
753 | return NULL; | |
754 | ||
755 | return st.st_rdev == diskno ? NULL : find_device(diskname); | |
539feec1 ML |
756 | } |
757 | ||
257a0035 | 758 | static int umount_partitions(struct eject_control *ctl) |
8ccd6edd | 759 | { |
3736c6e4 | 760 | struct path_cxt *pc = NULL; |
9b439aa9 KZ |
761 | dev_t devno; |
762 | DIR *dir = NULL; | |
763 | struct dirent *d; | |
566cd92c | 764 | int count = 0; |
9b439aa9 | 765 | |
3736c6e4 KZ |
766 | devno = sysfs_devname_to_devno(ctl->device); |
767 | if (devno) | |
768 | pc = ul_new_sysfs_path(devno, NULL, NULL); | |
769 | if (!pc) | |
566cd92c | 770 | return 0; |
9b439aa9 KZ |
771 | |
772 | /* open /sys/block/<wholedisk> */ | |
3736c6e4 | 773 | if (!(dir = ul_path_opendir(pc, NULL))) |
9b439aa9 KZ |
774 | goto done; |
775 | ||
776 | /* scan for partition subdirs */ | |
777 | while ((d = readdir(dir))) { | |
778 | if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) | |
779 | continue; | |
780 | ||
3736c6e4 | 781 | if (sysfs_blkdev_is_partition_dirent(dir, d, ctl->device)) { |
9b439aa9 KZ |
782 | char *mnt = NULL; |
783 | char *dev = find_device(d->d_name); | |
784 | ||
257a0035 SK |
785 | if (dev && device_get_mountpoint(ctl, &dev, &mnt) == 0) { |
786 | verbose(ctl, _("%s: mounted on %s"), dev, mnt); | |
787 | if (!ctl->M_option) | |
788 | umount_one(ctl, mnt); | |
566cd92c | 789 | count++; |
9b439aa9 KZ |
790 | } |
791 | free(dev); | |
8cd48d67 | 792 | free(mnt); |
539feec1 ML |
793 | } |
794 | } | |
539feec1 | 795 | |
9b439aa9 KZ |
796 | done: |
797 | if (dir) | |
798 | closedir(dir); | |
3736c6e4 | 799 | ul_unref_path(pc); |
566cd92c KZ |
800 | |
801 | return count; | |
9b439aa9 | 802 | } |
539feec1 | 803 | |
257a0035 | 804 | static int is_hotpluggable(const struct eject_control *ctl) |
de12e1bb | 805 | { |
3736c6e4 | 806 | struct path_cxt *pc = NULL; |
de12e1bb KZ |
807 | dev_t devno; |
808 | int rc = 0; | |
de12e1bb | 809 | |
3736c6e4 KZ |
810 | devno = sysfs_devname_to_devno(ctl->device); |
811 | if (devno) | |
812 | pc = ul_new_sysfs_path(devno, NULL, NULL); | |
813 | if (!pc) | |
de12e1bb KZ |
814 | return 0; |
815 | ||
3736c6e4 KZ |
816 | rc = sysfs_blkdev_is_hotpluggable(pc); |
817 | ul_unref_path(pc); | |
de12e1bb KZ |
818 | return rc; |
819 | } | |
820 | ||
821 | ||
539feec1 | 822 | /* handle -x option */ |
25a3fa81 | 823 | static void set_device_speed(struct eject_control *ctl) |
8ccd6edd | 824 | { |
257a0035 | 825 | if (!ctl->x_option) |
f1989b79 KZ |
826 | return; |
827 | ||
257a0035 SK |
828 | if (ctl->x_arg == 0) |
829 | verbose(ctl, _("setting CD-ROM speed to auto")); | |
f1989b79 | 830 | else |
257a0035 | 831 | verbose(ctl, _("setting CD-ROM speed to %ldX"), ctl->x_arg); |
f1989b79 | 832 | |
25a3fa81 SK |
833 | open_device(ctl); |
834 | select_speed(ctl); | |
f1989b79 | 835 | exit(EXIT_SUCCESS); |
539feec1 ML |
836 | } |
837 | ||
838 | ||
839 | /* main program */ | |
fc5af4b4 KZ |
840 | int main(int argc, char **argv) |
841 | { | |
9b439aa9 | 842 | char *disk = NULL; |
9a41136b | 843 | char *mountpoint = NULL; |
539feec1 | 844 | int worked = 0; /* set to 1 when successfully ejected */ |
87918040 | 845 | struct eject_control ctl = { NULL }; |
539feec1 | 846 | |
7f0cd302 | 847 | setlocale(LC_ALL,""); |
aa8cb034 KZ |
848 | bindtextdomain(PACKAGE, LOCALEDIR); |
849 | textdomain(PACKAGE); | |
2c308875 | 850 | close_stdout_atexit(); |
539feec1 | 851 | |
539feec1 | 852 | /* parse the command line arguments */ |
257a0035 | 853 | parse_args(&ctl, argc, argv); |
539feec1 | 854 | |
539feec1 | 855 | /* handle -d option */ |
257a0035 | 856 | if (ctl.d_option) { |
f1989b79 | 857 | info(_("default device: `%s'"), EJECT_DEFAULT_DEVICE); |
15c939c8 | 858 | return EXIT_SUCCESS; |
539feec1 ML |
859 | } |
860 | ||
257a0035 SK |
861 | if (!ctl.device) { |
862 | ctl.device = mnt_resolve_path(EJECT_DEFAULT_DEVICE, NULL); | |
863 | verbose(&ctl, _("using default device `%s'"), ctl.device); | |
7ec1eca8 KZ |
864 | } else { |
865 | char *p; | |
539feec1 | 866 | |
257a0035 SK |
867 | if (ctl.device[strlen(ctl.device) - 1] == '/') |
868 | ctl.device[strlen(ctl.device) - 1] = '\0'; | |
539feec1 | 869 | |
7ec1eca8 | 870 | /* figure out full device or mount point name */ |
257a0035 | 871 | p = find_device(ctl.device); |
28094b1e | 872 | if (p) |
257a0035 | 873 | free(ctl.device); |
28094b1e | 874 | else |
257a0035 | 875 | p = ctl.device; |
539feec1 | 876 | |
257a0035 | 877 | ctl.device = mnt_resolve_spec(p, NULL); |
7ec1eca8 KZ |
878 | free(p); |
879 | } | |
f1989b79 | 880 | |
257a0035 SK |
881 | if (!ctl.device) |
882 | errx(EXIT_FAILURE, _("%s: unable to find device"), ctl.device); | |
539feec1 | 883 | |
257a0035 | 884 | verbose(&ctl, _("device name is `%s'"), ctl.device); |
539feec1 | 885 | |
257a0035 | 886 | device_get_mountpoint(&ctl, &ctl.device, &mountpoint); |
9b439aa9 | 887 | if (mountpoint) |
257a0035 | 888 | verbose(&ctl, _("%s: mounted on %s"), ctl.device, mountpoint); |
f1989b79 | 889 | else |
257a0035 | 890 | verbose(&ctl, _("%s: not mounted"), ctl.device); |
9b439aa9 | 891 | |
257a0035 | 892 | disk = get_disk_devname(ctl.device); |
9b439aa9 | 893 | if (disk) { |
257a0035 SK |
894 | verbose(&ctl, _("%s: disc device: %s (disk device will be used for eject)"), ctl.device, disk); |
895 | free(ctl.device); | |
896 | ctl.device = disk; | |
9b439aa9 | 897 | disk = NULL; |
8388bbb3 KZ |
898 | } else { |
899 | struct stat st; | |
900 | ||
257a0035 | 901 | if (stat(ctl.device, &st) != 0 || !S_ISBLK(st.st_mode)) |
8388bbb3 | 902 | errx(EXIT_FAILURE, _("%s: not found mountpoint or device " |
257a0035 | 903 | "with the given name"), ctl.device); |
8388bbb3 | 904 | |
257a0035 | 905 | verbose(&ctl, _("%s: is whole-disk device"), ctl.device); |
8388bbb3 | 906 | } |
539feec1 | 907 | |
257a0035 SK |
908 | if (ctl.F_option == 0 && is_hotpluggable(&ctl) == 0) |
909 | errx(EXIT_FAILURE, _("%s: is not hot-pluggable device"), ctl.device); | |
de12e1bb | 910 | |
539feec1 | 911 | /* handle -n option */ |
257a0035 SK |
912 | if (ctl.n_option) { |
913 | info(_("device is `%s'"), ctl.device); | |
914 | verbose(&ctl, _("exiting due to -n/--noop option")); | |
15c939c8 | 915 | return EXIT_SUCCESS; |
539feec1 ML |
916 | } |
917 | ||
0bcfb489 | 918 | /* handle -i option */ |
257a0035 | 919 | if (ctl.i_option) { |
25a3fa81 | 920 | open_device(&ctl); |
257a0035 | 921 | manual_eject(&ctl); |
15c939c8 | 922 | return EXIT_SUCCESS; |
0bcfb489 KZ |
923 | } |
924 | ||
539feec1 | 925 | /* handle -a option */ |
257a0035 SK |
926 | if (ctl.a_option) { |
927 | if (ctl.a_arg) | |
928 | verbose(&ctl, _("%s: enabling auto-eject mode"), ctl.device); | |
f1989b79 | 929 | else |
257a0035 | 930 | verbose(&ctl, _("%s: disabling auto-eject mode"), ctl.device); |
25a3fa81 | 931 | open_device(&ctl); |
257a0035 | 932 | auto_eject(&ctl); |
15c939c8 | 933 | return EXIT_SUCCESS; |
539feec1 ML |
934 | } |
935 | ||
936 | /* handle -t option */ | |
257a0035 SK |
937 | if (ctl.t_option) { |
938 | verbose(&ctl, _("%s: closing tray"), ctl.device); | |
25a3fa81 | 939 | open_device(&ctl); |
257a0035 SK |
940 | close_tray(ctl.fd); |
941 | set_device_speed(&ctl); | |
15c939c8 | 942 | return EXIT_SUCCESS; |
539feec1 ML |
943 | } |
944 | ||
945 | /* handle -T option */ | |
257a0035 SK |
946 | if (ctl.T_option) { |
947 | verbose(&ctl, _("%s: toggling tray"), ctl.device); | |
25a3fa81 | 948 | open_device(&ctl); |
257a0035 SK |
949 | toggle_tray(ctl.fd); |
950 | set_device_speed(&ctl); | |
15c939c8 | 951 | return EXIT_SUCCESS; |
539feec1 ML |
952 | } |
953 | ||
1abc3326 | 954 | /* handle -X option */ |
257a0035 SK |
955 | if (ctl.X_option) { |
956 | verbose(&ctl, _("%s: listing CD-ROM speed"), ctl.device); | |
25a3fa81 | 957 | open_device(&ctl); |
257a0035 | 958 | list_speeds(&ctl); |
15c939c8 | 959 | return EXIT_SUCCESS; |
1abc3326 KZ |
960 | } |
961 | ||
539feec1 | 962 | /* handle -x option only */ |
257a0035 SK |
963 | if (!ctl.c_option) |
964 | set_device_speed(&ctl); | |
539feec1 | 965 | |
539feec1 | 966 | |
8cd48d67 KZ |
967 | /* |
968 | * Unmount all partitions if -m is not specified; or umount given | |
969 | * mountpoint if -M is specified, otherwise print error of another | |
970 | * partition is mounted. | |
971 | */ | |
257a0035 SK |
972 | if (!ctl.m_option) { |
973 | int ct = umount_partitions(&ctl); | |
566cd92c | 974 | |
8cd48d67 | 975 | if (ct == 0 && mountpoint) |
257a0035 | 976 | umount_one(&ctl, mountpoint); /* probably whole-device */ |
566cd92c | 977 | |
257a0035 | 978 | if (ctl.M_option) { |
8cd48d67 | 979 | if (ct == 1 && mountpoint) |
257a0035 | 980 | umount_one(&ctl, mountpoint); |
8cd48d67 | 981 | else if (ct) |
257a0035 | 982 | errx(EXIT_FAILURE, _("error: %s: device in use"), ctl.device); |
8cd48d67 | 983 | } |
9b439aa9 | 984 | } |
539feec1 ML |
985 | |
986 | /* handle -c option */ | |
257a0035 SK |
987 | if (ctl.c_option) { |
988 | verbose(&ctl, _("%s: selecting CD-ROM disc #%ld"), ctl.device, ctl.c_arg); | |
25a3fa81 | 989 | open_device(&ctl); |
257a0035 SK |
990 | changer_select(&ctl); |
991 | set_device_speed(&ctl); | |
15c939c8 | 992 | return EXIT_SUCCESS; |
539feec1 ML |
993 | } |
994 | ||
995 | /* if user did not specify type of eject, try all four methods */ | |
257a0035 SK |
996 | if (ctl.r_option + ctl.s_option + ctl.f_option + ctl.q_option == 0) |
997 | ctl.r_option = ctl.s_option = ctl.f_option = ctl.q_option = 1; | |
539feec1 ML |
998 | |
999 | /* open device */ | |
25a3fa81 | 1000 | open_device(&ctl); |
539feec1 ML |
1001 | |
1002 | /* try various methods of ejecting until it works */ | |
257a0035 SK |
1003 | if (ctl.r_option) { |
1004 | verbose(&ctl, _("%s: trying to eject using CD-ROM eject command"), ctl.device); | |
1005 | worked = eject_cdrom(ctl.fd); | |
1006 | verbose(&ctl, worked ? _("CD-ROM eject command succeeded") : | |
f1989b79 | 1007 | _("CD-ROM eject command failed")); |
539feec1 ML |
1008 | } |
1009 | ||
257a0035 SK |
1010 | if (ctl.s_option && !worked) { |
1011 | verbose(&ctl, _("%s: trying to eject using SCSI commands"), ctl.device); | |
1012 | worked = eject_scsi(&ctl); | |
1013 | verbose(&ctl, worked ? _("SCSI eject succeeded") : | |
f1989b79 | 1014 | _("SCSI eject failed")); |
539feec1 ML |
1015 | } |
1016 | ||
257a0035 SK |
1017 | if (ctl.f_option && !worked) { |
1018 | verbose(&ctl, _("%s: trying to eject using floppy eject command"), ctl.device); | |
1019 | worked = eject_floppy(ctl.fd); | |
1020 | verbose(&ctl, worked ? _("floppy eject command succeeded") : | |
f1989b79 | 1021 | _("floppy eject command failed")); |
539feec1 ML |
1022 | } |
1023 | ||
257a0035 SK |
1024 | if (ctl.q_option && !worked) { |
1025 | verbose(&ctl, _("%s: trying to eject using tape offline command"), ctl.device); | |
1026 | worked = eject_tape(ctl.fd); | |
1027 | verbose(&ctl, worked ? _("tape offline command succeeded") : | |
f1989b79 | 1028 | _("tape offline command failed")); |
539feec1 ML |
1029 | } |
1030 | ||
f1989b79 KZ |
1031 | if (!worked) |
1032 | errx(EXIT_FAILURE, _("unable to eject")); | |
539feec1 ML |
1033 | |
1034 | /* cleanup */ | |
257a0035 SK |
1035 | close(ctl.fd); |
1036 | free(ctl.device); | |
9a41136b | 1037 | free(mountpoint); |
9b439aa9 | 1038 | |
257a0035 | 1039 | mnt_unref_table(ctl.mtab); |
9b439aa9 | 1040 | |
15c939c8 | 1041 | return EXIT_SUCCESS; |
539feec1 | 1042 | } |