852 lines
17 KiB
C
852 lines
17 KiB
C
/* $OpenBSD: scsi.c,v 1.32 2022/12/04 23:50:47 cheloha Exp $ */
|
|
/* $FreeBSD: scsi.c,v 1.11 1996/04/06 11:00:28 joerg Exp $ */
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* (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.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/scsiio.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#include <err.h>
|
|
#include <paths.h>
|
|
|
|
#include "libscsi.h"
|
|
|
|
int fd;
|
|
int debuglevel;
|
|
int debugflag;
|
|
int commandflag;
|
|
int verbose = 0;
|
|
|
|
int modeflag;
|
|
int editflag;
|
|
int modepage = 0; /* Read this mode page */
|
|
int pagectl = 0; /* Mode sense page control */
|
|
int seconds = 2;
|
|
|
|
void procargs(int *argc_p, char ***argv_p);
|
|
int iget(void *hook, char *name);
|
|
char *cget(void *hook, char *name);
|
|
void arg_put(void *hook, int letter, void *arg, int count, char *name);
|
|
void mode_sense(int fd, u_char *data, int len, int pc, int page);
|
|
void mode_select(int fd, u_char *data, int len, int perm);
|
|
int editit(const char *pathname);
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr,
|
|
"usage: scsi -f device -d debug_level\n"
|
|
" scsi -f device -m page [-e] [-P pc]\n"
|
|
" scsi -f device [-v] [-s seconds] -c cmd_fmt [arg ...]"
|
|
" -o count out_fmt\n"
|
|
" [arg ...] -i count in_fmt [arg ...]\n");
|
|
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
procargs(int *argc_p, char ***argv_p)
|
|
{
|
|
int argc = *argc_p;
|
|
char **argv = *argv_p;
|
|
int fflag, ch;
|
|
|
|
fflag = 0;
|
|
commandflag = 0;
|
|
debugflag = 0;
|
|
while ((ch = getopt(argc, argv, "cef:d:m:P:s:v")) != -1) {
|
|
switch (ch) {
|
|
case 'c':
|
|
commandflag = 1;
|
|
break;
|
|
case 'e':
|
|
editflag = 1;
|
|
break;
|
|
case 'f':
|
|
if ((fd = scsi_open(optarg, O_RDWR)) < 0)
|
|
err(1, "unable to open device %s", optarg);
|
|
fflag = 1;
|
|
break;
|
|
case 'd':
|
|
debuglevel = strtol(optarg, 0, 0);
|
|
debugflag = 1;
|
|
break;
|
|
case 'm':
|
|
modeflag = 1;
|
|
modepage = strtol(optarg, 0, 0);
|
|
break;
|
|
case 'P':
|
|
pagectl = strtol(optarg, 0, 0);
|
|
break;
|
|
case 's':
|
|
seconds = strtol(optarg, 0, 0);
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
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("%ld ", (long)arg);
|
|
break;
|
|
|
|
case 'c':
|
|
case 'z':
|
|
{
|
|
char *p = malloc(count + 1);
|
|
if (p == NULL)
|
|
err(1, NULL);
|
|
|
|
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);
|
|
free(p);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
printf("Unknown format letter: '%c'\n", letter);
|
|
}
|
|
if (verbose)
|
|
putchar('\n');
|
|
}
|
|
|
|
/* 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)
|
|
err(1, NULL);
|
|
|
|
if (data_phase == out) {
|
|
if (strcmp(data_fmt, "-") == 0) {
|
|
bp = (char *)scsireq->databuf;
|
|
while (count > 0 &&
|
|
(amount = read(STDIN_FILENO,
|
|
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(1);
|
|
}
|
|
|
|
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(STDOUT_FILENO, 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",
|
|
scsireq->datalen - count,
|
|
scsireq->datalen);
|
|
|
|
}
|
|
else
|
|
{
|
|
scsireq_decode_visit(scsireq, data_fmt, arg_put, 0);
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
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(1);
|
|
}
|
|
|
|
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(1);
|
|
}
|
|
|
|
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)))
|
|
continue;
|
|
|
|
if (c == '#') {
|
|
while ((c = getc(f)) != '\n' && c != EOF)
|
|
continue;
|
|
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 == NULL)
|
|
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)
|
|
fprintf(stderr, "Expected %c.\n", 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, "Stupid program: Buffer overflow.\n");
|
|
}
|
|
|
|
fmt[next++] = (u_char)c;
|
|
}
|
|
}
|
|
}
|
|
fclose(modes);
|
|
fmt[next] = 0;
|
|
|
|
return (found) ? fmt : 0;
|
|
}
|
|
|
|
/* -------- edit: Mode Select Editor ---------
|
|
*/
|
|
struct editinfo
|
|
{
|
|
long can_edit;
|
|
long 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 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))
|
|
perror(edit_name);
|
|
if (unlink(edit_name))
|
|
perror(edit_name);
|
|
}
|
|
}
|
|
|
|
static void
|
|
edit_init(void)
|
|
{
|
|
int fd;
|
|
|
|
edit_rewind();
|
|
strlcpy(edit_name, "/var/tmp/scXXXXXXXX", sizeof edit_name);
|
|
if ((fd = mkstemp(edit_name)) == -1)
|
|
err(1, "mkstemp");
|
|
if ( (edit_file = fdopen(fd, "w+")) == 0)
|
|
err(1, "fdopen");
|
|
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.\n", letter);
|
|
}
|
|
|
|
if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) {
|
|
errx(1, "edit table overflow");
|
|
}
|
|
editinfo[editind].can_edit = ((long)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.\n", letter);
|
|
}
|
|
|
|
editinfo[editind].default_value = ((long)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.\n", letter);
|
|
}
|
|
|
|
fprintf(edit_file, "%s: %ld\n", name, (long)arg);
|
|
}
|
|
|
|
editind++;
|
|
}
|
|
|
|
static int
|
|
edit_get(void *hook, char *name)
|
|
{
|
|
int arg = editinfo[editind].default_value;
|
|
|
|
if (editinfo[editind].can_edit) {
|
|
char line[80];
|
|
size_t len;
|
|
if (fgets(line, sizeof(line), edit_file) == NULL)
|
|
err(1, "fgets");
|
|
|
|
len = strlen(line);
|
|
if (len && line[len - 1] == '\n')
|
|
line[len - 1] = '\0';
|
|
|
|
if (strncmp(name, line, strlen(name)) != 0) {
|
|
errx(1, "Expected \"%s\" and read \"%s\"\n",
|
|
name, line);
|
|
}
|
|
|
|
arg = strtoul(line + strlen(name) + 2, 0, 0);
|
|
}
|
|
|
|
editind++;
|
|
return arg;
|
|
}
|
|
|
|
int
|
|
editit(const char *pathname)
|
|
{
|
|
char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
|
|
sig_t sighup, sigint, sigquit;
|
|
pid_t pid;
|
|
int st;
|
|
|
|
ed = getenv("VISUAL");
|
|
if (ed == NULL || ed[0] == '\0')
|
|
ed = getenv("EDITOR");
|
|
if (ed == NULL || ed[0] == '\0')
|
|
ed = _PATH_VI;
|
|
if (asprintf(&p, "%s %s", ed, pathname) == -1)
|
|
return (-1);
|
|
argp[2] = p;
|
|
|
|
top:
|
|
sighup = signal(SIGHUP, SIG_IGN);
|
|
sigint = signal(SIGINT, SIG_IGN);
|
|
sigquit = signal(SIGQUIT, SIG_IGN);
|
|
if ((pid = fork()) == -1) {
|
|
int saved_errno = errno;
|
|
|
|
(void)signal(SIGHUP, sighup);
|
|
(void)signal(SIGINT, sigint);
|
|
(void)signal(SIGQUIT, sigquit);
|
|
if (saved_errno == EAGAIN) {
|
|
sleep(1);
|
|
goto top;
|
|
}
|
|
free(p);
|
|
errno = saved_errno;
|
|
return (-1);
|
|
}
|
|
if (pid == 0) {
|
|
execv(_PATH_BSHELL, argp);
|
|
_exit(127);
|
|
}
|
|
free(p);
|
|
for (;;) {
|
|
if (waitpid(pid, &st, 0) == -1) {
|
|
if (errno != EINTR)
|
|
return (-1);
|
|
} else
|
|
break;
|
|
}
|
|
(void)signal(SIGHUP, sighup);
|
|
(void)signal(SIGINT, sigint);
|
|
(void)signal(SIGQUIT, sigquit);
|
|
if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
|
|
errno = ECHILD;
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
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, "Sorry: can't edit without a format.\n");
|
|
}
|
|
|
|
if (pagectl != 0 && pagectl != 3) {
|
|
errx(1,
|
|
"It only makes sense to edit page 0 (current) or page 3 (saved values)\n");
|
|
}
|
|
|
|
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);
|
|
|
|
fclose(edit_file);
|
|
if (editit(edit_name) == -1 && errno != ECHILD)
|
|
err(1, "edit %s", edit_name);
|
|
if ((edit_file = fopen(edit_name, "r")) == NULL)
|
|
err(1, "open %s", edit_name);
|
|
|
|
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 = 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);
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
procargs(&argc,&argv);
|
|
|
|
/* XXX This has grown to the point that it should be cleaned up.
|
|
*/
|
|
if (debugflag) {
|
|
if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1)
|
|
err(1, "SCIOCDEBUG");
|
|
} else if (commandflag) {
|
|
char *fmt;
|
|
|
|
if (argc < 1) {
|
|
fprintf(stderr, "Need the command format string.\n");
|
|
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);
|
|
}
|