1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include "sg_lib.h"
11 #include "sg_io_linux.h"
12
13 /* This is a simple program that tests the O_EXCL flag in sg while
14 executing a SCSI INQUIRY command and a
15 TEST UNIT READY command using the SCSI generic (sg) driver
16
17 * Copyright (C) 2003-2013 D. Gilbert
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2, or (at your option)
21 * any later version.
22
23 Invocation: sg_excl [-x] <sg_device>
24
25 Version 3.58 (20130731)
26
27 6 byte INQUIRY command:
28 [0x12][ |lu][pg cde][res ][al len][cntrl ]
29
30 6 byte TEST UNIT READY command:
31 [0x00][ |lu][res ][res ][res ][res ]
32
33 */
34
35 #define INQ_REPLY_LEN 96
36 #define INQ_CMD_LEN 6
37 #define TUR_CMD_LEN 6
38
39 #define EBUFF_SZ 256
40
41 #define ME "sg_excl: "
42
main(int argc,char * argv[])43 int main(int argc, char * argv[])
44 {
45 int sg_fd, k, ok /*, sg_fd2 */;
46 unsigned char inqCmdBlk [INQ_CMD_LEN] =
47 {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
48 unsigned char turCmdBlk [TUR_CMD_LEN] =
49 {0x00, 0, 0, 0, 0, 0};
50 unsigned char inqBuff[INQ_REPLY_LEN];
51 sg_io_hdr_t io_hdr;
52 char * file_name = 0;
53 char ebuff[EBUFF_SZ];
54 unsigned char sense_buffer[32];
55 int do_extra = 0;
56
57 for (k = 1; k < argc; ++k) {
58 if (0 == memcmp("-x", argv[k], 2))
59 do_extra = 1;
60 else if (*argv[k] == '-') {
61 printf("Unrecognized switch: %s\n", argv[k]);
62 file_name = 0;
63 break;
64 }
65 else if (0 == file_name)
66 file_name = argv[k];
67 else {
68 printf("too many arguments\n");
69 file_name = 0;
70 break;
71 }
72 }
73 if (0 == file_name) {
74 printf("Usage: 'sg_excl [-x] <sg_device>'\n");
75 return 1;
76 }
77
78 /* N.B. An access mode of O_RDWR is required for some SCSI commands */
79 if ((sg_fd = open(file_name, O_RDWR | O_EXCL | O_NONBLOCK)) < 0) {
80 snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name);
81 perror(ebuff);
82 return 1;
83 }
84 /* Just to be safe, check we have a new sg device by trying an ioctl */
85 if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
86 printf(ME "%s doesn't seem to be an new sg device\n",
87 file_name);
88 close(sg_fd);
89 return 1;
90 }
91 #if 0
92 if ((sg_fd2 = open(file_name, O_RDWR | O_EXCL)) < 0) {
93 snprintf(ebuff, EBUFF_SZ,
94 ME "error opening file: %s a second time", file_name);
95 perror(ebuff);
96 return 1;
97 } else {
98 printf(ME "second open of %s in violation of O_EXCL\n", file_name);
99 close(sg_fd2);
100 }
101 #endif
102
103 /* Prepare INQUIRY command */
104 memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
105 io_hdr.interface_id = 'S';
106 io_hdr.cmd_len = sizeof(inqCmdBlk);
107 /* io_hdr.iovec_count = 0; */ /* memset takes care of this */
108 io_hdr.mx_sb_len = sizeof(sense_buffer);
109 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
110 io_hdr.dxfer_len = INQ_REPLY_LEN;
111 io_hdr.dxferp = inqBuff;
112 io_hdr.cmdp = inqCmdBlk;
113 io_hdr.sbp = sense_buffer;
114 io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
115 /* io_hdr.flags = 0; */ /* take defaults: indirect IO, etc */
116 /* io_hdr.pack_id = 0; */
117 /* io_hdr.usr_ptr = NULL; */
118
119 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
120 perror(ME "Inquiry SG_IO ioctl error");
121 close(sg_fd);
122 return 1;
123 }
124
125 /* now for the error processing */
126 ok = 0;
127 switch (sg_err_category3(&io_hdr)) {
128 case SG_LIB_CAT_CLEAN:
129 ok = 1;
130 break;
131 case SG_LIB_CAT_RECOVERED:
132 printf("Recovered error on INQUIRY, continuing\n");
133 ok = 1;
134 break;
135 default: /* won't bother decoding other categories */
136 sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
137 break;
138 }
139
140 if (ok) { /* output result if it is available */
141 char * p = (char *)inqBuff;
142 int f = (int)*(p + 7);
143 printf("Some of the INQUIRY command's results:\n");
144 printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32);
145 printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
146 !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
147 /* Extra info, not necessary to look at */
148 if (do_extra)
149 printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
150 io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
151 }
152
153
154 /* Prepare TEST UNIT READY command */
155 memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
156 io_hdr.interface_id = 'S';
157 io_hdr.cmd_len = sizeof(turCmdBlk);
158 io_hdr.mx_sb_len = sizeof(sense_buffer);
159 io_hdr.dxfer_direction = SG_DXFER_NONE;
160 io_hdr.cmdp = turCmdBlk;
161 io_hdr.sbp = sense_buffer;
162 io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
163
164 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
165 perror(ME "Test Unit Ready SG_IO ioctl error");
166 close(sg_fd);
167 return 1;
168 }
169
170 /* now for the error processing */
171 ok = 0;
172 switch (sg_err_category3(&io_hdr)) {
173 case SG_LIB_CAT_CLEAN:
174 ok = 1;
175 break;
176 case SG_LIB_CAT_RECOVERED:
177 printf("Recovered error on Test Unit Ready, continuing\n");
178 ok = 1;
179 break;
180 default: /* won't bother decoding other categories */
181 sg_chk_n_print3("Test Unit Ready command error", &io_hdr, 1);
182 break;
183 }
184
185 if (ok)
186 printf("Test Unit Ready successful so unit is ready!\n");
187 else
188 printf("Test Unit Ready failed so unit may _not_ be ready!\n");
189
190 if (do_extra)
191 printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
192 "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
193 (int)io_hdr.msg_status);
194
195 sleep(60);
196 close(sg_fd);
197 return 0;
198 }
199