/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1990 MIPS Computer Systems, Inc.            |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 52.227-7013.   |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Drive                                |
 * |         Sunnyvale, CA 94086                               |
 * |-----------------------------------------------------------|
 */
/* $Header: main.c,v 1.37.2.1 92/06/24 13:55:41 marker Exp $ */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/vdisk.h>
#include <sys/dkio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/dvh.h>
#include <sys/termio.h>
#include "vdisk.h"

#ifndef DIOCGETATTR
#define DIOCGETATTR	_DIOC_(37)	/* Get virtual disk attributes */
#endif /* DIOCGETATTR */
#ifndef DIOCVDINIT
#define DIOCVDINIT	_DIOC_(38)	/* InitiGet virtual disk attributes */
#endif /* DIOCVDINIT */

int	vdfd;		/* file descriptor for ioctl to vdisk */
void attrcmd ();
#ifdef later
void reinitcmd ();
#endif /* later */

#define ATTR_CHECK_PARENT	1
#define ATTR_GET_ATTR		2

struct attrlist {
	int flags;
	int type;
	int flags_type;
	char *name;
	int attr;
	int check_attr;
	int check_value;
};

struct attrlist attrlist[] = {
	{ NULL, NULL, NULL, "open", DK_ATTR_OPEN, 0, 0  },
	{ NULL, NULL, NULL, "auto_sync", DK_ATTR_AUTO_SYNC, 0, 0 },
	{ NULL, NULL, NULL, "balanced_minimum", DK_ATTR_BALANCED_MINIMUM, 0, 0 },
	{ NULL, NULL, NULL, "burst_total", DK_ATTR_BURST_TOTAL, 0, 0 },
	{ NULL, NULL, NULL, "dirty", DK_ATTR_DIRTY, 0, 0 },
	{ NULL, NULL, NULL, "disabled", DK_ATTR_DISABLE, 0, 0 },
	{ NULL, NULL, NULL, "in_sync", DK_ATTR_IN_SYNC, 0, 0 },
	{ NULL, NULL, NULL, "read_algorithm", DK_ATTR_RD_ALGORITHM, 0, 0 },
	{ NULL, NULL, NULL, "write_sequential", DK_ATTR_WR_SEQ, 0, 0 },
	{ NULL, NULL, NULL, "read_error_count", DK_ATTR_READ_ERROR_COUNT, 0, 0 },
	{ NULL, NULL, NULL, "read_error_threshold", DK_ATTR_READ_ERROR_THRESHOLD, 0, 0 },
	{ NULL, NULL, NULL, "split_end", DK_ATTR_SPLIT_END, 0, 0 },
	{ NULL, NULL, NULL, "split_start", DK_ATTR_SPLIT_START, 0, 0 },
	{ NULL, NULL, NULL, "stripe_size", DK_ATTR_STRIPE_SIZE, 0, 0 },
	{ NULL, NULL, NULL, "sync_blocksize", DK_ATTR_SYNC_BLKSIZE, 0, 0 },
	{ NULL, NULL, NULL, "scratch", DK_ATTR_SCRATCH, 0, 0 },
	{ NULL, NULL, NULL, "sync_in_progress", DK_ATTR_SYNC_IN_PROGRESS, 0, 0 },
	{ NULL, NULL, NULL, "sync_location", DK_ATTR_SYNC_LOCATION, 0, 0 },
	{ NULL, NULL, NULL, "sync_delay", DK_ATTR_SYNC_DELAY, 0, 0 },
	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};

struct attrlist rdalist[] = {
	{ NULL, NULL, NULL, "balanced", MI_RD_BALANCED, 0, 0 },
	{ NULL, NULL, NULL, "burst", MI_RD_BURST, 0, 0 },
	{ NULL, NULL, NULL, "cost", MI_RD_COST, 0, 0 },
	{ NULL, NULL, NULL, "min_queue", MI_RD_MIN_QUEUE, 0, 0 },
	{ NULL, NULL, NULL, "next", MI_RD_NEXT, 0, 0 },
	{ NULL, NULL, NULL, "primary", MI_RD_PRIMARY, 0, 0 },
	{ NULL, NULL, NULL, "stripe", MI_RD_STRIPE, 0, 0 },
	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};


struct attrlist flaglist[] = {
	{ 0, DK_SIMPLE, DK_SIMPLE, "open", DK_OPEN, 0, 0 },
	{ 0, DK_SPAN, DK_SIMPLE, "open", DK_OPEN, 0, 0 },
	{ 0, DK_SPLIT, DK_SIMPLE, "open", DK_OPEN, 0, 0 },
	{ 0, DK_STRIPE, DK_SIMPLE, "open", DK_OPEN, 0, 0 },
	{ 0, DK_MIRROR, DK_SIMPLE, "open", DK_OPEN, 0, 0 },
	{ 0, DK_SIMPLE, DK_SIMPLE, "disabled", DK_DISABLE, 0, 0 },
	{ 0, DK_SPAN, DK_SIMPLE, "disabled", DK_DISABLE, 0, 0 },
	{ 0, DK_SPLIT, DK_SIMPLE, "disabled", DK_DISABLE, 0, 0 },
	{ 0, DK_STRIPE, DK_SIMPLE, "disabled", DK_DISABLE, 0, 0 },
	{ 0, DK_MIRROR, DK_SIMPLE, "disabled", DK_DISABLE, 0, 0 },
	{ 0, DK_MIRROR, DK_MIRROR, "auto_sync", MI_AUTO_SYNC, 0, 0 },
	{ 1, DK_MIRROR, DK_SIMPLE, "dirty", DK_DIRTY, 0, 0 },
	{ 1, DK_MIRROR, DK_SIMPLE, "in_sync", DK_IN_SYNC, 0, 0 },
	{ 0, DK_MIRROR, DK_MIRROR, "write_sequential", MI_WR_SEQ, 0, 0 },
	{ 0, DK_MIRROR, DK_SIMPLE, "scratch", DK_SCRATCH, 0, 0 },
	{ 2, DK_MIRROR, DK_MIRROR, "sync_in_progress", DK_ATTR_SYNC_IN_PROGRESS, 0, 0 },
	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};

struct attrlist dkattrlist[] = {
	{ NULL, DK_MIRROR, NULL, "read_algorithm", DK_ATTR_RD_ALGORITHM, 0, 0 },
	{ NULL, DK_MIRROR, NULL, "balanced_minimum", DK_ATTR_BALANCED_MINIMUM, DK_ATTR_RD_ALGORITHM, MI_RD_BALANCED },
	{ NULL, DK_MIRROR, NULL, "burst_total", DK_ATTR_BURST_TOTAL, DK_ATTR_RD_ALGORITHM, MI_RD_BURST },
	{ NULL, DK_MIRROR, NULL, "sync_blocksize", DK_ATTR_SYNC_BLKSIZE, DK_ATTR_SYNC_IN_PROGRESS, 1 },
	{ NULL, DK_MIRROR, NULL, "sync_location", DK_ATTR_SYNC_LOCATION, DK_ATTR_SYNC_IN_PROGRESS, 1 },
	{ NULL, DK_MIRROR, NULL, "sync_delay", DK_ATTR_SYNC_DELAY, DK_ATTR_SYNC_IN_PROGRESS, 1 },
	{ NULL, DK_STRIPE, NULL, "stripe_size", DK_ATTR_STRIPE_SIZE, 0, 0 },
	{ 1, DK_MIRROR, NULL, "read_error_count", DK_ATTR_READ_ERROR_COUNT, 0, 0 },
	{ 1, DK_MIRROR, NULL, "read_error_threshold", DK_ATTR_READ_ERROR_THRESHOLD, 0, 0 },
	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};

char * getattrname ();
char *get_entry ();
void add_dir ();
int dev_first_call = 1;

main(argc, argv)
	int argc;
	char **argv;
{
	char **argp;
	int i;
	struct	stat buf;

	/*
	 * initialization
	 */

	vdfd = open(VDISK_PATH, O_RDONLY);
	if (vdfd < 0) {
		if (errno != ENOENT) {
			perror ("vdisk: open vdisk");
			exit (2);
		}
		if (mknod(VDISK_PATH, S_IFCHR + S_IRUSR,
			  makedev(VDISK_MAJOR, VDISKIOCTL))) {
			perror("vdisk: mknod vdisk");
			exit (2);
		}
		
		vdfd = open(VDISK_PATH, O_RDONLY);
		if (vdfd < 0) {
			perror("vdisk : open vdisk");
			exit (3);
		}
	}

	/*
	 * parse the command
	 */
	if (argc == 1) {
		vdisk_usage (0, VDISK_DEFAULT);
		exit (1);
	}
	argp = &argv[1];
	if (strcmp(*argp, "split") == 0) {
		split(argc-2, argp+1);
	} else if (strcmp(*argp, "span") == 0) {
		span(argc-2, argp+1);
	} else if (strcmp(*argp, "stripe") == 0) {
		stripe(argc-2, argp+1);
	} else if (strcmp(*argp, "mirror") == 0) {
		mirror(argc-2, argp+1);
	} else if (strcmp(*argp, "attr") == 0) {
		attrcmd(argc-2, argp+1);
#ifdef later
	} else if (strcmp (*argp, "reinit") == 0) {
		reinitcmd (argc-2, argp+1);
#endif /* later */
	} else if (strcmp(*argp, "info") == 0) {
		if (argc == 2) {
			vdisk_info(-1, NULL);
			exit (0);
		} else if (argc == 3) {
			vdisk_info (-1, argv[2]);
		} else {
			vdisk_usage (0, VDISK_DEFAULT);
			exit (1);
		}
	} else {
		vdisk_usage(0, VDISK_DEFAULT);
		exit(1);
	}
	exit (0);
}

vdisk_usage(cmd, arg)
     int cmd;
     int arg;
{
	switch (cmd) {
	default:
#ifdef later
		printf ("Usage: vdisk [split | span | stripe | mirror | attr | info] [options]\n");
#else
		printf ("Usage: vdisk [split | span | stripe | mirror | attr | info | reinit] [options]\n");
#endif /* later */
		break;
	case DK_SPLIT:
		switch (arg) {
		case VDISK_ADD:
			printf ("Usage: vdisk split add vdisk partition start end\n");
			break;

		case VDISK_DELETE:
			printf ("Usage: vdisk split delete vdisk\n");
			break;

		case VDISK_INFO:
			printf ("Usage: vdisk split info [vdisk]\n");
			break;

		default:
			printf ("Usage: vdisk split [add | delete | info] [options]\n");
			break;
		}
		break;

	case DK_STRIPE:
		switch (arg) {
		case VDISK_ADD:
			printf ("Usage: vdisk stripe add [-s size] [-b partition] [-a partition] vdisk partition [... partition]\n");
			break;

		case VDISK_DELETE:
			printf ("Usage: vdisk stripe delete vdisk partition [... partition]\n");
			break;

		case VDISK_INFO:
			printf ("Usage: vdisk stripe info [vdisk]\n");
			break;

		default:
			printf ("Usage: vdisk stripe [add | delete | info] [options]\n");
			break;
		}
		break;

	case DK_SPAN:
		switch (arg) {
		case VDISK_ADD:
			printf ("Usage: vdisk span add [-b partition] [-a partition] vdisk partition [... partition]\n");
			break;

		case VDISK_DELETE:
			printf ("Usage: vdisk span delete vdisk partition [... partition]\n");
			break;

		case VDISK_INFO:
			printf ("Usage: vdisk span info [vdisk]\n");
			break;

		default:
			printf ("Usage: vdisk span [add | delete | info] [options]\n");
			break;
		}
		break;

	case DK_MIRROR:
		switch (arg) {
		case VDISK_ADD:
			printf ("Usage: vdisk mirror add [-b partition] [-a partition] vdisk partition [... partition]\n");
			break;

		case VDISK_DELETE:
			printf ("Usage: vdisk mirror delete vdisk partition [... partition]\n");
			break;

		case VDISK_INFO:
			printf ("Usage: vdisk mirror info [vdisk]\n");
			break;

		default:
			printf ("Usage: vdisk mirror [add | delete | info] [options]\n");
			break;
		}
		break;

	}
}

#define INDENT_STRING	"    "

int
indent_level (level)
	int level;
{
	int i;

	for (i = 0; i < level; i++) {
		printf ("    ");
	}
	return (indent_length (level));
}

int
indent_length (level)
	int level;
{
	int i;

	return (level * sizeof (INDENT_STRING));
}

print_dk_flags (dktab, level)
	struct dktab *dktab;
	int level;
{
	struct dktab vdhead;
	struct io_arg io_arg;
	char str[32], initstr[32];
	char *strp;
	int len, curcol, initlen, i;
	int total_columns;
	int first_flag = 1;
	int found_parent;
	struct attrlist *fattr;

	total_columns = get_window_size ();
	vdhead.dk_dev = dktab->dk_v.dk_vdev;
	io_arg.datasz = sizeof(struct dktab);
	io_arg.memaddr = (unsigned long)&vdhead;
	if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
		found_parent = 0;
	} else {
		found_parent = 1;
	}

	for (fattr = flaglist; fattr->name; fattr++) {
		if (fattr->flags & ATTR_CHECK_PARENT) {
			if (!found_parent ||
				(fattr->type != vdhead.dk_type)) {
				continue;
			}
		} else if (fattr->type != dktab->dk_type) {
			continue;
		}

		if (first_flag) {
			first_flag = 0;
			initlen = 8;
			curcol = indent_level (level) + initlen;
			printf ("Flags:  ");
			for (i = 0; i < initlen; i++) {
				initstr[i] = ' ';
			}
			initstr[initlen] = NULL;
		} else {
			curcol += 2;
			printf (", ");
		}
		len = 0;
		if (fattr->flags & ATTR_GET_ATTR) {
			struct vd_dkattr vd_dkattr;
			struct io_arg io_arg;

			vd_dkattr.dev = dktab->dk_dev;
			vd_dkattr.attr = fattr->attr;
			io_arg.datasz = sizeof (struct vd_dkattr);
			io_arg.memaddr = (unsigned long)&vd_dkattr;
			if (ioctl (vdfd, DIOCGETATTR, &io_arg) < 0) {
				perror ("vdisk: getattr");
			}
			if (!vd_dkattr.value) {
				sprintf (str, "!");
				len++;
			}
		} else {
			switch (fattr->flags_type) {
			case DK_MIRROR:
				if (!(fattr->attr & dktab->dk_mirror.mi_flags)) {
					sprintf (str, "!");
					len++;
				}
				break;
			default:
				if (!(fattr->attr & dktab->dk_flags)) {
					sprintf (str, "!");
					len++;
				}
				break;
			}
		}
		sprintf (&str[len], "%s", fattr->name);
		len = strlen (str);
		if (curcol + len + 2 > total_columns) {
			printf ("\n");
			curcol = indent_level (level) + len + initlen;
			printf (initstr);
		} else {
			curcol += len;
		}
		printf (str);
	}
	if (!first_flag) {
		printf ("\n");
	}
}


void
get_attr_value (str, dktab, attr)
	char *str;
	struct dktab *dktab;
	int attr;
{
	struct attrlist *myrda;

	struct vd_dkattr vd_dkattr;
	struct io_arg io_arg;

	vd_dkattr.dev = dktab->dk_dev;
	vd_dkattr.attr = attr;
	io_arg.datasz = sizeof (struct vd_dkattr);
	io_arg.memaddr = (unsigned long)&vd_dkattr;
	if (ioctl (vdfd, DIOCGETATTR, &io_arg) < 0) {
		perror ("vdisk: get_attr_value");
		sprintf (str, "UNKNOWN");
		return;
	}
	switch (attr) {
	case DK_ATTR_RD_ALGORITHM:
		myrda = rdalist;
		while (myrda->name != NULL) {
			if (myrda->attr == vd_dkattr.value) {
				sprintf (str, "%s", myrda->name);
				return;
			}
			myrda++;
		}
		sprintf (str, "UNKNOWN");
		break;

	default:
		sprintf (str, "%d", vd_dkattr.value);
		break;
	}
}

void
print_dk_attributes (dktab, level)
	struct dktab *dktab;
	int level;
{
	struct attrlist *myrda;
	int len, curcol, total_columns;
	char str[64];
	struct attrlist *attr;
	int first = 1;
	struct vd_dkattr vd_dkattr;
	struct io_arg io_arg;
	struct dktab vdhead;
	int found_parent;

	total_columns = get_window_size ();
	vdhead.dk_dev = dktab->dk_v.dk_vdev;
	io_arg.datasz = sizeof(struct dktab);
	io_arg.memaddr = (unsigned long)&vdhead;
	if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
		found_parent = 0;
	} else {
		found_parent = 1;
	}

	for (attr = dkattrlist; attr->name != NULL; attr++) {
		if (attr->flags & ATTR_CHECK_PARENT) {
			if (!found_parent ||
				(attr->type != vdhead.dk_type)) {
				continue;
			}
		} else 	if (attr->type != dktab->dk_type) {
			continue;
		}
		if (attr->check_attr) {

			vd_dkattr.dev = dktab->dk_dev;
			vd_dkattr.attr = attr->check_attr;
			io_arg.datasz = sizeof (struct vd_dkattr);
			io_arg.memaddr = (unsigned long)&vd_dkattr;
			if (ioctl (vdfd, DIOCGETATTR, &io_arg) < 0) {
				perror ("vdisk: getattr");
				continue;
			}
			if (vd_dkattr.value != attr->check_value) {
				continue;
			}
		}

		if (first) {
			first = 0;
			curcol = indent_level (level + 1) + 11;
			printf ("Attributes:");
		}
		sprintf (str, "  %s: ", attr->name);
		len = strlen (str);
		get_attr_value (&str[len], dktab, attr->attr);
		len = strlen (str);
		sprintf (&str[len], ".");
		len++;
		if (curcol + len > total_columns) {
			printf ("\n");
			curcol = indent_level (level + 1) + 11;
			printf ("           ");
		}
		curcol += len;
		printf (str);	
	}
	if (!first) {
		printf ("\n");
	}
}

char *
get_device_name (dev, path)
	dev_t dev;
	char *path;
{

	if (dev_first_call) {
		dev_first_call = 0;
		add_dir ("/dev/dsk", "/etc/vdisk_data");
	}

	return (get_entry (dev, path));
}

long
get_device_size (dktab, path)
	struct dktab *dktab;
	char *path;
{
	char buf[MAXPATHLEN];
	struct stat *sb;
	int fd;
	struct io_arg ia;
	struct volume_header vh;
	int part;
	char *ptr;
	int bcnt;

	strcpy (buf, "/dev/rdsk/");
	strcpy (&buf[10], path);

	if ((fd = open(buf, 0)) < 0) 
		return 0;

	if (ioctl (fd, DIOCGETSIZE, &ia) >= 0) {
		(void) close (fd);
		return (ia.retval);
	} else {
		ptr = path;
		ptr++;ptr++;
		if (*ptr != 'c') {	/* no 'c' */
			(void) close (fd);
			return 0;
		}
		ptr++;
		while (*ptr && *ptr >= '0' && *ptr <= '9') ptr++;
		if (*ptr != 'd') {	/* no 'd' */
			(void) close (fd);
			return 0;
		}
		ptr++;
		while (*ptr && *ptr >= '0' && *ptr <= '9') ptr++;
		if (*ptr != 's') {	/* no 's' */
			(void) close (fd);
			return 0;
		}
		ptr++;
		part = atoi(ptr);
		if (part < 0 || part > 15) {
			(void) close (fd);
			return 0;
		}
		ia.retval = ia.sectst = 0;
		ia.memaddr = (unsigned long)&vh;
		ia.datasz = sizeof(vh);
		if (ioctl(fd, DIOCGETVH, &ia) < 0) {
			(void) close(fd);
			return 0;
		}
		(void) close(fd);
		if (vh.vh_magic != VHMAGIC) {
			return 0;
		}
		return (vh.vh_pt[part].pt_nblks);
	}
}

print_dktab(dktab, level)
	struct dktab *dktab;
	int level;
{
	int ctrl, disk, slice;
	char path[MAXPATHLEN];

	indent_level (level);
	if (!strcmp (get_device_name (dktab->dk_dev, path), "")) {
		printf ("%d,%d    ", ((dktab->dk_dev >> 8) & 0xFF),
			(dktab->dk_dev & 0xFF));
	} else {
		printf ("/dev/dsk/%s    ", path);
	}

	switch (dktab->dk_type) {
		case DK_SIMPLE:
			if (!strcmp (path, "")) {
				printf ("SIMPLE  ");
			} else {
				printf ("SIMPLE  %d  ", get_device_size (dktab, path));
			}
			if (dktab->dk_v.dk_vdev != -1) {
				if (!strcmp (get_device_name (dktab->dk_v.dk_vdev, path), "")) {
					printf ("(part of %d,%d)\n", ((dktab->dk_v.dk_vdev >> 8) & 0xFF),
						(dktab->dk_v.dk_vdev & 0xFF));
				} else {
					printf ("(part of /dev/dsk/%s)\n", path);
				}
			} else {
				printf ("\n");
			}
			break;
		case DK_SPLIT:
			if (!strcmp (path, "")) {
				printf ("SPLIT  ");
			} else {
				printf ("SPLIT  %d  ", get_device_size (dktab, path));
			}
			if (dktab->dk_v.dk_vdev != -1) {
				if (!strcmp (get_device_name (dktab->dk_v.dk_vdev, path), "")) {
					printf ("(part of %d,%d)\n", ((dktab->dk_v.dk_vdev >> 8) & 0xFF),
						(dktab->dk_v.dk_vdev & 0xFF));
				} else {
					printf ("(part of /dev/dsk/%s)\n", path);
				}
			} else {
				printf ("\n");
			}
			if (level == 0) {
				indent_level (level + 1);
				if (!strcmp (get_device_name (dktab->dk_fi.dk_fidev, path), "")) {
					printf ("%d,%d    ", ((dktab->dk_fi.dk_fidev >> 8) & 0xFF),
						(dktab->dk_fi.dk_fidev & 0xFF));
				} else {
					printf ("/dev/dsk/%s    ", path);
				}
				printf ("start: %d  end: %d\n", dktab->dk_split.start,
					dktab->dk_split.end);
			}
			break;
		case DK_SPAN:
			if (!strcmp (path, "")) {
				printf ("SPAN  ");
			} else {
				printf ("SPAN  %d  ", get_device_size (dktab, path));
			}
			if (dktab->dk_v.dk_vdev != -1) {
				if (!strcmp (get_device_name (dktab->dk_v.dk_vdev, path), "")) {
					printf ("(part of %d,%d)\n", ((dktab->dk_v.dk_vdev >> 8) & 0xFF),
						(dktab->dk_v.dk_vdev & 0xFF));
				} else {
					printf ("(part of /dev/dsk/%s)\n", path);
				}
			} else {
				printf ("\n");
			}
			break;
		case DK_MIRROR:
			if (!strcmp (path, "")) {
				printf ("MIRROR  ");
			} else {
				printf ("MIRROR  %d  ", get_device_size (dktab, path));
			}
			if (dktab->dk_v.dk_vdev != -1) {
				if (!strcmp (get_device_name (dktab->dk_v.dk_vdev, path), "")) {
					printf ("(part of %d,%d)\n", ((dktab->dk_v.dk_vdev >> 8) & 0xFF),
						(dktab->dk_v.dk_vdev & 0xFF));
				} else {
					printf ("(part of /dev/dsk/%s)\n", path);
				}
			} else {
				printf ("\n");
			}
			break;
		case DK_STRIPE:
			if (!strcmp (path, "")) {
				printf ("STRIPE  ");
			} else {
				printf ("STRIPE  %d  ", get_device_size (dktab, path));
			}
			if (dktab->dk_v.dk_vdev != -1) {
				if (!strcmp (get_device_name (dktab->dk_v.dk_vdev, path), "")) {
					printf ("%d,%d    ", ((dktab->dk_v.dk_vdev >> 8) & 0xFF),
						(dktab->dk_v.dk_vdev & 0xFF));
				} else {
					printf ("(part of /dev/dsk/%s)\n", path);
				}
			} else {
				printf ("\n");
			}
			break;
		case DK_EMPTY:
			printf ("EMPTY  ");
			if (dktab->dk_v.dk_vdev != -1) {
				if (!strcmp (get_device_name (dktab->dk_v.dk_vdev, path), "")) {
					printf ("%d,%d    ", ((dktab->dk_v.dk_vdev >> 8) & 0xFF),
						(dktab->dk_v.dk_vdev & 0xFF));
				} else {
					printf ("(part of /dev/dsk/%s)\n", path);
				}
			} else {
				printf ("\n");
			}
			break;
		default:
			printf ("\n");
			break;
	}
	if (level == 0) {
		print_dk_attributes (dktab, level);
		print_dk_flags (dktab, level + 1);
	}
}

struct dktab*
vdisk_get_dktab(vdisk)
	char	*vdisk;
{
	struct	dktab *dktab;
	struct 	io_arg io_arg;
	struct	stat buf;
	char path[MAXPATHLEN];
	int ret;

	if ((ret = stat(vdisk, &buf)) < 0) {
		if (errno == ENOENT) {
			memcpy (path, "/dev/dsk/", 9);
			strcpy (&path[9], vdisk);
			ret = stat(path, &buf);
		}
		if (ret < 0) {
			perror("vdisk: stat vdisk");
			exit (2);
		} 
	}

	dktab = (struct dktab *)malloc(sizeof(struct dktab));
	dktab->dk_dev = buf.st_rdev;
	io_arg.datasz = sizeof(struct dktab);
	io_arg.memaddr = (unsigned long)dktab;
	if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
		return ((struct dktab *) NULL);
	}
	return(dktab);
}

#define SIMPLE_COUNT	1
struct simple {
	dev_t devices[SIMPLE_COUNT];
	int count;
	struct simple *next;
};

void
add_simple (simple_list, dev)
	struct simple *simple_list;
	dev_t dev;
{
	int i;
	struct simple *cursimple, *newsimple;

	cursimple = simple_list;
	while (cursimple) {
		for (i = 0; i < cursimple->count; i++) {
			if (cursimple->devices[i] == dev) {
				return;
			}
		}
		if (cursimple->next == NULL) {
			break;
		}
		cursimple = cursimple->next;
	}

	if (cursimple->count == SIMPLE_COUNT) {
		newsimple = (struct simple *) malloc (sizeof (struct simple));
		newsimple->count = 0;
		newsimple->next = NULL;
		cursimple->next = newsimple;
		cursimple = newsimple;
	}

	cursimple->devices[cursimple->count] = dev;
	cursimple->count++;
}

void
display_simple_devices (simple_list)
	struct simple *simple_list;
{
	int i;
	struct simple *cursimple, *newsimple;
	struct io_arg io_arg;
	struct dktab *dktab;

	dktab = (struct dktab *)malloc(sizeof(struct dktab));
	io_arg.datasz = sizeof (struct dktab);
	io_arg.memaddr = (unsigned long)dktab;
	for (cursimple = simple_list; cursimple; cursimple = cursimple->next) {
		for (i = 0; i < cursimple->count; i++) {
			dktab->dk_dev = cursimple->devices[i];
			if (ioctl (vdfd, DIOCGETDKTAB, &io_arg) < 0) {
				continue;
			}

			print_dktab (dktab, 0);
			printf ("\n");
		}
	}

}

vdisk_info(type, vdisk)
	int	type;
	char	*vdisk;
{
	struct	dktab *dktab;
	struct	io_arg io_arg;
	int	i, j;
	dev_t	first;
	struct simple simple_list;

	if (mknod(VDISK_PATH, S_IFCHR + S_IRUSR, makedev(VDISK_MAJOR, 0))) {
		if (errno != EEXIST) {
			perror("vdisk info: mknod vdisk");
			exit (2);
		}
	}
	dktab = (struct dktab *)malloc(sizeof(struct dktab));
	if (vdisk != NULL) {
		dktab->dk_dev = special_device (vdisk);
		io_arg.datasz = sizeof(struct dktab);
		io_arg.memaddr = (unsigned long)dktab;
		if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
			return;
		}
		switch (dktab->dk_type & type) {
		case DK_SPAN:
		case DK_STRIPE:
		case DK_MIRROR:
			print_dktab(dktab, 0);
			first = dktab->dk_fi.dk_fidev;
			dktab->dk_dev = first;
			io_arg.datasz = sizeof(struct dktab);
			io_arg.memaddr = (unsigned long)dktab;
			if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
				return;
			}
			print_dktab(dktab, 1);
			while ((dktab->dk_f.dk_fdev != first) &&
				(dktab->dk_f.dk_fdev != -1) &&
				(dktab->dk_f.dk_fdev != dktab->dk_dev)) {
				dktab->dk_dev = dktab->dk_f.dk_fdev;
				io_arg.datasz = sizeof(struct dktab);
				io_arg.memaddr = (unsigned long)dktab;
				if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
					return;
				}
				print_dktab(dktab, 1);
			}
			if ((dktab->dk_f.dk_fdev != first) ||
				((dktab->dk_dev == first) &&
				(dktab->dk_b.dk_bdev != first))) {
				while ((dktab->dk_b.dk_bdev != first) &&
					(dktab->dk_b.dk_bdev != -1) &&
					(dktab->dk_b.dk_bdev != dktab->dk_dev)) {
					dktab->dk_dev = dktab->dk_b.dk_bdev;
					io_arg.datasz = sizeof(struct dktab);
					io_arg.memaddr = (unsigned long)dktab;
					if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
						return;
					}
				}
				indent_level (1);
				printf ("*** MISSING ENTRIES ***\n");
				if (dktab->dk_dev != first) {
					print_dktab(dktab, 1);
					while (dktab->dk_f.dk_fdev != first) {
						dktab->dk_dev = dktab->dk_f.dk_fdev;
						io_arg.datasz = sizeof(struct dktab);
						io_arg.memaddr = (unsigned long)dktab;
						if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
							return;
						}
						print_dktab(dktab, 1);
					}
				}
			}
			break;

		case DK_SPLIT:
		case DK_SIMPLE:
			print_dktab(dktab, 0);
			break;

		default:
			break;
		}
	} else {
		simple_list.count = 0;
		simple_list.next = NULL;
		for (i = 0; i < NUM_VDISK; i++) {
			for (j = 0; j < 256; j++) {
				dktab->dk_dev = makedev((i + VDISK_MAJOR), j);
				io_arg.datasz = sizeof(struct dktab);
				io_arg.memaddr = (unsigned long)dktab;
				if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
					continue;
				}
				switch (dktab->dk_type & type) {
				case DK_SPAN:
				case DK_STRIPE:
				case DK_MIRROR:
					print_dktab(dktab, 0);
					first = dktab->dk_fi.dk_fidev;
					dktab->dk_dev = first;
					io_arg.datasz = sizeof(struct dktab);
					io_arg.memaddr = (unsigned long)dktab;
					if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
						printf ("\n");
						continue;
					}
					print_dktab(dktab, 1);
					if (dktab->dk_type == DK_SIMPLE) {
						add_simple (&simple_list, dktab->dk_dev);
					}
					while ((dktab->dk_f.dk_fdev != -1) &&
						(dktab->dk_f.dk_fdev != first) &&
						(dktab->dk_f.dk_fdev != dktab->dk_dev)) {
						dktab->dk_dev = dktab->dk_f.dk_fdev;
						io_arg.datasz = sizeof(struct dktab);
						io_arg.memaddr = (unsigned long)dktab;
						if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
							continue;
						}
						print_dktab(dktab, 1);
						if (dktab->dk_type == DK_SIMPLE) {
							add_simple (&simple_list, dktab->dk_dev);
						}
					}
					if ((dktab->dk_f.dk_fdev != first) ||
						((dktab->dk_dev == first) &&
						(dktab->dk_b.dk_bdev != first))) {
						while ((dktab->dk_b.dk_bdev != first) &&
							(dktab->dk_b.dk_bdev != -1) &&
							(dktab->dk_b.dk_bdev != dktab->dk_dev)) {
							dktab->dk_dev = dktab->dk_b.dk_bdev;
							io_arg.datasz = sizeof(struct dktab);
							io_arg.memaddr = (unsigned long)dktab;
							if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
								return;
							}
						}
						indent_level (1);
						printf ("*** MISSING ENTRIES ***\n");
						if (dktab->dk_dev != first) {
							print_dktab(dktab, 1);
							if (dktab->dk_type == DK_SIMPLE) {
								add_simple (&simple_list, dktab->dk_dev);
							}
							while (dktab->dk_f.dk_fdev != first) {
								dktab->dk_dev = dktab->dk_f.dk_fdev;
								io_arg.datasz = sizeof(struct dktab);
								io_arg.memaddr = (unsigned long)dktab;
								if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
									return;
								}
								print_dktab(dktab, 1);
								if (dktab->dk_type == DK_SIMPLE) {
									add_simple (&simple_list, dktab->dk_dev);
								}
							}
						}
					}
					printf ("\n");
					break;

				case DK_SPLIT:
					print_dktab(dktab, 0);
					dktab->dk_dev = dktab->dk_fi.dk_fidev;
					io_arg.datasz = sizeof(struct dktab);
					io_arg.memaddr = (unsigned long)dktab;
					if (ioctl(vdfd, DIOCGETDKTAB, &io_arg) < 0) {
						printf ("\n");
						continue;
					}
					if (dktab->dk_type == DK_SIMPLE) {
						add_simple (&simple_list, dktab->dk_dev);
					}
					printf ("\n");
					break;

				case DK_SIMPLE:
					add_simple (&simple_list, dktab->dk_dev);
					break;

				default:
					break;
				}
			}
		}
		if (type == -1) {
			display_simple_devices (&simple_list);
		}
	}
}

setup_vdisk(dktab, vdisk, type, after, before, info_0, info_1)
	struct dktab *dktab;
	char	*vdisk;
	int	type;
	dev_t	after, before;
	int 	info_0, info_1;
{
	struct	stat buf;
	struct	io_arg io_arg;
	char path[MAXPATHLEN];
	int ret;

	if ((ret = stat(vdisk, &buf)) < 0) {
		if (errno == ENOENT) {
			memcpy (path, "/dev/dsk/", 9);
			strcpy (&path[9], vdisk);
			ret = stat(path, &buf);
		}
		if (ret < 0) {
			perror("vdisk: stat special");
			exit (2);
		} 
	}

	dktab->dk_v.dk_vdev = -1;
	dktab->dk_dev = buf.st_rdev;
	dktab->dk_fi.dk_fidev = -1;
	dktab->dk_type = type;
	dktab->dk_f.dk_fdev = before;
	dktab->dk_b.dk_bdev = after;
	dktab->dk_split.start = info_0;
	dktab->dk_split.end = info_1;
}

dev_t
special_device(special)
	char	*special;
{
	struct	stat buf;
	char path[MAXPATHLEN];
	int ret;

	if ((ret = stat(special, &buf)) < 0) {
		if (errno == ENOENT) {
			memcpy (path, "/dev/dsk/", 9);
			strcpy (&path[9], special);
			ret = stat(path, &buf);
		}
		if (ret < 0) {
			perror("vdisk: stat special");
			exit (2);
		} 
	}

	if (((buf.st_mode & S_IFMT) != S_IFCHR) &&
	    ((buf.st_mode & S_IFMT) != S_IFBLK)) {
		printf ("vdisk: %s not a special file\n", special);
		exit (2);
	}

	return (buf.st_rdev);
}

vdisk_ioctl (cmd, argc, argv, start, type, after, before, info_0, info_1)
	unsigned int cmd;
	int argc;
	char **argv;
	int start;
	int type;
	dev_t after;
	dev_t before;
	int info_0;
	int info_1;

{
	struct io_arg io_arg;
	struct dktab *dktab, *old_dktab;
	int n, i;

	if ((dktab = (struct dktab *) malloc (sizeof (struct dktab) * (argc - start + 1))) == NULL) {
		perror ("vdisk_ioctl");
		close (vdfd);
		exit (2);
	}

	setup_vdisk (&dktab[0], argv[start], type, after, before, info_0, info_1);
	for (i = start + 1, n = 1; i < argc; i++, n++) {
		if ((old_dktab = vdisk_get_dktab (argv[i])) == NULL) {
			printf ("vdisk_ioctl: Unable to get dktab for %s.\n", argv[i]);
			perror ("vdisk_ioctl");
			free (dktab);
			close (vdfd);
			exit (2);
		}
		bcopy (old_dktab, &dktab[n], sizeof (struct dktab));
	}
	io_arg.datasz = sizeof (struct dktab) * (argc - start);
	io_arg.memaddr = (unsigned long)dktab;

	if (ioctl (vdfd, cmd, &io_arg) < 0) {
		perror ("vdisk_ioctl");
		close (vdfd);
		free (dktab);
		exit (2);
	}
	free (dktab);
	return;
}

static int
name_to_attr (name)
	char * name;
{
	struct attrlist *myattr;

	myattr = attrlist;
	while (myattr->name != NULL) {
		if (strcmp (myattr->name, name) == 0) {
			return (myattr->attr);
		}
		myattr++;
	}
	return (-1);
}

static int
name_to_value (attr, name)
	int attr;
	char *name;
{
	struct attrlist *myrda;

	switch (attr) {
	case DK_ATTR_RD_ALGORITHM:
		myrda = rdalist;
		while (myrda->name != NULL) {
			if (strcmp (myrda->name, name) == 0) {
				return (myrda->attr);
			}
			myrda++;
		}
		return (-1);
		break;

	default:
		return (atoi (name));

	}
}

void
setattr (argc, argv)
	int argc;
	char **argv;
{
	struct io_arg io_arg;
	struct vd_dkattr vd_dkattr;

	if (argc != 3) {
		printf ("Usage: vdisk attr set partition attr value\n");
	} else {
		vd_dkattr.dev = special_device (argv[0]);
		if ((vd_dkattr.attr = name_to_attr (argv[1])) < 0) {
			printf ("vdisk: attr: set: bad attribute %s.\n", argv[1]);
			close (vdfd);
			exit (2);
		}
		if ((vd_dkattr.value = name_to_value (vd_dkattr.attr, argv[2])) < 0) {

			printf ("vdisk: attr: set: bad value %s.\n", argv[2]);
			close (vdfd);
			exit (2);
		}
		io_arg.datasz = sizeof (struct vd_dkattr);
		io_arg.memaddr = (unsigned long)&vd_dkattr;
		suser ();
		if (ioctl (vdfd, DIOCSETATTR, &io_arg) < 0) {
			perror ("vdisk: attr: set");
			close (vdfd);
			exit (2);
		}
	}
	return;
}

void
getattr (argc, argv)
	int argc;
	char **argv;
{
	dev_t dev;
	int attr;
	struct io_arg io_arg;
	struct attrlist *myrda;
	struct vd_dkattr vd_dkattr;

	if (argc != 2) {
		printf ("Usage: vdisk attr get partition attr\n");
		exit (1);
	}

	dev = special_device (argv[0]);
	if ((attr = name_to_attr (argv[1])) < 0) {
		printf ("vdisk: attr: get: bad attribute %s.\n", argv[1]);
		close (vdfd);
		exit (2);
	}
	vd_dkattr.dev = dev;
	vd_dkattr.attr = attr;
	io_arg.datasz = sizeof (struct vd_dkattr);
	io_arg.memaddr = (unsigned long)&vd_dkattr;
	if (ioctl (vdfd, DIOCGETATTR, &io_arg) < 0) {
		perror ("vdisk: attr: get");
		close (vdfd);
		exit (3);
	}
	printf ("%s = ", argv[1]);
	switch (attr) {
	case DK_ATTR_RD_ALGORITHM:
		myrda = rdalist;
		while (myrda->name != NULL) {
			if (myrda->attr == vd_dkattr.value) {
				printf ("%s\n", myrda->name);
				return;
			}
			myrda++;
		}
		printf ("UNKNOWN");
		break;

	default:
		printf ("%d", vd_dkattr.value);
		break;
	}
	printf ("\n");
}

char *
getattrname (attr)
	int attr;
{
	struct attrlist *myattr;

	myattr = attrlist;
	while (myattr->name != NULL) {
		if (myattr->attr == attr) {
			return myattr->name;
		}
		myattr++;
	}

	return (NULL);
}

int
get_window_size ()
{
#define TOTCOL 80
	struct winsize ws;
	int size;

	if (ioctl (fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
		size = ws.ws_col;
	}
	if (size == 0) {
	  size = TOTCOL;
	}

	return (size);
}
void
listattr (argc, argv)
	int argc;
	char **argv;
{
	struct attrlist *myattr;
	int curcol, namesize;
	int total_columns;

	if (argc) {
		printf ("Usage: vdisk attr list\n");
		return;
	}

	total_columns = get_window_size ();
	printf ("Attributes: ");
	curcol = 12;
	myattr = attrlist;
	while (myattr->name != NULL) {
		namesize = strlen (myattr->name);
		if (curcol + namesize + 2 > total_columns) {
			printf ("\n");
			printf ("            ");
			curcol = 12;
		}
		printf ("%s", myattr->name);
		curcol += namesize + 2;
		myattr++;
		if (myattr->name != NULL) {
			printf (", ");
		}
	}
	printf ("\n");
	printf ("Read Algorithms: ");
	myattr = rdalist;
	curcol = 17;
	while (myattr->name != NULL) {
		namesize = strlen (myattr->name);
		if (curcol + namesize + 2 > total_columns) {
			printf ("\n");
			printf ("                 ");
			curcol = 17;
		}
		printf ("%s", myattr->name);
		curcol += namesize + 2;
		myattr++;
		if (myattr->name != NULL) {
			printf (", ");
		}
	}
	printf ("\n");
}

void
attrcmd (argc, argv)
	int argc;
	char **argv;
{
	char **argp;

	if (argc == 0) {
		printf ("Usage: vdisk attr [get | set] [options]\n");
		exit (1);
	}
	argp = &argv[0];
	if (strcmp (*argp, "set") == 0) {
		setattr(argc-1, argp+1);
	} else if (strcmp(*argp, "get") == 0) {
		getattr(argc-1, argp+1);
	} else if (strcmp(*argp, "list") == 0) {
		listattr(argc-1, argp+1);
	} else {
		printf ("Usage: vdisk attr [get | set | list] [options]\n");
		exit (1);
	}
}

#ifdef later
void
reinitcmd (argc, argv)
	int argc;
	char **argv;
{
	struct io_arg io_arg;

	if (argc != 0) {
		printf ("Usage: vdisk reinit\n");
	}
	suser ();
	io_arg.datasz = 0;
	io_arg.memaddr = NULL;
	if (ioctl (vdfd, DIOCVDINIT, &io_arg) < 0) {
		perror ("vdisk: reinit");
		close (vdfd);
		exit (3);
	}
}
#endif /* later */

void
get_control_args (type, argc, argv, start, after, before, size)
	int type;
	int argc;
	char **argv;
	int *start;
	dev_t *after;
	dev_t *before;
	int *size;
{
	int idx;

	*after = -1;
	*before = -1;
	*size = -1;

	for (; *start < argc; *start = *start + 1) {
		if (strcmp(argv[*start], "-b") == 0) {
			if (*before != -1) {
				vdisk_usage (type, VDISK_ADD);
				exit (2);
			}
			*start = *start + 1;
			*before = special_device (argv[*start]);
		} else if (strcmp(argv[*start], "-a") == 0) {
			if (*after != -1) {
				vdisk_usage (type, VDISK_ADD);
				exit (2);
			}
			*start = *start + 1;
			*after = special_device (argv[*start]);
		} else if (strcmp(argv[*start], "-s") == 0) {
			if ((type != DK_STRIPE) || (*size != -1)) {
				vdisk_usage (type, VDISK_ADD);
				exit (2);
			}
			*start = *start + 1;
			*size = atoi (argv[*start]);
		} else {
			break;
		}
	}
}

void
suser()

{
	if (getuid()) {
		errno = EPERM;
		perror ("vdisk");
		exit (10);
	}
}
