Home | History | Annotate | Line # | Download | only in lib
      1 /* $NetBSD: storage.c,v 1.2 2009/06/30 02:44:52 agc Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Alistair Crooks (agc (at) netbsd.org)
      9  *
     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.
     18  *
     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.
     30  */
     31 #include "config.h"
     32 
     33 #include <sys/types.h>
     34 #include <sys/stat.h>
     35 
     36 #ifdef HAVE_INTTYPES_H
     37 #include <inttypes.h>
     38 #endif
     39 
     40 #ifdef HAVE_SIGNAL_H
     41 #include <signal.h>
     42 #endif
     43 
     44 #include <ctype.h>
     45 #include <errno.h>
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 
     49 #ifdef HAVE_STRING_H
     50 #include <string.h>
     51 #endif
     52 
     53 #include <unistd.h>
     54 
     55 #include "iscsiprotocol.h"
     56 #include "iscsiutil.h"
     57 #include "target.h"
     58 #include "device.h"
     59 
     60 #include "conffile.h"
     61 #include "storage.h"
     62 
     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,
     69 
     70 	DEVICE_NAME_COL = 0,
     71 	DEVICE_RAIDLEVEL_COL = 1,
     72 	DEVICE_LENGTH_COL = 2,
     73 
     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
     80 };
     81 
     82 #define DEFAULT_FLAGS	"ro"
     83 
     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))
     88 
     89 /* find an extent by name */
     90 static disc_extent_t *
     91 find_extent(extv_t *extents, char *s)
     92 {
     93 	size_t	i;
     94 
     95 	for (i = 0 ; i < extents->c ; i++) {
     96 		if (strcmp(extents->v[i].extent, s) == 0) {
     97 			return &extents->v[i];
     98 		}
     99 	}
    100 	return NULL;
    101 }
    102 
    103 /* allocate space for a new extent */
    104 static int
    105 do_extent(conffile_t *cf, extv_t *extents, ent_t *ep)
    106 {
    107 	disc_extent_t	*extent;
    108 	struct stat	 st;
    109 	char		*cp;
    110 
    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;
    118 	}
    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;
    129 		}
    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;
    146 			}
    147 		}
    148 	}
    149 	return 1;
    150 }
    151 
    152 /* find a device by name */
    153 static disc_device_t *
    154 find_device(devv_t *devvp, char *s)
    155 {
    156 	size_t	i;
    157 
    158 	for (i = 0 ; i < devvp->c ; i++) {
    159 		if (strcmp(devvp->v[i].dev, s) == 0) {
    160 			return &devvp->v[i];
    161 		}
    162 	}
    163 	return NULL;
    164 }
    165 
    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)
    169 {
    170 	disc_extent_t	*xp;
    171 	disc_device_t	*dp;
    172 
    173 	if ((xp = find_extent(extents, s)) != NULL) {
    174 		return xp->len;
    175 	}
    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;
    182 		}
    183 	}
    184 	(void) fprintf(stderr, "%s:%d: Warning: no sub-device/extent `%s'\n",
    185 				conffile_get_name(cf),
    186 				conffile_get_lineno(cf),
    187 				s);
    188 	return 0;
    189 }
    190 
    191 /* allocate space for a device */
    192 static int
    193 do_device(conffile_t *cf, devv_t *devvp, extv_t *extents, ent_t *ep)
    194 {
    195 	disc_device_t	*disk;
    196 	char		*device;
    197 
    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;
    206 	}
    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;
    231 			}
    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;
    244 			}
    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;
    259 			}
    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;
    272 		}
    273 	}
    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;
    284 		}
    285 	}
    286 	devvp->c += 1;
    287 	return 1;
    288 }
    289 
    290 /* find a target by name */
    291 static disc_target_t *
    292 find_target(targv_t *targs, char *s)
    293 {
    294 	size_t	i;
    295 
    296 	for (i = 0 ; i < targs->c ; i++) {
    297 		if (strcmp(targs->v[i].target, s) == 0) {
    298 			return &targs->v[i];
    299 		}
    300 	}
    301 	return NULL;
    302 }
    303 
    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)
    307 {
    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;
    315 
    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;
    323 	}
    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;
    331 	}
    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;
    349 	}
    350 	if (iqn != NULL) {
    351 		targs->v[targs->c].iqn = strdup(iqn);
    352 	}
    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;
    362 		}
    363 		targs->c += 1;
    364 		return 1;
    365 	}
    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;
    375 		}
    376 		targs->c += 1;
    377 		return 1;
    378 	}
    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;
    386 }
    387 
    388 /* print an extent */
    389 static void
    390 pextent(disc_extent_t *ep, int indent)
    391 {
    392 	int	i;
    393 
    394 	for (i = 0 ; i < indent ; i++) {
    395 		(void) fputc('\t', stdout);
    396 	}
    397 	printf("%s:%s:%" PRIu64 ":%" PRIu64 "\n", ep->extent, ep->dev,
    398 			ep->sacred, ep->len);
    399 }
    400 
    401 static void pdevice(disc_device_t *, int);
    402 
    403 /* print information about an extent or a device */
    404 static void
    405 pu(disc_de_t *dep, int indent)
    406 {
    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;
    414 	}
    415 }
    416 
    417 /* print information about a device */
    418 static void
    419 pdevice(disc_device_t *dp, int indent)
    420 {
    421 	size_t	j;
    422 	int	i;
    423 
    424 	for (i = 0 ; i < indent ; i++) {
    425 		(void) fputc('\t', stdout);
    426 	}
    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);
    430 	}
    431 }
    432 
    433 /* print informnation about a target */
    434 static void
    435 ptarget(disc_target_t *tp, int indent)
    436 {
    437 	int	i;
    438 
    439 	for (i = 0 ; i < indent ; i++) {
    440 		(void) fputc('\t', stdout);
    441 	}
    442 	printf("%s:%s:%s\n", tp->target,
    443 		(tp->flags & TARGET_READONLY) ? "ro" : "rw", tp->mask);
    444 	pu(&tp->de, indent + 1);
    445 }
    446 
    447 /* print all information */
    448 static void
    449 ptargets(targv_t *targs)
    450 {
    451 	size_t	i;
    452 
    453 	for (i = 0 ; i < targs->c ; i++) {
    454 		ptarget(&targs->v[i], 0);
    455 	}
    456 }
    457 
    458 /* read a configuration file */
    459 int
    460 read_conf_file(const char *cf, targv_t *targs, devv_t *devs, extv_t *extents)
    461 {
    462 	conffile_t	conf;
    463 	ent_t		e;
    464 
    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;
    469 	}
    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);
    480 		}
    481 		e.sv.c = 0;
    482 	}
    483 	ptargets(targs);
    484 	(void) conffile_close(&conf);
    485 	return 1;
    486 }
    487 
    488