1 /* 2 * Written By Julian ELischer 3 * Copyright julian Elischer 1993. 4 * Permission is granted to use or redistribute this file in any way as long 5 * as this notice remains. Julian Elischer does not guarantee that this file 6 * is totally correct for any given task and users of this file must 7 * accept responsibility for any damage that occurs from the application of this 8 * file. 9 * 10 * (julian@tfs.com julian@dialix.oz.au) 11 * 12 * User SCSI hooks added by Peter Dufault: 13 * 14 * Copyright (c) 1994 HD Associates 15 * (contact: dufault@hda.com) 16 * All rights reserved. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 3. The name of HD Associates 27 * may not be used to endorse or promote products derived from this software 28 * without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 /* 43 * Taken from the original scsi(8) program. 44 * from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $"; 45 */ 46 #ifndef lint 47 static const char rcsid[] = 48 "$FreeBSD$"; 49 #endif /* not lint */ 50 51 #include <ctype.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <string.h> 55 #include <stdlib.h> 56 #include <stdio.h> 57 #include <sys/file.h> 58 #include <signal.h> 59 #include <unistd.h> 60 61 #include <cam/cam.h> 62 #include <cam/cam_ccb.h> 63 #include <camlib.h> 64 #include "camcontrol.h" 65 66 int verbose = 0; 67 68 /* iget: Integer argument callback 69 */ 70 int 71 iget(void *hook, char *name) 72 { 73 struct get_hook *h = (struct get_hook *)hook; 74 int arg; 75 76 if (h->got >= h->argc) 77 { 78 fprintf(stderr, "Expecting an integer argument.\n"); 79 usage(0); 80 exit(1); 81 } 82 arg = strtol(h->argv[h->got], 0, 0); 83 h->got++; 84 85 if (verbose && name && *name) 86 printf("%s: %d\n", name, arg); 87 88 return arg; 89 } 90 91 /* cget: char * argument callback 92 */ 93 char * 94 cget(void *hook, char *name) 95 { 96 struct get_hook *h = (struct get_hook *)hook; 97 char *arg; 98 99 if (h->got >= h->argc) 100 { 101 fprintf(stderr, "Expecting a character pointer argument.\n"); 102 usage(0); 103 exit(1); 104 } 105 arg = h->argv[h->got]; 106 h->got++; 107 108 if (verbose && name) 109 printf("cget: %s: %s", name, arg); 110 111 return arg; 112 } 113 114 /* arg_put: "put argument" callback 115 */ 116 void 117 arg_put(void *hook, int letter, void *arg, int count, char *name) 118 { 119 if (verbose && name && *name) 120 printf("%s: ", name); 121 122 switch(letter) 123 { 124 case 'i': 125 case 'b': 126 printf("%d ", (intptr_t)arg); 127 break; 128 129 case 'c': 130 case 'z': 131 { 132 char *p; 133 134 p = malloc(count + 1); 135 136 bzero(p, count +1); 137 strncpy(p, (char *)arg, count); 138 if (letter == 'z') 139 { 140 int i; 141 for (i = count - 1; i >= 0; i--) 142 if (p[i] == ' ') 143 p[i] = 0; 144 else 145 break; 146 } 147 printf("%s ", p); 148 149 free(p); 150 } 151 152 break; 153 154 default: 155 printf("Unknown format letter: '%c'\n", letter); 156 } 157 if (verbose) 158 putchar('\n'); 159 } 160 161 #define START_ENTRY '{' 162 #define END_ENTRY '}' 163 164 static void 165 skipwhite(FILE *f) 166 { 167 int c; 168 169 skip_again: 170 171 while (isspace(c = getc(f))) 172 ; 173 174 if (c == '#') { 175 while ((c = getc(f)) != '\n' && c != EOF) 176 ; 177 goto skip_again; 178 } 179 180 ungetc(c, f); 181 } 182 183 /* mode_lookup: Lookup a format description for a given page. 184 */ 185 char *mode_db = "/usr/share/misc/scsi_modes"; 186 static char * 187 mode_lookup(int page) 188 { 189 char *new_db; 190 FILE *modes; 191 int match, next, found, c; 192 static char fmt[4096]; /* XXX This should be with strealloc */ 193 int page_desc; 194 new_db = getenv("SCSI_MODES"); 195 196 if (new_db) 197 mode_db = new_db; 198 199 modes = fopen(mode_db, "r"); 200 if (modes == 0) 201 return 0; 202 203 next = 0; 204 found = 0; 205 206 while (!found) { 207 208 skipwhite(modes); 209 210 if (fscanf(modes, "%i", &page_desc) != 1) 211 break; 212 213 if (page_desc == page) 214 found = 1; 215 216 skipwhite(modes); 217 if (getc(modes) != START_ENTRY) 218 errx(1, "expected %c", START_ENTRY); 219 220 match = 1; 221 while (match != 0) { 222 c = getc(modes); 223 if (c == EOF) { 224 warnx("expected %c", END_ENTRY); 225 } 226 227 if (c == START_ENTRY) { 228 match++; 229 } 230 if (c == END_ENTRY) { 231 match--; 232 if (match == 0) 233 break; 234 } 235 if (found && c != '\n') { 236 if (next >= sizeof(fmt)) 237 errx(1, "buffer overflow"); 238 239 fmt[next++] = (u_char)c; 240 } 241 } 242 } 243 fmt[next] = 0; 244 245 return (found) ? fmt : 0; 246 } 247 248 /* -------- edit: Mode Select Editor --------- 249 */ 250 struct editinfo 251 { 252 int can_edit; 253 int default_value; 254 } editinfo[64]; /* XXX Bogus fixed size */ 255 256 static int editind; 257 volatile int edit_opened; 258 static FILE *edit_file; 259 static char edit_name[L_tmpnam]; 260 261 static inline void 262 edit_rewind(void) 263 { 264 editind = 0; 265 } 266 267 static void 268 edit_done(void) 269 { 270 int opened; 271 272 sigset_t all, prev; 273 sigfillset(&all); 274 275 (void)sigprocmask(SIG_SETMASK, &all, &prev); 276 277 opened = (int)edit_opened; 278 edit_opened = 0; 279 280 (void)sigprocmask(SIG_SETMASK, &prev, 0); 281 282 if (opened) 283 { 284 if (fclose(edit_file)) 285 warn("%s", edit_name); 286 if (unlink(edit_name)) 287 warn("%s", edit_name); 288 } 289 } 290 291 static void 292 edit_init(void) 293 { 294 int fd; 295 296 edit_rewind(); 297 strlcpy(edit_name, "/tmp/camXXXXXX", sizeof(edit_name)); 298 if ((fd = mkstemp(edit_name)) == -1) 299 errx(1, "mkstemp failed"); 300 if ((edit_file = fdopen(fd, "w")) == 0) 301 err(1, "%s", edit_name); 302 edit_opened = 1; 303 304 atexit(edit_done); 305 } 306 307 static void 308 edit_check(void *hook, int letter, void *arg, int count, char *name) 309 { 310 if (letter != 'i' && letter != 'b') 311 errx(1, "can't edit format %c", letter); 312 313 if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) 314 errx(1, "edit table overflow"); 315 316 editinfo[editind].can_edit = (arg != NULL); 317 editind++; 318 } 319 320 static void 321 edit_defaults(void *hook, int letter, void *arg, int count, char *name) 322 { 323 if (letter != 'i' && letter != 'b') 324 errx(1, "can't edit format %c", letter); 325 326 editinfo[editind].default_value = (intptr_t)arg; /* truncated */ 327 editind++; 328 } 329 330 static void 331 edit_report(void *hook, int letter, void *arg, int count, char *name) 332 { 333 if (editinfo[editind].can_edit) { 334 if (letter != 'i' && letter != 'b') 335 errx(1, "can't report format %c", letter); 336 337 fprintf(edit_file, "%s: %d\n", name, (intptr_t)arg); 338 } 339 340 editind++; 341 } 342 343 static int 344 edit_get(void *hook, char *name) 345 { 346 int arg = editinfo[editind].default_value; 347 348 if (editinfo[editind].can_edit) { 349 char line[80]; 350 if (fgets(line, sizeof(line), edit_file) == 0) 351 err(1, "fgets"); 352 353 line[strlen(line) - 1] = 0; 354 355 if (strncmp(name, line, strlen(name)) != 0) 356 errx(1, "expected \"%s\" and read \"%s\"", name, line); 357 358 arg = strtoul(line + strlen(name) + 2, 0, 0); 359 } 360 361 editind++; 362 return arg; 363 } 364 365 static void 366 edit_edit(void) 367 { 368 char *system_line; 369 char *editor = getenv("EDITOR"); 370 if (!editor) 371 editor = "vi"; 372 373 fclose(edit_file); 374 375 system_line = malloc(strlen(editor) + strlen(edit_name) + 6); 376 sprintf(system_line, "%s %s", editor, edit_name); 377 system(system_line); 378 free(system_line); 379 380 if ((edit_file = fopen(edit_name, "r")) == 0) 381 err(1, "%s", edit_name); 382 } 383 384 void 385 mode_edit(struct cam_device *device, int page, int page_control, int dbd, 386 int edit, int retry_count, int timeout) 387 { 388 int i; 389 u_char data[255]; 390 u_char *mode_pars; 391 struct mode_header 392 { 393 u_char mdl; /* Mode data length */ 394 u_char medium_type; 395 u_char dev_spec_par; 396 u_char bdl; /* Block descriptor length */ 397 }; 398 399 struct mode_page_header 400 { 401 u_char page_code; 402 u_char page_length; 403 }; 404 405 struct mode_header *mh; 406 struct mode_page_header *mph; 407 408 char *fmt = mode_lookup(page); 409 if (!fmt && verbose) { 410 fprintf(stderr, 411 "No mode data base entry in \"%s\" for page %d; " 412 " binary %s only.\n", 413 mode_db, page, (edit ? "edit" : "display")); 414 } 415 416 if (edit) { 417 if (!fmt) 418 errx(1, "can't edit without a format"); 419 420 if (page_control != 0 && page_control != 3) 421 errx(1, "it only makes sense to edit page 0 " 422 "(current) or page 3 (saved values)"); 423 424 verbose = 1; 425 426 mode_sense(device, page, 1, dbd, retry_count, timeout, 427 data, sizeof(data)); 428 429 mh = (struct mode_header *)data; 430 mph = (struct mode_page_header *) 431 (((char *)mh) + sizeof(*mh) + mh->bdl); 432 433 mode_pars = (char *)mph + sizeof(*mph); 434 435 edit_init(); 436 buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0); 437 438 mode_sense(device, page, 0, dbd, retry_count, timeout, 439 data, sizeof(data)); 440 441 edit_rewind(); 442 buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0); 443 444 edit_rewind(); 445 buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0); 446 447 edit_edit(); 448 449 edit_rewind(); 450 buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0); 451 452 /* Eliminate block descriptors: 453 */ 454 bcopy((char *)mph, ((char *)mh) + sizeof(*mh), 455 sizeof(*mph) + mph->page_length); 456 457 mh->bdl = mh->dev_spec_par = 0; 458 mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh)); 459 mode_pars = ((char *)mph) + 2; 460 461 #if 0 462 /* Turn this on to see what you're sending to the 463 * device: 464 */ 465 edit_rewind(); 466 buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0); 467 #endif 468 469 edit_done(); 470 471 /* Make it permanent if pageselect is three. 472 */ 473 474 mph->page_code &= ~0xC0; /* Clear PS and RESERVED */ 475 mh->mdl = 0; /* Reserved for mode select */ 476 477 mode_select(device, (page_control == 3), retry_count, 478 timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl + 479 sizeof(*mph) + mph->page_length); 480 481 return; 482 } 483 484 mode_sense(device, page, page_control, dbd, retry_count, timeout, 485 data, sizeof(data)); 486 487 /* Skip over the block descriptors. 488 */ 489 mh = (struct mode_header *)data; 490 mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl); 491 mode_pars = (char *)mph + sizeof(*mph); 492 493 if (!fmt) { 494 for (i = 0; i < mh->mdl; i++) { 495 printf("%02x%c",mode_pars[i], 496 (((i + 1) % 8) == 0) ? '\n' : ' '); 497 } 498 putc('\n', stdout); 499 } else { 500 verbose = 1; 501 buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL); 502 } 503 } 504