No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / iscsi / dist / src / lib / storage.c
blobc430c38b0a87c41aa80ec55f6d28e5cfe8f6c2e6
1 /* $NetBSD: storage.c,v 1.13 2009/06/23 05:08:22 agc Exp $ */
3 /*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Alistair Crooks (agc@netbsd.org)
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include "config.h"
33 #include <sys/types.h>
34 #include <sys/stat.h>
36 #ifdef HAVE_INTTYPES_H
37 #include <inttypes.h>
38 #endif
40 #ifdef HAVE_SIGNAL_H
41 #include <signal.h>
42 #endif
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <stdlib.h>
49 #ifdef HAVE_STRING_H
50 #include <string.h>
51 #endif
53 #include <unistd.h>
55 #include "iscsiprotocol.h"
56 #include "iscsiutil.h"
57 #include "target.h"
58 #include "device.h"
60 #include "conffile.h"
61 #include "storage.h"
63 /* let's use symbolic names for the fields in the config file */
64 enum {
65 EXTENT_NAME_COL = 0,
66 EXTENT_DEVICE_COL = 1,
67 EXTENT_SACRED_COL = 2,
68 EXTENT_LENGTH_COL = 3,
70 DEVICE_NAME_COL = 0,
71 DEVICE_RAIDLEVEL_COL = 1,
72 DEVICE_LENGTH_COL = 2,
74 TARGET_NAME_COL = 0,
75 TARGET_V1_DEVICE_COL = 1,
76 TARGET_V1_NETMASK_COL = 2,
77 TARGET_V2_FLAGS_COL = 1,
78 TARGET_V2_DEVICE_COL = 2,
79 TARGET_V2_NETMASK_COL = 3
82 #define DEFAULT_FLAGS "ro"
84 #define TERABYTES(x) (uint64_t)(1024ULL * 1024ULL * 1024ULL * 1024ULL * (x))
85 #define GIGABYTES(x) (uint64_t)(1024ULL * 1024ULL * 1024ULL * (x))
86 #define MEGABYTES(x) (uint64_t)(1024ULL * 1024ULL * (x))
87 #define KILOBYTES(x) (uint64_t)(1024ULL * (x))
89 /* find an extent by name */
90 static disc_extent_t *
91 find_extent(extv_t *extents, char *s)
93 size_t i;
95 for (i = 0 ; i < extents->c ; i++) {
96 if (strcmp(extents->v[i].extent, s) == 0) {
97 return &extents->v[i];
100 return NULL;
103 /* allocate space for a new extent */
104 static int
105 do_extent(conffile_t *cf, extv_t *extents, ent_t *ep)
107 disc_extent_t *extent;
108 struct stat st;
109 char *cp;
111 if (find_extent(extents, ep->sv.v[EXTENT_NAME_COL]) != NULL) {
112 (void) fprintf(stderr,
113 "%s:%d: Error: attempt to re-define extent `%s'\n",
114 conffile_get_name(cf),
115 conffile_get_lineno(cf),
116 ep->sv.v[EXTENT_NAME_COL]);
117 return 0;
119 ALLOC(disc_extent_t, extents->v, extents->size, extents->c, 14, 14,
120 "do_extent", exit(EXIT_FAILURE));
121 extent = &extents->v[extents->c++];
122 extent->extent = strdup(ep->sv.v[EXTENT_NAME_COL]);
123 extent->dev = strdup(ep->sv.v[EXTENT_DEVICE_COL]);
124 extent->sacred = strtoll(ep->sv.v[EXTENT_SACRED_COL],
125 NULL, 10);
126 if (strcasecmp(ep->sv.v[EXTENT_LENGTH_COL], "size") == 0) {
127 if (stat(ep->sv.v[EXTENT_DEVICE_COL], &st) == 0) {
128 extent->len = st.st_size;
130 } else {
131 extent->len = strtoll(ep->sv.v[EXTENT_LENGTH_COL], &cp, 10);
132 if (cp != NULL) {
133 switch(tolower((unsigned)*cp)) {
134 case 't':
135 extent->len = TERABYTES(extent->len);
136 break;
137 case 'g':
138 extent->len = GIGABYTES(extent->len);
139 break;
140 case 'm':
141 extent->len = MEGABYTES(extent->len);
142 break;
143 case 'k':
144 extent->len = KILOBYTES(extent->len);
145 break;
149 return 1;
152 /* find a device by name */
153 static disc_device_t *
154 find_device(devv_t *devvp, char *s)
156 size_t i;
158 for (i = 0 ; i < devvp->c ; i++) {
159 if (strcmp(devvp->v[i].dev, s) == 0) {
160 return &devvp->v[i];
163 return NULL;
166 /* return the size of the sub-device/extent */
167 static uint64_t
168 getsize(conffile_t *cf, devv_t *devvp, extv_t *extents, char *s)
170 disc_extent_t *xp;
171 disc_device_t *dp;
173 if ((xp = find_extent(extents, s)) != NULL) {
174 return xp->len;
176 if ((dp = find_device(devvp, s)) != NULL) {
177 switch (dp->xv[0].type) {
178 case DE_EXTENT:
179 return dp->xv[0].u.xp->len;
180 case DE_DEVICE:
181 return dp->xv[0].u.dp->len;
184 (void) fprintf(stderr, "%s:%d: Warning: no sub-device/extent `%s'\n",
185 conffile_get_name(cf),
186 conffile_get_lineno(cf),
188 return 0;
191 /* allocate space for a device */
192 static int
193 do_device(conffile_t *cf, devv_t *devvp, extv_t *extents, ent_t *ep)
195 disc_device_t *disk;
196 char *device;
198 device = ep->sv.v[DEVICE_NAME_COL];
199 if ((disk = find_device(devvp, device)) != NULL) {
200 (void) fprintf(stderr,
201 "%s:%d: Error: attempt to re-define device `%s'\n",
202 conffile_get_name(cf),
203 conffile_get_lineno(cf),
204 device);
205 return 0;
207 ALLOC(disc_device_t, devvp->v, devvp->size, devvp->c, 14, 14,
208 "do_device", exit(EXIT_FAILURE));
209 disk = &devvp->v[devvp->c];
210 disk->dev = strdup(device);
211 disk->raid =
212 (strncasecmp(ep->sv.v[DEVICE_RAIDLEVEL_COL], "raid", 4) == 0) ?
213 atoi(&ep->sv.v[DEVICE_RAIDLEVEL_COL][4]) : 0;
214 disk->size = ep->sv.c - 2;
215 disk->len = getsize(cf, devvp, extents, ep->sv.v[DEVICE_LENGTH_COL]);
216 NEWARRAY(disc_de_t, disk->xv, ep->sv.c - 2, "do_device",
217 exit(EXIT_FAILURE));
218 for (disk->c = 0 ; disk->c < disk->size ; disk->c++) {
219 disk->xv[disk->c].u.xp =
220 find_extent(extents, ep->sv.v[disk->c + 2]);
221 if (disk->xv[disk->c].u.xp != NULL) {
222 /* a reference to an extent */
223 if (disk->xv[disk->c].u.xp->used) {
224 (void) fprintf(stderr,
225 "%s:%d: "
226 "Error: extent `%s' has already been used\n",
227 conffile_get_name(cf),
228 conffile_get_lineno(cf),
229 ep->sv.v[disk->c + 2]);
230 return 0;
232 if (disk->xv[disk->c].u.xp->len != disk->len &&
233 disk->raid != 0) {
234 (void) fprintf(stderr,
235 "%s:%d: "
236 "Error: extent `%s' has size %" PRIu64
237 ", not %" PRIu64"\n",
238 conffile_get_name(cf),
239 conffile_get_lineno(cf),
240 ep->sv.v[disk->c + 2],
241 disk->xv[disk->c].u.xp->len,
242 disk->len);
243 return 0;
245 disk->xv[disk->c].type = DE_EXTENT;
246 disk->xv[disk->c].size = disk->xv[disk->c].u.xp->len;
247 disk->xv[disk->c].u.xp->used = 1;
248 } else if ((disk->xv[disk->c].u.dp =
249 find_device(devvp, ep->sv.v[disk->c + 2])) != NULL) {
250 /* a reference to a device */
251 if (disk->xv[disk->c].u.dp->used) {
252 (void) fprintf(stderr,
253 "%s:%d: "
254 "Error: device `%s' has already been used\n",
255 conffile_get_name(cf),
256 conffile_get_lineno(cf),
257 ep->sv.v[disk->c + 2]);
258 return 0;
260 disk->xv[disk->c].type = DE_DEVICE;
261 disk->xv[disk->c].u.dp->used = 1;
262 disk->xv[disk->c].size = disk->xv[disk->c].u.dp->len;
263 } else {
264 /* not an extent or device */
265 (void) fprintf(stderr,
266 "%s:%d: "
267 "Error: no extent or device found for `%s'\n",
268 conffile_get_name(cf),
269 conffile_get_lineno(cf),
270 ep->sv.v[disk->c + 2]);
271 return 0;
274 if (disk->raid == 1) {
275 /* check we have more than 1 device/extent */
276 if (disk->c < 2) {
277 (void) fprintf(stderr,
278 "%s:%d: Error: device `%s' is RAID1, "
279 "but has only %d sub-devices/extents\n",
280 conffile_get_name(cf),
281 conffile_get_lineno(cf),
282 disk->dev, disk->c);
283 return 0;
286 devvp->c += 1;
287 return 1;
290 /* find a target by name */
291 static disc_target_t *
292 find_target(targv_t *targs, char *s)
294 size_t i;
296 for (i = 0 ; i < targs->c ; i++) {
297 if (strcmp(targs->v[i].target, s) == 0) {
298 return &targs->v[i];
301 return NULL;
304 /* allocate space for a new target */
305 static int
306 do_target(conffile_t *cf, targv_t *targs, devv_t *devvp, extv_t *extents, ent_t *ep)
308 disc_extent_t *xp;
309 disc_device_t *dp;
310 const char *flags;
311 char tgt[256];
312 char *iqn;
313 int netmaskcol;
314 int devcol;
316 if ((iqn = strchr(ep->sv.v[TARGET_NAME_COL], '=')) == NULL) {
317 (void) strlcpy(tgt, ep->sv.v[TARGET_NAME_COL], sizeof(tgt));
318 } else {
319 (void) snprintf(tgt, sizeof(tgt), "%.*s",
320 (int)(iqn - ep->sv.v[TARGET_NAME_COL]),
321 ep->sv.v[TARGET_NAME_COL]);
322 iqn += 1;
324 if (find_target(targs, tgt) != NULL) {
325 (void) fprintf(stderr,
326 "%s:%d: Error: attempt to re-define target `%s'\n",
327 conffile_get_name(cf),
328 conffile_get_lineno(cf),
329 tgt);
330 return 0;
332 ALLOC(disc_target_t, targs->v, targs->size, targs->c, 14, 14,
333 "do_target", exit(EXIT_FAILURE));
334 if (ep->sv.c == 3) {
335 /* 3 columns in entry - old style declaration */
336 (void) fprintf(stderr,
337 "%s:%d: "
338 "Warning: old 3 field \"targets\" entry"
339 "assuming read-only target\n",
340 conffile_get_name(cf),
341 conffile_get_lineno(cf));
342 devcol = TARGET_V1_DEVICE_COL;
343 netmaskcol = TARGET_V1_NETMASK_COL;
344 flags = DEFAULT_FLAGS;
345 } else {
346 devcol = TARGET_V2_DEVICE_COL;
347 flags = ep->sv.v[TARGET_V2_FLAGS_COL];
348 netmaskcol = TARGET_V2_NETMASK_COL;
350 if (iqn != NULL) {
351 targs->v[targs->c].iqn = strdup(iqn);
353 if ((dp = find_device(devvp, ep->sv.v[devcol])) != NULL) {
354 /* we have a device */
355 targs->v[targs->c].de.type = DE_DEVICE;
356 targs->v[targs->c].de.u.dp = dp;
357 targs->v[targs->c].target = strdup(tgt);
358 targs->v[targs->c].mask = strdup(ep->sv.v[netmaskcol]);
359 if (strcmp(flags, "readonly") == 0 ||
360 strcmp(flags, "ro") == 0 || strcmp(flags, "r") == 0) {
361 targs->v[targs->c].flags |= TARGET_READONLY;
363 targs->c += 1;
364 return 1;
366 if ((xp = find_extent(extents, ep->sv.v[devcol])) != NULL) {
367 /* we have an extent */
368 targs->v[targs->c].de.type = DE_EXTENT;
369 targs->v[targs->c].de.u.xp = xp;
370 targs->v[targs->c].target = strdup(tgt);
371 targs->v[targs->c].mask = strdup(ep->sv.v[netmaskcol]);
372 if (strcmp(flags, "readonly") == 0 ||
373 strcmp(flags, "ro") == 0 || strcmp(flags, "r") == 0) {
374 targs->v[targs->c].flags |= TARGET_READONLY;
376 targs->c += 1;
377 return 1;
379 (void) fprintf(stderr,
380 "%s:%d: "
381 "Error: no device or extent found for `%s'\n",
382 conffile_get_name(cf),
383 conffile_get_lineno(cf),
384 ep->sv.v[devcol]);
385 return 0;
388 /* print an extent */
389 static void
390 pextent(disc_extent_t *ep, int indent)
392 int i;
394 for (i = 0 ; i < indent ; i++) {
395 (void) fputc('\t', stdout);
397 printf("%s:%s:%" PRIu64 ":%" PRIu64 "\n", ep->extent, ep->dev,
398 ep->sacred, ep->len);
401 static void pdevice(disc_device_t *, int);
403 /* print information about an extent or a device */
404 static void
405 pu(disc_de_t *dep, int indent)
407 switch(dep->type) {
408 case DE_EXTENT:
409 pextent(dep->u.xp, indent);
410 break;
411 case DE_DEVICE:
412 pdevice(dep->u.dp, indent);
413 break;
417 /* print information about a device */
418 static void
419 pdevice(disc_device_t *dp, int indent)
421 size_t j;
422 int i;
424 for (i = 0 ; i < indent ; i++) {
425 (void) fputc('\t', stdout);
427 printf("%s:RAID%d\n", dp->dev, dp->raid);
428 for (j = 0 ; j < dp->c ; j++) {
429 pu(&dp->xv[j], indent + 1);
433 /* print informnation about a target */
434 static void
435 ptarget(disc_target_t *tp, int indent)
437 int i;
439 for (i = 0 ; i < indent ; i++) {
440 (void) fputc('\t', stdout);
442 printf("%s:%s:%s\n", tp->target,
443 (tp->flags & TARGET_READONLY) ? "ro" : "rw", tp->mask);
444 pu(&tp->de, indent + 1);
447 /* print all information */
448 static void
449 ptargets(targv_t *targs)
451 size_t i;
453 for (i = 0 ; i < targs->c ; i++) {
454 ptarget(&targs->v[i], 0);
458 /* read a configuration file */
460 read_conf_file(const char *cf, targv_t *targs, devv_t *devs, extv_t *extents)
462 conffile_t conf;
463 ent_t e;
465 (void) memset(&conf, 0x0, sizeof(conf));
466 if (!conffile_open(&conf, cf, "r", " \t", "#")) {
467 (void) fprintf(stderr, "Error: can't open `%s'\n", cf);
468 return 0;
470 printf("Reading configuration from `%s'\n", cf);
471 (void) memset(&e, 0x0, sizeof(e));
472 while (conffile_getent(&conf, &e)) {
473 if (strncmp(e.sv.v[0], "extent", 6) == 0) {
474 do_extent(&conf, extents, &e);
475 } else if (strncmp(e.sv.v[0], "device", 6) == 0) {
476 do_device(&conf, devs, extents, &e);
477 } else if (strncmp(e.sv.v[0], "target", 6) == 0 ||
478 strncmp(e.sv.v[0], "lun", 3) == 0) {
479 do_target(&conf, targs, devs, extents, &e);
481 e.sv.c = 0;
483 ptargets(targs);
484 (void) conffile_close(&conf);
485 return 1;