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