Import base vinum userland sources

This commit is contained in:
Greg Lehey 1998-09-16 05:57:36 +00:00
parent e4381fa521
commit 8644f7188f
6 changed files with 3375 additions and 0 deletions

23
sbin/vinum/Makefile Normal file
View File

@ -0,0 +1,23 @@
PROG= vinum
SRCS= v.c list.c parser.c util.c vext.h commands.c
MAN8= vinum.8
CFLAGS= -I${.CURDIR}/../../lkm/vinum -g -Wall -DDEBUG -DRAID5
LDADD+= -lutil -lreadline -ltermcap
# DPADD+= ${LIBKVM}
BINGRP= kmem
BINMODE= 2555
parser.c:
rm -f $@
ln -s ${.CURDIR}/../../lkm/vinum/$@ .
util.c: statetexts.h
rm -f $@
ln -s ${.CURDIR}/../../lkm/vinum/$@ .
statetexts.h:
(cd ${.CURDIR}/../../lkm/vinum; make $@)
.include <bsd.prog.mk>

1030
sbin/vinum/commands.c Normal file

File diff suppressed because it is too large Load Diff

743
sbin/vinum/list.c Normal file
View File

@ -0,0 +1,743 @@
/* list.c: vinum interface program, list routines
*/
/*-
* Copyright (c) 1997, 1998
* Nan Yang Computer Services Limited. All rights reserved.
*
* This software is distributed under the so-called ``Berkeley
* License'':
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Nan Yang Computer
* Services Limited.
* 4. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided ``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 company or contributors 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.
*
* $Id: list.c,v 1.12 1998/08/10 05:15:06 grog Exp grog $
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <netdb.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include "vinumhdr.h"
#include "vext.h"
/* Take a size in sectors and return a pointer to a
* string which represents the size best.
* If lj is != 0, return left justified, otherwise
* in a fixed 10 character field suitable for
* columnar printing.
*
* Note this uses a static string: it's only intended to
* be used immediately for printing */
char *
roughlength(long long bytes, int lj)
{
static char description[16];
if (bytes > (long long) MEGABYTE * 10000) /* gigabytes */
sprintf(description, lj ? "%d GB" : "%10d GB", bytes / GIGABYTE);
else if (bytes > KILOBYTE * 10000) /* megabytes */
sprintf(description, lj ? "%d MB" : "%10d MB", bytes / MEGABYTE);
else if (bytes > 10000) /* kilobytes */
sprintf(description, lj ? "%d kB" : "%10d kB", bytes / KILOBYTE);
else /* bytes */
sprintf(description, lj ? "%d B" : "%10d B", bytes);
return description;
}
void
vinum_list(int argc, char *argv[], char *argv0[])
{
int object;
int i;
enum objecttype type;
if (argc == 0)
listconfig(); /* list everything */
else
for (i = 0; i < argc; i++) {
object = find_object(argv[i], &type); /* look for it */
if (vinum_li(object, type))
fprintf(stderr, "Can't find object: %s\n", argv[i]);
}
}
/* List an object */
int
vinum_li(int object, enum objecttype type)
{
switch (type) {
case drive_object:
vinum_ldi(object, recurse);
break;
case sd_object:
vinum_lsi(object, recurse);
break;
case plex_object:
vinum_lpi(object, recurse);
break;
case volume_object:
vinum_lvi(object, recurse);
break;
default:
return -1;
}
return 0;
}
void
vinum_ldi(int driveno, int recurse)
{
get_drive_info(&drive, driveno);
if (drive.state != drive_unallocated) {
if (verbose) {
printf("Drive %s:\tDevice %s\n",
drive.label.name,
drive.devicename);
printf("\t\tCreated on %s at %s",
drive.label.sysname,
ctime(&drive.label.date_of_birth.tv_sec));
printf("\t\tConfig last updated %s", /* care: \n at end */
ctime(&drive.label.last_update.tv_sec));
printf("\t\tSize: %16qd bytes (%qd MB)\n\t\tUsed: %16qd bytes (%qd MB)\n"
"\t\tAvailable: %11qd bytes (%d MB)\n",
drive.label.drive_size, /* bytes used */
(drive.label.drive_size / MEGABYTE),
drive.label.drive_size - drive.sectors_available * DEV_BSIZE,
(drive.label.drive_size - drive.sectors_available * DEV_BSIZE) / MEGABYTE,
drive.sectors_available * DEV_BSIZE,
(int) (drive.sectors_available * DEV_BSIZE / MEGABYTE));
printf("\t\tState: %s\n", drive_state(drive.state));
if (drive.lasterror != 0)
printf("\t\tLast error: %s\n", strerror(drive.lasterror));
else
printf("\t\tLast error: none\n");
if (Verbose) { /* print the free list */
int fe; /* freelist entry */
struct drive_freelist freelist;
struct ferq { /* request to pass to ioctl */
int driveno;
int fe;
} *ferq = (struct ferq *) &freelist;
printf("\t\tFree list contains %d entries:\n\t\t Offset\t Size\n",
drive.freelist_entries);
for (fe = 0; fe < drive.freelist_entries; fe++) {
ferq->driveno = drive.driveno;
ferq->fe = fe;
if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) {
fprintf(stderr,
"Can't get free list element %d: %s\n",
fe,
strerror(errno));
longjmp(command_fail, -1);
}
printf("\t\t%9qd\t%9ld\n", freelist.offset, freelist.sectors);
}
}
} else
printf("D %-21s State: %s\tDevice %s\n",
drive.label.name,
drive_state(drive.state),
drive.devicename);
if (stats) {
printf("\t\tReads: \t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
drive.reads,
drive.bytes_read,
roughlength(drive.bytes_read, 1));
if (drive.reads != 0)
printf("\t\tAverage read:\t%16qd bytes\n", drive.bytes_read / drive.reads);
printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
drive.writes,
drive.bytes_written,
roughlength(drive.bytes_written, 1));
if (drive.writes != 0)
printf("\t\tAverage write:\t%16qd bytes\n",
drive.bytes_written / drive.writes);
}
}
}
void
vinum_ld(int argc, char *argv[], char *argv0[])
{
int i;
int driveno;
enum objecttype type;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
if (argc == 0) {
for (driveno = 0; driveno < vinum_conf.drives_used; driveno++)
vinum_ldi(driveno, recurse);
} else {
for (i = 0; i < argc; i++) {
driveno = find_object(argv[i], &type);
if (type == drive_object)
vinum_ldi(driveno, recurse);
else
fprintf(stderr, "%s is not a drive\n", argv[i]);
}
}
}
void
vinum_lvi(int volno, int recurse)
{
get_volume_info(&vol, volno);
if (vol.state != volume_unallocated) {
if (verbose) {
printf("Volume %s:\tSize: %qd bytes (%qd MB)\n"
"\t\tState: %s\n\t\tOpen by PID: %d\n\t\tFlags: %s%s\n",
vol.name,
((long long) vol.size) * DEV_BSIZE,
((long long) vol.size) * DEV_BSIZE / MEGABYTE,
volume_state(vol.state),
vol.pid,
(vol.flags & VF_WRITETHROUGH ? "writethrough " : ""),
(vol.flags & VF_RAW ? "raw" : ""));
printf("\t\t%d plexes\n\t\tRead policy: ", vol.plexes);
if (vol.preferred_plex < 0) /* round robin */
printf("round robin\n");
else {
get_plex_info(&plex, vol.plex[vol.preferred_plex]);
printf("plex %d (%s)\n", vol.preferred_plex, plex.name);
}
} else /* brief */
printf("V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
vol.name,
volume_state(vol.state),
vol.plexes,
roughlength(vol.size << DEV_BSHIFT, 0));
if (stats) {
printf("\t\tReads: \t%16qd\n\t\tRecovered:\t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
vol.reads,
vol.recovered_reads,
vol.bytes_read,
roughlength(vol.bytes_read, 1));
if (vol.reads != 0)
printf("\t\tAverage read:\t%16qd bytes\n", vol.bytes_read / vol.reads);
printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
vol.writes,
vol.bytes_written,
roughlength(vol.bytes_written, 1));
if (vol.writes != 0)
printf("\t\tAverage write:\t%16qd bytes\n",
vol.bytes_written / vol.writes);
printf("\t\tActive requests:\t%8d\n", vol.active);
}
if (vol.plexes > 0) {
int plexno;
if (Verbose) { /* brief list */
for (plexno = 0; plexno < vol.plexes; plexno++) {
get_plex_info(&plex, vol.plex[plexno]);
/* Just a brief summary here */
printf("\t\tPlex %2d:\t%s\t(%s), %s\n",
plexno,
plex.name,
plex_org(plex.organization),
roughlength(plex.length << DEV_BSHIFT, 0));
}
}
if (recurse) {
for (plexno = 0; plexno < vol.plexes; plexno++)
vinum_lpi(vol.plex[plexno], 0); /* first show the plexes */
for (plexno = 0; plexno < vol.plexes; plexno++) { /* then the subdisks */
get_plex_info(&plex, vol.plex[plexno]);
if (plex.subdisks > 0) {
int sdno;
for (sdno = 0; sdno < plex.subdisks; sdno++) {
get_plex_sd_info(&sd, vol.plex[plexno], sdno);
vinum_lsi(sd.sdno, 0);
}
}
}
if (verbose == 0) /* not verbose, but recursive */
printf("\n"); /* leave a line at the end of each hierarchy */
}
}
}
}
void
vinum_lv(int argc, char *argv[], char *argv0[])
{
int i;
int volno;
enum objecttype type;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
if (argc == 0)
for (volno = 0; volno < vinum_conf.volumes_used; volno++)
vinum_lvi(volno, recurse);
else {
for (i = 0; i < argc; i++) {
volno = find_object(argv[i], &type);
if (type == volume_object)
vinum_lvi(volno, recurse);
else
fprintf(stderr, "%s is not a volume\n", argv[i]);
}
}
}
void
vinum_lpi(int plexno, int recurse)
{
get_plex_info(&plex, plexno);
if (plex.state != plex_unallocated) {
if (verbose) {
printf("Plex %s:\tSize:\t%9qd bytes (%qd MB)\n\t\tSubdisks: %8d\n",
plex.name,
(long long) plex.length * DEV_BSIZE,
(long long) plex.length * DEV_BSIZE / MEGABYTE,
plex.subdisks);
printf("\t\tState: %s\n\t\tOrganization: %s",
plex_state(plex.state),
plex_org(plex.organization));
if ((plex.organization == plex_striped)
|| (plex.organization == plex_raid5))
printf("\tStripe size: %s\n", roughlength(plex.stripesize * DEV_BSIZE, 1));
else
printf("\n");
if (plex.volno >= 0) {
get_volume_info(&vol, plex.volno);
printf("\t\tPart of volume %s\n", vol.name);
}
if (plex.state == plex_reviving) {
printf("\t\tRevive pointer:\t\t%s\n",
roughlength(plex.revived << DEV_BSHIFT, 0));
printf("\t\tRevive blocksize:\t%s\n"
"\t\tRevive interval:\t%10d seconds\n",
roughlength(plex.revive_blocksize << DEV_BSHIFT, 0),
plex.revive_interval);
}
if (Verbose) { /* show the unmapped and defective parts */
int re; /* freelist entry */
struct plexregion region;
struct rerq { /* request to pass to ioctl */
int plexno; /* plex for the request */
int re; /* region */
} *rerq = (struct rerq *) &region;
if (plex.unmapped_regions) {
printf("\t\tPlex contains %d unmapped regions:\n\t\t Offset\t Size\n",
plex.unmapped_regions);
for (re = 0; re < plex.unmapped_regions; re++) {
rerq->plexno = plex.plexno;
rerq->re = re;
if (ioctl(superdev, VINUM_GETUNMAPPED, &region) < 0) {
fprintf(stderr,
"Can't get unmapped region %d: %s\n",
re,
strerror(errno));
longjmp(command_fail, -1);
}
printf("\t\t%9qd\t%9qd\n", region.offset, region.length);
}
}
if (plex.defective_regions) {
printf("\t\tPlex contains %d defective regions:\n\t\t Offset\t Size\n",
plex.defective_regions);
for (re = 0; re < plex.defective_regions; re++) {
rerq->plexno = plex.plexno;
rerq->re = re;
if (ioctl(superdev, VINUM_GETDEFECTIVE, &region) < 0) {
fprintf(stderr,
"Can't get defective region %d: %s\n",
re,
strerror(errno));
longjmp(command_fail, -1);
}
printf("\t\t%9qd\t%9qd\n", region.offset, region.length);
}
}
}
} else {
char *org = ""; /* organization */
switch (plex.organization) {
case plex_disorg: /* disorganized */
org = "??";
break;
case plex_concat: /* concatenated plex */
org = "C";
break;
case plex_striped: /* striped plex */
org = "S";
break;
case plex_raid5: /* RAID5 plex */
org = "R5";
break;
}
printf("P %-18s %2s State: %s\tSubdisks: %5d\tSize: %s\n",
plex.name,
org,
plex_state(plex.state),
plex.subdisks,
roughlength(plex.length << DEV_BSHIFT, 0));
}
if (stats) {
printf("\t\tReads: \t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
plex.reads,
plex.bytes_read,
roughlength(plex.bytes_read, 1));
if (plex.reads != 0)
printf("\t\tAverage read:\t%16qd bytes\n", plex.bytes_read / plex.reads);
printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
plex.writes,
plex.bytes_written,
roughlength(plex.bytes_written, 1));
if (plex.writes != 0)
printf("\t\tAverage write:\t%16qd bytes\n",
plex.bytes_written / plex.writes);
if ((plex.organization == plex_striped)
|| (plex.organization == plex_raid5))
printf("\t\tMultiblock:\t%16qd\n"
"\t\tMultistripe:\t%16qd\n",
plex.multiblock,
plex.multistripe);
}
if (plex.subdisks > 0) {
int sdno;
if (Verbose) {
printf("\n");
for (sdno = 0; sdno < plex.subdisks; sdno++) {
get_plex_sd_info(&sd, plexno, sdno);
printf("\t\tSubdisk %d:\t%s\n\t\t state: %s\tsize %11qd (%qd MB)\n",
sdno,
sd.name,
sd_state(sd.state),
(long long) sd.sectors * DEV_BSIZE,
(long long) sd.sectors * DEV_BSIZE / MEGABYTE);
if (plex.organization == plex_concat)
printf("\t\t\toffset %9ld (0x%lx)\n",
(long) sd.plexoffset,
(long) sd.plexoffset);
}
}
if (recurse)
for (sdno = 0; sdno < plex.subdisks; sdno++) {
get_plex_sd_info(&sd, plexno, sdno);
vinum_lsi(sd.sdno, 0);
}
}
}
}
void
vinum_lp(int argc, char *argv[], char *argv0[])
{
int i;
int plexno;
enum objecttype type;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
if (argc == 0) {
for (plexno = 0; plexno < vinum_conf.plexes_used; plexno++)
vinum_lpi(plexno, recurse);
} else {
for (i = 0; i < argc; i++) {
plexno = find_object(argv[i], &type);
if (type == plex_object)
vinum_lpi(plexno, recurse);
else
fprintf(stderr, "%s is not a plex\n", argv[i]);
}
}
}
void
vinum_lsi(int sdno, int recurse)
{
get_sd_info(&sd, sdno);
if (sd.state != sd_unallocated) {
if (verbose) {
printf("Subdisk %s:\n\t\tSize: %16qd bytes (%qd MB)\n\t\tState: %s\n",
sd.name,
(long long) sd.sectors * DEV_BSIZE,
(long long) sd.sectors / (MEGABYTE / DEV_BSIZE),
sd_state(sd.state));
if (sd.plexno >= 0) {
get_plex_info(&plex, sd.plexno);
printf("\t\tPlex %s", plex.name);
if (plex.organization == plex_concat)
printf(" at offset %qd\n", (long long) sd.plexoffset * DEV_BSIZE);
else
printf("\n");
}
} else {
printf("S %-21s State: %s\tPO: %s ",
sd.name,
sd_state(sd.state),
&(roughlength(sd.plexoffset << DEV_BSHIFT, 0))[2]); /* what a kludge! */
printf("Size: %s\n",
roughlength(sd.sectors << DEV_BSHIFT, 0));
}
if (stats) {
printf("\t\tReads: \t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
sd.reads,
sd.bytes_read,
roughlength(sd.bytes_read, 1));
if (sd.reads != 0)
printf("\t\tAverage read:\t%16qd bytes\n", sd.bytes_read / sd.reads);
printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
sd.writes,
sd.bytes_written,
roughlength(sd.bytes_written, 1));
if (sd.writes != 0)
printf("\t\tAverage write:\t%16qd bytes\n",
sd.bytes_written / sd.writes);
}
if (Verbose) {
get_drive_info(&drive, sd.driveno);
printf("\t\tDrive %15s\n\t\t\tDevice %-15s\n",
drive.label.name,
drive.devicename);
if (sd.driveoffset < 0)
printf("\t\t\tDrive offset\t *none*\n");
else
printf("\t\t\tDrive offset\t%9ld\n", (long) sd.driveoffset * DEV_BSIZE);
}
if (recurse)
vinum_ldi(sd.driveno, recurse);
if (verbose)
printf("\n"); /* make it more readable */
}
}
void
vinum_ls(int argc, char *argv[], char *argv0[])
{
int i;
int sdno;
/* Structures to read kernel data into */
struct _vinum_conf vinum_conf;
enum objecttype type;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
if (argc == 0) {
for (sdno = 0; sdno < vinum_conf.subdisks_used; sdno++)
vinum_lsi(sdno, recurse);
} else { /* specific subdisks */
for (i = 0; i < argc; i++) {
sdno = find_object(argv[i], &type);
if (type == sd_object)
vinum_lsi(sdno, recurse);
else
fprintf(stderr, "%s is not a subdisk\n", argv[i]);
}
}
}
/* List the complete configuration.
* XXX Change this to specific lists */
void
listconfig()
{
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
printf("Configuration summary\n\n");
printf("Drives:\t\t%d (%d configured)\n", vinum_conf.drives_used, vinum_conf.drives_allocated);
printf("Volumes:\t%d (%d configured)\n", vinum_conf.volumes_used, vinum_conf.volumes_allocated);
printf("Plexes:\t\t%d (%d configured)\n", vinum_conf.plexes_used, vinum_conf.plexes_allocated);
printf("Subdisks:\t%d (%d configured)\n\n", vinum_conf.subdisks_used, vinum_conf.subdisks_allocated);
vinum_ld(0, NULL, NULL);
printf("\n");
vinum_lv(0, NULL, NULL);
printf("\n");
vinum_lp(0, NULL, NULL);
printf("\n");
vinum_ls(0, NULL, NULL);
}
void
vinum_info(int argc, char *argv[], char *argv0[])
{
struct meminfo meminfo;
struct mc malloced;
int i;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
printf("Flags: 0x%x\t%d opens\n", vinum_conf.flags, vinum_conf.opencount);
if (ioctl(superdev, VINUM_MEMINFO, &meminfo) < 0) {
perror("Can't get information");
return;
}
printf("Total of %d blocks malloced, total memory: %d\nMaximum allocs: %8d, malloc table at 0x%08x\n",
meminfo.mallocs,
meminfo.total_malloced,
meminfo.highwater,
(int) meminfo.malloced);
if (Verbose)
for (i = 0; i < meminfo.mallocs; i++) {
malloced.seq = i;
if (ioctl(superdev, VINUM_MALLOCINFO, &malloced) < 0) {
perror("Can't get information");
return;
}
if (!(i & 63))
printf("Block\tSequence\t size\t address\t line\t\tfile\n\n");
printf("%6d\t%6d\t\t%6d\t0x%08x\t%6d\t\t%s\n",
i,
malloced.seq,
malloced.size,
(int) malloced.address,
malloced.line,
(char *) &malloced.file);
}
}
/* Print config file to a file. This is a userland version
* of kernel format_config */
void
vinum_printconfig(int argc, char *argv[], char *argv0[])
{
FILE *of;
struct utsname uname_s;
time_t now;
int i;
int j;
struct volume vol;
struct plex plex;
struct sd sd;
struct drive drive;
if (argc != 1) {
fprintf(stderr, "Usage: \tprintconfig <outfile>\n");
return;
}
of = fopen(argv[0], "w");
if (of == NULL) {
fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno));
return;
}
uname(&uname_s); /* get our system name */
time(&now); /* and the current time */
fprintf(of,
"# Vinum configuration of %s, saved at %s",
uname_s.nodename,
ctime(&now)); /* say who did it */
for (i = 0; i < vinum_conf.drives_used; i++) {
get_drive_info(&drive, i);
if (drive.state != drive_unallocated) {
fprintf(of,
"drive %s device %s\n",
drive.label.name,
drive.devicename);
}
}
for (i = 0; i < vinum_conf.volumes_used; i++) {
get_volume_info(&vol, i);
if (vol.state != volume_unallocated) {
if (vol.preferred_plex >= 0) /* preferences, */
fprintf(of,
"volume %s readpol prefer %s",
vol.name,
vinum_conf.plex[vol.preferred_plex].name);
else /* default round-robin */
fprintf(of, "volume %s", vol.name);
}
}
/* Then the plex configuration */
for (i = 0; i < vinum_conf.plexes_used; i++) {
get_volume_info(&vol, i);
if (plex.state != plex_unallocated) {
fprintf(of, "plex name %s state %s org %s ",
plex.name,
plex_state(plex.state),
plex_org(plex.organization));
if ((plex.organization == plex_striped)
) {
fprintf(of, "%db ", (int) plex.stripesize);
}
if (plex.volno >= 0) { /* we have a volume */
get_volume_info(&vol, plex.volno);
fprintf(of, "vol %s ", vol.name);
}
for (j = 0; j < plex.subdisks; j++) {
get_plex_sd_info(&sd, i, j);
fprintf(of, " sd %s", sd.name);
}
fprintf(of, "\n");
}
}
/* And finally the subdisk configuration */
for (i = 0; i < vinum_conf.subdisks_used; i++) {
get_sd_info(&sd, i);
if (sd.state != sd_unallocated) {
get_drive_info(&drive, sd.driveno);
get_plex_info(&plex, sd.plexno);
fprintf(of,
"sd name %s drive %s plex %s len %qdb driveoffset %qdb plexoffset %qdb\n",
sd.name,
drive.label.name,
plex.name,
sd.sectors,
sd.driveoffset,
sd.plexoffset);
}
}
}

561
sbin/vinum/v.c Normal file
View File

@ -0,0 +1,561 @@
/* vinum.c: vinum interface program */
/*-
* Copyright (c) 1997, 1998
* Nan Yang Computer Services Limited. All rights reserved.
*
* This software is distributed under the so-called ``Berkeley
* License'':
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Nan Yang Computer
* Services Limited.
* 4. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided ``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 company or contributors 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.
*
*/
/* $Id: v.c,v 1.22 1998/08/11 07:44:54 grog Exp grog $ */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <libutil.h>
#include <netdb.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "vinumhdr.h"
#include "vext.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <readline/history.h>
#include <readline/readline.h>
FILE *cf; /* config file handle */
char buffer[BUFSIZE]; /* buffer to read in to */
int line = 0; /* stdin line number for error messages */
int file_line = 0; /* and line in input file (yes, this is tacky) */
int inerror; /* set to 1 to exit after end of config file */
/* flags */
#if DEBUG
int debug = 0; /* debug flag, usage varies */
#endif
int force = 0; /* set to 1 to force some dangerous ops */
int verbose = 0; /* set verbose operation */
int Verbose = 0; /* set very verbose operation */
int recurse = 0; /* set recursion */
int stats = 0; /* show statistics */
/* Structures to read kernel data into */
struct _vinum_conf vinum_conf; /* configuration information */
struct volume vol;
struct plex plex;
struct sd sd;
struct drive drive;
jmp_buf command_fail; /* return on a failed command */
int superdev; /* vinum super device */
#define ofs(x) ((void *) (& ((struct confdata *) 0)->x)) /* offset of x in struct confdata */
/* create description-file
Create a volume as described in description-file
modify description-file
Modify the objects as described in description-file
list [-r] [volume | plex | subdisk]
List information about specified objects
set [-f] state volume | plex | subdisk | disk
Set the state of the object to state
rm [-f] [-r] volume | plex | subdisk
Remove an object
start [volume | plex | subdisk]
Allow the system to access the objects
stop [-f] [volume | plex | subdisk]
Terminate access the objects
*/
char *token[MAXARGS]; /* pointers to individual tokens */
int tokens; /* number of tokens */
int
main(int argc, char *argv[])
{
superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open it */
if (superdev < 0) { /* no go */
if (errno == ENOENT) /* we don't have our node, */
make_devices(); /* create them first */
if (superdev < 0) {
perror("Can't open " VINUM_SUPERDEV_NAME);
return 1;
}
}
if (argc > 1) { /* we have a command on the line */
if (setjmp(command_fail) != 0) /* long jumped out */
return -1;
parseline(argc - 1, &argv[1]); /* do it */
} else {
for (;;) { /* ugh */
char *c;
setjmp(command_fail); /* come back here on catastrophic failure */
c = readline("vinum -> "); /* get an input */
if (c == NULL) { /* EOF or error */
if (ferror(stdin)) {
fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno);
return 1;
} else { /* EOF */
printf("\n");
return 0;
}
} else if (*c) { /* got something there */
add_history(c); /* save it in the history */
strcpy(buffer, c); /* put it where we can munge it */
free(c);
line++; /* count the lines */
tokens = tokenize(buffer, token);
/* got something potentially worth parsing */
if (tokens)
parseline(tokens, token); /* and do what he says */
}
}
}
return 0; /* normal completion */
}
#define FUNKEY(x) { kw_##x, &vinum_##x } /* create pair "kw_foo", vinum_foo */
struct funkey {
enum keyword kw;
void (*fun) (int argc, char *argv[], char *arg0[]);
} funkeys[] = {
FUNKEY(create),
FUNKEY(read),
#ifdef DEBUG
FUNKEY(debug),
#endif
FUNKEY(volume),
FUNKEY(plex),
FUNKEY(sd),
FUNKEY(drive),
FUNKEY(modify),
FUNKEY(list),
FUNKEY(ld),
FUNKEY(ls),
FUNKEY(lp),
FUNKEY(lv),
FUNKEY(info),
FUNKEY(set),
FUNKEY(init),
FUNKEY(label),
FUNKEY(resetconfig),
FUNKEY(rm),
FUNKEY(attach),
FUNKEY(detach),
FUNKEY(rename),
FUNKEY(replace),
FUNKEY(printconfig),
FUNKEY(start),
FUNKEY(stop),
FUNKEY(resetstats)
};
/* Take args arguments at argv and attempt to perform the operation specified */
void
parseline(int args, char *argv[])
{
int i;
int j;
enum keyword command; /* command to execute */
if ((args == 0) /* empty line */
||(*argv[0] == '#')) /* or a comment, */
return;
if (args == MAXARGS) { /* too many arguments, */
fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]);
return;
}
command = get_keyword(argv[0], &keyword_set);
force = 0; /* initialize flags */
verbose = 0; /* initialize flags */
Verbose = 0; /* initialize flags */
recurse = 0; /* initialize flags */
stats = 0; /* initialize flags */
/* First handle generic options */
for (i = 1; (i < args) && (argv[i][0] == '-'); i++) { /* while we have flags */
for (j = 1; j < strlen(argv[i]); j++)
switch (argv[i][j]) {
#if DEBUG
case 'd': /* -d: debug */
debug = 1;
break;
#endif
case 'f': /* -f: force */
force = 1;
break;
case 'v': /* -v: verbose */
verbose++;
break;
case 'V': /* -V: Very verbose */
verbose++;
Verbose++;
break;
case 'r': /* -r: recurse */
recurse = 1;
break;
case 's': /* -s: show statistics */
stats = 1;
break;
default:
fprintf(stderr, "Invalid flag: %s\n", argv[i]);
}
}
/* Pass what we have left to the command to handle it */
for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) {
if (funkeys[j].kw == command) { /* found the command */
funkeys[j].fun(args - i, &argv[i], &argv[0]);
return;
}
}
fprintf(stderr, "Unknown command: %s\n", argv[0]);
}
void
get_drive_info(struct drive *drive, int index)
{
*(int *) drive = index; /* put in drive to hand to driver */
if (ioctl(superdev, VINUM_DRIVECONFIG, drive) < 0) {
fprintf(stderr,
"Can't get config for drive %d: %s\n",
index,
strerror(errno));
longjmp(command_fail, -1);
}
}
void
get_sd_info(struct sd *sd, int index)
{
*(int *) sd = index; /* put in sd to hand to driver */
if (ioctl(superdev, VINUM_SDCONFIG, sd) < 0) {
fprintf(stderr,
"Can't get config for subdisk %d: %s\n",
index,
strerror(errno));
longjmp(command_fail, -1);
}
}
/* Get the contents of the sd entry for subdisk <sdno>
* of the specified plex. */
void
get_plex_sd_info(struct sd *sd, int plexno, int sdno)
{
((int *) sd)[0] = plexno;
((int *) sd)[1] = sdno; /* pass parameters */
if (ioctl(superdev, VINUM_PLEXSDCONFIG, sd) < 0) {
fprintf(stderr,
"Can't get config for subdisk %d (part of plex %d): %s\n",
sdno,
plexno,
strerror(errno));
longjmp(command_fail, -1);
}
}
void
get_plex_info(struct plex *plex, int index)
{
*(int *) plex = index; /* put in plex to hand to driver */
if (ioctl(superdev, VINUM_PLEXCONFIG, plex) < 0) {
fprintf(stderr,
"Can't get config for plex %d: %s\n",
index,
strerror(errno));
longjmp(command_fail, -1);
}
}
void
get_volume_info(struct volume *volume, int index)
{
*(int *) volume = index; /* put in volume to hand to driver */
if (ioctl(superdev, VINUM_VOLCONFIG, volume) < 0) {
fprintf(stderr,
"Can't get config for volume %d: %s\n",
index,
strerror(errno));
longjmp(command_fail, -1);
}
}
/* Create the device nodes for vinum objects */
void
make_devices(void)
{
int volno;
int plexno;
int sdno;
int driveno;
char filename[PATH_MAX]; /* for forming file names */
if (superdev >= 0) /* super device open */
close(superdev);
system("rm -rf " VINUM_DIR " " VINUM_RDIR); /* remove the old directories */
system("mkdir -p " VINUM_DIR "/drive " /* and make them again */
VINUM_DIR "/plex "
VINUM_DIR "/sd "
VINUM_DIR "/vol "
VINUM_DIR "/rvol "
VINUM_RDIR);
if (mknod(VINUM_SUPERDEV_NAME,
S_IRWXU | S_IFBLK, /* block device, user only */
VINUM_SUPERDEV) < 0)
fprintf(stderr, "Can't create %s: %s\n", VINUM_SUPERDEV_NAME, strerror(errno));
superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open the super device */
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
/* First, create directories for the volumes */
for (volno = 0; volno < vinum_conf.volumes_used; volno++) {
dev_t voldev;
dev_t rvoldev;
get_volume_info(&vol, volno);
if (vol.state != volume_unallocated) { /* we could have holes in our lists */
voldev = VINUMBDEV(volno, 0, 0, VINUM_VOLUME_TYPE); /* create a block device number */
rvoldev = VINUMCDEV(volno, 0, 0, VINUM_VOLUME_TYPE); /* and a character device */
/* Create /dev/vinum/<myvol> */
sprintf(filename, VINUM_DIR "/%s", vol.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, voldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create /dev/rvinum/<myvol> */
sprintf(filename, VINUM_RDIR "/%s", vol.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, rvoldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create /dev/vinum/r<myvol> XXX until we fix fsck and friends */
sprintf(filename, VINUM_DIR "/r%s", vol.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, rvoldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create /dev/vinum/vol/<myvol> */
sprintf(filename, VINUM_DIR "/vol/%s", vol.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, voldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create /dev/vinum/rvol/<myvol> */
sprintf(filename, VINUM_DIR "/rvol/%s", vol.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, rvoldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create /dev/vinum/vol/<myvol>.plex/ */
sprintf(filename, VINUM_DIR "/vol/%s.plex", vol.name);
if (mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Now create device entries for the plexes in
* /dev/vinum/<vol>.plex/ and /dev/vinum/plex */
for (plexno = 0; plexno < vol.plexes; plexno++) {
dev_t plexdev;
get_plex_info(&plex, vol.plex[plexno]);
if (plex.state != plex_unallocated) {
plexdev = VINUMBDEV(volno, plexno, 0, VINUM_PLEX_TYPE);
/* Create device /dev/vinum/vol/<vol>.plex/<plex> */
sprintf(filename, VINUM_DIR "/vol/%s.plex/%s", vol.name, plex.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, plexdev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* And /dev/vinum/plex/<plex> */
sprintf(filename, VINUM_DIR "/plex/%s", plex.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, plexdev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create directory /dev/vinum/vol/<vol>.plex/<plex>.sd */
sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd", vol.name, plex.name);
if (mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* Create the contents of /dev/vinum/<vol>.plex/<plex>.sd */
for (sdno = 0; sdno < plex.subdisks; sdno++) {
dev_t sddev;
get_plex_sd_info(&sd, vol.plex[plexno], sdno);
if (sd.state != sd_unallocated) {
sddev = VINUMBDEV(volno, plexno, sdno, VINUM_SD_TYPE);
/* Create /dev/vinum/vol/<vol>.plex/<plex>.sd/<sd> */
sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd/%s", vol.name, plex.name, sd.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, sddev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
/* And /dev/vinum/sd/<sd> */
sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, sddev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
}
}
}
}
}
}
/* Drives. Do this later (both logical and physical names) XXX */
for (driveno = 0; driveno < vinum_conf.drives_used; driveno++) {
get_drive_info(&drive, driveno);
if (drive.state != drive_unallocated) {
sprintf(filename, "ln -s %s " VINUM_DIR "/drive/%s", drive.devicename, drive.label.name);
system(filename);
}
}
}
/* Find the object "name". Return object type at type,
* and the index as the return value.
* If not found, return -1 and invalid_object.
*/
int
find_object(const char *name, enum objecttype *type)
{
int object;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
*type = invalid_object;
return -1;
}
/* Search the drive table */
for (object = 0; object < vinum_conf.drives_used; object++) {
get_drive_info(&drive, object);
if (strcmp(name, drive.label.name) == 0) {
*type = drive_object;
return object;
}
}
/* Search the subdisk table */
for (object = 0; object < vinum_conf.subdisks_used; object++) {
get_sd_info(&sd, object);
if (strcmp(name, sd.name) == 0) {
*type = sd_object;
return object;
}
}
/* Search the plex table */
for (object = 0; object < vinum_conf.plexes_used; object++) {
get_plex_info(&plex, object);
if (strcmp(name, plex.name) == 0) {
*type = plex_object;
return object;
}
}
/* Search the volume table */
for (object = 0; object < vinum_conf.volumes_used; object++) {
get_volume_info(&vol, object);
if (strcmp(name, vol.name) == 0) {
*type = volume_object;
return object;
}
}
/* Didn't find the name: invalid */
*type = invalid_object;
return -1;
}
/* Continue reviving a plex in the background */
void
continue_revive(int plexno)
{
struct plex plex;
pid_t pid;
get_plex_info(&plex, plexno);
#if DEBUG
if (debug)
pid = 0; /* wander through into the "child" process */
else
pid = fork(); /* do this in the background */
#endif
if (pid == 0) { /* we're the child */
struct _ioctl_reply reply;
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
syslog(LOG_INFO | LOG_KERN, "reviving plex %s", plex.name);
for (reply.error = EAGAIN; reply.error == EAGAIN;) {
message->index = plexno; /* pass plex number */
message->type = plex_object; /* and type of object */
message->state = object_up;
ioctl(superdev, VINUM_SETSTATE, message);
}
if (reply.error) {
syslog(LOG_ERR | LOG_KERN,
"can't revive plex %s: %s",
plex.name,
reply.msg[0] ? reply.msg : strerror(reply.error));
exit(1);
} else {
get_plex_info(&plex, plexno); /* update the info */
syslog(LOG_INFO | LOG_KERN, "plex %s is %s", plex.name, plex_state(plex.state));
exit(0);
}
} else if (pid < 0) /* couldn't fork? */
fprintf(stderr, "Can't continue reviving %s: %s\n", plex.name, strerror(errno));
else
printf("Reviving %s in the background\n", plex.name);
}

119
sbin/vinum/vext.h Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 1997 Nan Yang Computer Services Limited
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project
* by Jason R. Thorpe.
* 4. 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.
*/
/* $Id: vext.h,v 1.9 1998/08/11 03:06:02 grog Exp grog $ */
#define MAXARGS 64 /* maximum number of args on a line */
#define PLEXINITSIZE MAXPHYS /* block size to write when initializing */
enum {
KILOBYTE = 1024,
MEGABYTE = 1048576,
GIGABYTE = 1073741824
};
/* Prototype declarations */
void parseline(int c, char *args[]); /* parse a line with c parameters at args */
void checkentry(int index);
int haveargs(int); /* check arg, error message if not valid */
void vinum_create(int argc, char *argv[], char *arg0[]);
void vinum_read(int argc, char *argv[], char *arg0[]);
void vinum_modify(int argc, char *argv[], char *arg0[]);
void vinum_volume(int argc, char *argv[], char *arg0[]);
void vinum_plex(int argc, char *argv[], char *arg0[]);
void vinum_sd(int argc, char *argv[], char *arg0[]);
void vinum_drive(int argc, char *argv[], char *arg0[]);
void vinum_list(int argc, char *argv[], char *arg0[]);
void vinum_info(int argc, char *argv[], char *arg0[]);
void vinum_set(int argc, char *argv[], char *arg0[]);
void vinum_rm(int argc, char *argv[], char *arg0[]);
void vinum_init(int argc, char *argv[], char *arg0[]);
void vinum_resetconfig(int argc, char *argv[], char *arg0[]);
void vinum_start(int argc, char *argv[], char *arg0[]);
void continue_revive(int plexno);
void vinum_stop(int argc, char *argv[], char *arg0[]);
void reset_volume_stats(int volno, int recurse);
void reset_plex_stats(int plexno, int recurse);
void reset_sd_stats(int sdno, int recurse);
void reset_drive_stats(int driveno);
void vinum_resetstats(int argc, char *argv[], char *arg0[]);
void vinum_attach(int argc, char *argv[], char *argv0[]);
void vinum_detach(int argc, char *argv[], char *argv0[]);
void vinum_rename(int argc, char *argv[], char *argv0[]);
void vinum_rename_2(char *, char *);
void vinum_replace(int argc, char *argv[], char *argv0[]);
void vinum_printconfig(int argc, char *argv[], char *argv0[]);
void vinum_label(int argc, char *argv[], char *arg0[]);
void vinum_ld(int argc, char *argv[], char *arg0[]);
void vinum_ls(int argc, char *argv[], char *arg0[]);
void vinum_lp(int argc, char *argv[], char *arg0[]);
void vinum_lv(int argc, char *argv[], char *arg0[]);
#ifdef DEBUG
void vinum_debug(int argc, char *argv[], char *arg0[]);
#endif
void make_devices(void);
void get_drive_info(struct drive *drive, int index);
void get_sd_info(struct sd *sd, int index);
void get_plex_sd_info(struct sd *sd, int plexno, int sdno);
void get_plex_info(struct plex *plex, int index);
void get_volume_info(struct volume *volume, int index);
int find_object(const char *name, enum objecttype *type);
char *lltoa(long long l, char *s);
void vinum_ldi(int, int);
void vinum_lvi(int, int);
void vinum_lpi(int, int);
void vinum_lsi(int, int);
int vinum_li(int object, enum objecttype type);
char *roughlength(long long bytes, int);
u_int64_t sizespec(char *spec);
extern int force; /* set to 1 to force some dangerous ops */
extern int verbose; /* set verbose operation */
extern int Verbose; /* very verbose operation */
extern int recurse; /* set recursion */
extern int stats; /* show statistics */
/* Structures to read kernel data into */
extern struct _vinum_conf vinum_conf; /* configuration information */
extern struct volume vol;
extern struct plex plex;
extern struct sd sd;
extern struct drive drive;
extern jmp_buf command_fail; /* return on a failed command */
extern int superdev; /* vinum super device */
extern int line; /* stdin line number for error messages */
extern int file_line; /* and line in input file (yes, this is tacky) */
extern char buffer[]; /* buffer to read in to */

899
sbin/vinum/vinum.8 Normal file
View File

@ -0,0 +1,899 @@
.\" Hey, Emacs, edit this file in -*- nroff-fill -*- mode
.\"
.Dd 11 July 1998
.Dt vinum 8
.Os FreeBSD
.Sh NAME
.Nm vinum
.Nd Logical Volume Manager control program
.Sh SYNOPSIS
.Nm
.Op command
.Sh COMMANDS
.Cd create
.Ar description-file
.in +1i
Create a volume as described in
.Ar description-file
.in
.\" XXX remove this
.Cd attach Ar plex Ar volume
.Op Nm rename
.Cd attach Ar subdisk Ar plex Ar [offset]
.Op Nm rename
.in +1i
Attach a plex to a volume, or a subdisk to a plex.
.in
.\" XXX remove this
.Cd debug
.in +1i
Cause the volume manager to enter the kernel debugger.
.in
.Cd detach
.Op Ar plex | subdisk
.in +1
Detach a plex or subdisk from the volume or plex to which it is attached.
.in
.Cd info
.Op Fl v
.in +1i
List information about volume manager state.
.in
.Cd init
.Op Fl v
.in +1i
.\" XXX
Initialize a plex by writing zeroes to all its subdisks.
.in
.Cd l
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op volume | plex | subdisk
.in +1i
List information about specified objects
.in
.Cd list
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op volume | plex | subdisk
.in +1i
List information about specified objects
.in
.Cd ld
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op volume
.in +1i
List information about drives
.in
.Cd ls
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op subdisk
.in +1i
List information about subdisks
.in
.Cd lp
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op plex
.in +1i
List information about plexes
.in
.Cd lv
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op volume
.in +1i
List information about volumes
.in
.Cd label
.Ar volume
.in +1i
Create a volume label
.in
.Cd read
.Ar disk-partition
.in +1i
Read the
.Nm
configuration from the specified disk partition.
.in
.Cd rename Op Fl r
.Ar [ drive | subdisk | plex | volume ]
.Ar newname
.in +1i
Change the name of the specified object.
.in
.Cd replace
.Ar [ subdisk | plex ]
.Ar newobject
.in +1i
Replace the object with an identical other object. XXX not implemented yet.
.in
.Cd resetconfig
.in +1i
Reset the complete
.Nm
configuration.
.in
.Cd resetstats
.Op Fl r
.Op volume | plex | subdisk
.in +1i
Reset statistisc counters for the specified objects, or for all objects if none
are specified.
.in
.Cd rm
.Op Fl f
.Op Fl r
.Ar volume | plex | subdisk
.in +1i
Remove an object
.in
.ig
XXX
.Cd set
.Op Fl f
.Ar state
.Ar volume | plex | subdisk | disk
.in +1i
Set the state of the object to \fIstate\fP\|
.in
..
.Cd start
.Op volume | plex | subdisk
.in +1i
Allow the system to access the objects
.in
.Cd stop
.Op Fl f
.Op volume | plex | subdisk
.in +1i
Terminate access the objects
.in
.Sh DESCRIPTION
.Nm
is a utility program to communicate with the \fBVinum\fP\| logical volume
manager. See
.Xr vinum 4
for more information about the volume manager.
.Xr vinum 8
is designed either for interactive use, when started without a command, or to
execute a single command if the command is supplied as arguments to
.Nm vinum.
.Ss OPTIONS
.Bl -hang
.It Cd -v
The
.Nm -v
option can be used with any command to request more detailed information. In
some cases, such as the
.Cd stop
command, it does not have any effect.
.Pp
.It Cd -V
The
.Nm -V
option can be used with any command to request more detailed information than
the
.Nm -v
option provides. As with the
.Nm -v
option, in some cases it does not have any effect.
.Pp
Other options are specific to the command. When specified directly on the
command line, they may be specified either before or after the command name.
For example, the following two commands are equivalent:
.Pp
.Bd -unfilled -offset indent
vinum -v stop -f sd0
vinum -v -f stop sd0
.Ed
.It Cd -f
The
.Nm -f
option overrides safety checks. Use with extreme care. This option is for
emergency use only. For example, the command
.Bd -unfilled -offset indent
rm -f myvolume
.Ed
.Pp
removes
.Nm myvolume
even if it is open. Any subsequent access to the volume will probably cause a
panic.
.It Cd -r
The
.Nm -r
(``recursive'') option is used by the list commands to display information not
only about the specified objects, but also about subordinate objects. For
example, in conjnction with the
.Nm lv
command, the
.Nm -r
option will also show information about the plexes and subdisks belonging to the
volume.
.It Cd -s
The
.Nm -s
option is used by the list commands to display statistical information.
.El
.Pp
.Ss COMMANDS IN DETAIL
.Pp
.Nm
commands perform the following functions:
.Bl -hang
.It Nm attach Ar plex Ar volume
.Op Nm rename
.sp -1v
.It Nm attach Ar subdisk Ar plex Ar [offset]
.Op Nm rename
.sp
.Nm
.Ar attach
inserts the specified plex or subdisk in a volume or plex. In the case of a
subdisk, an offset in the plex may be specified. If it is not, the subdisk will
be attached at the first possible location. After attaching a plex to a
non-empty volume,
.Nm
reintegrates the plex.
.Pp
If the keyword
.Nm rename
is specified,
.Nm
renames the object (and in the case of a plex, any subordinate subdisks) to fit
in with the default
.Nm
naming convention.
.It Nm create Ar description-file
.sp
.Nm
.Ar create
is used to create any object. In view of the relatively complicated
relationship and the potential dangers involved in creating a
.Nm
object, there is no interactive interface to this function. See the section
CONFIGURATION FILE below for more information.
.It Nm debug
.Pp
.Nm
.Ar debug
is used to enter the remote kernel debugger. It is only activated if
.Nm
is built with the DEBUG option. This option will stop the execution of the
operating system until the kernel debugger is exited. If there is no remote
connection for a kernel debugger, it will be necessary to reset the system and
reboot in order to leave the debugger.
.It Nm detach Op Fl f
.Ar plex
.sp -1v
.It Nm detach Op Fl f
.Ar subdisk
.sp
.Nm
.Ar detach
removes the specified plex or subdisk from the volume or plex to which it is
attached. If removing the object would impair the data integrity of the volume,
the operation will fail unless the
.Fl f
option is specified. If the object is named after the object above it (for
example, subdisk vol1.plex7.sd0 attached to plex vol1.plex7), the name will be
changed by prepending the text ``ex-'' (for example, ex-vol1.plex7.sd0). If
necessary, the name will be truncated in the process.
.It Nm info
.Pp
.Nm
.Ar info
displays information about
.Nm
memory usage. This is intended primarily for debugging. With the
.Fl v
option, it will give detailed information about the memory areas in use.
.\" XXX
.It Nm init Ar plex
.Pp
.Nm
.Ar init
initializes a plex by writing zeroes to all its subdisks. This is the only way
to ensure consistent data in a plex. You must perform this initialization
before using a RAID-5 plex. It is also recommended for other new plexes.
.Pp
.Nm
initializes all subdisks of a plex in parallel. Since this operation can take a
long time, it is performed in the background.
.Nm
prints a console message when the initialization is complete.
.It Nm list
.Op Fl r
.Op Fl V
.Op volume | plex | subdisk
.sp -1
.It Nm l
.Op Fl r
.Op Fl V
.Op volume | plex | subdisk
.sp -1
.It Nm ld
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op volume
.sp -1
.It Nm ls
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op subdisk
.sp -1
.It Nm lp
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op plex
.sp -1
.It Nm lv
.Op Fl r
.Op Fl s
.Op Fl v
.Op Fl V
.Op volume
.Pp
.Ar list
is used to show information about the specified object. If the argument is
omitted, information is shown about all objects known to
.Nm vinum .
The
.Ar l
command is a synonym for
.Ar list .
.Pp
The
.Fl r
option relates to volumes and plexes: if specified, it recursively lists
information for the subdisks and (for a volume) plexes subordinate to the
objects. The commands
.Ar lv ,
.Ar lp ,
.Ar ls
and
.Ar ld
commands list only volumes, plexes, subdisks and drives respectively. This is
particularly useful when used without parameters.
.Pp
The
.Fl s
option causes
.Nm
to output device statistics, the
.Op Fl v
(verbose) option causes some additional information to be output, and the
.Op Fl V
causes considerable additional information to be output.
.It Nm label
.Ar volume
.Pp
The
.Nm label
command writes a
.Ar ufs
style volume label on a volume. It is a simple alternative to an appropriate
call to
.Ar disklabel .
This is needed because some
.Ar ufs
commands still read the disk to find the label instead of using the correct
.Ar ioctl
call to access it.
.Nm
maintains a volume label separately from the volume data, so this command is not
needed for
.Ar newfs .
.Pp
.It Nm read
.Ar disk-partition
.Pp
The
.Nm read
command reads a previously created
.Nm
configuration from the specified disk partition.
.Nm
maintains an up-to-date copy of all configuration information on each of the
disk slices. You can specify any of the partitions in a configuration as the
parameter to this command.
.It Nm rename
.Op Fl r
.Ar [ drive | subdisk | plex | volume ]
.Ar newname
.Pp
Change the name of the specified object. If the
.Fl r
option is specified, subordinate objects will be named by the default rules:
plex names will be formed by appending .p\f(BInumber\fP to the volume name, and
subdisk names will be formed by appending .s\f(BInumber\fP to the plex name.
.It Nm replace
.Ar [ subdisk | plex ]
.Ar newobject
.Pp
Replace the object with an identical other object. XXX not implemented yet.
.It Nm resetconfig
.Pp
The
.Nm resetconfig
command completely obliterates the
.Nm
configuration on a system. Use this command only when you want to completely
delete the configuration.
.Nm
will ask for confirmation: you must type in the words NO FUTURE exactly
as shown:
.Bd -unfilled -offset indent
# \f(CBvinum resetconfig\f(CW
WARNING! This command will completely wipe out your vinum
configuration. All data will be lost. If you really want
to do this, enter the text
NO FUTURE
Enter text -> \f(BINO FUTURE\fP
Vinum configuration obliterated
.Ed
.ft R
.It Nm resetstats
.Op Fl r
.Op volume | plex | subdisk
.Pp
.Nm
maintains a number of statistical counters for each object. See the header file
.Fi vinumvar.h
for more information.
.\" XXX put it in here when it's finalized
Use the
.Nm resetstats
command to reset these counters. In conjunction with the
.Fl r
option,
.Nm
also resets the counters of subordinate objects.
.It Nm rm
.Op Fl f
.Op Fl r
.Ar volume | plex | subdisk
.Pp
.Nm rm
removes an object from the
.Nm
configuration. Once an object has been removed, there is no way to recover it.
Normally
.Nm
performs a large amount of consistency checking before removing an object. The
.Fl f
option tells
.Nm
to omit this checking and remove the object anyway. Use this option with great
care: it can result in total loss of data on a volume.
.Pp
Normally,
.Nm
refuses to remove a volume or plex if it has subordinate plexes or subdisks
respectively. You can tell
.Nm
to remove the object anyway by using the
.Fl f
flag, or you can cause
.Nm
to remove the subordinate objects as well by using the
.Fl r
(recursive) flag. If you remove a volume with the
.Fl r
flag, it will remove both the plexes and the subdisks which belong to the
plexes.
.ig
.It Nm set
.Op Fl f
.Ar state
.Ar volume | plex | subdisk | disk
.Nm set
sets the state of the specified object to one of the valid states (see OBJECT
STATES below). Normally
.Nm
performs a large amount of consistency checking before making the change. The
.Fl f
option tells
.Nm
to omit this checking and perform the change anyway. Use this option with great
care: it can result in total loss of data on a volume.
.\"XXX
.Nm This command has not yet been implemented.
..
.It Nm start
.Op volume | plex | subdisk
.Pp
.Nm start
starts the
.Nm
subsystem or one of its components. To start a plex in a multi-plex volume, the
data must be copied from another plex in the volume. This frequently takes a
long time and is done in the background.
.ig
XXX
When invoked without arguments, it checks
all disks connected to the system for BSD partitions (type 165) and scans the
slices for a
.Nm
slice, which it calls a \fIdrive\fR\|. The
.Nm
drive contains a header with all information about the data stored on the drive,
including the names of the other drives which are required in order to represent
plexes and volumes.
.\" XXX
.Nm The scan function has not yet been implemented.
..
.It Nm stop
.Op Fl f
.Op volume | plex | subdisk
.Pp
.Nm stop
disables access to the specified objects and any subordinate objects. It does
not remove the objects from the configuration. They can be accessed again after
a
.Nm start
command.
.Pp
By default,
.Nm
does not remove active objects. For example, you cannot remove a plex which is
attached to an active volume, and you cannot remove a volume which is open. The
.Fl f
option tells
.Nm
to omit this checking and remove the object anyway. Use this option with great
care and understanding: used incorrectly, it can result in serious data
corruption.
.El
.Ss CONFIGURATION FILE
.Nm
requires that all parameters to the
.Nm create
and
.Nm modify
commands must be in a configuration file. Entries in the configuration file
define volumes, plexes and subdisks, and may be in free format, except that each
entry must be on a single line.
.Pp
Some configuration file parameters specify a size (lengths, stripe sizes).
These lengths can be specified as bytes, as sectors of 512 bytes (by appending
the letter \f(CWb\fR), as kilobytes (by appending the letter \f(CWk\fR), as
megabytes (by appending the letter \f(CWm\fR) or as gigabytes (by appending the
letter \f(CWg\fR). These quantities represent the values 2**10, 2**20 and 2**30
respectively. For example, the value \f(CW16777216\fR bytes can also be written
as \f(CW16m\fR, \f(CW16384k\fR or \f(CW32768b\fR.
.Pp
The configuration file can contain the following entries:
.Pp
.Bl -hang
.It Nm volume
.Ar name
.Op options
.Pp
Define a volume with name
.Ar name .
.Pp
Options are:
.Pp
.TS H
tab(#) ;
l lw50 .
Option#Meaning
.TH N
T{
.Nm plex
.Ar plexname
T}#T{
Add the specified plex to the volume. If
.Ar plexname
is specified as
.Ar * ,
.Nm
will look for the definition of the plex as the next possible entry in the
configuration file after the definition of the volume.
T}
.sp
T{
.Nm readpol
.Ar policy
T}#T{
Define a
.Ar read policy
for the volume.
.Ar policy
may be either
.Nm round
or
.Nm prefer Ar plexname .
.Nm
satisfies a read request from only one of the plexes. A
.Ar round
read policy specifies that each read should be performed from a different plex
in \fIround-robin\fR\| fashion. A
.Ar prefer
read policy reads from the specified plex every time.
T}
.sp
T{
.Nm setupstate
T}#T{
When creating a multi-plex volume, assume that the contents of all the plexes
are consistent. This is normally not the case, and correctly you should use the
.Nm init
command to first bring them to a consistent state. In the case of striped and
concatenated plexes, however, it does not normally cause problems to leave them
inconsistent: when using a volume for a file system or a swap partition, the
previous contents of the disks are not of interest, so they may be ignored.
If you want to take this risk, use this keyword.
.Pp
Note that you \fImust\fP\| use the
.Nm init
command with RAID-5 plexes: otherwise extreme data corruption will result if one
subdisk fails.
T}
.fi
.TE
.It Nm plex
.Op options
.Pp
Define a plex. Unlike a volume, a plex does not need a name.
.Pp
.TS H
tab(#) ;
l lw50 .
Option#Meaning
.TH N
T{
.Nm name
.Ar plexname
T}#T{
Specify the name of the plex. Note that you must use the keyword
.Ar name
when naming a plex or subdisk.
T}
.sp
T{
.Nm org
.Ar organization
.Op stripesize
T}#T{
Specify the organization of the plex.
.Ar organization
can be one of
.Ar concat ,
.Ar striped
or
.Ar raid5 .
For
.Ar striped
and
.Ar raid5
plexes, the parameter
.Ar stripesize
must be specified, while for
.Ar concat
it must be omitted. For type
.Ar striped ,
it specifies the width of each stripe. For type
.Ar raid5 ,
it specifies the size of a group. A group is a portion of a plex which
stores the parity bits all in the same subdisk. It must be a factor of the plex size (in
other words, the result of dividing the plex size by the stripe size must be an
integer), and it must be a multiple of a disk sector (512 bytes).
T}
.Pp
#T{
A striped plex must have at least two subdisks (otherwise it is a concatenated
plex), and each must be the same size. A RAID-5 plex must have at least three
subdisks, and each must be the same size. In practice, a RAID-5 plex should
have at least 5 subdisks.
T}
.Pp
T{
.Nm volume
.Ar volname
T}#T{
Add the plex to the specified volume. If no
.Nm volume
keyword is specified, the plex will be added to the last volume mentioned in the
configuration file.
T}
.sp
T{
.Nm sd
.Ar sdname
.Ar offset
T}#T{
Add the specified subdisk to the plex at offset
.Ar offset .
T}
.br
.fi
.TE
.It Nm subdisk
.Op options
.Pp
Define a subdisk.
.Pp
.TS H
tab(#) ;
l lw50 .
Option#Meaning
.nf
.sp
T{
.Nm name
.Ar name
T}#T{
Specify the name of a subdisk. It is not necessary to specify a name for a
subdisk\(emsee OBJECT NAMING above. Note that you must specify the keyword
.Ar name
if you wish to name a subdisk.
T}
.sp
T{
.Nm plexoffset
.Ar offset
T}#T{
Specify the starting offset of the subdisk in the plex. If not specified,
.Nm
allocates the space immediately after the previous subdisk, if any, or otherwise
at the beginning of the plex.
T}
.sp
T{
.Nm driveoffset
.Ar offset
T}#T{
Specify the starting offset of the subdisk in the drive. If not specified,
.Nm
allocates the first contiguous
.Ar length
bytes of free space on the drive.
T}
.sp
T{
.Nm length
.Ar length
T}#T{
Specify the length of the subdisk. This keyword must be specified. There is no
default.
.Nm length
may be shortened to
.Nm len .
T}
.sp
T{
.Nm plex
.Ar plex
T}#T{
Specify the plex to which the subdisk belongs. By default, the subdisk belongs
to the last plex specified.
T}
.sp
T{
.Nm drive
.Ar drive
T}#T{
Specify the drive on which the subdisk resides. By default, the subdisk resides
on the last drive specified.
T}
.br
.fi
.TE
.El
.Sh EXAMPLE CONFIGURATION FILE
.nf
# Sample vinum configuration file
#
# Our drives
drive drive1 device /dev/sd1h
drive drive2 device /dev/sd2h
drive drive3 device /dev/sd3h
drive drive4 device /dev/sd4h
drive drive5 device /dev/sd5h
drive drive6 device /dev/sd6h
# A volume with one striped plex
volume tinyvol
plex org striped 32b
sd length 64m drive drive2
sd length 64m drive drive4
volume stripe
plex org striped 32b
sd length 512m drive drive2
sd length 512m drive drive4
# Two plexes
volume concat
plex org concat
sd length 100m drive drive2
sd length 50m drive drive4
plex org concat
sd length 100m drive drive4
# A volume with one striped plex and one concatenated plex
volume strcon
plex org striped 32b
sd length 100m drive drive2
sd length 100m drive drive4
plex org concat
sd length 100m drive drive2
sd length 50m drive drive4
# a volume with a RAID-5 and a striped plex
# note that the RAID-5 volume is longer by
# the length of one subdisk
volume vol5
plex org striped 64k
sd length 1000m drive drive2
sd length 1000m drive drive4
plex org raid5 32k
sd length 500m drive drive1
sd length 500m drive drive2
sd length 500m drive drive3
sd length 500m drive drive4
sd length 500m drive drive5
.fi
.Ss DRIVE LAYOUT CONSIDERATIONS
.Nm
drives are currently BSD disk partitions. They must be of type
.Ar unused
in order to avoid overwriting file systems. In later versions of
.Nm
this requirement will change to type
.Ar vinum .
.Nm
uses the first 265 sectors on each partition for configuration information, so
the maximum size of a subdisk is 265 sectors smaller than the drive.
.Sh BUGS
.Nm
is currently in alpha test. Many bugs can be expected. The configuration
mechanism is not yet fully functional.
.Sh FILES
.Ar /dev/vinum
- directory with device nodes for
.Nm
objects.
.br
.Ar /dev/vinum/control
- control device for
.Nm vinum
.br
.Ar /dev/vinum/plex
- directory containing device nodes for
.Nm
plexes.
.br
.Ar /dev/vinum/sd
- directory containing device nodes for
.Nm
subdisks.
.Sh SEE ALSO
.Xr vinum 4
.Sh AUTHOR
Greg Lehey
.Sh HISTORY
The
.Nm
command first appeared in FreeBSD 2.2.6.