#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <netdb.h>
#include <setjmp.h>
#include <fstab.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <dev/raid/vinum/vinumhdr.h>
#include "vext.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/resource.h>
FILE *cf;
FILE *hist;
char *historyfile;
char *dateformat;
char buffer[BUFSIZE];
int line = 0;
int file_line = 0;
int inerror;
#if VINUMDEBUG
int debug = 0;
#endif
int force = 0;
int interval = 0;
int vflag = 0;
int Verbose = 0;
int recurse = 0;
int sflag = 0;
int SSize = 0;
int dowait = 0;
char *objectname;
struct _vinum_conf vinum_conf;
struct volume vol;
struct plex plex;
struct sd sd;
struct drive drive;
jmp_buf command_fail;
int superdev;
void start_daemon(void);
char *token[MAXARGS];
int tokens;
int
main(int argc, char *argv[], char *envp[])
{
struct stat histstat;
if (modfind(VINUMMOD) < 0) {
if (kldload(VINUMMOD) < 0 || modfind(VINUMMOD) < 0) {
perror(VINUMMOD ": Kernel module not available");
return 1;
}
}
dateformat = getenv("VINUM_DATEFORMAT");
if (dateformat == NULL)
dateformat = "%e %b %Y %H:%M:%S";
historyfile = getenv("VINUM_HISTORY");
if (historyfile == NULL)
historyfile = DEFAULT_HISTORYFILE;
if (stat(historyfile, &histstat) == 0) {
if ((histstat.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr,
"Vinum history file %s must be a regular file\n",
historyfile);
exit(1);
}
} else if ((errno != ENOENT)
&&(errno != EROFS)) {
fprintf(stderr,
"Can't open %s: %s (%d)\n",
historyfile,
strerror(errno),
errno);
exit(1);
}
hist = fopen(historyfile, "a+");
if (hist != NULL) {
timestamp();
fprintf(hist, "*** " VINUMMOD " started ***\n");
fflush(hist);
}
superdev = open(VINUM_SUPERDEV_NAME, O_RDWR);
if (superdev < 0) {
if (errno == ENODEV) {
superdev = open(VINUM_WRONGSUPERDEV_NAME, O_RDWR);
if (superdev >= 0) {
#if VINUMDEBUG
fprintf(stderr,
"This program is compiled with debug support, but the kernel module does\n"
"not have debug support. This program must be matched with the kernel\n"
"module. Please alter /usr/src/sbin/" VINUMMOD "/Makefile and remove\n"
"the option -DVINUMDEBUG from the CFLAGS definition, or alternatively\n"
"edit /usr/src/sys/modules/" VINUMMOD "/Makefile and add the option\n"
"-DVINUMDEBUG to the CFLAGS definition. Then rebuild the component\n"
"of your choice with 'make clean all install'. If you rebuild the kernel\n"
"module, you must stop " VINUMMOD " and restart it\n");
#else
fprintf(stderr,
"This program is compiled without debug support, but the kernel module\n"
"includes debug support. This program must be matched with the kernel\n"
"module. Please alter /usr/src/sbin/" VINUMMOD "/Makefile and add\n"
"the option -DVINUMDEBUG to the CFLAGS definition, or alternatively\n"
"edit /usr/src/sys/modules/" VINUMMOD "/Makefile and remove the option\n"
"-DVINUMDEBUG from the CFLAGS definition. Then rebuild the component\n"
"of your choice with 'make clean all install'. If you rebuild the kernel\n"
"module, you must stop " VINUMMOD " and restart it\n");
#endif
return 1;
}
} else if (errno == ENOENT)
make_devices();
if (superdev < 0) {
perror("Can't open " VINUM_SUPERDEV_NAME);
return 1;
}
}
start_daemon();
if (argc > 1) {
if (setjmp(command_fail) != 0)
return -1;
parseline(argc - 1, &argv[1]);
} else {
if (setjmp(command_fail))
return 1;
setsigs();
for (;;) {
char *c;
int childstatus;
if (setjmp(command_fail) == 2)
fprintf(stderr, "*** interrupted ***\n");
while (wait4(-1, &childstatus, WNOHANG, NULL) > 0);
c = readline(VINUMMOD " -> ");
if (c == NULL) {
if (ferror(stdin)) {
fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno);
return 1;
} else {
printf("\n");
return 0;
}
} else if (*c) {
add_history(c);
strcpy(buffer, c);
free(c);
line++;
tokens = tokenize(buffer, token);
if (tokens)
parseline(tokens, token);
}
if (hist)
fflush(hist);
}
}
return 0;
}
void
vinum_quit(int argc, char *argv[], char *argv0[])
{
exit(0);
}
void
setsigs(void)
{
struct sigaction act;
act.sa_handler = catchsig;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
}
void
catchsig(int ignore)
{
longjmp(command_fail, 2);
}
#define FUNKEY(x) { kw_##x, &vinum_##x }
#define vinum_move vinum_mv
struct funkey {
enum keyword kw;
void (*fun) (int argc, char *argv[], char *arg0[]);
} funkeys[] = {
FUNKEY(create),
FUNKEY(read),
#ifdef VINUMDEBUG
FUNKEY(debug),
#endif
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(mv),
FUNKEY(move),
FUNKEY(attach),
FUNKEY(detach),
FUNKEY(rename),
FUNKEY(replace),
FUNKEY(printconfig),
FUNKEY(saveconfig),
FUNKEY(start),
FUNKEY(stop),
FUNKEY(makedev),
FUNKEY(help),
FUNKEY(quit),
FUNKEY(concat),
FUNKEY(stripe),
FUNKEY(raid4),
FUNKEY(raid5),
FUNKEY(mirror),
FUNKEY(setdaemon),
FUNKEY(readpol),
FUNKEY(resetstats),
FUNKEY(setstate),
FUNKEY(checkparity),
FUNKEY(rebuildparity),
FUNKEY(dumpconfig)
};
void
parseline(int args, char *argv[])
{
int i;
int j;
enum keyword command;
if (hist != NULL) {
timestamp();
for (i = 0; i < args; i++)
fprintf(hist, "%s ", argv[i]);
fputs("\n", hist);
}
if ((args == 0)
||(*argv[0] == '#'))
return;
if (args == MAXARGS) {
fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]);
return;
}
command = get_keyword(argv[0], &keyword_set);
dowait = 0;
force = 0;
vflag = 0;
Verbose = 0;
recurse = 0;
sflag = 0;
objectname = NULL;
for (i = 1; (i < args) && (argv[i][0] == '-'); i++) {
for (j = 1; j < strlen(argv[i]); j++)
switch (argv[i][j]) {
#if VINUMDEBUG
case 'd':
debug = 1;
break;
#endif
case 'f':
force = 1;
break;
case 'i':
interval = 0;
if (argv[i][j + 1] != '\0')
interval = atoi(&argv[i][j + 1]);
else if (args > (i + 1))
interval = atoi(argv[++i]);
if (interval == 0)
fprintf(stderr, "-i: no interval specified\n");
break;
case 'n':
if (i == args - 1) {
fprintf(stderr, "-n requires a name parameter\n");
return;
}
objectname = argv[++i];
j = strlen(argv[i]);
break;
case 'r':
recurse = 1;
break;
case 's':
sflag = 1;
break;
case 'S':
SSize = 0;
if (argv[i][j + 1] != '\0')
SSize = atoi(&argv[i][j + 1]);
else if (args > (i + 1))
SSize = atoi(argv[++i]);
if (SSize == 0)
fprintf(stderr, "-S: no size specified\n");
break;
case 'v':
vflag++;
break;
case 'V':
vflag++;
Verbose++;
break;
case 'w':
dowait = 1;
break;
default:
fprintf(stderr, "Invalid flag: %s\n", argv[i]);
}
}
for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) {
if (funkeys[j].kw == 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;
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;
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);
}
}
void
get_plex_sd_info(struct sd *sd, int plexno, int sdno)
{
((int *) sd)[0] = plexno;
((int *) sd)[1] = sdno;
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;
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;
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);
}
}
struct drive *
find_drive_by_devname(char *name)
{
int driveno;
char *devpath;
struct drive *drivep = NULL;
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return NULL;
}
devpath = getdevpath(name, 0);
for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
get_drive_info(&drive, driveno);
if (drive.state == drive_unallocated)
continue;
if (strcmp(drive.devicename, name) == 0) {
drivep = &drive;
break;
}
if (strcmp(drive.devicename, devpath) == 0) {
drivep = &drive;
break;
}
}
free(devpath);
return (drivep);
}
void
make_devices(void)
{
#if 0
int volno;
int plexno;
int sdno;
int driveno;
#endif
if (hist) {
timestamp();
fprintf(hist, "*** Created devices ***\n");
}
#if 0
system("rm -rf " VINUM_DIR);
system("mkdir -p " VINUM_DIR "/drive "
VINUM_DIR "/plex "
VINUM_DIR "/sd "
VINUM_DIR "/vol");
if (mknod(VINUM_SUPERDEV_NAME,
S_IRUSR | S_IWUSR | S_IFCHR,
makedev(VINUM_CDEV_MAJOR, VINUM_SUPERDEV)) < 0)
fprintf(stderr, "Can't create %s: %s\n", VINUM_SUPERDEV_NAME, strerror(errno));
if (mknod(VINUM_WRONGSUPERDEV_NAME,
S_IRUSR | S_IWUSR | S_IFCHR,
makedev(VINUM_CDEV_MAJOR, VINUM_WRONGSUPERDEV)) < 0)
fprintf(stderr, "Can't create %s: %s\n", VINUM_WRONGSUPERDEV_NAME, strerror(errno));
superdev = open(VINUM_SUPERDEV_NAME, O_RDWR);
if (mknod(VINUM_DAEMON_DEV_NAME,
S_IRUSR | S_IWUSR | S_IFCHR,
makedev(VINUM_CDEV_MAJOR, VINUM_DAEMON_DEV)) < 0)
fprintf(stderr, "Can't create %s: %s\n", VINUM_DAEMON_DEV_NAME, strerror(errno));
#endif
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
#if 0
for (volno = 0; volno < vinum_conf.volumes_allocated; volno++)
make_vol_dev(volno, 0);
for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++)
make_plex_dev(plexno, 0);
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++)
make_sd_dev(sdno);
for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
char filename[PATH_MAX];
get_drive_info(&drive, driveno);
if (drive.state > drive_referenced) {
sprintf(filename, "ln -s %s " VINUM_DIR "/drive/%s", drive.devicename, drive.label.name);
system(filename);
}
}
#endif
}
#if 0
void
make_vol_dev(int volno, int recurse)
{
dev_t voldev;
char filename[PATH_MAX];
int plexno;
get_volume_info(&vol, volno);
if (vol.state != volume_unallocated) {
voldev = VINUMDEV(volno, 0, 0, VINUM_VOLUME_TYPE);
sprintf(filename, VINUM_DIR "/%s", vol.name);
if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
sprintf(filename, VINUM_DIR "/vol/%s", vol.name);
if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, voldev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
if (vol.plexes > 0) {
sprintf(filename, VINUM_DIR "/vol/%s.plex", vol.name);
if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
}
if (recurse)
for (plexno = 0; plexno < vol.plexes; plexno++)
make_plex_dev(plex.plexno, recurse);
}
}
void
make_plex_dev(int plexno, int recurse)
{
dev_t plexdev;
char filename[PATH_MAX];
int sdno;
get_plex_info(&plex, plexno);
if (plex.state != plex_unallocated) {
plexdev = VINUM_PLEX(plexno);
sprintf(filename, VINUM_DIR "/plex/%s", plex.name);
if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
if (plex.volno >= 0) {
get_volume_info(&vol, plex.volno);
plexdev = VINUMDEV(plex.volno, plexno, 0, VINUM_PLEX_TYPE);
sprintf(filename, VINUM_DIR "/vol/%s.plex/%s", vol.name, plex.name);
if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, plexdev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd", vol.name, plex.name);
if (mkdir(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
}
if (recurse) {
for (sdno = 0; sdno < plex.subdisks; sdno++) {
get_plex_sd_info(&sd, plex.plexno, sdno);
make_sd_dev(sd.sdno);
}
}
}
}
void
make_sd_dev(int sdno)
{
dev_t sddev;
char filename[PATH_MAX];
get_sd_info(&sd, sdno);
if (sd.state != sd_unallocated) {
sddev = VINUM_SD(sdno);
sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
if (mknod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, sddev) < 0)
fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
}
}
#endif
void
vinum_makedev(int argc, char *argv[], char *arg0[])
{
make_devices();
}
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;
}
for (object = 0; object < vinum_conf.drives_allocated; object++) {
get_drive_info(&drive, object);
if (strcmp(name, drive.label.name) == 0) {
*type = drive_object;
return object;
}
}
for (object = 0; object < vinum_conf.subdisks_allocated; object++) {
get_sd_info(&sd, object);
if (strcmp(name, sd.name) == 0) {
*type = sd_object;
return object;
}
}
for (object = 0; object < vinum_conf.plexes_allocated; object++) {
get_plex_info(&plex, object);
if (strcmp(name, plex.name) == 0) {
*type = plex_object;
return object;
}
}
for (object = 0; object < vinum_conf.volumes_allocated; object++) {
get_volume_info(&vol, object);
if (strcmp(name, vol.name) == 0) {
*type = volume_object;
return object;
}
}
*type = invalid_object;
return -1;
}
void
continue_revive(int sdno)
{
struct sd sd;
pid_t pid;
get_sd_info(&sd, sdno);
if (dowait == 0)
pid = fork();
else
pid = 0;
if (pid == 0) {
struct _ioctl_reply reply;
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
openlog(VINUMMOD, LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
syslog(LOG_INFO | LOG_KERN, "reviving %s", sd.name);
setproctitle("reviving %s", sd.name);
for (reply.error = EAGAIN; reply.error == EAGAIN;) {
if (interval)
usleep(interval * 1000);
message->index = sdno;
message->type = sd_object;
message->state = object_up;
if (SSize != 0) {
if (SSize < 512)
SSize <<= DEV_BSHIFT;
message->blocksize = SSize;
} else
message->blocksize = DEFAULT_REVIVE_BLOCKSIZE;
ioctl(superdev, VINUM_SETSTATE, message);
}
if (reply.error) {
syslog(LOG_ERR | LOG_KERN,
"can't revive %s: %s",
sd.name,
reply.msg[0] ? reply.msg : strerror(reply.error));
if (dowait == 0)
exit(1);
} else {
get_sd_info(&sd, sdno);
syslog(LOG_INFO | LOG_KERN, "%s is %s", sd.name, sd_state(sd.state));
if (dowait == 0)
exit(0);
}
} else if (pid < 0)
fprintf(stderr, "Can't continue reviving %s: %s\n", sd.name, strerror(errno));
else
printf("Reviving %s in the background\n", sd.name);
}
void
start_daemon(void)
{
int pid;
int status;
int error;
pid = (int) fork();
if (pid == 0) {
close(superdev);
superdev = open(VINUM_DAEMON_DEV_NAME, O_RDWR);
if (superdev < 0) {
perror("Can't open " VINUM_DAEMON_DEV_NAME);
exit(1);
}
error = daemon(0, 0);
if (error != 0) {
fprintf(stderr, "Can't start daemon: %s (%d)\n", strerror(errno), errno);
exit(1);
}
setproctitle(VINUMMOD " daemon");
status = ioctl(superdev, VINUM_FINDDAEMON, NULL);
if (status != 0) {
ioctl(superdev, VINUM_DAEMON, &vflag);
syslog(LOG_ERR | LOG_KERN, "%s", strerror(errno));
exit(1);
}
exit(0);
} else if (pid < 0)
printf("Can't fork to check daemon\n");
}
void
timestamp(void)
{
struct timeval now;
struct tm *date;
char datetext[MAXDATETEXT];
time_t sec;
if (hist != NULL) {
if (gettimeofday(&now, NULL) != 0) {
fprintf(stderr, "Can't get time: %s\n", strerror(errno));
return;
}
sec = now.tv_sec;
date = localtime(&sec);
strftime(datetext, MAXDATETEXT, dateformat, date);
fprintf(hist, "%s.%06ld ", datetext, now.tv_usec);
}
}