1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
/*
* Handling of scsi devices - this is needed to find the
* scsi generic device corresponding to e.g. a CD writer
* on Linux.
*
* Copyright (C) 2002, Olaf Kirch <okir@lst.de>
*/
#include <sys/ioctl.h>
#include <scsi/scsi.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include "resmgrd.h"
typedef struct scsi_id {
u_int32_t id;
u_int32_t mask;
} scsi_id_t;
typedef struct res_scsi_name res_scsi_name_t;
struct res_scsi_name {
res_name_t base;
scsi_id_t scsi_id;
};
static res_name_t * res_scsi_parse_name(const char *);
static const char * res_scsi_print_name(res_name_t *);
static void res_scsi_free_name(res_name_t *);
static int res_scsi_match(res_name_t *, res_device_t *);
static int res_scsi_open(res_name_t *, int);
static int get_scsi_id(const char *, scsi_id_t *);
#define scsi_id_match(namep, idp) \
((namep)->scsi_id.id == ((idp)->id & ((namep)->scsi_id.mask)))
res_family_t res_family_scsi = {
"scsi",
DEV_FLAGS_SCSI, /* devices must have this flag set
* otherwise we will not allow access
* to the raw SCSI device */
res_scsi_parse_name,
res_scsi_print_name,
res_scsi_free_name,
res_scsi_match,
res_scsi_open
};
/*
* SCSI devices can be named
* scsi:/dev/foobar
* scsi:bus.target.lun
* scsi:target.lun
* scsi:bus.target.lun:/dev/foobar
* scsi:target.lun:/dev/foobar
*/
res_name_t *
res_scsi_parse_name(const char *name)
{
res_scsi_name_t *sp;
scsi_id_t scsi_id;
if (name[0] == '/') {
if (get_scsi_id(name, &scsi_id) < 0)
return 0;
} else {
unsigned int n = 0, val;
scsi_id.mask = scsi_id.id = 0;
while (n < 3 && isdigit(name[0])) {
val = strtoul(name, (char **) &name, 0);
if (*name == '.' || *name == ',')
name++;
scsi_id.mask = (scsi_id.mask << 8) | 0xFF;
scsi_id.id = (scsi_id.id << 8) | (val & 0xFF);
}
if (*name != '\0' && *name != ':')
return NULL;
}
sp = (res_scsi_name_t *) calloc(1, sizeof(*sp));
sp->base.ops = &res_family_scsi;
sp->scsi_id = scsi_id;
return (res_name_t *) sp;
}
void
res_scsi_free_name(res_name_t *np)
{
free(np);
}
const char *
res_scsi_print_name(res_name_t *np)
{
static char namebuf[64];
u_int32_t id;
id = ((res_scsi_name_t *) np)->scsi_id.id;
snprintf(namebuf, sizeof(namebuf), "scsi:%u,%u,%u",
id >> 16, (id >> 8) & 0xFF, id & 0xFF);
return namebuf;
}
int
res_scsi_match(res_name_t *np, res_device_t *dev)
{
res_scsi_name_t *sp = (res_scsi_name_t *) np;
scsi_id_t dev_id;
if (get_scsi_id(dev->name, &dev_id) < 0)
return 0;
return scsi_id_match(sp, &dev_id);
}
int
res_scsi_open(res_name_t *np, int flags)
{
res_scsi_name_t *sp = (res_scsi_name_t *) np;
scsi_id_t sg_id;
static char sg_name[64];
unsigned int num;
int fd = -1;
for (num = 0; num < 64; num++) {
snprintf(sg_name, sizeof(sg_name), "/dev/sg%u", num);
if (get_scsi_id(sg_name, &sg_id) >= 0
&& scsi_id_match(sp, &sg_id)) {
/* Open the sg device */
if ((np = res_name_parse(sg_name)) != NULL)
fd = res_name_open(np, flags);
res_name_free(np);
if (fd >= 0)
return fd;
}
}
errno = EACCES;
return -1;
}
int
get_scsi_id(const char *name, struct scsi_id *idp)
{
u_int32_t ident[2];
int fd, res, bus;
if ((fd = open(name, O_RDONLY|O_NONBLOCK)) < 0)
return -1;
res = ioctl(fd, SCSI_IOCTL_GET_IDLUN, ident);
if (res >= 0)
res = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
close(fd);
/* build the bus,target,lun triple.
* This is the format cdrecord uses */
if (res >= 0) {
idp->id = ident[0] & 0xFFFF; /* target,lun */
idp->id |= (bus & 0xFF) << 16;
idp->mask = 0xFFFFFF;
}
return res;
}
|