1 | /* $NetBSD: chio.c,v 1.33 2017/10/16 17:08:35 jnemeth Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1996, 1998, 1999 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
9 | * NASA Ames Research Center. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr. |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | #ifndef lint |
39 | __COPYRIGHT( |
40 | "@(#) Copyright (c) 1996, 1998, 1999\ |
41 | The NetBSD Foundation, Inc. All rights reserved." ); |
42 | __RCSID("$NetBSD: chio.c,v 1.33 2017/10/16 17:08:35 jnemeth Exp $" ); |
43 | #endif |
44 | |
45 | #include <sys/param.h> |
46 | #include <sys/ioctl.h> |
47 | #include <sys/chio.h> |
48 | #include <sys/cdio.h> /* for ATAPI CD changer; too bad it uses a lame API */ |
49 | |
50 | #include <ctype.h> |
51 | #include <err.h> |
52 | #include <errno.h> |
53 | #include <fcntl.h> |
54 | #include <limits.h> |
55 | #include <stdio.h> |
56 | #include <stdlib.h> |
57 | #include <string.h> |
58 | #include <unistd.h> |
59 | |
60 | #include "defs.h" |
61 | #include "pathnames.h" |
62 | |
63 | __dead static void usage(void); |
64 | static void cleanup(void); |
65 | static int parse_element_type(const char *); |
66 | static int parse_element_unit(const char *); |
67 | static int parse_special(const char *); |
68 | static int is_special(const char *); |
69 | static const char *bits_to_string(int, const char *); |
70 | |
71 | static int do_move(const char *, int, char **); |
72 | static int do_exchange(const char *, int, char **); |
73 | static int do_position(const char *, int, char **); |
74 | static int do_params(const char *, int, char **); |
75 | static int do_getpicker(const char *, int, char **); |
76 | static int do_setpicker(const char *, int, char **); |
77 | static int do_status(const char *, int, char **); |
78 | static int do_ielem(const char *, int, char **); |
79 | static int do_cdlu(const char *, int, char **); |
80 | |
81 | /* Valid changer element types. */ |
82 | const struct element_type elements[] = { |
83 | { "picker" , CHET_MT }, |
84 | { "slot" , CHET_ST }, |
85 | { "portal" , CHET_IE }, |
86 | { "drive" , CHET_DT }, |
87 | { NULL, 0 }, |
88 | }; |
89 | |
90 | /* Valid commands. */ |
91 | const struct changer_command commands[] = { |
92 | { "move" , " <from ET> <from EU> <to ET> <to EU> [inv]" , |
93 | do_move }, |
94 | |
95 | { "exchange" , " <src ET> <src EU> <dst1 ET> <dst1 EU>\n" |
96 | "\t\t [<dst2 ET> <dst2 EU>] [inv1] [inv2]" , |
97 | do_exchange }, |
98 | |
99 | { "position" , " <to ET> <to EU> [inv]" , do_position }, |
100 | |
101 | { "params" , "" , |
102 | do_params }, |
103 | |
104 | { "getpicker" , "" , |
105 | do_getpicker }, |
106 | |
107 | { "setpicker" , " <picker>" , |
108 | do_setpicker }, |
109 | |
110 | { "status" , " [<ET> [unit [count]]] [voltags]" , |
111 | do_status }, |
112 | |
113 | { "ielem" , "" , |
114 | do_ielem }, |
115 | |
116 | { "cdlu" , " load|unload <slot>\n" |
117 | "\t abort" , |
118 | do_cdlu }, |
119 | |
120 | { NULL, NULL, |
121 | NULL }, |
122 | }; |
123 | |
124 | /* Valid special words. */ |
125 | const struct special_word specials[] = { |
126 | { "inv" , SW_INVERT }, |
127 | { "inv1" , SW_INVERT1 }, |
128 | { "inv2" , SW_INVERT2 }, |
129 | { "voltags" , SW_VOLTAGS }, |
130 | { NULL, 0 }, |
131 | }; |
132 | |
133 | static const char *changer_name; |
134 | static int changer_fd; |
135 | |
136 | int |
137 | main(int argc, char *argv[]) |
138 | { |
139 | int ch, i; |
140 | |
141 | setprogname(argv[0]); |
142 | while ((ch = getopt(argc, argv, "f:" )) != -1) { |
143 | switch (ch) { |
144 | case 'f': |
145 | changer_name = optarg; |
146 | break; |
147 | default: |
148 | usage(); |
149 | /* NOTREACHED */ |
150 | } |
151 | } |
152 | argc -= optind; |
153 | argv += optind; |
154 | |
155 | if (argc == 0) |
156 | usage(); |
157 | /* NOTREACHED */ |
158 | |
159 | /* Get the default changer if not already specified. */ |
160 | if (changer_name == NULL) |
161 | if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) |
162 | changer_name = _PATH_CH; |
163 | |
164 | /* Open the changer device. */ |
165 | if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) |
166 | err(EXIT_FAILURE, "%s: open" , changer_name); |
167 | /* NOTREACHED */ |
168 | |
169 | /* Register cleanup function. */ |
170 | if (atexit(cleanup)) |
171 | err(EXIT_FAILURE, "can't register cleanup function" ); |
172 | /* NOTREACHED */ |
173 | |
174 | /* Find the specified command. */ |
175 | for (i = 0; commands[i].cc_name != NULL; ++i) |
176 | if (strcmp(*argv, commands[i].cc_name) == 0) |
177 | break; |
178 | if (commands[i].cc_name == NULL) |
179 | errx(EXIT_FAILURE, "unknown command: %s" , *argv); |
180 | /* NOTREACHED */ |
181 | |
182 | /* Skip over the command name and call handler. */ |
183 | ++argv; --argc; |
184 | exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); |
185 | /* NOTREACHED */ |
186 | } |
187 | |
188 | static int |
189 | do_move(const char *cname, int argc, char **argv) |
190 | { |
191 | struct changer_move_request cmd; |
192 | int val; |
193 | |
194 | /* |
195 | * On a move command, we expect the following: |
196 | * |
197 | * <from ET> <from EU> <to ET> <to EU> [inv] |
198 | * |
199 | * where ET == element type and EU == element unit. |
200 | */ |
201 | if (argc < 4) { |
202 | warnx("%s: too few arguments" , cname); |
203 | usage(); |
204 | /*NOTREACHED*/ |
205 | } else if (argc > 5) { |
206 | warnx("%s: too many arguments" , cname); |
207 | usage(); |
208 | /*NOTREACHED*/ |
209 | } |
210 | (void)memset(&cmd, 0, sizeof(cmd)); |
211 | |
212 | /* <from ET> */ |
213 | cmd.cm_fromtype = parse_element_type(*argv); |
214 | ++argv; --argc; |
215 | |
216 | /* <from EU> */ |
217 | cmd.cm_fromunit = parse_element_unit(*argv); |
218 | ++argv; --argc; |
219 | |
220 | /* <to ET> */ |
221 | cmd.cm_totype = parse_element_type(*argv); |
222 | ++argv; --argc; |
223 | |
224 | /* <to EU> */ |
225 | cmd.cm_tounit = parse_element_unit(*argv); |
226 | ++argv; --argc; |
227 | |
228 | /* Deal with optional command modifier. */ |
229 | if (argc) { |
230 | val = parse_special(*argv); |
231 | switch (val) { |
232 | case SW_INVERT: |
233 | cmd.cm_flags |= CM_INVERT; |
234 | break; |
235 | default: |
236 | errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'" , |
237 | cname, *argv); |
238 | /* NOTREACHED */ |
239 | } |
240 | } |
241 | |
242 | /* Send command to changer. */ |
243 | if (ioctl(changer_fd, CHIOMOVE, &cmd)) |
244 | err(EXIT_FAILURE, "%s: CHIOMOVE" , changer_name); |
245 | /* NOTREACHED */ |
246 | |
247 | return (0); |
248 | } |
249 | |
250 | static int |
251 | do_exchange(const char *cname, int argc, char **argv) |
252 | { |
253 | struct changer_exchange_request cmd; |
254 | int val; |
255 | |
256 | /* |
257 | * On an exchange command, we expect the following: |
258 | * |
259 | * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] |
260 | * |
261 | * where ET == element type and EU == element unit. |
262 | */ |
263 | if (argc < 4) { |
264 | warnx("%s: too few arguments" , cname); |
265 | usage(); |
266 | /*NOTREACHED*/ |
267 | } else if (argc > 8) { |
268 | warnx("%s: too many arguments" , cname); |
269 | usage(); |
270 | /*NOTREACHED*/ |
271 | } |
272 | (void)memset(&cmd, 0, sizeof(cmd)); |
273 | |
274 | /* <src ET> */ |
275 | cmd.ce_srctype = parse_element_type(*argv); |
276 | ++argv; --argc; |
277 | |
278 | /* <src EU> */ |
279 | cmd.ce_srcunit = parse_element_unit(*argv); |
280 | ++argv; --argc; |
281 | |
282 | /* <dst1 ET> */ |
283 | cmd.ce_fdsttype = parse_element_type(*argv); |
284 | ++argv; --argc; |
285 | |
286 | /* <dst1 EU> */ |
287 | cmd.ce_fdstunit = parse_element_unit(*argv); |
288 | ++argv; --argc; |
289 | |
290 | /* |
291 | * If the next token is a special word or there are no more |
292 | * arguments, then this is a case of simple exchange. |
293 | * dst2 == src. |
294 | */ |
295 | if ((argc == 0) || is_special(*argv)) { |
296 | cmd.ce_sdsttype = cmd.ce_srctype; |
297 | cmd.ce_sdstunit = cmd.ce_srcunit; |
298 | goto do_special; |
299 | } |
300 | |
301 | /* <dst2 ET> */ |
302 | cmd.ce_sdsttype = parse_element_type(*argv); |
303 | ++argv; --argc; |
304 | |
305 | /* <dst2 EU> */ |
306 | cmd.ce_sdstunit = parse_element_unit(*argv); |
307 | ++argv; --argc; |
308 | |
309 | do_special: |
310 | /* Deal with optional command modifiers. */ |
311 | while (argc) { |
312 | val = parse_special(*argv); |
313 | ++argv; --argc; |
314 | switch (val) { |
315 | case SW_INVERT1: |
316 | cmd.ce_flags |= CE_INVERT1; |
317 | break; |
318 | case SW_INVERT2: |
319 | cmd.ce_flags |= CE_INVERT2; |
320 | break; |
321 | default: |
322 | errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'" , |
323 | cname, *argv); |
324 | /* NOTREACHED */ |
325 | } |
326 | } |
327 | |
328 | /* Send command to changer. */ |
329 | if (ioctl(changer_fd, CHIOEXCHANGE, &cmd)) |
330 | err(EXIT_FAILURE, "%s: CHIOEXCHANGE" , changer_name); |
331 | /* NOTREACHED */ |
332 | |
333 | return (0); |
334 | } |
335 | |
336 | static int |
337 | do_position(const char *cname, int argc, char **argv) |
338 | { |
339 | struct changer_position_request cmd; |
340 | int val; |
341 | |
342 | /* |
343 | * On a position command, we expect the following: |
344 | * |
345 | * <to ET> <to EU> [inv] |
346 | * |
347 | * where ET == element type and EU == element unit. |
348 | */ |
349 | if (argc < 2) { |
350 | warnx("%s: too few arguments" , cname); |
351 | usage(); |
352 | /*NOTREACHED*/ |
353 | } else if (argc > 3) { |
354 | warnx("%s: too many arguments" , cname); |
355 | usage(); |
356 | /*NOTREACHED*/ |
357 | } |
358 | (void)memset(&cmd, 0, sizeof(cmd)); |
359 | |
360 | /* <to ET> */ |
361 | cmd.cp_type = parse_element_type(*argv); |
362 | ++argv; --argc; |
363 | |
364 | /* <to EU> */ |
365 | cmd.cp_unit = parse_element_unit(*argv); |
366 | ++argv; --argc; |
367 | |
368 | /* Deal with optional command modifier. */ |
369 | if (argc) { |
370 | val = parse_special(*argv); |
371 | switch (val) { |
372 | case SW_INVERT: |
373 | cmd.cp_flags |= CP_INVERT; |
374 | break; |
375 | default: |
376 | errx(EXIT_FAILURE, "%s: inappropriate modifier `%s'" , |
377 | cname, *argv); |
378 | /* NOTREACHED */ |
379 | } |
380 | } |
381 | |
382 | /* Send command to changer. */ |
383 | if (ioctl(changer_fd, CHIOPOSITION, &cmd)) |
384 | err(EXIT_FAILURE, "%s: CHIOPOSITION" , changer_name); |
385 | /* NOTREACHED */ |
386 | |
387 | return (0); |
388 | } |
389 | |
390 | /* ARGSUSED */ |
391 | static int |
392 | do_params(const char *cname, int argc, char **argv) |
393 | { |
394 | struct changer_params data; |
395 | |
396 | /* No arguments to this command. */ |
397 | if (argc) { |
398 | warnx("%s: no arguments expected" , cname); |
399 | usage(); |
400 | /* NOTREACHED */ |
401 | } |
402 | |
403 | /* Get params from changer and display them. */ |
404 | (void)memset(&data, 0, sizeof(data)); |
405 | if (ioctl(changer_fd, CHIOGPARAMS, &data)) |
406 | err(EXIT_FAILURE, "%s: CHIOGPARAMS" , changer_name); |
407 | /* NOTREACHED */ |
408 | |
409 | #define PLURAL(n) (n) > 1 ? "s" : "" |
410 | |
411 | (void)printf("%s: %d slot%s, %d drive%s, %d picker%s" , |
412 | changer_name, |
413 | data.cp_nslots, PLURAL(data.cp_nslots), |
414 | data.cp_ndrives, PLURAL(data.cp_ndrives), |
415 | data.cp_npickers, PLURAL(data.cp_npickers)); |
416 | if (data.cp_nportals) |
417 | (void)printf(", %d portal%s" , data.cp_nportals, |
418 | PLURAL(data.cp_nportals)); |
419 | |
420 | #undef PLURAL |
421 | |
422 | (void)printf("\n%s: current picker: %d\n" , changer_name, data.cp_curpicker); |
423 | |
424 | return (0); |
425 | } |
426 | |
427 | /* ARGSUSED */ |
428 | static int |
429 | do_getpicker(const char *cname, int argc, char **argv) |
430 | { |
431 | int picker; |
432 | |
433 | /* No arguments to this command. */ |
434 | if (argc) { |
435 | warnx("%s: no arguments expected" , cname); |
436 | usage(); |
437 | /*NOTREACHED*/ |
438 | } |
439 | |
440 | /* Get current picker from changer and display it. */ |
441 | if (ioctl(changer_fd, CHIOGPICKER, &picker)) |
442 | err(EXIT_FAILURE, "%s: CHIOGPICKER" , changer_name); |
443 | /* NOTREACHED */ |
444 | |
445 | (void)printf("%s: current picker: %d\n" , changer_name, picker); |
446 | |
447 | return (0); |
448 | } |
449 | |
450 | static int |
451 | do_setpicker(const char *cname, int argc, char **argv) |
452 | { |
453 | int picker; |
454 | |
455 | if (argc < 1) { |
456 | warnx("%s: too few arguments" , cname); |
457 | usage(); |
458 | /*NOTREACHED*/ |
459 | } else if (argc > 1) { |
460 | warnx("%s: too many arguments" , cname); |
461 | usage(); |
462 | /*NOTREACHED*/ |
463 | } |
464 | |
465 | picker = parse_element_unit(*argv); |
466 | |
467 | /* Set the changer picker. */ |
468 | if (ioctl(changer_fd, CHIOSPICKER, &picker)) |
469 | err(EXIT_FAILURE, "%s: CHIOSPICKER" , changer_name); |
470 | |
471 | return (0); |
472 | } |
473 | |
474 | static int |
475 | do_status(const char *cname, int argc, char **argv) |
476 | { |
477 | struct changer_element_status_request cmd; |
478 | struct changer_params data; |
479 | struct changer_element_status *ces; |
480 | int i, chet, count, echet, flags, have_ucount, have_unit; |
481 | int schet, ucount, unit; |
482 | size_t size; |
483 | |
484 | flags = 0; |
485 | ucount = 0; |
486 | unit = 0; |
487 | have_ucount = 0; |
488 | have_unit = 0; |
489 | |
490 | /* |
491 | * On a status command, we expect the following: |
492 | * |
493 | * [<ET> [unit [count]]] [voltags] |
494 | * |
495 | * where ET == element type. |
496 | * |
497 | * If we get no element-related arguments, we get the status of all |
498 | * known element types. |
499 | */ |
500 | if (argc > 4) { |
501 | warnx("%s: too many arguments" , cname); |
502 | usage(); |
503 | /*NOTREACHED*/ |
504 | } |
505 | |
506 | /* |
507 | * Get params from changer. Specifically, we need the element |
508 | * counts. |
509 | */ |
510 | (void)memset(&data, 0, sizeof(data)); |
511 | if (ioctl(changer_fd, CHIOGPARAMS, &data)) |
512 | err(EXIT_FAILURE, "%s: CHIOGPARAMS" , changer_name); |
513 | /* NOTREACHED */ |
514 | |
515 | schet = CHET_MT; |
516 | echet = CHET_DT; |
517 | |
518 | for (; argc != 0; argc--, argv++) { |
519 | /* |
520 | * If we have the voltags modifier, it must be the |
521 | * last argument. |
522 | */ |
523 | if (is_special(argv[0])) { |
524 | if (argc != 1) { |
525 | warnx("%s: malformed command line" , cname); |
526 | usage(); |
527 | /*NOTREACHED*/ |
528 | } |
529 | if (parse_special(argv[0]) != SW_VOLTAGS) |
530 | errx(EXIT_FAILURE, |
531 | "%s: inappropriate special word: %s" , |
532 | cname, argv[0]); |
533 | /* NOTREACHED */ |
534 | flags |= CESR_VOLTAGS; |
535 | continue; |
536 | } |
537 | |
538 | /* |
539 | * If we get an element type, we can't have specified |
540 | * anything else. |
541 | */ |
542 | if (isdigit((unsigned char)*argv[0]) == 0) { |
543 | if (schet == echet || flags != 0 || have_unit || |
544 | have_ucount) { |
545 | warnx("%s: malformed command line" , cname); |
546 | usage(); |
547 | /*NOTREACHED*/ |
548 | } |
549 | schet = echet = parse_element_type(argv[0]); |
550 | continue; |
551 | } |
552 | |
553 | /* |
554 | * We know we have a digit here. If we do, we must |
555 | * have specified an element type. |
556 | */ |
557 | if (schet != echet) { |
558 | warnx("%s: malformed command line" , cname); |
559 | usage(); |
560 | /*NOTREACHED*/ |
561 | } |
562 | |
563 | i = parse_element_unit(argv[0]); |
564 | |
565 | if (have_unit == 0) { |
566 | unit = i; |
567 | have_unit = 1; |
568 | } else if (have_ucount == 0) { |
569 | ucount = i; |
570 | have_ucount = 1; |
571 | } else { |
572 | warnx("%s: malformed command line" , cname); |
573 | usage(); |
574 | /*NOTREACHED*/ |
575 | } |
576 | } |
577 | |
578 | for (chet = schet; chet <= echet; ++chet) { |
579 | switch (chet) { |
580 | case CHET_MT: |
581 | count = data.cp_npickers; |
582 | break; |
583 | case CHET_ST: |
584 | count = data.cp_nslots; |
585 | break; |
586 | case CHET_IE: |
587 | count = data.cp_nportals; |
588 | break; |
589 | case CHET_DT: |
590 | count = data.cp_ndrives; |
591 | break; |
592 | default: |
593 | /* To appease gcc -Wuninitialized. */ |
594 | count = 0; |
595 | } |
596 | |
597 | if (count == 0) { |
598 | if (schet != echet) |
599 | continue; |
600 | else { |
601 | (void)printf("%s: no %s elements\n" , |
602 | changer_name, |
603 | elements[chet].et_name); |
604 | return (0); |
605 | } |
606 | } |
607 | |
608 | /* |
609 | * If we have a unit, we may or may not have a count. |
610 | * If we don't have a unit, we don't have a count, either. |
611 | * |
612 | * Make sure both are initialized. |
613 | */ |
614 | if (have_unit) { |
615 | if (have_ucount == 0) |
616 | ucount = 1; |
617 | } else { |
618 | unit = 0; |
619 | ucount = count; |
620 | } |
621 | |
622 | if ((unit + ucount) > count) |
623 | errx(EXIT_FAILURE, "%s: invalid unit/count %d/%d" , |
624 | cname, unit, ucount); |
625 | /* NOTREACHED */ |
626 | |
627 | size = ucount * sizeof(struct changer_element_status); |
628 | |
629 | /* Allocate storage for the status bytes. */ |
630 | if ((ces = malloc(size)) == NULL) |
631 | errx(EXIT_FAILURE, "can't allocate status storage" ); |
632 | /* NOTREACHED */ |
633 | |
634 | (void)memset(ces, 0, size); |
635 | (void)memset(&cmd, 0, sizeof(cmd)); |
636 | |
637 | cmd.cesr_type = chet; |
638 | cmd.cesr_unit = unit; |
639 | cmd.cesr_count = ucount; |
640 | cmd.cesr_flags = flags; |
641 | cmd.cesr_data = ces; |
642 | |
643 | /* |
644 | * Should we deal with this eventually? |
645 | */ |
646 | cmd.cesr_vendor_data = NULL; |
647 | |
648 | if (ioctl(changer_fd, CHIOGSTATUS, &cmd)) { |
649 | free(ces); |
650 | err(EXIT_FAILURE, "%s: CHIOGSTATUS" , changer_name); |
651 | /* NOTREACHED */ |
652 | } |
653 | |
654 | /* Dump the status for each element of this type. */ |
655 | for (i = 0; i < ucount; i++) { |
656 | (void)printf("%s %d: " , elements[chet].et_name, |
657 | unit + i); |
658 | if ((ces[i].ces_flags & CESTATUS_STATUS_VALID) == 0) { |
659 | (void)printf("status not available\n" ); |
660 | continue; |
661 | } |
662 | (void)printf("%s" , bits_to_string(ces[i].ces_flags, |
663 | CESTATUS_BITS)); |
664 | if (ces[i].ces_flags & CESTATUS_XNAME_VALID) |
665 | (void)printf(" (%s)" , ces[i].ces_xname); |
666 | (void)printf("\n" ); |
667 | if (ces[i].ces_flags & CESTATUS_PVOL_VALID) |
668 | (void)printf("\tPrimary volume tag: %s " |
669 | "ver. %d\n" , |
670 | ces[i].ces_pvoltag.cv_tag, |
671 | ces[i].ces_pvoltag.cv_serial); |
672 | if (ces[i].ces_flags & CESTATUS_AVOL_VALID) |
673 | (void)printf("\tAlternate volume tag: %s " |
674 | "ver. %d\n" , |
675 | ces[i].ces_avoltag.cv_tag, |
676 | ces[i].ces_avoltag.cv_serial); |
677 | if (ces[i].ces_flags & CESTATUS_FROM_VALID) |
678 | (void)printf("\tFrom: %s %d\n" , |
679 | elements[ces[i].ces_from_type].et_name, |
680 | ces[i].ces_from_unit); |
681 | if (ces[i].ces_vendor_len) |
682 | (void)printf("\tVendor-specific data size: " |
683 | "%lu\n" , (u_long)ces[i].ces_vendor_len); |
684 | } |
685 | free(ces); |
686 | } |
687 | |
688 | return (0); |
689 | } |
690 | |
691 | /* ARGSUSED */ |
692 | static int |
693 | do_ielem(const char *cname, int argc, char **argv) |
694 | { |
695 | |
696 | if (ioctl(changer_fd, CHIOIELEM, NULL)) |
697 | err(EXIT_FAILURE, "%s: CHIOIELEM" , changer_name); |
698 | /* NOTREACHED */ |
699 | |
700 | return (0); |
701 | } |
702 | |
703 | /* ARGSUSED */ |
704 | static int |
705 | do_cdlu(const char *cname, int argc, char **argv) |
706 | { |
707 | static const struct special_word cdlu_subcmds[] = { |
708 | { "load" , CD_LU_LOAD }, |
709 | { "unload" , CD_LU_UNLOAD }, |
710 | { "abort" , CD_LU_ABORT }, |
711 | { NULL, 0 }, |
712 | }; |
713 | struct ioc_load_unload cmd; |
714 | int i; |
715 | |
716 | /* |
717 | * This command is a little different, since we are mostly dealing |
718 | * with ATAPI CD changers, which have a lame API (since ATAPI doesn't |
719 | * have LUNs). |
720 | * |
721 | * We have 3 sub-commands: "load", "unload", and "abort". The |
722 | * first two take a slot number. The latter does not. |
723 | */ |
724 | |
725 | if (argc < 1 || argc > 2) |
726 | usage(); |
727 | /*NOTREACHED*/ |
728 | |
729 | for (i = 0; cdlu_subcmds[i].sw_name != NULL; i++) { |
730 | if (strcmp(argv[0], cdlu_subcmds[i].sw_name) == 0) { |
731 | cmd.options = cdlu_subcmds[i].sw_value; |
732 | break; |
733 | } |
734 | } |
735 | if (cdlu_subcmds[i].sw_name == NULL) |
736 | usage(); |
737 | /*NOTREACHED*/ |
738 | |
739 | if (strcmp(argv[0], "abort" ) == 0) |
740 | cmd.slot = 0; |
741 | else |
742 | cmd.slot = parse_element_unit(argv[1]); |
743 | |
744 | /* |
745 | * XXX Should maybe do something different with the device |
746 | * XXX handling for cdlu; think about this some more. |
747 | */ |
748 | if (ioctl(changer_fd, CDIOCLOADUNLOAD, &cmd)) |
749 | err(EXIT_FAILURE, "%s: CDIOCLOADUNLOAD" , changer_name); |
750 | /* NOTREACHED */ |
751 | |
752 | return (0); |
753 | } |
754 | |
755 | static int |
756 | parse_element_type(const char *cp) |
757 | { |
758 | int i; |
759 | |
760 | for (i = 0; elements[i].et_name != NULL; ++i) |
761 | if (strcmp(elements[i].et_name, cp) == 0) |
762 | return (elements[i].et_type); |
763 | |
764 | errx(EXIT_FAILURE, "invalid element type `%s'" , cp); |
765 | /* NOTREACHED */ |
766 | } |
767 | |
768 | static int |
769 | parse_element_unit(const char *cp) |
770 | { |
771 | char *p; |
772 | int i; |
773 | |
774 | i = (int)strtol(cp, &p, 10); |
775 | if ((i < 0) || (*p != '\0')) |
776 | errx(EXIT_FAILURE, "invalid unit number `%s'" , cp); |
777 | |
778 | return (i); |
779 | } |
780 | |
781 | static int |
782 | parse_special(const char *cp) |
783 | { |
784 | int val; |
785 | |
786 | val = is_special(cp); |
787 | if (val) |
788 | return (val); |
789 | |
790 | errx(EXIT_FAILURE, "invalid modifier `%s'" , cp); |
791 | /* NOTREACHED */ |
792 | } |
793 | |
794 | static int |
795 | is_special(const char *cp) |
796 | { |
797 | int i; |
798 | |
799 | for (i = 0; specials[i].sw_name != NULL; ++i) |
800 | if (strcmp(specials[i].sw_name, cp) == 0) |
801 | return (specials[i].sw_value); |
802 | |
803 | return (0); |
804 | } |
805 | |
806 | static const char * |
807 | bits_to_string(int v, const char *cp) |
808 | { |
809 | static char buf[128]; |
810 | const char *np; |
811 | char *bp, f; |
812 | int first; |
813 | |
814 | bp = buf; |
815 | *bp++ = '<'; |
816 | for (first = 1; (f = *cp++) != 0; cp = np) { |
817 | for (np = cp; *np >= ' ';) |
818 | np++; |
819 | if ((v & (1 << (f - 1))) == 0) |
820 | continue; |
821 | if (first) |
822 | first = 0; |
823 | else |
824 | *bp++ = ','; |
825 | (void)memcpy(bp, cp, np - cp); |
826 | bp += np - cp; |
827 | } |
828 | *bp++ = '>'; |
829 | *bp = '\0'; |
830 | |
831 | return (buf); |
832 | } |
833 | |
834 | static void |
835 | cleanup(void) |
836 | { |
837 | |
838 | /* Simple enough... */ |
839 | (void)close(changer_fd); |
840 | } |
841 | |
842 | static void |
843 | usage(void) |
844 | { |
845 | int i; |
846 | |
847 | (void)fprintf(stderr, |
848 | "usage: %s [-f changer] command arg1 arg2 [arg3 [...]]\n" , |
849 | getprogname()); |
850 | |
851 | (void)fprintf(stderr, "Where command (and args) are:\n" ); |
852 | for (i = 0; commands[i].cc_name != NULL; i++) |
853 | (void)fprintf(stderr, "\t%s%s\n" , commands[i].cc_name, |
854 | commands[i].cc_args); |
855 | exit(1); |
856 | /* NOTREACHED */ |
857 | } |
858 | |