1 /* $OpenBSD: scsi.c,v 1.25 2007/10/18 00:30:32 ray Exp $ */ 2 /* $FreeBSD: scsi.c,v 1.11 1996/04/06 11:00:28 joerg Exp $ */ 3 4 /* 5 * Written By Julian ELischer 6 * Copyright julian Elischer 1993. 7 * Permission is granted to use or redistribute this file in any way as long 8 * as this notice remains. Julian Elischer does not guarantee that this file 9 * is totally correct for any given task and users of this file must 10 * accept responsibility for any damage that occurs from the application of this 11 * file. 12 * 13 * (julian@tfs.com julian@dialix.oz.au) 14 * 15 * User SCSI hooks added by Peter Dufault: 16 * 17 * Copyright (c) 1994 HD Associates 18 * (contact: dufault@hda.com) 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 3. The name of HD Associates 30 * may not be used to endorse or promote products derived from this software 31 * without specific prior written permission. 32 * 33 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND 34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE 37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 * SUCH DAMAGE. 44 */ 45 46 #include <sys/types.h> 47 #include <sys/wait.h> 48 49 #include <stdio.h> 50 #include <string.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <errno.h> 54 #include <sys/scsiio.h> 55 #include <sys/file.h> 56 #include <ctype.h> 57 #include <signal.h> 58 #include <err.h> 59 #include <paths.h> 60 61 #include "libscsi.h" 62 63 int fd; 64 int debuglevel; 65 int debugflag; 66 int commandflag; 67 int verbose = 0; 68 69 int modeflag; 70 int editflag; 71 int modepage = 0; /* Read this mode page */ 72 int pagectl = 0; /* Mode sense page control */ 73 int seconds = 2; 74 75 void procargs(int *argc_p, char ***argv_p); 76 int iget(void *hook, char *name); 77 char *cget(void *hook, char *name); 78 void arg_put(void *hook, int letter, void *arg, int count, char *name); 79 void mode_sense(int fd, u_char *data, int len, int pc, int page); 80 void mode_select(int fd, u_char *data, int len, int perm); 81 82 static void 83 usage(void) 84 { 85 fprintf(stderr, 86 "Usage:\n" 87 "\n" 88 " scsi -f device -d debug_level # To set debug level\n" 89 " scsi -f device -m page [-P pc] # To read mode pages\n" 90 " scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command...\n" 91 " -o count out_fmt [arg0 ... argn] # EITHER (data out)\n" 92 " -i count in_fmt # OR (data in)\n" 93 "\n" 94 "\"out_fmt\" can be \"-\" to read output data from stdin;\n" 95 "\"in_fmt\" can be \"-\" to write input data to stdout;\n" 96 "\n" 97 "If debugging is not compiled in the kernel, \"-d\" will have no effect\n" 98 99 ); 100 101 exit (1); 102 } 103 104 void 105 procargs(int *argc_p, char ***argv_p) 106 { 107 int argc = *argc_p; 108 char **argv = *argv_p; 109 int fflag, ch; 110 111 fflag = 0; 112 commandflag = 0; 113 debugflag = 0; 114 while ((ch = getopt(argc, argv, "cef:d:m:P:s:v")) != -1) { 115 switch (ch) { 116 case 'c': 117 commandflag = 1; 118 break; 119 case 'e': 120 editflag = 1; 121 break; 122 case 'f': 123 if ((fd = scsi_open(optarg, O_RDWR)) < 0) 124 err(errno, "unable to open device %s", optarg); 125 fflag = 1; 126 break; 127 case 'd': 128 debuglevel = strtol(optarg, 0, 0); 129 debugflag = 1; 130 break; 131 case 'm': 132 modeflag = 1; 133 modepage = strtol(optarg, 0, 0); 134 break; 135 case 'P': 136 pagectl = strtol(optarg, 0, 0); 137 break; 138 case 's': 139 seconds = strtol(optarg, 0, 0); 140 break; 141 case 'v': 142 verbose = 1; 143 break; 144 case '?': 145 default: 146 usage(); 147 } 148 } 149 *argc_p = argc - optind; 150 *argv_p = argv + optind; 151 152 if (!fflag) usage(); 153 } 154 155 /* get_hook: Structure for evaluating args in a callback. 156 */ 157 struct get_hook 158 { 159 int argc; 160 char **argv; 161 int got; 162 }; 163 164 /* iget: Integer argument callback 165 */ 166 int 167 iget(void *hook, char *name) 168 { 169 struct get_hook *h = (struct get_hook *)hook; 170 int arg; 171 172 if (h->got >= h->argc) 173 { 174 fprintf(stderr, "Expecting an integer argument.\n"); 175 usage(); 176 } 177 arg = strtol(h->argv[h->got], 0, 0); 178 h->got++; 179 180 if (verbose && name && *name) 181 printf("%s: %d\n", name, arg); 182 183 return arg; 184 } 185 186 /* cget: char * argument callback 187 */ 188 char * 189 cget(void *hook, char *name) 190 { 191 struct get_hook *h = (struct get_hook *)hook; 192 char *arg; 193 194 if (h->got >= h->argc) 195 { 196 fprintf(stderr, "Expecting a character pointer argument.\n"); 197 usage(); 198 } 199 arg = h->argv[h->got]; 200 h->got++; 201 202 if (verbose && name) 203 printf("cget: %s: %s", name, arg); 204 205 return arg; 206 } 207 208 /* arg_put: "put argument" callback 209 */ 210 void arg_put(void *hook, int letter, void *arg, int count, char *name) 211 { 212 if (verbose && name && *name) 213 printf("%s: ", name); 214 215 switch(letter) 216 { 217 case 'i': 218 case 'b': 219 printf("%ld ", (long)arg); 220 break; 221 222 case 'c': 223 case 'z': 224 { 225 char *p = malloc(count + 1); 226 p[count] = 0; 227 strncpy(p, (char *)arg, count); 228 if (letter == 'z') 229 { 230 int i; 231 for (i = count - 1; i >= 0; i--) 232 if (p[i] == ' ') 233 p[i] = 0; 234 else 235 break; 236 } 237 printf("%s ", p); 238 } 239 240 break; 241 242 default: 243 printf("Unknown format letter: '%c'\n", letter); 244 } 245 if (verbose) 246 putchar('\n'); 247 } 248 249 /* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer. 250 */ 251 enum data_phase {none = 0, in, out}; 252 253 /* do_cmd: Send a command to a SCSI device 254 */ 255 static void 256 do_cmd(int fd, char *fmt, int argc, char **argv) 257 { 258 struct get_hook h; 259 scsireq_t *scsireq = scsireq_new(); 260 enum data_phase data_phase; 261 int count, amount; 262 char *data_fmt, *bp; 263 264 h.argc = argc; 265 h.argv = argv; 266 h.got = 0; 267 268 scsireq_reset(scsireq); 269 270 scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h); 271 272 /* Three choices here: 273 * 1. We've used up all the args and have no data phase. 274 * 2. We have input data ("-i") 275 * 3. We have output data ("-o") 276 */ 277 278 if (h.got >= h.argc) 279 { 280 data_phase = none; 281 count = scsireq->datalen = 0; 282 } 283 else 284 { 285 char *flag = cget(&h, 0); 286 287 if (strcmp(flag, "-o") == 0) 288 { 289 data_phase = out; 290 scsireq->flags = SCCMD_WRITE; 291 } 292 else if (strcmp(flag, "-i") == 0) 293 { 294 data_phase = in; 295 scsireq->flags = SCCMD_READ; 296 } 297 else 298 { 299 fprintf(stderr, 300 "Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag); 301 usage(); 302 } 303 304 count = scsireq->datalen = iget(&h, 0); 305 if (count) { 306 data_fmt = cget(&h, 0); 307 308 scsireq->databuf = malloc(count); 309 310 if (data_phase == out) { 311 if (strcmp(data_fmt, "-") == 0) { 312 bp = (char *)scsireq->databuf; 313 while (count > 0 && 314 (amount = read(STDIN_FILENO, 315 bp, count)) > 0) { 316 count -= amount; 317 bp += amount; 318 } 319 if (amount == -1) 320 err(errno, "read"); 321 else if (amount == 0) { 322 /* early EOF */ 323 fprintf(stderr, 324 "Warning: only read %lu bytes out of %lu.\n", 325 scsireq->datalen - (u_long)count, 326 scsireq->datalen); 327 scsireq->datalen -= (u_long)count; 328 } 329 } 330 else 331 { 332 bzero(scsireq->databuf, count); 333 scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h); 334 } 335 } 336 } 337 } 338 339 340 scsireq->timeout = seconds * 1000; 341 342 if (scsireq_enter(fd, scsireq) == -1) 343 { 344 scsi_debug(stderr, -1, scsireq); 345 exit(errno); 346 } 347 348 if (SCSIREQ_ERROR(scsireq)) 349 scsi_debug(stderr, 0, scsireq); 350 351 if (count && data_phase == in) 352 { 353 if (strcmp(data_fmt, "-") == 0) /* stdout */ 354 { 355 bp = (char *)scsireq->databuf; 356 while (count > 0 && (amount = write(STDOUT_FILENO, bp, count)) > 0) 357 { 358 count -= amount; 359 bp += amount; 360 } 361 if (amount < 0) 362 err(errno, "write"); 363 else if (amount == 0) 364 fprintf(stderr, "Warning: wrote only %lu bytes out of %lu.\n", 365 scsireq->datalen - count, 366 scsireq->datalen); 367 368 } 369 else 370 { 371 scsireq_decode_visit(scsireq, data_fmt, arg_put, 0); 372 putchar('\n'); 373 } 374 } 375 } 376 377 void mode_sense(int fd, u_char *data, int len, int pc, int page) 378 { 379 scsireq_t *scsireq; 380 381 bzero(data, len); 382 383 scsireq = scsireq_new(); 384 385 if (scsireq_enter(fd, scsireq_build(scsireq, 386 len, data, SCCMD_READ, 387 "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0", 388 pc, page, len)) == -1) /* Mode sense */ 389 { 390 scsi_debug(stderr, -1, scsireq); 391 exit(errno); 392 } 393 394 if (SCSIREQ_ERROR(scsireq)) 395 { 396 scsi_debug(stderr, 0, scsireq); 397 exit(1); 398 } 399 400 free(scsireq); 401 } 402 403 void mode_select(int fd, u_char *data, int len, int perm) 404 { 405 scsireq_t *scsireq; 406 407 scsireq = scsireq_new(); 408 409 if (scsireq_enter(fd, scsireq_build(scsireq, 410 len, data, SCCMD_WRITE, 411 "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1) /* Mode select */ 412 { 413 scsi_debug(stderr, -1, scsireq); 414 exit(errno); 415 } 416 417 if (SCSIREQ_ERROR(scsireq)) 418 { 419 scsi_debug(stderr, 0, scsireq); 420 exit(1); 421 } 422 423 free(scsireq); 424 } 425 426 427 #define START_ENTRY '{' 428 #define END_ENTRY '}' 429 430 static void 431 skipwhite(FILE *f) 432 { 433 int c; 434 435 skip_again: 436 437 while (isspace(c = getc(f))) 438 ; 439 440 if (c == '#') { 441 while ((c = getc(f)) != '\n' && c != EOF) 442 ; 443 goto skip_again; 444 } 445 446 ungetc(c, f); 447 } 448 449 /* mode_lookup: Lookup a format description for a given page. 450 */ 451 char *mode_db = "/usr/share/misc/scsi_modes"; 452 static char *mode_lookup(int page) 453 { 454 char *new_db; 455 FILE *modes; 456 int match, next, found, c; 457 static char fmt[1024]; /* XXX This should be with strealloc */ 458 int page_desc; 459 new_db = getenv("SCSI_MODES"); 460 461 if (new_db) 462 mode_db = new_db; 463 464 modes = fopen(mode_db, "r"); 465 if (modes == NULL) 466 return 0; 467 468 next = 0; 469 found = 0; 470 471 while (!found) { 472 473 skipwhite(modes); 474 475 if (fscanf(modes, "%i", &page_desc) != 1) 476 break; 477 478 if (page_desc == page) 479 found = 1; 480 481 skipwhite(modes); 482 if (getc(modes) != START_ENTRY) { 483 fprintf(stderr, "Expected %c.\n", START_ENTRY); 484 exit(1); 485 } 486 487 match = 1; 488 while (match != 0) { 489 c = getc(modes); 490 if (c == EOF) 491 fprintf(stderr, "Expected %c.\n", END_ENTRY); 492 493 if (c == START_ENTRY) { 494 match++; 495 } 496 if (c == END_ENTRY) { 497 match--; 498 if (match == 0) 499 break; 500 } 501 if (found && c != '\n') { 502 if (next >= sizeof(fmt)) { 503 fprintf(stderr, 504 "Stupid program: Buffer overflow.\n"); 505 exit(ENOMEM); 506 } 507 508 fmt[next++] = (u_char)c; 509 } 510 } 511 } 512 fclose(modes); 513 fmt[next] = 0; 514 515 return (found) ? fmt : 0; 516 } 517 518 /* -------- edit: Mode Select Editor --------- 519 */ 520 struct editinfo 521 { 522 long can_edit; 523 long default_value; 524 } editinfo[64]; /* XXX Bogus fixed size */ 525 526 static int editind; 527 volatile int edit_opened; 528 static FILE *edit_file; 529 static char edit_name[L_tmpnam]; 530 531 static void 532 edit_rewind(void) 533 { 534 editind = 0; 535 } 536 537 static void 538 edit_done(void) 539 { 540 int opened; 541 542 sigset_t all, prev; 543 sigfillset(&all); 544 545 (void)sigprocmask(SIG_SETMASK, &all, &prev); 546 547 opened = (int)edit_opened; 548 edit_opened = 0; 549 550 (void)sigprocmask(SIG_SETMASK, &prev, 0); 551 552 if (opened) 553 { 554 if (fclose(edit_file)) 555 perror(edit_name); 556 if (unlink(edit_name)) 557 perror(edit_name); 558 } 559 } 560 561 static void 562 edit_init(void) 563 { 564 int fd; 565 566 edit_rewind(); 567 strlcpy(edit_name, "/var/tmp/scXXXXXXXX", sizeof edit_name); 568 if ((fd = mkstemp(edit_name)) == -1) 569 err(errno, "mkstemp failed"); 570 if ( (edit_file = fdopen(fd, "w+")) == 0) 571 err(errno, "fdopen failed"); 572 edit_opened = 1; 573 574 atexit(edit_done); 575 } 576 577 static void 578 edit_check(void *hook, int letter, void *arg, int count, char *name) 579 { 580 if (letter != 'i' && letter != 'b') { 581 fprintf(stderr, "Can't edit format %c.\n", letter); 582 exit(1); 583 } 584 585 if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) { 586 fprintf(stderr, "edit table overflow\n"); 587 exit(ENOMEM); 588 } 589 editinfo[editind].can_edit = ((long)arg != 0); 590 editind++; 591 } 592 593 static void 594 edit_defaults(void *hook, int letter, void *arg, int count, char *name) 595 { 596 if (letter != 'i' && letter != 'b') { 597 fprintf(stderr, "Can't edit format %c.\n", letter); 598 exit(1); 599 } 600 601 editinfo[editind].default_value = ((long)arg); 602 editind++; 603 } 604 605 static void 606 edit_report(void *hook, int letter, void *arg, int count, char *name) 607 { 608 if (editinfo[editind].can_edit) { 609 if (letter != 'i' && letter != 'b') { 610 fprintf(stderr, "Can't report format %c.\n", letter); 611 exit(1); 612 } 613 614 fprintf(edit_file, "%s: %ld\n", name, (long)arg); 615 } 616 617 editind++; 618 } 619 620 static int 621 edit_get(void *hook, char *name) 622 { 623 int arg = editinfo[editind].default_value; 624 625 if (editinfo[editind].can_edit) { 626 char line[80]; 627 size_t len; 628 if (fgets(line, sizeof(line), edit_file) == NULL) 629 err(errno, "fgets"); 630 631 len = strlen(line); 632 if (len && line[len - 1] == '\n') 633 line[len - 1] = '\0'; 634 635 if (strncmp(name, line, strlen(name)) != 0) { 636 fprintf(stderr, "Expected \"%s\" and read \"%s\"\n", 637 name, line); 638 exit(1); 639 } 640 641 arg = strtoul(line + strlen(name) + 2, 0, 0); 642 } 643 644 editind++; 645 return arg; 646 } 647 648 int 649 editit(const char *pathname) 650 { 651 char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p; 652 sig_t sighup, sigint, sigquit; 653 pid_t pid; 654 int st; 655 656 ed = getenv("VISUAL"); 657 if (ed == NULL || ed[0] == '\0') 658 ed = getenv("EDITOR"); 659 if (ed == NULL || ed[0] == '\0') 660 ed = _PATH_VI; 661 if (asprintf(&p, "%s %s", ed, pathname) == -1) 662 return (-1); 663 argp[2] = p; 664 665 top: 666 sighup = signal(SIGHUP, SIG_IGN); 667 sigint = signal(SIGINT, SIG_IGN); 668 sigquit = signal(SIGQUIT, SIG_IGN); 669 if ((pid = fork()) == -1) { 670 int saved_errno = errno; 671 672 (void)signal(SIGHUP, sighup); 673 (void)signal(SIGINT, sigint); 674 (void)signal(SIGQUIT, sigquit); 675 if (saved_errno == EAGAIN) { 676 sleep(1); 677 goto top; 678 } 679 free(p); 680 errno = saved_errno; 681 return (-1); 682 } 683 if (pid == 0) { 684 execv(_PATH_BSHELL, argp); 685 _exit(127); 686 } 687 free(p); 688 for (;;) { 689 if (waitpid(pid, &st, 0) == -1) { 690 if (errno != EINTR) 691 return (-1); 692 } else 693 break; 694 } 695 (void)signal(SIGHUP, sighup); 696 (void)signal(SIGINT, sigint); 697 (void)signal(SIGQUIT, sigquit); 698 if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 699 errno = ECHILD; 700 return (-1); 701 } 702 return (0); 703 } 704 705 static void 706 mode_edit(int fd, int page, int edit, int argc, char *argv[]) 707 { 708 int i; 709 u_char data[255]; 710 u_char *mode_pars; 711 struct mode_header 712 { 713 u_char mdl; /* Mode data length */ 714 u_char medium_type; 715 u_char dev_spec_par; 716 u_char bdl; /* Block descriptor length */ 717 }; 718 719 struct mode_page_header 720 { 721 u_char page_code; 722 u_char page_length; 723 }; 724 725 struct mode_header *mh; 726 struct mode_page_header *mph; 727 728 char *fmt = mode_lookup(page); 729 if (!fmt && verbose) { 730 fprintf(stderr, 731 "No mode data base entry in \"%s\" for page %d; binary %s only.\n", 732 mode_db, page, (edit ? "edit" : "display")); 733 } 734 735 if (edit) { 736 if (!fmt) { 737 fprintf(stderr, "Sorry: can't edit without a format.\n"); 738 exit(1); 739 } 740 741 if (pagectl != 0 && pagectl != 3) { 742 fprintf(stderr, 743 "It only makes sense to edit page 0 (current) or page 3 (saved values)\n"); 744 exit(1); 745 } 746 747 verbose = 1; 748 749 mode_sense(fd, data, sizeof(data), 1, page); 750 751 mh = (struct mode_header *)data; 752 mph = (struct mode_page_header *) 753 (((char *)mh) + sizeof(*mh) + mh->bdl); 754 755 mode_pars = (char *)mph + sizeof(*mph); 756 757 edit_init(); 758 scsireq_buff_decode_visit(mode_pars, mh->mdl, 759 fmt, edit_check, 0); 760 761 mode_sense(fd, data, sizeof(data), 0, page); 762 763 edit_rewind(); 764 scsireq_buff_decode_visit(mode_pars, mh->mdl, 765 fmt, edit_defaults, 0); 766 767 edit_rewind(); 768 scsireq_buff_decode_visit(mode_pars, mh->mdl, 769 fmt, edit_report, 0); 770 771 fclose(edit_file); 772 if (editit(edit_name) == -1 && errno != ECHILD) 773 err(1, "edit %s", edit_name); 774 if ((edit_file = fopen(edit_name, "r")) == NULL) 775 err(1, "open %s", edit_name); 776 777 edit_rewind(); 778 scsireq_buff_encode_visit(mode_pars, mh->mdl, 779 fmt, edit_get, 0); 780 781 /* Eliminate block descriptors: 782 */ 783 bcopy((char *)mph, ((char *)mh) + sizeof(*mh), 784 sizeof(*mph) + mph->page_length); 785 786 mh->bdl = 0; 787 mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh)); 788 mode_pars = ((char *)mph) + 2; 789 790 #if 0 791 /* Turn this on to see what you're sending to the 792 * device: 793 */ 794 edit_rewind(); 795 scsireq_buff_decode_visit(mode_pars, 796 mh->mdl, fmt, arg_put, 0); 797 #endif 798 799 edit_done(); 800 801 /* Make it permanent if pageselect is three. 802 */ 803 804 mph->page_code &= ~0xC0; /* Clear PS and RESERVED */ 805 mh->mdl = 0; /* Reserved for mode select */ 806 807 mode_select(fd, (char *)mh, 808 sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length, 809 (pagectl == 3)); 810 811 exit(0); 812 } 813 814 mode_sense(fd, data, sizeof(data), pagectl, page); 815 816 /* Skip over the block descriptors. 817 */ 818 mh = (struct mode_header *)data; 819 mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl); 820 mode_pars = (char *)mph + sizeof(*mph); 821 822 if (!fmt) { 823 for (i = 0; i < mh->mdl; i++) { 824 printf("%02x%c",mode_pars[i], 825 (((i + 1) % 8) == 0) ? '\n' : ' '); 826 } 827 putc('\n', stdout); 828 } else { 829 verbose = 1; 830 scsireq_buff_decode_visit(mode_pars, 831 mh->mdl, fmt, arg_put, 0); 832 } 833 } 834 835 int 836 main(int argc, char **argv) 837 { 838 procargs(&argc,&argv); 839 840 /* XXX This has grown to the point that it should be cleaned up. 841 */ 842 if (debugflag) { 843 if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1) 844 err(errno, "SCIODEBUG"); 845 } else if (commandflag) { 846 char *fmt; 847 848 if (argc < 1) { 849 fprintf(stderr, "Need the command format string.\n"); 850 usage(); 851 } 852 853 854 fmt = argv[0]; 855 856 argc -= 1; 857 argv += 1; 858 859 do_cmd(fd, fmt, argc, argv); 860 } else if (modeflag) 861 mode_edit(fd, modepage, editflag, argc, argv); 862 863 exit(0); 864 } 865