File: | src/bin/chio/chio.c |
Warning: | line 570, column 13 The left operand of '==' is a garbage value |
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; | |||
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 | } |