summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils/scsi/rbscsi.c212
-rw-r--r--utils/scsi/rbscsi.h25
2 files changed, 236 insertions, 1 deletions
diff --git a/utils/scsi/rbscsi.c b/utils/scsi/rbscsi.c
index a43608a08b..8e15f1fd6a 100644
--- a/utils/scsi/rbscsi.c
+++ b/utils/scsi/rbscsi.c
@@ -18,6 +18,8 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#define _XOPEN_SOURCE 500
22#define _DEFAULT_SOURCE
21#include <stdlib.h> 23#include <stdlib.h>
22#include <stdio.h> 24#include <stdio.h>
23#include <stdarg.h> 25#include <stdarg.h>
@@ -47,6 +49,10 @@ typedef HANDLE rb_scsi_handle_t;
47#include <errno.h> 49#include <errno.h>
48#include <sys/ioctl.h> 50#include <sys/ioctl.h>
49#include <scsi/sg.h> 51#include <scsi/sg.h>
52#include <dirent.h>
53#include <linux/limits.h>
54#include <sys/types.h>
55#include <sys/stat.h>
50#define RB_SCSI_LINUX 56#define RB_SCSI_LINUX
51typedef int rb_scsi_handle_t; 57typedef int rb_scsi_handle_t;
52#else 58#else
@@ -151,7 +157,175 @@ void rb_scsi_close(rb_scsi_device_t dev)
151 free(dev); 157 free(dev);
152} 158}
153 159
154/* Windpws */ 160static int is_hctl(const char *name)
161{
162 char *end;
163 strtoul(name, &end, 0); /* h */
164 if(*end != ':')
165 return 0;
166 strtoul(end + 1, &end, 0); /* c */
167 if(*end != ':')
168 return 0;
169 strtoul(end + 1, &end, 0); /* t */
170 if(*end != ':')
171 return 0;
172 strtoul(end + 1, &end, 0); /* l */
173 return *end == 0;
174}
175
176static int _resolve_link_dev_path(char *path, size_t pathsz)
177{
178 /* make sure it is a directory */
179 struct stat st;
180 if(stat(path, &st) < 0)
181 return 0;
182 if(!S_ISDIR(st.st_mode))
183 return 0;
184 if(chdir(path) < 0)
185 return 0;
186 if(getcwd(path, pathsz) == NULL)
187 return 0;
188 return 1;
189}
190
191static int resolve_link_dev_path(char *path, size_t pathsz)
192{
193 /* change directory, ask the current path and resolve current directory */
194 char curdir[PATH_MAX];
195 if(getcwd(curdir, sizeof(curdir)) == NULL)
196 return 0;
197 int ret = _resolve_link_dev_path(path, pathsz);
198 chdir(curdir);
199 return ret;
200}
201
202static int scan_resolve_first_dev_path(char *path, size_t pathsz)
203{
204 size_t pathlen = strlen(path);
205 char *pathend = path + pathlen;
206 DIR *dir = opendir(path);
207 if(dir == NULL)
208 return 0;
209 struct dirent *d;
210 int ret = 0;
211 while((d = readdir(dir)))
212 {
213 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
214 continue;
215 /* we found the entry, if it is a symlink, resolve it, otherwise it must be a directory */
216 if(d->d_type == DT_LNK)
217 {
218 snprintf(pathend, pathsz - pathlen, "/%s", d->d_name);
219 ret = resolve_link_dev_path(path, pathsz);
220 }
221 else if(d->d_type == DT_DIR)
222 {
223 snprintf(path, pathsz, "/dev/%s", d->d_name);
224 ret = 1;
225 }
226 break;
227 }
228 closedir(dir);
229 return ret;
230}
231
232static int scan_resolve_dev_path(char *path, size_t pathsz, const char *match)
233{
234 size_t pathlen = strlen(path);
235 char *pathend = path + pathlen;
236 DIR *dir = opendir(path);
237 if(dir == NULL)
238 return 0;
239 struct dirent *d;
240 int ret = 0;
241 while((d = readdir(dir)))
242 {
243 if(strcmp(d->d_name, match))
244 continue;
245 /* we found the entry, there are two case:
246 * - directory: we need to scan it and find the first entry
247 * - symlink: we need to see where it goes and extract the basename */
248 snprintf(pathend, pathsz - pathlen, "/%s", d->d_name);
249 if(d->d_type == DT_DIR)
250 ret = scan_resolve_first_dev_path(path, pathsz);
251 else if(d->d_type == DT_LNK)
252 ret = resolve_link_dev_path(path, pathsz);
253 break;
254 }
255 closedir(dir);
256 return ret;
257}
258
259static char *read_file_or_null(const char *path)
260{
261 FILE *f = fopen(path, "r");
262 if(f == NULL)
263 return NULL;
264 char buffer[1024];
265 if(fgets(buffer, sizeof(buffer), f) == NULL)
266 {
267 fclose(f);
268 return NULL;
269 }
270 fclose(f);
271 /* the kernel appends a '\n' at the end, remove it */
272 size_t len = strlen(buffer);
273 if(len > 0 && buffer[len - 1] == '\n')
274 buffer[len - 1] = 0;
275 return strdup(buffer);
276}
277
278struct rb_scsi_devent_t *rb_scsi_list(void)
279{
280 /* list devices in /sys/bus/scsi/devices
281 * we only keep entries of the form h:c:t:l */
282#define SYS_SCSI_DEV_PATH "/sys/bus/scsi/devices"
283 DIR *dir = opendir(SYS_SCSI_DEV_PATH);
284 if(dir == NULL)
285 return NULL;
286 struct dirent *d;
287 struct rb_scsi_devent_t *dev = malloc(sizeof(struct rb_scsi_devent_t));
288 dev[0].scsi_path = NULL;
289 dev[0].block_path = NULL;
290 int nr_dev = 0;
291 while((d = readdir(dir)))
292 {
293 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
294 continue;
295 /* make sure the name is of the form h:c:t:l, we do not want targets or hosts */
296 if(!is_hctl(d->d_name))
297 continue;
298 /* we now need to scan the directory to find the block and scsi generic device path:
299 * block: there should be a 'block' entry
300 * scsi: there should be a 'scsi_generic' entry
301 * Both entries can either be a symlink to the devide, or a directory with a single entry */
302 char scsi_path[PATH_MAX];
303 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s", d->d_name);
304 if(!scan_resolve_dev_path(scsi_path, sizeof(scsi_path), "scsi_generic"))
305 continue;
306 char block_path[PATH_MAX];
307 snprintf(block_path, sizeof(block_path), SYS_SCSI_DEV_PATH "/%s", d->d_name);
308 if(!scan_resolve_dev_path(block_path, sizeof(block_path), "block"))
309 block_path[0] = 0;
310 dev = realloc(dev, (2 + nr_dev) * sizeof(struct rb_scsi_devent_t));
311 dev[nr_dev].scsi_path = strdup(scsi_path);
312 dev[nr_dev].block_path = block_path[0] == 0 ? NULL : strdup(block_path);
313 /* fill vendor/model/rev */
314 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s/vendor", d->d_name);
315 dev[nr_dev].vendor = read_file_or_null(scsi_path);
316 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s/model", d->d_name);
317 dev[nr_dev].model = read_file_or_null(scsi_path);
318 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s/rev", d->d_name);
319 dev[nr_dev].rev = read_file_or_null(scsi_path);
320
321 /* sentinel */
322 dev[++nr_dev].scsi_path = NULL;
323 dev[nr_dev].block_path = NULL;
324 }
325 closedir(dir);
326 return dev;
327}
328/* Windows */
155#elif defined(RB_SCSI_WINDOWS) 329#elif defined(RB_SCSI_WINDOWS)
156/* return either path or something allocated with malloc() */ 330/* return either path or something allocated with malloc() */
157static const char *map_to_physical_drive(const char *path, unsigned flags, void *user, 331static const char *map_to_physical_drive(const char *path, unsigned flags, void *user,
@@ -275,6 +449,14 @@ void rb_scsi_close(rb_scsi_device_t dev)
275 free(dev); 449 free(dev);
276} 450}
277 451
452struct rb_scsi_devent_t *rb_scsi_list(void)
453{
454 /* unimplemented */
455 struct rb_scsi_devent_t *dev = malloc(sizeof(struct rb_scsi_devent_t));
456 dev[0].scsi_path = NULL;
457 dev[0].block_path = NULL;
458 return dev;
459}
278/* other targets */ 460/* other targets */
279#else 461#else
280rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user, 462rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user,
@@ -297,6 +479,15 @@ void rb_scsi_close(rb_scsi_device_t dev)
297{ 479{
298 free(dev); 480 free(dev);
299} 481}
482
483struct rb_scsi_devent_t *rb_scsi_list(void)
484{
485 /* unimplemented */
486 struct rb_scsi_devent_t *dev = malloc(sizeof(struct rb_scsi_devent_t));
487 dev[0].scsi_path = NULL;
488 dev[0].block_path = NULL;
489 return dev;
490}
300#endif 491#endif
301 492
302void rb_scsi_decode_sense(rb_scsi_device_t dev, void *_sense, int sense_len) 493void rb_scsi_decode_sense(rb_scsi_device_t dev, void *_sense, int sense_len)
@@ -333,3 +524,22 @@ void rb_scsi_decode_sense(rb_scsi_device_t dev, void *_sense, int sense_len)
333 524
334#undef rb_printf 525#undef rb_printf
335} 526}
527
528void rb_scsi_free_list(struct rb_scsi_devent_t *list)
529{
530 if(list == NULL)
531 return;
532 for(struct rb_scsi_devent_t *p = list; p->scsi_path; p++)
533 {
534 free(p->scsi_path);
535 if(p->block_path)
536 free(p->block_path);
537 if(p->vendor)
538 free(p->vendor);
539 if(p->model)
540 free(p->model);
541 if(p->rev)
542 free(p->rev);
543 }
544 free(list);
545}
diff --git a/utils/scsi/rbscsi.h b/utils/scsi/rbscsi.h
index c7345a6cdf..322d94ec53 100644
--- a/utils/scsi/rbscsi.h
+++ b/utils/scsi/rbscsi.h
@@ -89,6 +89,31 @@ void rb_scsi_decode_sense(rb_scsi_device_t dev, void *sense, int sense_len);
89/* close a device */ 89/* close a device */
90void rb_scsi_close(rb_scsi_device_t dev); 90void rb_scsi_close(rb_scsi_device_t dev);
91 91
92/* SCSI device reported by rb_scsi_list() */
93struct rb_scsi_devent_t
94{
95 /* device path to the raw SCSI device, typically:
96 * - Linux: /dev/sgX
97 * - Windows: TODO
98 * This path can be used directly with scsi_rb_open(), and is guaranteed to
99 * be valid. */
100 char *scsi_path;
101 /* device path to the corresponding block device, if it exists, typically:
102 * - Linux: /dev/sdX
103 * - Windows: TODO
104 * If this path is not-NULL, then it can used directly with scsi_rb_open() */
105 char *block_path;
106 /* various information about the device, can be NULL on error */
107 char *vendor;
108 char *model;
109 char *rev;
110};
111/* try to list all SCSI devices, returns a list of devices or NULL on error
112 * the list is terminated by an entry with scsi_path=NULL */
113struct rb_scsi_devent_t *rb_scsi_list(void);
114/* free the list returned by rb_scsi_list */
115void rb_scsi_free_list(struct rb_scsi_devent_t *list);
116
92#ifdef __cplusplus 117#ifdef __cplusplus
93} 118}
94#endif 119#endif