NetBSD-5.0.2/dist/iscsi/src/storage.c

Compare this file to the similar file:
Show the results in this format:

/* $NetBSD: storage.c,v 1.11 2008/04/11 08:22:37 agc Exp $ */

/*
 * Copyright © 2006 Alistair Crooks.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "config.h"

#include <sys/types.h>
#include <sys/stat.h>

#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
 
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
    
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <unistd.h>

#include "iscsi.h"
#include "iscsiutil.h"
#include "target.h"
#include "device.h"

#include "conffile.h"
#include "storage.h"

/* let's use symbolic names for the fields in the config file */
enum {
	EXTENT_NAME_COL = 0,
	EXTENT_DEVICE_COL = 1,
	EXTENT_SACRED_COL = 2,
	EXTENT_LENGTH_COL = 3,

	DEVICE_NAME_COL = 0,
	DEVICE_RAIDLEVEL_COL = 1,
	DEVICE_LENGTH_COL = 2,

	TARGET_NAME_COL = 0,
	TARGET_V1_DEVICE_COL = 1,
	TARGET_V1_NETMASK_COL = 2,
	TARGET_V2_FLAGS_COL = 1,
	TARGET_V2_DEVICE_COL = 2,
	TARGET_V2_NETMASK_COL = 3
};

#define DEFAULT_FLAGS	"ro"


/* find an extent by name */
static disc_extent_t *
find_extent(extv_t *evp, char *s)
{
	int	i;

	for (i = 0 ; i < evp->c ; i++) {
		if (strcmp(evp->v[i].extent, s) == 0) {
			return &evp->v[i];
		}
	}
	return NULL;
}

/* allocate space for a new extent */
static int
do_extent(conffile_t *cf, extv_t *evp, ent_t *ep)
{
	struct stat	 st;
	char		*cp;

	if (find_extent(evp, ep->sv.v[EXTENT_NAME_COL]) != NULL) {
		(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
		(void) fprintf(stderr, "Error: attempt to re-define extent `%s'\n", ep->sv.v[EXTENT_NAME_COL]);
		return 0;
	}
	ALLOC(disc_extent_t, evp->v, evp->size, evp->c, 14, 14, "do_extent", exit(EXIT_FAILURE));
	evp->v[evp->c].extent = strdup(ep->sv.v[EXTENT_NAME_COL]);
	evp->v[evp->c].dev = strdup(ep->sv.v[EXTENT_DEVICE_COL]);
	evp->v[evp->c].sacred = strtoll(ep->sv.v[EXTENT_SACRED_COL], NULL, 10);
	if (strcasecmp(ep->sv.v[EXTENT_LENGTH_COL], "size") == 0) {
		if (stat(ep->sv.v[EXTENT_DEVICE_COL], &st) == 0) {
			evp->v[evp->c].len = st.st_size;
		}
	} else {
		evp->v[evp->c].len = strtoll(ep->sv.v[EXTENT_LENGTH_COL], &cp, 10);
		if (cp != NULL) {
			switch(tolower((unsigned)*cp)) {
			case 't':
				evp->v[evp->c].len *= (uint64_t)(1024ULL * 1024ULL * 1024ULL * 1024ULL);
				break;
			case 'g':
				evp->v[evp->c].len *= (uint64_t)(1024ULL * 1024ULL * 1024ULL);
				break;
			case 'm':
				evp->v[evp->c].len *= (uint64_t)(1024ULL * 1024ULL);
				break;
			case 'k':
				evp->v[evp->c].len *= (uint64_t)1024ULL;
				break;
			}
		}
	}
	evp->c += 1;
	return 1;
}

/* find a device by name */
static disc_device_t *
find_device(devv_t *devvp, char *s)
{
	int	i;

	for (i = 0 ; i < devvp->c ; i++) {
		if (strcmp(devvp->v[i].dev, s) == 0) {
			return &devvp->v[i];
		}
	}
	return NULL;
}

/* return the size of the sub-device/extent */
static uint64_t
getsize(conffile_t *cf, devv_t *devvp, extv_t *evp, char *s)
{
	disc_extent_t	*xp;
	disc_device_t	*dp;

	if ((xp = find_extent(evp, s)) != NULL) {
		return xp->len;
	}
	if ((dp = find_device(devvp, s)) != NULL) {
		switch (dp->xv[0].type) {
		case DE_EXTENT:
			return dp->xv[0].u.xp->len;
		case DE_DEVICE:
			return dp->xv[0].u.dp->len;
		}
	}
	(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
	(void) fprintf(stderr, "Warning: sub-device/extent `%s' not found\n", s);
	return 0;
}

/* allocate space for a device */
static int
do_device(conffile_t *cf, devv_t *devvp, extv_t *evp, ent_t *ep)
{
	if (find_device(devvp, ep->sv.v[DEVICE_NAME_COL]) != NULL) {
		(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
		(void) fprintf(stderr, "Error: attempt to re-define device `%s'\n", ep->sv.v[DEVICE_NAME_COL]);
		return 0;
	}
	ALLOC(disc_device_t, devvp->v, devvp->size, devvp->c, 14, 14, "do_device", exit(EXIT_FAILURE));
	devvp->v[devvp->c].dev = strdup(ep->sv.v[DEVICE_NAME_COL]);
	devvp->v[devvp->c].raid = (strncasecmp(ep->sv.v[DEVICE_RAIDLEVEL_COL], "raid", 4) == 0) ? atoi(&ep->sv.v[DEVICE_RAIDLEVEL_COL][4]) : 0;
	devvp->v[devvp->c].size = ep->sv.c - 2;
	devvp->v[devvp->c].len = getsize(cf, devvp, evp, ep->sv.v[DEVICE_LENGTH_COL]);
	NEWARRAY(disc_de_t, devvp->v[devvp->c].xv, ep->sv.c - 2, "do_device", exit(EXIT_FAILURE));
	for (devvp->v[devvp->c].c = 0 ; devvp->v[devvp->c].c < devvp->v[devvp->c].size ; devvp->v[devvp->c].c++) {
		if ((devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.xp = find_extent(evp, ep->sv.v[devvp->v[devvp->c].c + 2])) != NULL) {
			if (devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.xp->used) {
				(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
				(void) fprintf(stderr, "Error: extent `%s' has already been used\n", ep->sv.v[devvp->v[devvp->c].c + 2]);
				return 0;
			}
			if (devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.xp->len != devvp->v[devvp->c].len && devvp->v[devvp->c].raid != 0) {
				(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
				(void) fprintf(stderr, "Error: extent `%s' has size %" PRIu64 ", not %" PRIu64"\n", ep->sv.v[devvp->v[devvp->c].c + 2], devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.xp->len, devvp->v[devvp->c].len);
				return 0;
			}
			devvp->v[devvp->c].xv[devvp->v[devvp->c].c].type = DE_EXTENT;
			devvp->v[devvp->c].xv[devvp->v[devvp->c].c].size = devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.xp->len;
			devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.xp->used = 1;
		} else if ((devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.dp = find_device(devvp, ep->sv.v[devvp->v[devvp->c].c + 2])) != NULL) {
			if (devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.dp->used) {
				(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
				(void) fprintf(stderr, "Error: device `%s' has already been used\n", ep->sv.v[devvp->v[devvp->c].c + 2]);
				return 0;
			}
			devvp->v[devvp->c].xv[devvp->v[devvp->c].c].type = DE_DEVICE;
			devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.dp->used = 1;
			devvp->v[devvp->c].xv[devvp->v[devvp->c].c].size = devvp->v[devvp->c].xv[devvp->v[devvp->c].c].u.dp->len;
		} else {
			(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
			(void) fprintf(stderr, "Error: no extent or device found for `%s'\n", ep->sv.v[devvp->v[devvp->c].c + 2]);
			return 0;
		}
	}
	if (devvp->v[devvp->c].raid == 1) {
		/* check we have more than 1 device/extent */
		if (devvp->v[devvp->c].c < 2) {
			(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
			(void) fprintf(stderr, "Error: device `%s' is specified as RAID1, but has only %d sub-devices/extents\n", devvp->v[devvp->c].dev, devvp->v[devvp->c].c);
			return 0;
		}
	}
	devvp->c += 1;
	return 1;
}

/* find a target by name */
static disc_target_t *
find_target(targv_t *tvp, char *s)
{
	int	i;

	for (i = 0 ; i < tvp->c ; i++) {
		if (strcmp(tvp->v[i].target, s) == 0) {
			return &tvp->v[i];
		}
	}
	return NULL;
}

/* allocate space for a new target */
static int
do_target(conffile_t *cf, targv_t *tvp, devv_t *devvp, extv_t *evp, ent_t *ep)
{
	disc_extent_t	*xp;
	disc_device_t	*dp;
	const char	*flags;
	char		 tgt[256];
	char		*iqn;
	int		 netmaskcol;
	int		 devcol;

	if ((iqn = strchr(ep->sv.v[TARGET_NAME_COL], '=')) == NULL) {
		(void) strlcpy(tgt, ep->sv.v[TARGET_NAME_COL], sizeof(tgt));
	} else {
		(void) snprintf(tgt, sizeof(tgt), "%.*s", (int)(iqn - ep->sv.v[TARGET_NAME_COL]), ep->sv.v[TARGET_NAME_COL]);
		iqn += 1;
	}
	if (find_target(tvp, tgt) != NULL) {
		(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
		(void) fprintf(stderr, "Error: attempt to re-define target `%s'\n", tgt);
		return 0;
	}
	ALLOC(disc_target_t, tvp->v, tvp->size, tvp->c, 14, 14, "do_target", exit(EXIT_FAILURE));
	if (ep->sv.c == 3) {
		(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
		(void) fprintf(stderr, "Warning: old 3 field \"targets\" entry, assuming read-only target\n");
		devcol = TARGET_V1_DEVICE_COL;
		netmaskcol = TARGET_V1_NETMASK_COL;
		flags = DEFAULT_FLAGS;
	} else {
		devcol = TARGET_V2_DEVICE_COL;
		flags = ep->sv.v[TARGET_V2_FLAGS_COL];
		netmaskcol = TARGET_V2_NETMASK_COL;
	}
	if (iqn != NULL) {
		tvp->v[tvp->c].iqn = strdup(iqn);
	}
	if ((dp = find_device(devvp, ep->sv.v[devcol])) != NULL) {
		tvp->v[tvp->c].de.type = DE_DEVICE;
		tvp->v[tvp->c].de.u.dp = dp;
		tvp->v[tvp->c].target = strdup(tgt);
		tvp->v[tvp->c].mask = strdup(ep->sv.v[netmaskcol]);
		if (strcmp(flags, "readonly") == 0 || strcmp(flags, "ro") == 0 || strcmp(flags, "r") == 0) {
			tvp->v[tvp->c].flags |= TARGET_READONLY;
		}
		tvp->c += 1;
		return 1;
	}
	if ((xp = find_extent(evp, ep->sv.v[devcol])) != NULL) {
		tvp->v[tvp->c].de.type = DE_EXTENT;
		tvp->v[tvp->c].de.u.xp = xp;
		tvp->v[tvp->c].target = strdup(tgt);
		tvp->v[tvp->c].mask = strdup(ep->sv.v[netmaskcol]);
		if (strcmp(flags, "readonly") == 0 || strcmp(flags, "ro") == 0 || strcmp(flags, "r") == 0) {
			tvp->v[tvp->c].flags |= TARGET_READONLY;
		}
		tvp->c += 1;
		return 1;
	}
	(void) fprintf(stderr, "%s:%d: ", conffile_get_name(cf), conffile_get_lineno(cf));
	(void) fprintf(stderr, "Error: no device or extent found for `%s'\n", ep->sv.v[devcol]);
	return 0;
}

/* print an extent */
static void
pextent(disc_extent_t *ep, int indent)
{
	int	i;

	for (i = 0 ; i < indent ; i++) {
		(void) fputc('\t', stdout);
	}
	printf("%s:%s:%" PRIu64 ":%" PRIu64 "\n", ep->extent, ep->dev, ep->sacred, ep->len);
}

static void pdevice(disc_device_t *, int);

/* print information about an extent or a device */
static void
pu(disc_de_t *dep, int indent)
{
	switch(dep->type) {
	case DE_EXTENT:
		pextent(dep->u.xp, indent);
		break;
	case DE_DEVICE:
		pdevice(dep->u.dp, indent);
		break;
	}
}

/* print information about a device */
static void
pdevice(disc_device_t *dp, int indent)
{
	int	i;
	int	j;

	for (i = 0 ; i < indent ; i++) {
		(void) fputc('\t', stdout);
	}
	printf("%s:RAID%d\n", dp->dev, dp->raid);
	for (j = 0 ; j < dp->c ; j++) {
		pu(&dp->xv[j], indent + 1);
	}
}

/* print informnation about a target */
static void
ptarget(disc_target_t *tp, int indent)
{
	int	i;

	for (i = 0 ; i < indent ; i++) {
		(void) fputc('\t', stdout);
	}
	printf("%s:%s:%s\n", tp->target, (tp->flags & TARGET_READONLY) ? "ro" : "rw", tp->mask);
	pu(&tp->de, indent + 1);
}

/* print all information */
static void
ptargets(targv_t *tvp)
{
	int	i;

	for (i = 0 ; i < tvp->c ; i++) {
		ptarget(&tvp->v[i], 0);
	}
}

/* read a configuration file */
int
read_conf_file(const char *cf, targv_t *tvp, devv_t *dvp, extv_t *evp)
{
	conffile_t	conf;
	ent_t		e;

	(void) memset(&conf, 0x0, sizeof(conf));
	if (!conffile_open(&conf, cf, "r", " \t", "#")) {
		(void) fprintf(stderr, "Error: can't open `%s'\n", cf);
		return 0;
	}
	printf("Reading configuration from `%s'\n", cf);
	(void) memset(&e, 0x0, sizeof(e));
	while (conffile_getent(&conf, &e)) {
		if (strncmp(e.sv.v[0], "extent", 6) == 0) {
			do_extent(&conf, evp, &e);
		} else if (strncmp(e.sv.v[0], "device", 6) == 0) {
			do_device(&conf, dvp, evp, &e);
		} else if (strncmp(e.sv.v[0], "target", 6) == 0 ||
			   strncmp(e.sv.v[0], "lun", 3) == 0) {
			do_target(&conf, tvp, dvp, evp, &e);
		}
		e.sv.c = 0;
	}
	ptargets(tvp);
	(void) conffile_close(&conf);
	return 1;
}

/* write the pid to the pid file */
void
write_pid_file(const char *f)
{
	FILE	*fp;

	if ((fp = fopen(f, "w")) == NULL) {
		(void) fprintf(stderr, "Couldn't create pid file \"%s\": %s", f, strerror(errno)); 
	} else {
		fprintf(fp, "%ld\n", (long) getpid());
		fclose(fp);
	}
}