1 /* $OpenBSD: scsi.c,v 1.26 2012/03/24 15:39:54 jsg 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 free(p); 239 } 240 241 break; 242 243 default: 244 printf("Unknown format letter: '%c'\n", letter); 245 } 246 if (verbose) 247 putchar('\n'); 248 } 249 250 /* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer. 251 */ 252 enum data_phase {none = 0, in, out}; 253 254 /* do_cmd: Send a command to a SCSI device 255 */ 256 static void 257 do_cmd(int fd, char *fmt, int argc, char **argv) 258 { 259 struct get_hook h; 260 scsireq_t *scsireq = scsireq_new(); 261 enum data_phase data_phase; 262 int count, amount; 263 char *data_fmt, *bp; 264 265 h.argc = argc; 266 h.argv = argv; 267 h.got = 0; 268 269 scsireq_reset(scsireq); 270 271 scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h); 272 273 /* Three choices here: 274 * 1. We've used up all the args and have no data phase. 275 * 2. We have input data ("-i") 276 * 3. We have output data ("-o") 277 */ 278 279 if (h.got >= h.argc) 280 { 281 data_phase = none; 282 count = scsireq->datalen = 0; 283 } 284 else 285 { 286 char *flag = cget(&h, 0); 287 288 if (strcmp(flag, "-o") == 0) 289 { 290 data_phase = out; 291 scsireq->flags = SCCMD_WRITE; 292 } 293 else if (strcmp(flag, "-i") == 0) 294 { 295 data_phase = in; 296 scsireq->flags = SCCMD_READ; 297 } 298 else 299 { 300 fprintf(stderr, 301 "Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag); 302 usage(); 303 } 304 305 count = scsireq->datalen = iget(&h, 0); 306 if (count) { 307 data_fmt = cget(&h, 0); 308 309 scsireq->databuf = malloc(count); 310 311 if (data_phase == out) { 312 if (strcmp(data_fmt, "-") == 0) { 313 bp = (char *)scsireq->databuf; 314 while (count > 0 && 315 (amount = read(STDIN_FILENO, 316 bp, count)) > 0) { 317 count -= amount; 318 bp += amount; 319 } 320 if (amount == -1) 321 err(errno, "read"); 322 else if (amount == 0) { 323 /* early EOF */ 324 fprintf(stderr, 325 "Warning: only read %lu bytes out of %lu.\n", 326 scsireq->datalen - (u_long)count, 327 scsireq->datalen); 328 scsireq->datalen -= (u_long)count; 329 } 330 } 331 else 332 { 333 bzero(scsireq->databuf, count); 334 scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h); 335 } 336 } 337 } 338 } 339 340 341 scsireq->timeout = seconds * 1000; 342 343 if (scsireq_enter(fd, scsireq) == -1) 344 { 345 scsi_debug(stderr, -1, scsireq); 346 exit(errno); 347 } 348 349 if (SCSIREQ_ERROR(scsireq)) 350 scsi_debug(stderr, 0, scsireq); 351 352 if (count && data_phase == in) 353 { 354 if (strcmp(data_fmt, "-") == 0) /* stdout */ 355 { 356 bp = (char *)scsireq->databuf; 357 while (count > 0 && (amount = write(STDOUT_FILENO, bp, count)) > 0) 358 { 359 count -= amount; 360 bp += amount; 361 } 362 if (amount < 0) 363 err(errno, "write"); 364 else if (amount == 0) 365 fprintf(stderr, "Warning: wrote only %lu bytes out of %lu.\n", 366 scsireq->datalen - count, 367 scsireq->datalen); 368 369 } 370 else 371 { 372 scsireq_decode_visit(scsireq, data_fmt, arg_put, 0); 373 putchar('\n'); 374 } 375 } 376 } 377 378 void mode_sense(int fd, u_char *data, int len, int pc, int page) 379 { 380 scsireq_t *scsireq; 381 382 bzero(data, len); 383 384 scsireq = scsireq_new(); 385 386 if (scsireq_enter(fd, scsireq_build(scsireq, 387 len, data, SCCMD_READ, 388 "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0", 389 pc, page, len)) == -1) /* Mode sense */ 390 { 391 scsi_debug(stderr, -1, scsireq); 392 exit(errno); 393 } 394 395 if (SCSIREQ_ERROR(scsireq)) 396 { 397 scsi_debug(stderr, 0, scsireq); 398 exit(1); 399 } 400 401 free(scsireq); 402 } 403 404 void mode_select(int fd, u_char *data, int len, int perm) 405 { 406 scsireq_t *scsireq; 407 408 scsireq = scsireq_new(); 409 410 if (scsireq_enter(fd, scsireq_build(scsireq, 411 len, data, SCCMD_WRITE, 412 "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1) /* Mode select */ 413 { 414 scsi_debug(stderr, -1, scsireq); 415 exit(errno); 416 } 417 418 if (SCSIREQ_ERROR(scsireq)) 419 { 420 scsi_debug(stderr, 0, scsireq); 421 exit(1); 422 } 423 424 free(scsireq); 425 } 426 427 428 #define START_ENTRY '{' 429 #define END_ENTRY '}' 430 431 static void 432 skipwhite(FILE *f) 433 { 434 int c; 435 436 skip_again: 437 438 while (isspace(c = getc(f))) 439 ; 440 441 if (c == '#') { 442 while ((c = getc(f)) != '\n' && c != EOF) 443 ; 444 goto skip_again; 445 } 446 447 ungetc(c, f); 448 } 449 450 /* mode_lookup: Lookup a format description for a given page. 451 */ 452 char *mode_db = "/usr/share/misc/scsi_modes"; 453 static char *mode_lookup(int page) 454 { 455 char *new_db; 456 FILE *modes; 457 int match, next, found, c; 458 static char fmt[1024]; /* XXX This should be with strealloc */ 459 int page_desc; 460 new_db = getenv("SCSI_MODES"); 461 462 if (new_db) 463 mode_db = new_db; 464 465 modes = fopen(mode_db, "r"); 466 if (modes == NULL) 467 return 0; 468 469 next = 0; 470 found = 0; 471 472 while (!found) { 473 474 skipwhite(modes); 475 476 if (fscanf(modes, "%i", &page_desc) != 1) 477 break; 478 479 if (page_desc == page) 480 found = 1; 481 482 skipwhite(modes); 483 if (getc(modes) != START_ENTRY) { 484 fprintf(stderr, "Expected %c.\n", START_ENTRY); 485 exit(1); 486 } 487 488 match = 1; 489 while (match != 0) { 490 c = getc(modes); 491 if (c == EOF) 492 fprintf(stderr, "Expected %c.\n", END_ENTRY); 493 494 if (c == START_ENTRY) { 495 match++; 496 } 497 if (c == END_ENTRY) { 498 match--; 499 if (match == 0) 500 break; 501 } 502 if (found && c != '\n') { 503 if (next >= sizeof(fmt)) { 504 fprintf(stderr, 505 "Stupid program: Buffer overflow.\n"); 506 exit(ENOMEM); 507 } 508 509 fmt[next++] = (u_char)c; 510 } 511 } 512 } 513 fclose(modes); 514 fmt[next] = 0; 515 516 return (found) ? fmt : 0; 517 } 518 519 /* -------- edit: Mode Select Editor --------- 520 */ 521 struct editinfo 522 { 523 long can_edit; 524 long default_value; 525 } editinfo[64]; /* XXX Bogus fixed size */ 526 527 static int editind; 528 volatile int edit_opened; 529 static FILE *edit_file; 530 static char edit_name[L_tmpnam]; 531 532 static void 533 edit_rewind(void) 534 { 535 editind = 0; 536 } 537 538 static void 539 edit_done(void) 540 { 541 int opened; 542 543 sigset_t all, prev; 544 sigfillset(&all); 545 546 (void)sigprocmask(SIG_SETMASK, &all, &prev); 547 548 opened = (int)edit_opened; 549 edit_opened = 0; 550 551 (void)sigprocmask(SIG_SETMASK, &prev, 0); 552 553 if (opened) 554 { 555 if (fclose(edit_file)) 556 perror(edit_name); 557 if (unlink(edit_name)) 558 perror(edit_name); 559 } 560 } 561 562 static void 563 edit_init(void) 564 { 565 int fd; 566 567 edit_rewind(); 568 strlcpy(edit_name, "/var/tmp/scXXXXXXXX", sizeof edit_name); 569 if ((fd = mkstemp(edit_name)) == -1) 570 err(errno, "mkstemp failed"); 571 if ( (edit_file = fdopen(fd, "w+")) == 0) 572 err(errno, "fdopen failed"); 573 edit_opened = 1; 574 575 atexit(edit_done); 576 } 577 578 static void 579 edit_check(void *hook, int letter, void *arg, int count, char *name) 580 { 581 if (letter != 'i' && letter != 'b') { 582 fprintf(stderr, "Can't edit format %c.\n", letter); 583 exit(1); 584 } 585 586 if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) { 587 fprintf(stderr, "edit table overflow\n"); 588 exit(ENOMEM); 589 } 590 editinfo[editind].can_edit = ((long)arg != 0); 591 editind++; 592 } 593 594 static void 595 edit_defaults(void *hook, int letter, void *arg, int count, char *name) 596 { 597 if (letter != 'i' && letter != 'b') { 598 fprintf(stderr, "Can't edit format %c.\n", letter); 599 exit(1); 600 } 601 602 editinfo[editind].default_value = ((long)arg); 603 editind++; 604 } 605 606 static void 607 edit_report(void *hook, int letter, void *arg, int count, char *name) 608 { 609 if (editinfo[editind].can_edit) { 610 if (letter != 'i' && letter != 'b') { 611 fprintf(stderr, "Can't report format %c.\n", letter); 612 exit(1); 613 } 614 615 fprintf(edit_file, "%s: %ld\n", name, (long)arg); 616 } 617 618 editind++; 619 } 620 621 static int 622 edit_get(void *hook, char *name) 623 { 624 int arg = editinfo[editind].default_value; 625 626 if (editinfo[editind].can_edit) { 627 char line[80]; 628 size_t len; 629 if (fgets(line, sizeof(line), edit_file) == NULL) 630 err(errno, "fgets"); 631 632 len = strlen(line); 633 if (len && line[len - 1] == '\n') 634 line[len - 1] = '\0'; 635 636 if (strncmp(name, line, strlen(name)) != 0) { 637 fprintf(stderr, "Expected \"%s\" and read \"%s\"\n", 638 name, line); 639 exit(1); 640 } 641 642 arg = strtoul(line + strlen(name) + 2, 0, 0); 643 } 644 645 editind++; 646 return arg; 647 } 648 649 int 650 editit(const char *pathname) 651 { 652 char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p; 653 sig_t sighup, sigint, sigquit; 654 pid_t pid; 655 int st; 656 657 ed = getenv("VISUAL"); 658 if (ed == NULL || ed[0] == '\0') 659 ed = getenv("EDITOR"); 660 if (ed == NULL || ed[0] == '\0') 661 ed = _PATH_VI; 662 if (asprintf(&p, "%s %s", ed, pathname) == -1) 663 return (-1); 664 argp[2] = p; 665 666 top: 667 sighup = signal(SIGHUP, SIG_IGN); 668 sigint = signal(SIGINT, SIG_IGN); 669 sigquit = signal(SIGQUIT, SIG_IGN); 670 if ((pid = fork()) == -1) { 671 int saved_errno = errno; 672 673 (void)signal(SIGHUP, sighup); 674 (void)signal(SIGINT, sigint); 675 (void)signal(SIGQUIT, sigquit); 676 if (saved_errno == EAGAIN) { 677 sleep(1); 678 goto top; 679 } 680 free(p); 681 errno = saved_errno; 682 return (-1); 683 } 684 if (pid == 0) { 685 execv(_PATH_BSHELL, argp); 686 _exit(127); 687 } 688 free(p); 689 for (;;) { 690 if (waitpid(pid, &st, 0) == -1) { 691 if (errno != EINTR) 692 return (-1); 693 } else 694 break; 695 } 696 (void)signal(SIGHUP, sighup); 697 (void)signal(SIGINT, sigint); 698 (void)signal(SIGQUIT, sigquit); 699 if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) { 700 errno = ECHILD; 701 return (-1); 702 } 703 return (0); 704 } 705 706 static void 707 mode_edit(int fd, int page, int edit, int argc, char *argv[]) 708 { 709 int i; 710 u_char data[255]; 711 u_char *mode_pars; 712 struct mode_header 713 { 714 u_char mdl; /* Mode data length */ 715 u_char medium_type; 716 u_char dev_spec_par; 717 u_char bdl; /* Block descriptor length */ 718 }; 719 720 struct mode_page_header 721 { 722 u_char page_code; 723 u_char page_length; 724 }; 725 726 struct mode_header *mh; 727 struct mode_page_header *mph; 728 729 char *fmt = mode_lookup(page); 730 if (!fmt && verbose) { 731 fprintf(stderr, 732 "No mode data base entry in \"%s\" for page %d; binary %s only.\n", 733 mode_db, page, (edit ? "edit" : "display")); 734 } 735 736 if (edit) { 737 if (!fmt) { 738 fprintf(stderr, "Sorry: can't edit without a format.\n"); 739 exit(1); 740 } 741 742 if (pagectl != 0 && pagectl != 3) { 743 fprintf(stderr, 744 "It only makes sense to edit page 0 (current) or page 3 (saved values)\n"); 745 exit(1); 746 } 747 748 verbose = 1; 749 750 mode_sense(fd, data, sizeof(data), 1, page); 751 752 mh = (struct mode_header *)data; 753 mph = (struct mode_page_header *) 754 (((char *)mh) + sizeof(*mh) + mh->bdl); 755 756 mode_pars = (char *)mph + sizeof(*mph); 757 758 edit_init(); 759 scsireq_buff_decode_visit(mode_pars, mh->mdl, 760 fmt, edit_check, 0); 761 762 mode_sense(fd, data, sizeof(data), 0, page); 763 764 edit_rewind(); 765 scsireq_buff_decode_visit(mode_pars, mh->mdl, 766 fmt, edit_defaults, 0); 767 768 edit_rewind(); 769 scsireq_buff_decode_visit(mode_pars, mh->mdl, 770 fmt, edit_report, 0); 771 772 fclose(edit_file); 773 if (editit(edit_name) == -1 && errno != ECHILD) 774 err(1, "edit %s", edit_name); 775 if ((edit_file = fopen(edit_name, "r")) == NULL) 776 err(1, "open %s", edit_name); 777 778 edit_rewind(); 779 scsireq_buff_encode_visit(mode_pars, mh->mdl, 780 fmt, edit_get, 0); 781 782 /* Eliminate block descriptors: 783 */ 784 bcopy((char *)mph, ((char *)mh) + sizeof(*mh), 785 sizeof(*mph) + mph->page_length); 786 787 mh->bdl = 0; 788 mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh)); 789 mode_pars = ((char *)mph) + 2; 790 791 #if 0 792 /* Turn this on to see what you're sending to the 793 * device: 794 */ 795 edit_rewind(); 796 scsireq_buff_decode_visit(mode_pars, 797 mh->mdl, fmt, arg_put, 0); 798 #endif 799 800 edit_done(); 801 802 /* Make it permanent if pageselect is three. 803 */ 804 805 mph->page_code &= ~0xC0; /* Clear PS and RESERVED */ 806 mh->mdl = 0; /* Reserved for mode select */ 807 808 mode_select(fd, (char *)mh, 809 sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length, 810 (pagectl == 3)); 811 812 exit(0); 813 } 814 815 mode_sense(fd, data, sizeof(data), pagectl, page); 816 817 /* Skip over the block descriptors. 818 */ 819 mh = (struct mode_header *)data; 820 mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl); 821 mode_pars = (char *)mph + sizeof(*mph); 822 823 if (!fmt) { 824 for (i = 0; i < mh->mdl; i++) { 825 printf("%02x%c",mode_pars[i], 826 (((i + 1) % 8) == 0) ? '\n' : ' '); 827 } 828 putc('\n', stdout); 829 } else { 830 verbose = 1; 831 scsireq_buff_decode_visit(mode_pars, 832 mh->mdl, fmt, arg_put, 0); 833 } 834 } 835 836 int 837 main(int argc, char **argv) 838 { 839 procargs(&argc,&argv); 840 841 /* XXX This has grown to the point that it should be cleaned up. 842 */ 843 if (debugflag) { 844 if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1) 845 err(errno, "SCIODEBUG"); 846 } else if (commandflag) { 847 char *fmt; 848 849 if (argc < 1) { 850 fprintf(stderr, "Need the command format string.\n"); 851 usage(); 852 } 853 854 855 fmt = argv[0]; 856 857 argc -= 1; 858 argv += 1; 859 860 do_cmd(fd, fmt, argc, argv); 861 } else if (modeflag) 862 mode_edit(fd, modepage, editflag, argc, argv); 863 864 exit(0); 865 } 866