Import base vinum userland sources
This commit is contained in:
parent
e4381fa521
commit
8644f7188f
23
sbin/vinum/Makefile
Normal file
23
sbin/vinum/Makefile
Normal 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
1030
sbin/vinum/commands.c
Normal file
File diff suppressed because it is too large
Load Diff
743
sbin/vinum/list.c
Normal file
743
sbin/vinum/list.c
Normal 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 *) ®ion;
|
||||
|
||||
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, ®ion) < 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, ®ion) < 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
561
sbin/vinum/v.c
Normal 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
119
sbin/vinum/vext.h
Normal 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
899
sbin/vinum/vinum.8
Normal 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.
|
Loading…
Reference in New Issue
Block a user