File: scsi.c

package info (click to toggle)
resmgr 1.0-2sarge2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 308 kB
  • ctags: 313
  • sloc: ansic: 3,165; sh: 556; makefile: 109
file content (168 lines) | stat: -rw-r--r-- 3,650 bytes parent folder | download
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;
}