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 "$Id$"; 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(); 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(); 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 ", (int)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 edit_rewind(); 295 if (tmpnam(edit_name) == 0) 296 errx(1, "tmpnam failed"); 297 if ((edit_file = fopen(edit_name, "w")) == 0) 298 err(1, "%s", edit_name); 299 edit_opened = 1; 300 301 atexit(edit_done); 302 } 303 304 static void 305 edit_check(void *hook, int letter, void *arg, int count, char *name) 306 { 307 if (letter != 'i' && letter != 'b') 308 errx(1, "can't edit format %c", letter); 309 310 if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) 311 errx(1, "edit table overflow"); 312 313 editinfo[editind].can_edit = ((int)arg != 0); 314 editind++; 315 } 316 317 static void 318 edit_defaults(void *hook, int letter, void *arg, int count, char *name) 319 { 320 if (letter != 'i' && letter != 'b') 321 errx(1, "can't edit format %c", letter); 322 323 editinfo[editind].default_value = ((int)arg); 324 editind++; 325 } 326 327 static void 328 edit_report(void *hook, int letter, void *arg, int count, char *name) 329 { 330 if (editinfo[editind].can_edit) { 331 if (letter != 'i' && letter != 'b') 332 errx(1, "can't report format %c", letter); 333 334 fprintf(edit_file, "%s: %d\n", name, (int)arg); 335 } 336 337 editind++; 338 } 339 340 static int 341 edit_get(void *hook, char *name) 342 { 343 int arg = editinfo[editind].default_value; 344 345 if (editinfo[editind].can_edit) { 346 char line[80]; 347 if (fgets(line, sizeof(line), edit_file) == 0) 348 err(1, "fgets"); 349 350 line[strlen(line) - 1] = 0; 351 352 if (strncmp(name, line, strlen(name)) != 0) 353 errx(1, "expected \"%s\" and read \"%s\"", name, line); 354 355 arg = strtoul(line + strlen(name) + 2, 0, 0); 356 } 357 358 editind++; 359 return arg; 360 } 361 362 static void 363 edit_edit(void) 364 { 365 char *system_line; 366 char *editor = getenv("EDITOR"); 367 if (!editor) 368 editor = "vi"; 369 370 fclose(edit_file); 371 372 system_line = malloc(strlen(editor) + strlen(edit_name) + 6); 373 sprintf(system_line, "%s %s", editor, edit_name); 374 system(system_line); 375 free(system_line); 376 377 if ((edit_file = fopen(edit_name, "r")) == 0) 378 err(1, "%s", edit_name); 379 } 380 381 void 382 mode_edit(struct cam_device *device, int page, int page_control, int dbd, 383 int edit, int retry_count, int timeout) 384 { 385 int i; 386 u_char data[255]; 387 u_char *mode_pars; 388 struct mode_header 389 { 390 u_char mdl; /* Mode data length */ 391 u_char medium_type; 392 u_char dev_spec_par; 393 u_char bdl; /* Block descriptor length */ 394 }; 395 396 struct mode_page_header 397 { 398 u_char page_code; 399 u_char page_length; 400 }; 401 402 struct mode_header *mh; 403 struct mode_page_header *mph; 404 405 char *fmt = mode_lookup(page); 406 if (!fmt && verbose) { 407 fprintf(stderr, 408 "No mode data base entry in \"%s\" for page %d; " 409 " binary %s only.\n", 410 mode_db, page, (edit ? "edit" : "display")); 411 } 412 413 if (edit) { 414 if (!fmt) 415 errx(1, "can't edit without a format"); 416 417 if (page_control != 0 && page_control != 3) 418 errx(1, "it only makes sense to edit page 0 " 419 "(current) or page 3 (saved values)"); 420 421 verbose = 1; 422 423 mode_sense(device, page, 1, dbd, retry_count, timeout, 424 data, sizeof(data)); 425 426 mh = (struct mode_header *)data; 427 mph = (struct mode_page_header *) 428 (((char *)mh) + sizeof(*mh) + mh->bdl); 429 430 mode_pars = (char *)mph + sizeof(*mph); 431 432 edit_init(); 433 buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0); 434 435 mode_sense(device, page, 0, dbd, retry_count, timeout, 436 data, sizeof(data)); 437 438 edit_rewind(); 439 buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0); 440 441 edit_rewind(); 442 buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0); 443 444 edit_edit(); 445 446 edit_rewind(); 447 buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0); 448 449 /* Eliminate block descriptors: 450 */ 451 bcopy((char *)mph, ((char *)mh) + sizeof(*mh), 452 sizeof(*mph) + mph->page_length); 453 454 mh->bdl = mh->dev_spec_par = 0; 455 mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh)); 456 mode_pars = ((char *)mph) + 2; 457 458 #if 0 459 /* Turn this on to see what you're sending to the 460 * device: 461 */ 462 edit_rewind(); 463 buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0); 464 #endif 465 466 edit_done(); 467 468 /* Make it permanent if pageselect is three. 469 */ 470 471 mph->page_code &= ~0xC0; /* Clear PS and RESERVED */ 472 mh->mdl = 0; /* Reserved for mode select */ 473 474 mode_select(device, (page_control == 3), retry_count, 475 timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl + 476 sizeof(*mph) + mph->page_length); 477 478 return; 479 } 480 481 mode_sense(device, page, page_control, dbd, retry_count, timeout, 482 data, sizeof(data)); 483 484 /* Skip over the block descriptors. 485 */ 486 mh = (struct mode_header *)data; 487 mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl); 488 mode_pars = (char *)mph + sizeof(*mph); 489 490 if (!fmt) { 491 for (i = 0; i < mh->mdl; i++) { 492 printf("%02x%c",mode_pars[i], 493 (((i + 1) % 8) == 0) ? '\n' : ' '); 494 } 495 putc('\n', stdout); 496 } else { 497 verbose = 1; 498 buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL); 499 } 500 } 501