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