Index: head/sbin/scsi/scsi.8 =================================================================== --- head/sbin/scsi/scsi.8 (revision 37909) +++ head/sbin/scsi/scsi.8 (revision 37910) @@ -1,320 +1,320 @@ .\" .\" Written By Julian ELischer .\" Copyright julian Elischer 1993. .\" Permission is granted to use or redistribute this file in any way as long .\" as this notice remains. Julian Elischer does not guarantee that this file .\" is totally correct for any given task and users of this file must -.\" accept responsibility for any damage that occurs from the application of this -.\" file. +.\" accept responsibility for any damage that occurs from the application +.\" of this file. .\" .\" (julian@tfs.com julian@dialix.oz.au) .\" User SCSI hooks added by Peter Dufault: .\" .\" Copyright (c) 1994 HD Associates .\" (contact: dufault@hda.com) .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of HD Associates .\" may not be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" -.\" $Id: scsi.8,v 1.16 1998/01/03 10:11:38 brian Exp $ +.\" $Id: scsi.8,v 1.17 1998/06/03 03:59:00 jkoshy Exp $ .\" .Dd October 11, 1993 .Dt SCSI 8 .Os BSD 4 .Sh NAME .Nm scsi .Nd program to assist with scsi devices .Sh SYNOPSIS .Nm scsi .Fl f Ar device .Fl d Ar debug_level .Nm scsi .Fl f Ar device .Fl z Ar seconds .Op Fl v .Nm scsi .Fl f Ar device .Fl m Ar page .Op Fl P Ar pc .Op Fl e .Nm scsi .Fl f Ar device .Fl p .Op Fl b Ar bus .Op Fl l Ar lun .Nm scsi .Fl f Ar device .Fl r .Op Fl b Ar bus .Op Fl t Ar targ .Op Fl l Ar lun .Nm scsi .Fl f Ar device .Fl c Ar cmd_fmt .Op Ar arg0 ... argn .Op Fl s Ar seconds .Op Fl v .Fl o Ar count out_fmt .Op Ar arg0 ... argn .Fl i Ar count in_fmt .Sh DESCRIPTION The .Nm scsi program is used to send commands to a scsi device. It is also a sample usage of the user level SCSI commands. .Pp The .Fl f option must be specified, and .Ar device must be the name of a functional SCSI device. If the device is a disk device, it should be the control device, .Pq for example Pa /dev/rsd0.ctl . Use the .Fl p option with the .Sq super scsi device to reprobe a bus where there are currently no valid devices attached. .Pp The .Fl d option sets the SCSI kernel debug level. The kernel must have been compiled with the .Dv SCSIDEBUG option. See .Pa /sys/scsi/scsi_debug.h to figure out what to set the kernel debug level to. .Pp The .Fl z option freezes all activity on all SCSI busses for a given number of seconds. If .Fl v is also specified then a BEL character is sent to the standard output at the start and finish of the bus freeze. This requires that the kernel be built with the .Dv SCSI_FREEZE kernel option. This kernel code is not committed yet. .Pp The .Fl m option is used to read a device mode page. The file .Pa /usr/share/misc/scsi_modes is read to look at for how to interpret the mode data. The environment variable .Ev SCSI_MODES can specify a different file to use. .Pp The .Fl P option can be used to specify a page control field. The page control fields are: .Pp .Bl -tag -width xxxx -indent offset -compact .It 0 Current Values .It 1 Changeable Values .It 2 Default Values .It 3 Saved Values .El .Pp The .Fl e option permits you to edit the fields. It will use the editor specified by your .Ev EDITOR environment variable. To store changes permanently, edit page control 3 using the .Fl P option. .Pp The .Fl p option can be used against the .Sq super scsi device .Pa /dev/ssc to probe all devices with a given SCSI lun on a given SCSI bus. The bus can be selected with the .Fl b option and the default is 0. The lun can be selected with the .Fl l option and the default is 0. .Pp The .Fl r option can be used to reprobe all SCSI devices on all SCSI busses that were present at boot-time. See .Xr scsi 4 for a description of fixed scsi devices. .Pp The .Fl c option permits you to send user level SCSI commands specified on the command line to a device. The command is sent using the .Dv SCIOCCOMMAND ioctl, so the device you are accessing must permit this ioctl. See .Xr scsi 4 for full details of which minor devices permit the ioctl, and .Xr scsi 3 for the full details on how to build up the commands and data phases using the format arguments. .Pp .Fl v turns on more verbose information. .Pp .Fl s sets the command timeout in seconds. The default is two seconds. .Pp .Fl c Ar cmd_fmt specifies the command as described in .Xr scsi 3 "." The additional arguments provide values for any variables specified in the command format. Note that the arguments to the .Fl c option are hexadecimal numbers, while all normal arguments on the command-line are subject to the common .Dq C number notation. .Pp .Fl o .Ar count .Ar out_fmt .Op Ar arg0 ... argn indicates that this is a data out command (i.e., data will be sent from the system to the device) with .Fr count bytes of data. The data out is built up using the facilities described in .Xr scsi 3 using the provided arguments to fill in any integer variables. .Ar out_fmt can be specified as a hyphen ("-") to indicate that the .Ar count bytes of data should be read from the standard input. .Pp .Fl i Ar count Ar in_fmt indicates that this is a data in command (i.e., data will be read from the device into the system) with .Ar count bytes of data read in. The information is extracted according to .Ar in_fmt using the facilities described in .Xr scsi 3 and displayed on the standard output. .Ar in_fmt can be specified as a hyphen ("-") to indicate that the .Ar count bytes of data input should be written to the standard output. .Sh EXAMPLES To verify that the device type for the disk .Pa /dev/rsd0c is 0 (direct access device): .Bd -literal -offset indent root# scsi -f /dev/rsd0c -c "12 0 0 0 40 0" -i 64 "*b3 b5" 0 .Ed .Pp To do an inquiry to .Pa /dev/rsd2c : .Bd -literal -offset indent root# scsi -f /dev/rsd2c -c "12 0 0 0 v 0" 0x40 -i 64 \e "s8 z8 z16 z4" .Ed .Pp To edit mode page 1 (the Read-Write Error Recovery Page) on .Pa /dev/rsd2c , and store it permanently on the drive: .Bd -literal -offset indent root# scsi -f /dev/rsd2c -m 1 -e -P 3 .Ed .Pp To simply re-probe the first scsi bus: .Bd -literal -offset indent root# scsi -f /dev/ssc -p .Ed .Pp .Sh ENVIRONMENT The .Ev SU_DEBUG_OUTPUT variable can be set to a file to send debugging output to that file. .Pp The .Ev SU_DEBUG_LEVEL variable can be set to a non-zero integer to increase the level of debugging. Currently this is a on or off thing; it should perhaps use the ioctl to set the debug level in the kernel and then set it back to zero at program exit. .Pp The .Ev SU_DEBUG_TRUNCATE variable can be set to an integer to limit the amount of data phase output sent to the debugging file. .Pp The .Ev EDITOR variable determines the editor to use for the mode editor. .Sh SEE ALSO .Xr scsi 3 , .Xr scsi 4 , .Xr ssc 4 .Sh BUGS .Pp -Some devices respond to an inquiry for all LUNS. This will cause them +Some devices respond to an inquiry for all LUNs. This will cause them to come on line to 8 times during reprobe to different logical units. .Pp The .Fl i option to do an inquiry went away in .Fx 2.1 . The new facilities provided by .Fl c -supercede that. +supersede that. .Pp Check your permissions carefully. .Ql scsi -f /dev/rsd0c -c "4 0 0 0 0 0 permits anyone who can open .Pa /dev/rsd0c to format the disk drive. This must be changed to at least require write access to the drive. .Sh HISTORY The .Nm scsi command appeared in 386BSD 0.1.2.4 to support the new reprobe and user SCSI commands. It first appeared in .Tn FreeBSD in .Fx 2.0.5 . Index: head/sbin/scsi/scsi.c =================================================================== --- head/sbin/scsi/scsi.c (revision 37909) +++ head/sbin/scsi/scsi.c (revision 37910) @@ -1,927 +1,934 @@ /* * Written By Julian ELischer * Copyright julian Elischer 1993. * Permission is granted to use or redistribute this file in any way as long * as this notice remains. Julian Elischer does not guarantee that this file * is totally correct for any given task and users of this file must - * accept responsibility for any damage that occurs from the application of this - * file. + * accept responsibility for any damage that occurs from the application + * of this file. * * (julian@tfs.com julian@dialix.oz.au) * * User SCSI hooks added by Peter Dufault: * * Copyright (c) 1994 HD Associates * (contact: dufault@hda.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of HD Associates * may not be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = - "$Id: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $"; + "$Id: scsi.c,v 1.18 1998/06/28 20:31:48 bde Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include int fd; int debuglevel; int debugflag; int commandflag; int reprobe; int probe_all; int verbose = 0; int bus = -1; /* all busses */ int targ = -1; /* all targs */ int lun = 0; /* just lun 0 */ int freeze = 0; /* Freeze this many seconds */ int modeflag; int editflag; int modepage = 0; /* Read this mode page */ int pagectl = 0; /* Mode sense page control */ int seconds = 2; void usage(void) { fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "usage: scsi -f device -d debug_level", " scsi -f device [-v] -z seconds", " scsi -f device -m page [-P pc] [-e]", " scsi -f device -p [-b bus] [-l lun]", " scsi -f device -r [-b bus] [-t targ] [-l lun]", " scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn]", " -o count out_fmt [arg0 ... argn]", " -i count in_fmt"); exit (1); } void procargs(int *argc_p, char ***argv_p) { int argc = *argc_p; char **argv = *argv_p; - extern char *optarg; - extern int optind; int fflag, ch; fflag = 0; commandflag = 0; debugflag = 0; while ((ch = getopt(argc, argv, "ceprvf:d:b:t:l:z:m:P:s:")) != -1) { switch (ch) { case 'p': probe_all = 1; break; case 'r': reprobe = 1; break; case 'c': commandflag = 1; break; case 'v': verbose = 1; break; case 'e': editflag = 1; break; case 'f': if ((fd = scsi_open(optarg, O_RDWR)) < 0) err(errno, "unable to open device %s", optarg); fflag = 1; break; case 'd': debuglevel = strtol(optarg, 0, 0); debugflag = 1; break; case 'b': bus = strtol(optarg, 0, 0); break; case 't': targ = strtol(optarg, 0, 0); break; case 'l': lun = strtol(optarg, 0, 0); break; case 'z': freeze = strtol(optarg, 0, 0); break; case 'P': pagectl = strtol(optarg, 0, 0); break; case 's': seconds = strtol(optarg, 0, 0); break; case 'm': modeflag = 1; modepage = strtol(optarg, 0, 0); break; case '?': default: usage(); } } *argc_p = argc - optind; *argv_p = argv + optind; if (!fflag) usage(); } /* get_hook: Structure for evaluating args in a callback. */ struct get_hook { int argc; char **argv; int got; }; /* iget: Integer argument callback */ int iget(void *hook, char *name) { struct get_hook *h = (struct get_hook *)hook; int arg; if (h->got >= h->argc) { fprintf(stderr, "Expecting an integer argument.\n"); usage(); } arg = strtol(h->argv[h->got], 0, 0); h->got++; if (verbose && name && *name) printf("%s: %d\n", name, arg); return arg; } /* cget: char * argument callback */ char *cget(void *hook, char *name) { struct get_hook *h = (struct get_hook *)hook; char *arg; if (h->got >= h->argc) { fprintf(stderr, "Expecting a character pointer argument.\n"); usage(); } arg = h->argv[h->got]; h->got++; if (verbose && name) printf("cget: %s: %s", name, arg); return arg; } /* arg_put: "put argument" callback */ void arg_put(void *hook, int letter, void *arg, int count, char *name) { if (verbose && name && *name) printf("%s: ", name); switch(letter) { case 'i': case 'b': printf("%d ", (int)arg); break; case 'c': case 'z': { char *p = malloc(count + 1); + if (p == NULL) + errx(1, "malloc failed"); p[count] = 0; strncpy(p, (char *)arg, count); if (letter == 'z') { int i; for (i = count - 1; i >= 0; i--) if (p[i] == ' ') p[i] = 0; else break; } printf("%s ", p); } break; default: printf("Unknown format letter: '%c'\n", letter); } if (verbose) putchar('\n'); } int arg_get (void *hook, char *field_name) { printf("get \"%s\".\n", field_name); return 0; } /* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer. */ enum data_phase {none = 0, in, out}; /* do_cmd: Send a command to a SCSI device */ static void do_cmd(int fd, char *fmt, int argc, char **argv) { struct get_hook h; scsireq_t *scsireq = scsireq_new(); enum data_phase data_phase; int count, amount; char *data_fmt, *bp; h.argc = argc; h.argv = argv; h.got = 0; scsireq_reset(scsireq); scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h); /* Three choices here: * 1. We've used up all the args and have no data phase. * 2. We have input data ("-i") * 3. We have output data ("-o") */ if (h.got >= h.argc) { data_phase = none; count = scsireq->datalen = 0; } else { char *flag = cget(&h, 0); if (strcmp(flag, "-o") == 0) { data_phase = out; scsireq->flags = SCCMD_WRITE; } else if (strcmp(flag, "-i") == 0) { data_phase = in; scsireq->flags = SCCMD_READ; } else { fprintf(stderr, "Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag); usage(); } count = scsireq->datalen = iget(&h, 0); if (count) { data_fmt = cget(&h, 0); scsireq->databuf = malloc(count); + if (scsireq->databuf == NULL) + errx(1, "malloc failed"); if (data_phase == out) { if (strcmp(data_fmt, "-") == 0) /* Read data from stdin */ { bp = (char *)scsireq->databuf; while (count > 0 && (amount = read(0, bp, count)) > 0) { count -= amount; bp += amount; } if (amount == -1) { err(1, "read"); } else if (amount == 0) { /* early EOF */ fprintf(stderr, "Warning: only read %lu bytes out of %lu.\n", scsireq->datalen - (u_long)count, scsireq->datalen); scsireq->datalen -= (u_long)count; } } else { bzero(scsireq->databuf, count); scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h); } } } } scsireq->timeout = seconds * 1000; if (scsireq_enter(fd, scsireq) == -1) { scsi_debug(stderr, -1, scsireq); exit(errno); } if (SCSIREQ_ERROR(scsireq)) scsi_debug(stderr, 0, scsireq); if (count && data_phase == in) { if (strcmp(data_fmt, "-") == 0) /* stdout */ { bp = (char *)scsireq->databuf; while (count > 0 && (amount = write(1, bp, count)) > 0) { count -= amount; bp += amount; } if (amount < 0) { err(1, "write"); } else if (amount == 0) fprintf(stderr, "Warning: wrote only %lu bytes out of %lu.\n", (u_long)scsireq->datalen - count, (u_long)scsireq->datalen); } else { scsireq_decode_visit(scsireq, data_fmt, arg_put, 0); putchar('\n'); } } } static void freeze_ioctl(int fd, int op, void *data) { if (ioctl(fd, SCIOCFREEZE, 0) == -1) { if (errno == ENODEV) errx(1, "your kernel must be configured with option SCSI_FREEZE"); else err(1, "ioctl [SCIOCFREEZE]"); } } /* do_freeze: Freeze the bus for a given number of seconds. */ static void do_freeze(int seconds) { if (seconds == -1) { printf("Hit return to thaw: "); fflush(stdout); sync(); freeze_ioctl(fd, SCIOCFREEZE, 0); (void)getchar(); freeze_ioctl(fd, SCIOCTHAW, 0); } else { sync(); freeze_ioctl(fd, SCIOCFREEZETHAW, &seconds); if (verbose) { putchar('\007'); fflush(stdout); } freeze_ioctl(fd, SCIOCWAITTHAW, 0); if (verbose) { putchar('\007'); fflush(stdout); } } } void mode_sense(int fd, u_char *data, int len, int pc, int page) { scsireq_t *scsireq; bzero(data, len); scsireq = scsireq_new(); if (scsireq_enter(fd, scsireq_build(scsireq, len, data, SCCMD_READ, "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0", pc, page, len)) == -1) /* Mode sense */ { scsi_debug(stderr, -1, scsireq); exit(errno); } if (SCSIREQ_ERROR(scsireq)) { scsi_debug(stderr, 0, scsireq); exit(1); } free(scsireq); } void mode_select(int fd, u_char *data, int len, int perm) { scsireq_t *scsireq; scsireq = scsireq_new(); if (scsireq_enter(fd, scsireq_build(scsireq, len, data, SCCMD_WRITE, "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1) /* Mode select */ { scsi_debug(stderr, -1, scsireq); exit(errno); } if (SCSIREQ_ERROR(scsireq)) { scsi_debug(stderr, 0, scsireq); exit(1); } free(scsireq); } #define START_ENTRY '{' #define END_ENTRY '}' static void skipwhite(FILE *f) { int c; skip_again: while (isspace(c = getc(f))) ; if (c == '#') { while ((c = getc(f)) != '\n' && c != EOF) ; goto skip_again; } ungetc(c, f); } /* mode_lookup: Lookup a format description for a given page. */ char *mode_db = "/usr/share/misc/scsi_modes"; static char *mode_lookup(int page) { char *new_db; FILE *modes; int match, next, found, c; static char fmt[1024]; /* XXX This should be with strealloc */ int page_desc; new_db = getenv("SCSI_MODES"); if (new_db) mode_db = new_db; modes = fopen(mode_db, "r"); if (modes == 0) return 0; next = 0; found = 0; while (!found) { skipwhite(modes); if (fscanf(modes, "%i", &page_desc) != 1) break; if (page_desc == page) found = 1; skipwhite(modes); if (getc(modes) != START_ENTRY) errx(1, "expected %c", START_ENTRY); match = 1; while (match != 0) { c = getc(modes); if (c == EOF) { warnx("expected %c", END_ENTRY); } if (c == START_ENTRY) { match++; } if (c == END_ENTRY) { match--; if (match == 0) break; } if (found && c != '\n') { if (next >= sizeof(fmt)) errx(1, "buffer overflow"); fmt[next++] = (u_char)c; } } } fmt[next] = 0; return (found) ? fmt : 0; } /* -------- edit: Mode Select Editor --------- */ struct editinfo { int can_edit; int default_value; } editinfo[64]; /* XXX Bogus fixed size */ static int editind; volatile int edit_opened; static FILE *edit_file; static char edit_name[L_tmpnam]; static inline void edit_rewind(void) { editind = 0; } static void edit_done(void) { int opened; sigset_t all, prev; sigfillset(&all); (void)sigprocmask(SIG_SETMASK, &all, &prev); opened = (int)edit_opened; edit_opened = 0; (void)sigprocmask(SIG_SETMASK, &prev, 0); if (opened) { if (fclose(edit_file)) warn("%s", edit_name); if (unlink(edit_name)) warn("%s", edit_name); } } static void edit_init(void) { edit_rewind(); if (tmpnam(edit_name) == 0) errx(1, "tmpnam failed"); if ((edit_file = fopen(edit_name, "w")) == 0) err(1, "%s", edit_name); edit_opened = 1; atexit(edit_done); } static void edit_check(void *hook, int letter, void *arg, int count, char *name) { if (letter != 'i' && letter != 'b') errx(1, "can't edit format %c", letter); if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) errx(1, "edit table overflow"); editinfo[editind].can_edit = ((int)arg != 0); editind++; } static void edit_defaults(void *hook, int letter, void *arg, int count, char *name) { if (letter != 'i' && letter != 'b') errx(1, "can't edit format %c", letter); editinfo[editind].default_value = ((int)arg); editind++; } static void edit_report(void *hook, int letter, void *arg, int count, char *name) { if (editinfo[editind].can_edit) { if (letter != 'i' && letter != 'b') errx(1, "can't report format %c", letter); fprintf(edit_file, "%s: %d\n", name, (int)arg); } editind++; } static int edit_get(void *hook, char *name) { int arg = editinfo[editind].default_value; if (editinfo[editind].can_edit) { char line[80]; if (fgets(line, sizeof(line), edit_file) == 0) err(1, "fgets"); line[strlen(line) - 1] = 0; if (strncmp(name, line, strlen(name)) != 0) errx(1, "expected \"%s\" and read \"%s\"", name, line); arg = strtoul(line + strlen(name) + 2, 0, 0); } editind++; return arg; } static void edit_edit(void) { char *system_line; char *editor = getenv("EDITOR"); if (!editor) editor = "vi"; fclose(edit_file); system_line = malloc(strlen(editor) + strlen(edit_name) + 6); + if (system_line == NULL) + errx(1, "malloc failed"); sprintf(system_line, "%s %s", editor, edit_name); system(system_line); free(system_line); if ((edit_file = fopen(edit_name, "r")) == 0) err(1, "%s", edit_name); } static void mode_edit(int fd, int page, int edit, int argc, char *argv[]) { int i; u_char data[255]; u_char *mode_pars; struct mode_header { u_char mdl; /* Mode data length */ u_char medium_type; u_char dev_spec_par; u_char bdl; /* Block descriptor length */ }; struct mode_page_header { u_char page_code; u_char page_length; }; struct mode_header *mh; struct mode_page_header *mph; char *fmt = mode_lookup(page); if (!fmt && verbose) { fprintf(stderr, "No mode data base entry in \"%s\" for page %d; binary %s only.\n", mode_db, page, (edit ? "edit" : "display")); } if (edit) { if (!fmt) errx(1, "can't edit without a format"); if (pagectl != 0 && pagectl != 3) errx(1, "it only makes sense to edit page 0 (current) or page 3 (saved values)"); verbose = 1; mode_sense(fd, data, sizeof(data), 1, page); mh = (struct mode_header *)data; mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh) + mh->bdl); mode_pars = (char *)mph + sizeof(*mph); edit_init(); scsireq_buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0); mode_sense(fd, data, sizeof(data), 0, page); edit_rewind(); scsireq_buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0); edit_rewind(); scsireq_buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0); edit_edit(); edit_rewind(); scsireq_buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0); /* Eliminate block descriptors: */ bcopy((char *)mph, ((char *)mh) + sizeof(*mh), sizeof(*mph) + mph->page_length); mh->bdl = mh->dev_spec_par = 0; mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh)); mode_pars = ((char *)mph) + 2; #if 0 /* Turn this on to see what you're sending to the * device: */ edit_rewind(); scsireq_buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0); #endif edit_done(); /* Make it permanent if pageselect is three. */ mph->page_code &= ~0xC0; /* Clear PS and RESERVED */ mh->mdl = 0; /* Reserved for mode select */ mode_select(fd, (char *)mh, sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length, (pagectl == 3)); exit(0); } mode_sense(fd, data, sizeof(data), pagectl, page); /* Skip over the block descriptors. */ mh = (struct mode_header *)data; mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl); mode_pars = (char *)mph + sizeof(*mph); if (!fmt) { for (i = 0; i < mh->mdl; i++) { printf("%02x%c",mode_pars[i], (((i + 1) % 8) == 0) ? '\n' : ' '); } putc('\n', stdout); } else { verbose = 1; scsireq_buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0); } } /* do_probe_all: Loop over all SCSI IDs and see if something is * there. This only does BUS 0 LUN 0. */ void do_probe_all(void) { scsireq_t *scsireq; char vendor_id[8 + 1], product_id[16 + 1], revision[4 + 1]; int id; - u_char *inq_buf = malloc(96); + u_char *inq_buf; struct scsi_addr addr; + inq_buf = malloc(96); + if (inq_buf == NULL) + errx(1, "malloc failed"); scsireq = scsireq_build(scsireq_new(), 96, inq_buf, SCCMD_READ, "12 0 0 0 v 0", 96); addr.scbus = (bus == -1) ? 0 : bus; addr.lun = lun; if (addr.scbus || addr.lun) { printf("For bus %d lun %d:\n", addr.scbus, addr.lun); } for (id = 0; id < 8; id++) { addr.target = id; printf("%d: ", id); if (ioctl(fd, SCIOCADDR, &addr) == -1) { if (errno == ENXIO) { errno = 0; printf("nothing.\n"); } else printf("SCIOCADDR: %s\n", strerror(errno)); continue; } if (scsireq_enter(fd, scsireq) == -1) { printf("scsireq_enter: %s\n", strerror(errno)); continue; } vendor_id[sizeof(vendor_id) - 1] = 0; product_id[sizeof(product_id) - 1] = 0; revision[sizeof(revision) - 1] = 0; scsireq_decode(scsireq, "s8 c8 c16 c4", vendor_id, product_id, revision); printf("%s %s %s\n", vendor_id, product_id, revision); } } void main(int argc, char **argv) { struct scsi_addr scaddr; procargs(&argc,&argv); /* XXX This has grown to the point that it should be cleaned up. */ if (freeze) { do_freeze(freeze); } else if (probe_all) { do_probe_all(); } else if(reprobe) { scaddr.scbus = bus; scaddr.target = targ; scaddr.lun = lun; if (ioctl(fd,SCIOCREPROBE,&scaddr) == -1) warn("ioctl [SCIOCREPROBE]"); } else if(debugflag) { if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1) err(1, "ioctl [SCIODEBUG]"); } else if (commandflag) { char *fmt; if (argc < 1) { warnx("need the command format string"); usage(); } fmt = argv[0]; argc -= 1; argv += 1; do_cmd(fd, fmt, argc, argv); } else if (modeflag) { mode_edit(fd, modepage, editflag, argc, argv); } exit(0); }