diff --git a/sbin/vinum/commands.c b/sbin/vinum/commands.c index 4ead132d29d7..ded3f78b9be8 100644 --- a/sbin/vinum/commands.c +++ b/sbin/vinum/commands.c @@ -1,1204 +1,1212 @@ /* commands.c: vinum interface program, main commands */ /*- * 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: commands.c,v 1.6 1999/03/23 03:40:07 grog Exp grog $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vext.h" #include #include #include #include #include #include #include static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen); void vinum_create(int argc, char *argv[], char *arg0[]) { int error; FILE *dfd; /* file descriptor for the config file */ char buffer[BUFSIZE]; /* read config file in here */ char commandline[BUFSIZE]; /* issue command from here */ struct _ioctl_reply *reply; int ioctltype; /* for ioctl call */ if (argc != 1) { /* wrong arg count */ fprintf(stderr, "Expecting 1 parameter, not %d\n", argc); return; } reply = (struct _ioctl_reply *) &buffer; dfd = fopen(argv[0], "r"); if (dfd == NULL) { /* no go */ fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno)); return; } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } file_line = 0; /* start with line 1 */ /* Parse the configuration, and add it to the global configuration */ for (;;) { /* love this style(9) */ char *configline; configline = fgets(buffer, BUFSIZE, dfd); if (configline == NULL) { if (ferror(dfd)) perror("Can't read config file"); break; } file_line++; /* count the lines */ if (verbose) printf("%4d: %s", file_line, buffer); strcpy(commandline, buffer); /* make a copy */ ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (!verbose) /* print this line anyway */ printf("%4d: %s", file_line, commandline); fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error)); /* XXX at the moment, we reset the config * lock on error, so try to get it again. * If we fail, don't cry again */ if (ioctl(superdev, VINUM_STARTCONFIG, &force)) /* can't get config? */ return; } } fclose(dfd); /* done with the config file */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); make_devices(); listconfig(); } /* Read vinum config from a disk */ void vinum_read(int argc, char *argv[], char *arg0[]) { int error; char buffer[BUFSIZE]; /* read config file in here */ struct _ioctl_reply *reply; int i; reply = (struct _ioctl_reply *) &buffer; if (argc < 1) { /* wrong arg count */ fprintf(stderr, "Usage: read drive [drive ...]\n"); return; } strcpy(buffer, "read "); for (i = 0; i < argc; i++) { /* each drive name */ strcat(buffer, argv[i]); strcat(buffer, " "); } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ fprintf(stderr, "Can't configure: %s (%d)\n", strerror(errno), errno); return; } ioctl(superdev, VINUM_CREATE, &buffer); if (reply->error != 0) { /* error in config */ fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error)); error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); } else { error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); make_devices(); } } void vinum_volume(int argc, char *argv[], char *arg0[]) { int i; char *line; struct _ioctl_reply *reply; line = arg0[0]; for (i = 0; i < argc; i++) line[strlen(line)] = ' '; /* remove the blocks */ ioctl(superdev, VINUM_CREATE, line); reply = (struct _ioctl_reply *) line; if (reply->error != 0) /* error in config */ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error)); } void vinum_plex(int argc, char *argv[], char *arg0[]) { int i; char *line; struct _ioctl_reply *reply; line = arg0[0]; for (i = 0; i < argc; i++) line[strlen(line)] = ' '; /* remove the blocks */ ioctl(superdev, VINUM_CREATE, line); reply = (struct _ioctl_reply *) line; if (reply->error != 0) /* error in config */ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error)); } void vinum_sd(int argc, char *argv[], char *arg0[]) { int i; char *line; struct _ioctl_reply *reply; line = arg0[0]; for (i = 0; i < argc; i++) line[strlen(line)] = ' '; /* remove the blocks */ ioctl(superdev, VINUM_CREATE, line); reply = (struct _ioctl_reply *) line; if (reply->error != 0) /* error in config */ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error)); } void vinum_drive(int argc, char *argv[], char *arg0[]) { int i; char *line; struct _ioctl_reply *reply; line = arg0[0]; for (i = 0; i < argc; i++) line[strlen(line)] = ' '; /* remove the blocks */ ioctl(superdev, VINUM_CREATE, line); reply = (struct _ioctl_reply *) line; if (reply->error != 0) /* error in config */ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error)); } #ifdef VINUMDEBUG void vinum_debug(int argc, char *argv[], char *arg0[]) { struct debuginfo info; if (argc > 0) { info.param = atoi(argv[0]); info.changeit = 1; } else { info.changeit = 0; sleep(2); /* give a chance to leave the window */ } ioctl(superdev, VINUM_DEBUG, (caddr_t) & info); } #endif void vinum_modify(int argc, char *argv[], char *arg0[]) { fprintf(stderr, "Modify command is currently not implemented\n"); } void vinum_set(int argc, char *argv[], char *arg0[]) { fprintf(stderr, "set is not implemented yet\n"); } void vinum_rm(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; if (argc == 0) /* start everything */ fprintf(stderr, "Usage: rm object [object...]\n"); else { /* start specified objects */ int index; enum objecttype type; for (index = 0; index < argc; index++) { object = find_object(argv[index], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[index]); else { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->force = force; /* do we want to force the operation? */ message->recurse = recurse; /* do we want to remove subordinates? */ ioctl(superdev, VINUM_REMOVE, message); if (reply.error != 0) { fprintf(stderr, "Can't remove %s: %s (%d)\n", argv[index], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); } else if (verbose) fprintf(stderr, "%s removed\n", argv[index]); } } } } void vinum_resetconfig(int argc, char *argv[], char *arg0[]) { char reply[32]; int error; printf(" WARNING! This command will completely wipe out your vinum configuration.\n" " All data will be lost. If you really want to do this, enter the text\n\n" " NO FUTURE\n" " Enter text -> "); fgets(reply, sizeof(reply), stdin); if (strcmp(reply, "NO FUTURE\n")) /* changed his mind */ printf("\n No change\n"); else { error = ioctl(superdev, VINUM_RESETCONFIG, NULL); /* trash config on disk */ if (error) { if (errno == EBUSY) fprintf(stderr, "Can't reset configuration: objects are in use\n"); else perror("Can't find vinum config"); } else { make_devices(); /* recreate the /dev/vinum hierarchy */ printf("\b Vinum configuration obliterated\n"); start_daemon(); /* then restart the daemon */ } } } /* Initialize a plex */ void vinum_init(int argc, char *argv[], char *arg0[]) { if (argc > 0) { /* initialize plexes */ int plexindex; int sdno; int plexno; int plexfh = NULL; /* file handle for plex */ pid_t pid; enum objecttype type; /* type returned */ struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; char filename[MAXPATHLEN]; /* create a file name here */ + /* Variables for use by children */ + int failed = 0; /* set if a child dies badly */ + int sdfh; /* and for subdisk */ + char zeros[PLEXINITSIZE]; + int count; /* write count */ + long long offset; /* offset in subdisk */ + long long sdsize; /* size of subdisk */ + for (plexindex = 0; plexindex < argc; plexindex++) { plexno = find_object(argv[plexindex], &type); /* find the object */ if (plexno < 0) printf("Can't find %s\n", argv[plexindex]); else if (type != plex_object) { /* XXX Consider doing this for all plexes in * a volume, etc. */ printf("%s is not a plex\n", argv[plexindex]); break; } else if (plex.state == plex_unallocated) /* not a real plex, */ printf("%s is not allocated, can't initialize\n", plex.name); else { sprintf(filename, VINUM_DIR "/plex/%s", argv[plexindex]); if ((plexfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* got a plex, open it */ /* We don't actually write anything to the plex, * since the system will try to format it. We open * it to ensure that nobody else tries to open it * while we initialize its subdisks */ fprintf(stderr, "can't open plex %s: %s\n", filename, strerror(errno)); return; } } - pid = fork(); - if (pid == 0) { /* we're the child */ - int failed = 0; /* set if a child dies badly */ - int sdfh; /* and for subdisk */ - char zeros[PLEXINITSIZE]; - int count; /* write count */ - long long offset; /* offset in subdisk */ - long long sdsize; /* size of subdisk */ - - bzero(zeros, sizeof(zeros)); - openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); - for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */ - /* We already have the plex data in global - * plex from the call to find_object */ - pid = fork(); /* into the background with you */ - if (pid == 0) { /* I'm the child */ - get_plex_sd_info(&sd, plexno, sdno); - sdsize = sd.sectors * DEV_BSIZE; /* size of subdisk in bytes */ - sprintf(filename, VINUM_DIR "/rsd/%s", sd.name); - setproctitle("initializing %s", filename); /* show what we're doing */ - syslog(LOG_INFO | LOG_KERN, "initializing subdisk %s", filename); - if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */ + if (dowait == 0) { /* don't wait for completion */ + pid = fork(); + if (pid == 0) { /* non-waiting parent */ + close(plexfh); /* we don't need this any more */ + sleep(1); /* give them a chance to print */ + return; /* and go on about our business */ + } + } + /* + * If we get here, we're either the first-level child + * (if we're not waiting) or we're going to wait. + */ + bzero(zeros, sizeof(zeros)); + openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); + for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */ + /* We already have the plex data in global + * plex from the call to find_object */ + pid = fork(); /* into the background with you */ + if (pid == 0) { /* I'm the child */ + get_plex_sd_info(&sd, plexno, sdno); + sdsize = sd.sectors * DEV_BSIZE; /* size of subdisk in bytes */ + sprintf(filename, VINUM_DIR "/rsd/%s", sd.name); + setproctitle("initializing %s", filename); /* show what we're doing */ + syslog(LOG_INFO | LOG_KERN, "initializing subdisk %s", filename); + if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */ + syslog(LOG_ERR | LOG_KERN, + "can't open subdisk %s: %s", + filename, + strerror(errno)); + exit(1); + } + for (offset = 0; offset < sdsize; offset += count) { + count = write(sdfh, zeros, PLEXINITSIZE); /* write a block */ + if (count < 0) { syslog(LOG_ERR | LOG_KERN, - "can't open subdisk %s: %s", + "can't write subdisk %s: %s", filename, strerror(errno)); exit(1); } - for (offset = 0; offset < sdsize; offset += count) { - count = write(sdfh, zeros, PLEXINITSIZE); /* write a block */ - if (count < 0) { - syslog(LOG_ERR | LOG_KERN, - "can't write subdisk %s: %s", - filename, - strerror(errno)); - exit(1); - } /* XXX Grrrr why doesn't this thing recognize EOF? */ - else if (count == 0) - break; - } - syslog(LOG_INFO | LOG_KERN, "subdisk %s initialized", filename); - /* Bring the subdisk up */ - message->index = sd.sdno; /* pass object number */ - message->type = sd_object; /* and type of object */ - message->state = object_up; - message->force = 1; /* insist */ - ioctl(superdev, VINUM_SETSTATE, message); - exit(0); - } else if (pid < 0) /* failure */ - printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno)); - } - /* Now wait for them to complete */ - for (sdno = 0; sdno < plex.subdisks; sdno++) { - int status; - pid = wait(&status); - if (WEXITSTATUS(status) != 0) { /* oh, oh */ - printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status)); - failed++; + else if (count == 0) + break; } + syslog(LOG_INFO | LOG_KERN, "subdisk %s initialized", filename); + /* Bring the subdisk up */ + message->index = sd.sdno; /* pass object number */ + message->type = sd_object; /* and type of object */ + message->state = object_up; + message->force = 1; /* insist */ + ioctl(superdev, VINUM_SETSTATE, message); + exit(0); + } else if (pid < 0) /* failure */ + printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno)); + } + /* Now wait for them to complete */ + for (sdno = 0; sdno < plex.subdisks; sdno++) { + int status; + pid = wait(&status); + if (WEXITSTATUS(status) != 0) { /* oh, oh */ + printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status)); + failed++; } - if (failed == 0) { - syslog(LOG_INFO | LOG_KERN, "plex %s initialized", plex.name); - } else - syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died", - plex.name, - failed); - exit(0); - } else { - close(plexfh); /* we don't need this any more */ - sleep(1); /* give them a chance to print */ } + if (failed == 0) { + syslog(LOG_INFO | LOG_KERN, "plex %s initialized", plex.name); + } else + syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died", + plex.name, + failed); + if (dowait == 0) /* we're the waiting child, */ + exit(0); /* we've done our dash */ } } } void vinum_start(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; if (argc == 0) { /* start everything */ int devs = getnumdevs(); struct statinfo statinfo; char *namelist; char *enamelist; /* end of name list */ int i; char **token; /* list of tokens */ int tokens; /* and their number */ statinfo.dinfo = malloc(devs * sizeof(struct statinfo)); namelist = malloc(devs * (DEVSTAT_NAME_LEN + 8)); token = malloc((devs + 1) * sizeof(char *)); if ((statinfo.dinfo == NULL) || (namelist == NULL) || (token == NULL)) { fprintf(stderr, "Can't allocate memory for drive list\n"); return; } tokens = 0; /* no tokens yet */ if (getdevs(&statinfo) < 0) { /* find out what devices we have */ perror("Can't get device list"); return; } namelist[0] = '\0'; /* start with empty namelist */ enamelist = namelist; /* point to the end of the list */ for (i = 0; i < devs; i++) { struct devstat *stat = &statinfo.dinfo->devices[i]; if (((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) /* disk device */ &&((stat->device_type & DEVSTAT_TYPE_PASS) == 0) /* and not passthrough */ &&((stat->device_name[0] != '\0'))) { /* and it has a name */ sprintf(enamelist, "/dev/%s%d", stat->device_name, stat->unit_number); token[tokens] = enamelist; /* point to it */ tokens++; /* one more token */ enamelist = &enamelist[strlen(enamelist) + 1]; /* and start beyond the end */ } } free(statinfo.dinfo); /* don't need the list any more */ vinum_read(tokens, token, &token[0]); /* start the system */ free(namelist); free(token); } else { /* start specified objects */ int index; enum objecttype type; for (index = 0; index < argc; index++) { object = find_object(argv[index], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[index]); else { int doit = 0; /* set to 1 if we pass our tests */ switch (type) { case drive_object: fprintf(stderr, "Can't start a drive: %s\n", argv[index]); break; case sd_object: if (sd.state == sd_up) /* already up */ fprintf(stderr, "%s is already up\n", sd.name); else doit = 1; break; case plex_object: if (plex.state == plex_up) /* already up */ fprintf(stderr, "%s is already up\n", plex.name); else { int sdno; for (sdno = 0; sdno < plex.subdisks; sdno++) { get_plex_sd_info(&sd, object, sdno); if ((sd.state >= sd_empty) && (sd.state <= sd_stale)) { /* candidate for init */ message->index = sd.sdno; /* pass object number */ message->type = sd_object; /* it's a subdisk */ message->state = object_up; message->force = 0; /* don't force it, use a larger hammer */ ioctl(superdev, VINUM_SETSTATE, message); if (reply.error != 0) { if (reply.error == EAGAIN) /* we're reviving */ continue_revive(sd.sdno); else fprintf(stderr, "Can't start %s: %s (%d)\n", argv[index], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); } if (Verbose) vinum_lsi(sd.sdno, 0); } } } break; case volume_object: if (vol.state == volume_up) /* already up */ fprintf(stderr, "%s is already up\n", vol.name); else doit = 1; break; default: } if (doit) { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->state = object_up; message->force = 0; /* don't force it, use a larger hammer */ ioctl(superdev, VINUM_SETSTATE, message); if (reply.error != 0) { if ((reply.error == EAGAIN) /* we're reviving */ &&(type == sd_object)) continue_revive(object); else fprintf(stderr, "Can't start %s: %s (%d)\n", argv[index], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); } if (Verbose) vinum_li(object, type); } } } } } void vinum_stop(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; message->force = force; /* should we force the transition? */ if (argc == 0) { /* stop vinum */ int fileid = 0; /* ID of Vinum kld */ close(superdev); /* we can't stop if we have vinum open */ sleep(1); /* wait for the daemon to let go */ fileid = kldfind(VINUMMOD); if ((fileid < 0) /* no go */ ||(kldunload(fileid) < 0)) perror("Can't unload " VINUMMOD); else { fprintf(stderr, VINUMMOD " unloaded\n"); exit(0); } /* If we got here, the stop failed. Reopen the superdevice. */ superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* reopen vinum superdevice */ if (superdev < 0) { perror("Can't reopen Vinum superdevice"); exit(1); } } else { /* stop specified objects */ int i; enum objecttype type; for (i = 0; i < argc; i++) { object = find_object(argv[i], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[i]); else { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->state = object_down; ioctl(superdev, VINUM_SETSTATE, message); if (reply.error != 0) fprintf(stderr, "Can't stop %s: %s (%d)\n", argv[i], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); if (Verbose) vinum_li(object, type); } } } } void vinum_label(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; int *message = (int *) &reply; if (argc == 0) /* start everything */ fprintf(stderr, "label: please specify one or more volume names\n"); else { /* start specified objects */ int i; enum objecttype type; for (i = 0; i < argc; i++) { object = find_object(argv[i], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[i]); else if (type != volume_object) /* it exists, but it isn't a volume */ fprintf(stderr, "%s is not a volume\n", argv[i]); else { message[0] = object; /* pass object number */ ioctl(superdev, VINUM_LABEL, message); if (reply.error != 0) fprintf(stderr, "Can't label %s: %s (%d)\n", argv[i], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); if (Verbose) vinum_li(object, type); } } } } void reset_volume_stats(int volno, int recurse) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = volno; msg.type = volume_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for volume %d: %s\n", volno, reply->msg); longjmp(command_fail, -1); } else if (recurse) { struct volume vol; int plexno; get_volume_info(&vol, volno); for (plexno = 0; plexno < vol.plexes; plexno++) reset_plex_stats(vol.plex[plexno], recurse); } } void reset_plex_stats(int plexno, int recurse) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = plexno; msg.type = plex_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for plex %d: %s\n", plexno, reply->msg); longjmp(command_fail, -1); } else if (recurse) { struct plex plex; struct sd sd; int sdno; get_plex_info(&plex, plexno); for (sdno = 0; sdno < plex.subdisks; sdno++) { get_plex_sd_info(&sd, plex.plexno, sdno); reset_sd_stats(sd.sdno, recurse); } } } void reset_sd_stats(int sdno, int recurse) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = sdno; msg.type = sd_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for subdisk %d: %s\n", sdno, reply->msg); longjmp(command_fail, -1); } else if (recurse) { get_sd_info(&sd, sdno); /* get the info */ reset_drive_stats(sd.driveno); /* and clear the drive */ } } void reset_drive_stats(int driveno) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = driveno; msg.type = drive_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for drive %d: %s\n", driveno, reply->msg); longjmp(command_fail, -1); } } void vinum_resetstats(int argc, char *argv[], char *argv0[]) { int i; int objno; enum objecttype type; if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } if (argc == 0) { for (objno = 0; objno < vinum_conf.volumes_used; objno++) reset_volume_stats(objno, 1); /* clear everything recursively */ } else { for (i = 0; i < argc; i++) { objno = find_object(argv[i], &type); if (objno >= 0) { /* not invalid */ switch (type) { case drive_object: reset_drive_stats(objno); break; case sd_object: reset_sd_stats(objno, recurse); break; case plex_object: reset_plex_stats(objno, recurse); break; case volume_object: reset_volume_stats(objno, recurse); break; case invalid_object: /* can't get this */ break; } } } } } /* Attach a subdisk to a plex, or a plex to a volume. * attach subdisk plex [offset] [rename] * attach plex volume [rename] */ void vinum_attach(int argc, char *argv[], char *argv0[]) { int i; enum objecttype supertype; struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; const char *objname = argv[0]; const char *supername = argv[1]; int sdno = -1; int plexno = -1; char newname[MAXNAME + 8]; int rename = 0; /* set if we want to rename the object */ if ((argc < 2) || (argc > 4)) { fprintf(stderr, "Usage: \tattach [rename] []\n" "\tattach [rename]\n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } msg.index = find_object(objname, &msg.type); /* find the object to attach */ msg.otherobject = find_object(supername, &supertype); /* and the object to attach to */ msg.force = force; /* did we specify the use of force? */ msg.recurse = recurse; msg.offset = -1; /* and no offset */ for (i = 2; i < argc; i++) { if (!strcmp(argv[i], "rename")) { rename = 1; msg.rename = 1; /* do renaming */ } else if (!isdigit(argv[i][0])) { /* not an offset */ fprintf(stderr, "Unknown attribute: %s\n", supername); return; } else msg.offset = sizespec(argv[i]); } switch (msg.type) { case sd_object: if (supertype != plex_object) { /* huh? */ fprintf(stderr, "%s can only be attached to a plex\n", objname); return; } get_plex_info(&plex, supertype); if (plex.organization != plex_concat) { /* not a cat plex, */ fprintf(stderr, "Can't attach subdisks to a %s plex\n", plex_org(plex.organization)); return; } sdno = msg.index; /* note the subdisk number for later */ break; case plex_object: if (supertype != volume_object) { /* huh? */ fprintf(stderr, "%s can only be attached to a volume\n", objname); return; } break; case volume_object: case drive_object: fprintf(stderr, "Can only attach subdisks and plexes\n"); return; default: fprintf(stderr, "%s is not a Vinum object\n", objname); return; } ioctl(superdev, VINUM_ATTACH, &msg); if (reply->error != 0) { if (reply->error == EAGAIN) /* reviving */ continue_revive(sdno); /* continue the revive */ else fprintf(stderr, "Can't attach %s to %s: %s (%d)\n", objname, supername, reply->msg[0] ? reply->msg : strerror(reply->error), reply->error); } if (rename) { struct sd; struct plex; struct volume; /* we've overwritten msg with the * ioctl reply, start again */ msg.index = find_object(objname, &msg.type); /* find the object to rename */ switch (msg.type) { case sd_object: get_sd_info(&sd, msg.index); get_plex_info(&plex, sd.plexno); for (sdno = 0; sdno < plex.subdisks; sdno++) { if (plex.sdnos[sdno] == msg.index) /* found our subdisk */ break; } sprintf(newname, "%s.s%d", plex.name, sdno); vinum_rename_2(sd.name, newname); break; case plex_object: get_plex_info(&plex, msg.index); get_volume_info(&vol, plex.volno); for (plexno = 0; plexno < vol.plexes; plexno++) { if (vol.plex[plexno] == msg.index) /* found our subdisk */ break; } sprintf(newname, "%s.p%d", vol.name, plexno); vinum_rename_2(plex.name, newname); /* this may recurse */ break; default: /* can't get here */ } } } /* Detach a subdisk from a plex, or a plex from a volume. * detach subdisk plex [rename] * detach plex volume [rename] */ void vinum_detach(int argc, char *argv[], char *argv0[]) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; if ((argc < 1) || (argc > 2)) { fprintf(stderr, "Usage: \tdetach [rename]\n" "\tdetach [rename]\n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } msg.index = find_object(argv[0], &msg.type); /* find the object to detach */ msg.force = force; /* did we specify the use of force? */ msg.rename = 0; /* don't specify new name */ msg.recurse = recurse; /* but recurse if we have to */ /* XXX are we going to keep this? * Don't document it yet, since the * kernel side of things doesn't * implement it */ if (argc == 2) { if (!strcmp(argv[1], "rename")) msg.rename = 1; /* do renaming */ else { fprintf(stderr, "Unknown attribute: %s\n", argv[1]); return; } } if ((msg.type != sd_object) && (msg.type != plex_object)) { fprintf(stderr, "Can only detach subdisks and plexes\n"); return; } ioctl(superdev, VINUM_DETACH, &msg); if (reply->error != 0) fprintf(stderr, "Can't detach %s: %s (%d)\n", argv[0], reply->msg[0] ? reply->msg : strerror(reply->error), reply->error); } static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen) { struct _ioctl_reply *reply = (struct _ioctl_reply *) msg; if (strlen(name) > maxlen) { fprintf(stderr, "%s is too long\n", name); return; } strcpy(msg->newname, name); ioctl(superdev, VINUM_RENAME, msg); if (reply->error != 0) fprintf(stderr, "Can't rename %s to %s: %s (%d)\n", oldname, name, reply->msg[0] ? reply->msg : strerror(reply->error), reply->error); } /* Rename an object: * rename "newname" */ void vinum_rename_2(char *oldname, char *newname) { struct vinum_rename_msg msg; int volno; int plexno; msg.index = find_object(oldname, &msg.type); /* find the object to rename */ msg.recurse = recurse; /* Ugh. Determine how long the name may be */ switch (msg.type) { case drive_object: dorename(&msg, oldname, newname, MAXDRIVENAME); break; case sd_object: dorename(&msg, oldname, newname, MAXSDNAME); break; case plex_object: plexno = msg.index; dorename(&msg, oldname, newname, MAXPLEXNAME); if (recurse) { int sdno; get_plex_info(&plex, plexno); /* find out who we are */ msg.type = sd_object; for (sdno = 0; sdno < plex.subdisks; sdno++) { char sdname[MAXPLEXNAME + 8]; get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ sprintf(sdname, "%s.s%d", newname, sdno); msg.index = sd.sdno; /* number of the subdisk */ dorename(&msg, sd.name, sdname, MAXSDNAME); } } break; case volume_object: volno = msg.index; dorename(&msg, oldname, newname, MAXVOLNAME); if (recurse) { int sdno; int plexno; get_volume_info(&vol, volno); /* find out who we are */ for (plexno = 0; plexno < vol.plexes; plexno++) { char plexname[MAXVOLNAME + 8]; msg.type = plex_object; sprintf(plexname, "%s.p%d", newname, plexno); msg.index = vol.plex[plexno]; /* number of the plex */ dorename(&msg, plex.name, plexname, MAXPLEXNAME); get_plex_info(&plex, vol.plex[plexno]); /* find out who we are */ msg.type = sd_object; for (sdno = 0; sdno < plex.subdisks; sdno++) { char sdname[MAXPLEXNAME + 8]; get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ sprintf(sdname, "%s.s%d", plexname, sdno); msg.index = sd.sdno; /* number of the subdisk */ dorename(&msg, sd.name, sdname, MAXSDNAME); } } } break; default: fprintf(stderr, "%s is not a Vinum object\n", oldname); return; } } void vinum_rename(int argc, char *argv[], char *argv0[]) { if (argc != 2) { fprintf(stderr, "Usage: \trename \n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } vinum_rename_2(argv[0], argv[1]); } #ifdef COMPLETE /* Replace an object. Syntax and semantics TBD */ void vinum_replace(int argc, char *argv[], char *argv0[]) { int maxlen; struct vinum_rename_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; if (argc != 2) { fprintf(stderr, "Usage: \trename \n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } fprintf(stderr, "Not implemented yet\n"); } #endif /* Replace an object. Syntax and semantics TBD */ void vinum_replace(int argc, char *argv[], char *argv0[]) { fprintf(stderr, "replace function not implemented yet\n"); } /* Primitive help function */ void vinum_help(int argc, char *argv[], char *argv0[]) { char commands[] = { "COMMANDS\n" " create description-file\n" " Create a volume as described in description-file\n" " attach plex volume [rename]\n" " attach subdisk plex [offset] [rename]\n" " Attach a plex to a volume, or a subdisk to a plex.\n" " debug\n" " Cause the volume manager to enter the kernel debugger.\n" " detach [plex | subdisk]\n" " Detach a plex or subdisk from the volume or plex to which it is at-\n" " tached.\n" " info [-v]\n" " List information about volume manager state.\n" " init [-v]\n" " Initialize a plex by writing zeroes to all its subdisks.\n" " label volume\n" " Create a volume label\n" " list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n" " List information about specified objects\n" " l [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n" " List information about specified objects (alternative to\n" " list command)\n" " ld [-r] [-s] [-v] [-V] [volume]\n" " List information about drives\n" " ls [-r] [-s] [-v] [-V] [subdisk]\n" " List information about subdisks\n" " lp [-r] [-s] [-v] [-V] [plex]\n" " List information about plexes\n" " lv [-r] [-s] [-v] [-V] [volume]\n" " List information about volumes\n" " makedev\n" " Remake the device nodes in /dev/vinum.\n" " read disk-partition\n" " Read the vinum configuration from the specified disk partition.\n" " rename [-r] [drive | subdisk | plex | volume] newname\n" " Change the name of the specified object.\n" " replace [subdisk | plex] newobject\n" " Replace the object with an identical other object. XXX not im-\n" " plemented yet.\n" " resetconfig\n" " Reset the complete vinum configuration.\n" " resetstats [-r] [volume | plex | subdisk]\n" " Reset statistisc counters for the specified objects, or for all\n" " objects if none are specified.\n" " rm [-f] [-r] volume | plex | subdisk\n" " Remove an object\n" " setdaemon options\n" " set the daemon options\n" " start [volume | plex | subdisk]\n" " Allow the system to access the objects\n" " stop [-f] [volume | plex | subdisk]\n" " Terminate access the objects\n" }; puts(commands); } /* Set daemon options. * XXX quick and dirty: use a bitmap, which requires * knowing which bit does what. FIXME */ void vinum_setdaemon(int argc, char *argv[], char *argv0[]) { int options; switch (argc) { case 0: if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0) fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno); else printf("Options mask: %d\n", options); break; case 1: options = atoi(argv[0]); if (ioctl(superdev, VINUM_SETDAEMON, &options) < 0) fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno); break; default: fprintf(stderr, "Usage: \tsetdaemon []\n"); } } /* Save config info */ void vinum_saveconfig(int argc, char *argv[], char *argv0[]) { int ioctltype; if (argc != 0) { printf("Usage: saveconfig\n"); return; } ioctltype = 1; /* user saveconfig */ if (ioctl(superdev, VINUM_SAVECONFIG, &ioctltype) < 0) fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno); } diff --git a/sbin/vinum/v.c b/sbin/vinum/v.c index 7dfd8dddc299..ff2acd39f83b 100644 --- a/sbin/vinum/v.c +++ b/sbin/vinum/v.c @@ -1,709 +1,714 @@ /* 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.25 1999/03/21 01:18:23 grog Exp grog $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vext.h" #include #include #include #include #include #include #include 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 VINUMDEBUG 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 */ +int dowait = 0; /* wait for completion */ /* 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 */ void start_daemon(void); #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[], char *envp[]) { #if __FreeBSD__ >= 3 if (modfind(WRONGMOD) >= 0) { /* wrong module loaded, */ fprintf(stderr, "Wrong module loaded: %s. Starting %s.\n", VINUMMOD, WRONGMOD); argv[0] = "/sbin/" WRONGMOD; execve(argv[0], argv, envp); exit(1); } if (modfind(VINUMMOD) < 0) { /* need to load the vinum module */ if (kldload(VINUMMOD) < 0 || modfind(VINUMMOD) < 0) { perror(VINUMMOD ": Kernel module not available"); return 1; } } #endif superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open vinum superdevice */ if (superdev < 0) { /* no go */ if (errno == ENODEV) { /* not configured, */ superdev = open(VINUM_WRONGSUPERDEV_NAME, O_RDWR); /* do we have a debug mismatch? */ if (superdev >= 0) { /* yup! */ #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) /* we don't have our node, */ make_devices(); /* create them first */ if (superdev < 0) { perror("Can't open " VINUM_SUPERDEV_NAME); return 1; } } /* Check if the dæmon is running. If not, start it in the * background */ start_daemon(); 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; int childstatus; /* from wait4 */ setjmp(command_fail); /* come back here on catastrophic failure */ while (wait4(-1, &childstatus, WNOHANG, NULL) > 0); /* wait for all dead children */ c = readline(VINUMMOD " -> "); /* 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 */ } /* stop the hard way */ void vinum_quit(int argc, char *argv[], char *argv0[]) { exit(0); } #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 VINUMDEBUG 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(saveconfig), FUNKEY(start), FUNKEY(stop), FUNKEY(makedev), FUNKEY(help), FUNKEY(quit), FUNKEY(setdaemon), 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 VINUMDEBUG 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; + case 'w': /* -w: wait for completion */ + dowait = 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 * 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 (access("/dev", W_OK) < 0) { /* can't access /dev to write? */ if (errno == EROFS) /* because it's read-only, */ fprintf(stderr, VINUMMOD ": /dev is mounted read-only, not rebuilding " VINUM_DIR "\n"); else perror(VINUMMOD ": Can't write to /dev"); return; } 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 "/rsd " 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)); if (mknod(VINUM_WRONGSUPERDEV_NAME, S_IRWXU | S_IFBLK, /* block device, user only */ VINUM_WRONGSUPERDEV) < 0) fprintf(stderr, "Can't create %s: %s\n", VINUM_WRONGSUPERDEV_NAME, strerror(errno)); superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open the super device */ if (mknod(VINUM_DAEMON_DEV_NAME, /* daemon super device */ S_IRWXU | S_IFBLK, /* block device, user only */ VINUM_DAEMON_DEV) < 0) fprintf(stderr, "Can't create %s: %s\n", VINUM_DAEMON_DEV_NAME, strerror(errno)); 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/ */ 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/ */ 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 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/ */ 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/ */ 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/.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/.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/.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/ */ 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/.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/.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/.plex/.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)); /* /dev/vinum/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)); /* And /dev/vinum/rsd/ */ sprintf(filename, VINUM_DIR "/rsd/%s", sd.name); sddev = VINUMCDEV(volno, plexno, sdno, VINUM_SD_TYPE); if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, 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); } } } /* command line interface for the 'makedev' command */ void vinum_makedev(int argc, char *argv[], char *arg0[]) { make_devices(); } /* * 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 subdisk in the background */ void continue_revive(int sdno) { struct sd sd; pid_t pid; get_sd_info(&sd, sdno); #if VINUMDEBUG if (debug) pid = 0; /* wander through into the "child" process */ else pid = fork(); /* do this in the background */ #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(VINUMMOD, LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); syslog(LOG_INFO | LOG_KERN, "reviving %s", sd.name); for (reply.error = EAGAIN; reply.error == EAGAIN;) { message->index = sdno; /* pass sd number */ message->type = sd_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 %s: %s", sd.name, reply.msg[0] ? reply.msg : strerror(reply.error)); exit(1); } else { get_sd_info(&sd, sdno); /* update the info */ syslog(LOG_INFO | LOG_KERN, "%s is %s", sd.name, sd_state(sd.state)); exit(0); } } else if (pid < 0) /* couldn't fork? */ fprintf(stderr, "Can't continue reviving %s: %s\n", sd.name, strerror(errno)); else printf("Reviving %s in the background\n", sd.name); } /* * Check if the daemon is running, * start it if it isn't. The check itself * could take a while, so we do it as a separate * process, which will become the daemon if one isn't * running already */ void start_daemon(void) { int pid; int status; int error; pid = (int) fork(); if (pid == 0) { /* We're the child, do the work */ /* * We have a problem when stopping the subsystem: * The only way to know that we're idle is when * all open superdevs close. But we want the * daemon to clean up for us, and since we can't * count the opens, we need to have the main device * closed when we stop. We solve this conundrum * by getting the daemon to open a separate device. */ close(superdev); /* this is the wrong device */ superdev = open(VINUM_DAEMON_DEV_NAME, O_RDWR); /* open deamon superdevice */ if (superdev < 0) { perror("Can't open " VINUM_DAEMON_DEV_NAME); exit(1); } error = daemon(0, 0); /* this will fork again, but who's counting? */ if (error != 0) { fprintf(stderr, "Can't start daemon: %s (%d)\n", strerror(errno), errno); exit(1); } setproctitle(VINUMMOD " daemon"); /* show what we're doing */ status = ioctl(superdev, VINUM_FINDDAEMON, NULL); if (status != 0) { /* no daemon, */ ioctl(superdev, VINUM_DAEMON, &verbose); /* we should hang here */ syslog(LOG_ERR | LOG_KERN, "%s", strerror(errno)); exit(1); } exit(0); /* when told to die */ } else if (pid < 0) /* couldn't fork */ printf("Can't fork to check daemon\n"); }