File: | src/bin/chio/chio.c |
Warning: | line 767, column 5 Value stored to 'found' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: chio.c,v 1.30 2022/10/11 03:37:14 jsg Exp $ */ |
2 | /* $NetBSD: chio.c,v 1.1.1.1 1996/04/03 00:34:38 thorpej Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. All advertising materials mentioning features or use of this software |
17 | * must display the following acknowledgments: |
18 | * This product includes software developed by Jason R. Thorpe |
19 | * for And Communications, http://www.and.com/ |
20 | * 4. The name of the author may not be used to endorse or promote products |
21 | * derived from this software without specific prior written permission. |
22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
24 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
25 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
26 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
29 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
30 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
31 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. |
34 | */ |
35 | |
36 | #include <sys/types.h> |
37 | #include <sys/ioctl.h> |
38 | #include <sys/mtio.h> |
39 | #include <sys/chio.h> |
40 | #include <err.h> |
41 | #include <errno(*__errno()).h> |
42 | #include <fcntl.h> |
43 | #include <limits.h> |
44 | #include <stdio.h> |
45 | #include <stdlib.h> |
46 | #include <string.h> |
47 | #include <unistd.h> |
48 | #include <util.h> |
49 | |
50 | #include "defs.h" |
51 | #include "pathnames.h" |
52 | |
53 | #define _PATH_CH_CONF"/etc/chio.conf" "/etc/chio.conf" |
54 | extern char *parse_tapedev(const char *, const char *, int); /* parse.y */ |
55 | extern char *__progname; /* from crt0.o */ |
56 | |
57 | static void usage(void); |
58 | static int parse_element_type(char *); |
59 | static int parse_element_unit(char *); |
60 | static int parse_special(char *); |
61 | static int is_special(char *); |
62 | static const char * element_type_name(int et); |
63 | static char *bits_to_string(int, const char *); |
64 | static void find_voltag(char *, int *, int *); |
65 | static void check_source_drive(int); |
66 | |
67 | static int do_move(char *, int, char **); |
68 | static int do_exchange(char *, int, char **); |
69 | static int do_position(char *, int, char **); |
70 | static int do_params(char *, int, char **); |
71 | static int do_getpicker(char *, int, char **); |
72 | static int do_setpicker(char *, int, char **); |
73 | static int do_status(char *, int, char **); |
74 | |
75 | /* Valid changer element types. */ |
76 | const struct element_type elements[] = { |
77 | { "drive", CHET_DT3 }, |
78 | { "picker", CHET_MT0 }, |
79 | { "portal", CHET_IE2 }, |
80 | { "slot", CHET_ST1 }, |
81 | { NULL((void *)0), 0 }, |
82 | }; |
83 | |
84 | /* Valid commands. */ |
85 | const struct changer_command commands[] = { |
86 | { "exchange", do_exchange }, |
87 | { "getpicker", do_getpicker }, |
88 | { "move", do_move }, |
89 | { "params", do_params }, |
90 | { "position", do_position }, |
91 | { "setpicker", do_setpicker }, |
92 | { "status", do_status }, |
93 | { NULL((void *)0), 0 }, |
94 | }; |
95 | |
96 | /* Valid special words. */ |
97 | const struct special_word specials[] = { |
98 | { "inv", SW_INVERT1 }, |
99 | { "inv1", SW_INVERT12 }, |
100 | { "inv2", SW_INVERT23 }, |
101 | { NULL((void *)0), 0 }, |
102 | }; |
103 | |
104 | static int changer_fd; |
105 | static char *changer_name; |
106 | static int avoltag; |
107 | static int pvoltag; |
108 | static int sense; |
109 | static int source; |
110 | |
111 | int |
112 | main(int argc, char *argv[]) |
113 | { |
114 | int ch, i; |
115 | |
116 | while ((ch = getopt(argc, argv, "f:")) != -1) { |
117 | switch (ch) { |
118 | case 'f': |
119 | changer_name = optarg; |
120 | break; |
121 | default: |
122 | usage(); |
123 | } |
124 | } |
125 | argc -= optind; |
126 | argv += optind; |
127 | |
128 | if (argc == 0) |
129 | usage(); |
130 | |
131 | /* Get the default changer if not already specified. */ |
132 | if (changer_name == NULL((void *)0)) |
133 | if ((changer_name = getenv(CHANGER_ENV_VAR"CHANGER")) == NULL((void *)0)) |
134 | changer_name = _PATH_CH"/dev/ch0"; |
135 | |
136 | /* Open the changer device. */ |
137 | if ((changer_fd = open(changer_name, O_RDWR0x0002)) == -1) |
138 | err(1, "%s: open", changer_name); |
139 | |
140 | /* Find the specified command. */ |
141 | for (i = 0; commands[i].cc_name != NULL((void *)0); ++i) |
142 | if (strcmp(*argv, commands[i].cc_name) == 0) |
143 | break; |
144 | if (commands[i].cc_name == NULL((void *)0)) { |
145 | /* look for abbreviation */ |
146 | for (i = 0; commands[i].cc_name != NULL((void *)0); ++i) |
147 | if (strncmp(*argv, commands[i].cc_name, |
148 | strlen(*argv)) == 0) |
149 | break; |
150 | } |
151 | if (commands[i].cc_name == NULL((void *)0)) |
152 | errx(1, "unknown command: %s", *argv); |
153 | |
154 | exit((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); |
155 | } |
156 | |
157 | static int |
158 | do_move(char *cname, int argc, char *argv[]) |
159 | { |
160 | struct changer_move cmd; |
161 | int val; |
162 | |
163 | /* |
164 | * On a move command, we expect the following: |
165 | * |
166 | * <from ET> <from EU> <to ET> <to EU> [inv] |
167 | * |
168 | * where ET == element type and EU == element unit. |
169 | */ |
170 | |
171 | ++argv; --argc; |
172 | |
173 | if (argc < 4) { |
174 | warnx("%s: too few arguments", cname); |
175 | goto usage; |
176 | } else if (argc > 5) { |
177 | warnx("%s: too many arguments", cname); |
178 | goto usage; |
179 | } |
180 | bzero(&cmd, sizeof(cmd)); |
181 | |
182 | /* |
183 | * Get the from ET and EU - we search for it if the ET is |
184 | * "voltag", otherwise, we just use the ET and EU given to us. |
185 | */ |
186 | if (strcmp(*argv, "voltag") == 0) { |
187 | ++argv; --argc; |
188 | find_voltag(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit); |
189 | ++argv; --argc; |
190 | } else { |
191 | cmd.cm_fromtype = parse_element_type(*argv); |
192 | ++argv; --argc; |
193 | cmd.cm_fromunit = parse_element_unit(*argv); |
194 | ++argv; --argc; |
195 | } |
196 | |
197 | if (cmd.cm_fromtype == CHET_DT3) |
198 | check_source_drive(cmd.cm_fromunit); |
199 | |
200 | /* |
201 | * Don't allow voltag on the to ET, using a volume |
202 | * as a destination makes no sense on a move |
203 | */ |
204 | cmd.cm_totype = parse_element_type(*argv); |
205 | ++argv; --argc; |
206 | cmd.cm_tounit = parse_element_unit(*argv); |
207 | ++argv; --argc; |
208 | |
209 | /* Deal with optional command modifier. */ |
210 | if (argc) { |
211 | val = parse_special(*argv); |
212 | switch (val) { |
213 | case SW_INVERT1: |
214 | cmd.cm_flags |= CM_INVERT0x01; |
215 | break; |
216 | |
217 | default: |
218 | errx(1, "%s: inappropriate modifier `%s'", |
219 | cname, *argv); |
220 | /* NOTREACHED */ |
221 | } |
222 | } |
223 | |
224 | /* Send command to changer. */ |
225 | if (ioctl(changer_fd, CHIOMOVE((unsigned long)0x80000000 | ((sizeof(struct changer_move) & 0x1fff) << 16) | ((('c')) << 8) | ((0x41))), &cmd) == -1) |
226 | err(1, "%s: CHIOMOVE", changer_name); |
227 | |
228 | return (0); |
229 | |
230 | usage: |
231 | fprintf(stderr(&__sF[2]), "usage: %s %s " |
232 | "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname); |
233 | return (1); |
234 | } |
235 | |
236 | static int |
237 | do_exchange(char *cname, int argc, char *argv[]) |
238 | { |
239 | struct changer_exchange cmd; |
240 | int val; |
241 | |
242 | /* |
243 | * On an exchange command, we expect the following: |
244 | * |
245 | * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] |
246 | * |
247 | * where ET == element type and EU == element unit. |
248 | */ |
249 | |
250 | ++argv; --argc; |
251 | |
252 | if (argc < 4) { |
253 | warnx("%s: too few arguments", cname); |
254 | goto usage; |
255 | } else if (argc > 8) { |
256 | warnx("%s: too many arguments", cname); |
257 | goto usage; |
258 | } |
259 | bzero(&cmd, sizeof(cmd)); |
260 | |
261 | /* <src ET> */ |
262 | cmd.ce_srctype = parse_element_type(*argv); |
263 | ++argv; --argc; |
264 | |
265 | /* <src EU> */ |
266 | cmd.ce_srcunit = parse_element_unit(*argv); |
267 | ++argv; --argc; |
268 | |
269 | /* <dst1 ET> */ |
270 | cmd.ce_fdsttype = parse_element_type(*argv); |
271 | ++argv; --argc; |
272 | |
273 | /* <dst1 EU> */ |
274 | cmd.ce_fdstunit = parse_element_unit(*argv); |
275 | ++argv; --argc; |
276 | |
277 | /* |
278 | * If the next token is a special word or there are no more |
279 | * arguments, then this is a case of simple exchange. |
280 | * dst2 == src. |
281 | */ |
282 | if ((argc == 0) || is_special(*argv)) { |
283 | cmd.ce_sdsttype = cmd.ce_srctype; |
284 | cmd.ce_sdstunit = cmd.ce_srcunit; |
285 | goto do_special; |
286 | } |
287 | |
288 | /* <dst2 ET> */ |
289 | cmd.ce_sdsttype = parse_element_type(*argv); |
290 | ++argv; --argc; |
291 | |
292 | /* <dst2 EU> */ |
293 | cmd.ce_sdstunit = parse_element_unit(*argv); |
294 | ++argv; --argc; |
295 | |
296 | do_special: |
297 | /* Deal with optional command modifiers. */ |
298 | while (argc) { |
299 | val = parse_special(*argv); |
300 | ++argv; --argc; |
301 | switch (val) { |
302 | case SW_INVERT12: |
303 | cmd.ce_flags |= CE_INVERT10x01; |
304 | break; |
305 | |
306 | case SW_INVERT23: |
307 | cmd.ce_flags |= CE_INVERT20x02; |
308 | break; |
309 | |
310 | default: |
311 | errx(1, "%s: inappropriate modifier `%s'", |
312 | cname, *argv); |
313 | /* NOTREACHED */ |
314 | } |
315 | } |
316 | |
317 | /* Send command to changer. */ |
318 | if (ioctl(changer_fd, CHIOEXCHANGE((unsigned long)0x80000000 | ((sizeof(struct changer_exchange ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x42) )), &cmd) == -1) |
319 | err(1, "%s: CHIOEXCHANGE", changer_name); |
320 | |
321 | return (0); |
322 | |
323 | usage: |
324 | fprintf(stderr(&__sF[2]), "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" |
325 | " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", |
326 | __progname, cname); |
327 | return (1); |
328 | } |
329 | |
330 | static int |
331 | do_position(char *cname, int argc, char *argv[]) |
332 | { |
333 | struct changer_position cmd; |
334 | int val; |
335 | |
336 | /* |
337 | * On a position command, we expect the following: |
338 | * |
339 | * <to ET> <to EU> [inv] |
340 | * |
341 | * where ET == element type and EU == element unit. |
342 | */ |
343 | |
344 | ++argv; --argc; |
345 | |
346 | if (argc < 2) { |
347 | warnx("%s: too few arguments", cname); |
348 | goto usage; |
349 | } else if (argc > 3) { |
350 | warnx("%s: too many arguments", cname); |
351 | goto usage; |
352 | } |
353 | bzero(&cmd, sizeof(cmd)); |
354 | |
355 | /* <to ET> */ |
356 | cmd.cp_type = parse_element_type(*argv); |
357 | ++argv; --argc; |
358 | |
359 | /* <to EU> */ |
360 | cmd.cp_unit = parse_element_unit(*argv); |
361 | ++argv; --argc; |
362 | |
363 | /* Deal with optional command modifier. */ |
364 | if (argc) { |
365 | val = parse_special(*argv); |
366 | switch (val) { |
367 | case SW_INVERT1: |
368 | cmd.cp_flags |= CP_INVERT0x01; |
369 | break; |
370 | |
371 | default: |
372 | errx(1, "%s: inappropriate modifier `%s'", |
373 | cname, *argv); |
374 | /* NOTREACHED */ |
375 | } |
376 | } |
377 | |
378 | /* Send command to changer. */ |
379 | if (ioctl(changer_fd, CHIOPOSITION((unsigned long)0x80000000 | ((sizeof(struct changer_position ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x43) )), &cmd) == -1) |
380 | err(1, "%s: CHIOPOSITION", changer_name); |
381 | |
382 | return (0); |
383 | |
384 | usage: |
385 | fprintf(stderr(&__sF[2]), "usage: %s %s <to ET> <to EU> [inv]\n", |
386 | __progname, cname); |
387 | return (1); |
388 | } |
389 | |
390 | static int |
391 | do_params(char *cname, int argc, char *argv[]) |
392 | { |
393 | struct changer_params data; |
394 | |
395 | /* No arguments to this command. */ |
396 | |
397 | ++argv; --argc; |
398 | |
399 | if (argc) { |
400 | warnx("%s: no arguments expected", cname); |
401 | goto usage; |
402 | } |
403 | |
404 | /* Get params from changer and display them. */ |
405 | bzero(&data, sizeof(data)); |
406 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
407 | err(1, "%s: CHIOGPARAMS", changer_name); |
408 | |
409 | printf("%s: %d slot%s, %d drive%s, %d picker%s", |
410 | changer_name, |
411 | data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", |
412 | data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", |
413 | data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); |
414 | if (data.cp_nportals) |
415 | printf(", %d portal%s", data.cp_nportals, |
416 | (data.cp_nportals > 1) ? "s" : ""); |
417 | printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker); |
418 | |
419 | return (0); |
420 | |
421 | usage: |
422 | fprintf(stderr(&__sF[2]), "usage: %s %s\n", __progname, cname); |
423 | return (1); |
424 | } |
425 | |
426 | static int |
427 | do_getpicker(char *cname, int argc, char *argv[]) |
428 | { |
429 | int picker; |
430 | |
431 | /* No arguments to this command. */ |
432 | |
433 | ++argv; --argc; |
434 | |
435 | if (argc) { |
436 | warnx("%s: no arguments expected", cname); |
437 | goto usage; |
438 | } |
439 | |
440 | /* Get current picker from changer and display it. */ |
441 | if (ioctl(changer_fd, CHIOGPICKER((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) << 16) | ((('c')) << 8) | ((0x44))), &picker) == -1) |
442 | err(1, "%s: CHIOGPICKER", changer_name); |
443 | |
444 | printf("%s: current picker: %d\n", changer_name, picker); |
445 | |
446 | return (0); |
447 | |
448 | usage: |
449 | fprintf(stderr(&__sF[2]), "usage: %s %s\n", __progname, cname); |
450 | return (1); |
451 | } |
452 | |
453 | static int |
454 | do_setpicker(char *cname, int argc, char *argv[]) |
455 | { |
456 | int picker; |
457 | |
458 | ++argv; --argc; |
459 | |
460 | if (argc < 1) { |
461 | warnx("%s: too few arguments", cname); |
462 | goto usage; |
463 | } else if (argc > 1) { |
464 | warnx("%s: too many arguments", cname); |
465 | goto usage; |
466 | } |
467 | |
468 | picker = parse_element_unit(*argv); |
469 | |
470 | /* Set the changer picker. */ |
471 | if (ioctl(changer_fd, CHIOSPICKER((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('c')) << 8) | ((0x45))), &picker) == -1) |
472 | err(1, "%s: CHIOSPICKER", changer_name); |
473 | |
474 | return (0); |
475 | |
476 | usage: |
477 | fprintf(stderr(&__sF[2]), "usage: %s %s <picker>\n", __progname, cname); |
478 | return (1); |
479 | } |
480 | |
481 | static int |
482 | do_status(char *cname, int argc, char *argv[]) |
483 | { |
484 | struct changer_element_status_request cmd; |
485 | struct changer_params data; |
486 | int i, chet, schet, echet, c; |
487 | char *description; |
488 | size_t count; |
489 | |
490 | optreset = 1; |
491 | optind = 1; |
492 | while ((c = getopt(argc, argv, "SsvVa")) != -1) { |
493 | switch (c) { |
494 | case 's': |
495 | sense = 1; |
496 | break; |
497 | case 'S': |
498 | source = 1; |
499 | break; |
500 | case 'v': |
501 | pvoltag = 1; |
502 | break; |
503 | case 'V': |
504 | avoltag = 1; |
505 | break; |
506 | case 'a': |
507 | pvoltag = avoltag = source = sense = 1; |
508 | break; |
509 | default: |
510 | goto usage; |
511 | } |
512 | } |
513 | |
514 | argc -= optind; |
515 | argv += optind; |
516 | |
517 | /* |
518 | * On a status command, we expect the following: |
519 | * |
520 | * [<ET>] |
521 | * |
522 | * where ET == element type. |
523 | * |
524 | * If we get no arguments, we get the status of all |
525 | * known element types. |
526 | */ |
527 | if (argc > 1) { |
528 | warnx("%s: too many arguments", cname); |
529 | goto usage; |
530 | } |
531 | |
532 | /* |
533 | * Get params from changer. Specifically, we need the element |
534 | * counts. |
535 | */ |
536 | bzero(&data, sizeof(data)); |
537 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
538 | err(1, "%s: CHIOGPARAMS", changer_name); |
539 | |
540 | if (argc) |
541 | schet = echet = parse_element_type(*argv); |
542 | else { |
543 | schet = CHET_MT0; |
544 | echet = CHET_DT3; |
545 | } |
546 | |
547 | for (chet = schet; chet <= echet; ++chet) { |
548 | switch (chet) { |
549 | case CHET_MT0: |
550 | count = data.cp_npickers; |
551 | description = "picker"; |
552 | break; |
553 | |
554 | case CHET_ST1: |
555 | count = data.cp_nslots; |
556 | description = "slot"; |
557 | break; |
558 | |
559 | case CHET_IE2: |
560 | count = data.cp_nportals; |
561 | description = "portal"; |
562 | break; |
563 | |
564 | case CHET_DT3: |
565 | count = data.cp_ndrives; |
566 | description = "drive"; |
567 | break; |
568 | } |
569 | |
570 | if (count == 0) { |
571 | if (argc == 0) |
572 | continue; |
573 | else { |
574 | printf("%s: no %s elements\n", |
575 | changer_name, description); |
576 | return (0); |
577 | } |
578 | } |
579 | |
580 | bzero(&cmd, sizeof(cmd)); |
581 | |
582 | cmd.cesr_type = chet; |
583 | /* Allocate storage for the status info. */ |
584 | cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data)); |
585 | if ((cmd.cesr_data) == NULL((void *)0)) |
586 | errx(1, "can't allocate status storage"); |
587 | if (avoltag || pvoltag) |
588 | cmd.cesr_flags |= CESR_VOLTAGS0x01; |
589 | |
590 | if (ioctl(changer_fd, CHIOGSTATUS((unsigned long)0x80000000 | ((sizeof(struct changer_element_status_request ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x48) )), &cmd) == -1) { |
591 | free(cmd.cesr_data); |
592 | err(1, "%s: CHIOGSTATUS", changer_name); |
593 | } |
594 | |
595 | /* Dump the status for each element of this type. */ |
596 | for (i = 0; i < count; ++i) { |
597 | struct changer_element_status *ces = |
598 | &(cmd.cesr_data[i]); |
599 | printf("%s %d: %s", description, i, |
600 | bits_to_string(ces->ces_flags, CESTATUS_BITS"\20\6INEAB\5EXENAB\4ACCESS\3EXCEPT\2IMPEXP\1FULL")); |
601 | if (sense) |
602 | printf(" sense: <0x%02x/0x%02x>", |
603 | ces->ces_sensecode, |
604 | ces->ces_sensequal); |
605 | if (pvoltag) |
606 | printf(" voltag: <%s:%d>", |
607 | ces->ces_pvoltag.cv_volid, |
608 | ces->ces_pvoltag.cv_serial); |
609 | if (avoltag) |
610 | printf(" avoltag: <%s:%d>", |
611 | ces->ces_avoltag.cv_volid, |
612 | ces->ces_avoltag.cv_serial); |
613 | if (source) { |
614 | if (ces->ces_flags & CESTATUS_ACCESS0x08) |
615 | printf(" source: <%s %d>", |
616 | element_type_name( |
617 | ces->ces_source_type), |
618 | ces->ces_source_addr); |
619 | else |
620 | printf(" source: <>"); |
621 | } |
622 | printf("\n"); |
623 | } |
624 | |
625 | free(cmd.cesr_data); |
626 | } |
627 | |
628 | return (0); |
629 | |
630 | usage: |
631 | fprintf(stderr(&__sF[2]), "usage: %s %s [<element type>]\n", __progname, |
632 | cname); |
633 | return (1); |
634 | } |
635 | |
636 | /* |
637 | * Check a drive unit as the source for a move or exchange |
638 | * operation. If the drive is not accessible, we attempt |
639 | * to unmount the tape in it before moving to avoid |
640 | * errors in "disconnected" type pickers where the drive |
641 | * is on a separate target from the changer. |
642 | */ |
643 | static void |
644 | check_source_drive(int unit) |
645 | { |
646 | struct mtop mtoffl = { MTOFFL6, 1 }; |
647 | struct changer_element_status_request cmd; |
648 | struct changer_element_status *ces; |
649 | struct changer_params data; |
650 | size_t count = 0; |
651 | int mtfd; |
652 | char *tapedev; |
653 | |
654 | /* |
655 | * Get params from changer. Specifically, we need the element |
656 | * counts. |
657 | */ |
658 | bzero(&data, sizeof(data)); |
659 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
660 | err(1, "%s: CHIOGPARAMS", changer_name); |
661 | |
662 | count = data.cp_ndrives; |
663 | if (unit < 0 || unit >= count) |
664 | err(1, "%s: invalid drive: drive %d", changer_name, unit); |
665 | |
666 | bzero(&cmd, sizeof(cmd)); |
667 | cmd.cesr_type = CHET_DT3; |
668 | /* Allocate storage for the status info. */ |
669 | cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data)); |
670 | if ((cmd.cesr_data) == NULL((void *)0)) |
671 | errx(1, "can't allocate status storage"); |
672 | |
673 | if (ioctl(changer_fd, CHIOGSTATUS((unsigned long)0x80000000 | ((sizeof(struct changer_element_status_request ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x48) )), &cmd) == -1) { |
674 | free(cmd.cesr_data); |
675 | err(1, "%s: CHIOGSTATUS", changer_name); |
676 | } |
677 | ces = &(cmd.cesr_data[unit]); |
678 | |
679 | if ((ces->ces_flags & CESTATUS_FULL0x01) != CESTATUS_FULL0x01) |
680 | err(1, "%s: drive %d is empty!", changer_name, unit); |
681 | |
682 | if ((ces->ces_flags & CESTATUS_ACCESS0x08) == CESTATUS_ACCESS0x08) |
683 | return; /* changer thinks all is well - trust it */ |
684 | |
685 | /* |
686 | * Otherwise, drive is FULL, but not accessible. |
687 | * Try to make it accessible by doing an mt offline. |
688 | */ |
689 | tapedev = parse_tapedev(_PATH_CH_CONF"/etc/chio.conf", changer_name, unit); |
690 | mtfd = opendev(tapedev, O_RDONLY0x0000, 0, NULL((void *)0)); |
691 | if (mtfd == -1) |
692 | err(1, "%s drive %d (%s): open", changer_name, unit, tapedev); |
693 | if (ioctl(mtfd, MTIOCTOP((unsigned long)0x80000000 | ((sizeof(struct mtop) & 0x1fff ) << 16) | ((('m')) << 8) | ((1))), &mtoffl) == -1) |
694 | err(1, "%s drive %d (%s): rewoffl", changer_name, unit, |
695 | tapedev); |
696 | close(mtfd); |
697 | } |
698 | |
699 | void |
700 | find_voltag(char *voltag, int *type, int *unit) |
701 | { |
702 | struct changer_element_status_request cmd; |
703 | struct changer_params data; |
704 | int i, chet, schet, echet, found; |
705 | size_t count = 0; |
706 | |
707 | /* |
708 | * Get params from changer. Specifically, we need the element |
709 | * counts. |
710 | */ |
711 | bzero(&data, sizeof(data)); |
712 | if (ioctl(changer_fd, CHIOGPARAMS((unsigned long)0x40000000 | ((sizeof(struct changer_params) & 0x1fff) << 16) | ((('c')) << 8) | ((0x46))), &data) == -1) |
713 | err(1, "%s: CHIOGPARAMS", changer_name); |
714 | |
715 | found = 0; |
716 | schet = CHET_MT0; |
717 | echet = CHET_DT3; |
718 | |
719 | /* |
720 | * For each type of element, iterate through each one until |
721 | * we find the correct volume id. |
722 | */ |
723 | for (chet = schet; chet <= echet; ++chet) { |
724 | switch (chet) { |
725 | case CHET_MT0: |
726 | count = data.cp_npickers; |
727 | break; |
728 | case CHET_ST1: |
729 | count = data.cp_nslots; |
730 | break; |
731 | case CHET_IE2: |
732 | count = data.cp_nportals; |
733 | break; |
734 | case CHET_DT3: |
735 | count = data.cp_ndrives; |
736 | break; |
737 | } |
738 | if (count == 0 || found) |
739 | continue; |
740 | |
741 | bzero(&cmd, sizeof(cmd)); |
742 | cmd.cesr_type = chet; |
743 | /* Allocate storage for the status info. */ |
744 | cmd.cesr_data = calloc(count, sizeof(*cmd.cesr_data)); |
745 | if ((cmd.cesr_data) == NULL((void *)0)) |
746 | errx(1, "can't allocate status storage"); |
747 | cmd.cesr_flags |= CESR_VOLTAGS0x01; |
748 | |
749 | if (ioctl(changer_fd, CHIOGSTATUS((unsigned long)0x80000000 | ((sizeof(struct changer_element_status_request ) & 0x1fff) << 16) | ((('c')) << 8) | ((0x48) )), &cmd) == -1) { |
750 | free(cmd.cesr_data); |
751 | err(1, "%s: CHIOGSTATUS", changer_name); |
752 | } |
753 | |
754 | /* |
755 | * look through each element to see if it has our desired |
756 | * volume tag. |
757 | */ |
758 | for (i = 0; i < count; ++i) { |
759 | struct changer_element_status *ces = |
760 | &(cmd.cesr_data[i]); |
761 | if ((ces->ces_flags & CESTATUS_FULL0x01) != CESTATUS_FULL0x01) |
762 | continue; /* no tape in drive */ |
763 | if (strcasecmp(voltag, ces->ces_pvoltag.cv_volid) |
764 | == 0) { |
765 | *type = chet; |
766 | *unit = i; |
767 | found = 1; |
Value stored to 'found' is never read | |
768 | free(cmd.cesr_data); |
769 | return; |
770 | } |
771 | } |
772 | free(cmd.cesr_data); |
773 | } |
774 | errx(1, "%s: unable to locate voltag: %s", changer_name, voltag); |
775 | } |
776 | |
777 | |
778 | static int |
779 | parse_element_type(char *cp) |
780 | { |
781 | int i; |
782 | |
783 | for (i = 0; elements[i].et_name != NULL((void *)0); ++i) |
784 | if (strcmp(elements[i].et_name, cp) == 0) |
785 | return (elements[i].et_type); |
786 | |
787 | errx(1, "invalid element type `%s'", cp); |
788 | } |
789 | |
790 | static const char * |
791 | element_type_name(int et) |
792 | { |
793 | int i; |
794 | |
795 | for (i = 0; elements[i].et_name != NULL((void *)0); i++) |
796 | if (elements[i].et_type == et) |
797 | return elements[i].et_name; |
798 | |
799 | return "unknown"; |
800 | } |
801 | |
802 | static int |
803 | parse_element_unit(char *cp) |
804 | { |
805 | int i; |
806 | char *p; |
807 | |
808 | i = (int)strtol(cp, &p, 10); |
809 | if ((i < 0) || (*p != '\0')) |
810 | errx(1, "invalid unit number `%s'", cp); |
811 | |
812 | return (i); |
813 | } |
814 | |
815 | static int |
816 | parse_special(char *cp) |
817 | { |
818 | int val; |
819 | |
820 | val = is_special(cp); |
821 | if (val) |
822 | return (val); |
823 | |
824 | errx(1, "invalid modifier `%s'", cp); |
825 | } |
826 | |
827 | static int |
828 | is_special(char *cp) |
829 | { |
830 | int i; |
831 | |
832 | for (i = 0; specials[i].sw_name != NULL((void *)0); ++i) |
833 | if (strcmp(specials[i].sw_name, cp) == 0) |
834 | return (specials[i].sw_value); |
835 | |
836 | return (0); |
837 | } |
838 | |
839 | static char * |
840 | bits_to_string(int v, const char *cp) |
841 | { |
842 | const char *np; |
843 | char f, sep, *bp; |
844 | static char buf[128]; |
845 | |
846 | bp = buf; |
847 | bzero(buf, sizeof(buf)); |
848 | |
849 | for (sep = '<'; (f = *cp++) != 0; cp = np) { |
850 | for (np = cp; *np >= ' ';) |
851 | np++; |
852 | if ((v & (1 << (f - 1))) == 0) |
853 | continue; |
854 | (void)snprintf(bp, sizeof(buf) - (bp - &buf[0]), |
855 | "%c%.*s", sep, (int)(np - cp), cp); |
856 | bp += strlen(bp); |
857 | sep = ','; |
858 | } |
859 | if (sep != '<') |
860 | *bp = '>'; |
861 | |
862 | return (buf); |
863 | } |
864 | |
865 | static void |
866 | usage(void) |
867 | { |
868 | fprintf(stderr(&__sF[2]), "usage: %s [-f changer] command [arg ...]\n", |
869 | __progname); |
870 | exit(1); |
871 | } |