| 1 | /* commands.c: vinum interface program, main commands */ |
| 2 | /*- |
| 3 | * Copyright (c) 1997, 1998 |
| 4 | * Nan Yang Computer Services Limited. All rights reserved. |
| 5 | * |
| 6 | * Written by Greg Lehey |
| 7 | * |
| 8 | * This software is distributed under the so-called ``Berkeley |
| 9 | * License'': |
| 10 | * |
| 11 | * Redistribution and use in source and binary forms, with or without |
| 12 | * modification, are permitted provided that the following conditions |
| 13 | * are met: |
| 14 | * 1. Redistributions of source code must retain the above copyright |
| 15 | * notice, this list of conditions and the following disclaimer. |
| 16 | * 2. Redistributions in binary form must reproduce the above copyright |
| 17 | * notice, this list of conditions and the following disclaimer in the |
| 18 | * documentation and/or other materials provided with the distribution. |
| 19 | * 3. All advertising materials mentioning features or use of this software |
| 20 | * must display the following acknowledgement: |
| 21 | * This product includes software developed by Nan Yang Computer |
| 22 | * Services Limited. |
| 23 | * 4. Neither the name of the Company nor the names of its contributors |
| 24 | * may be used to endorse or promote products derived from this software |
| 25 | * without specific prior written permission. |
| 26 | * |
| 27 | * This software is provided ``as is'', and any express or implied |
| 28 | * warranties, including, but not limited to, the implied warranties of |
| 29 | * merchantability and fitness for a particular purpose are disclaimed. |
| 30 | * In no event shall the company or contributors be liable for any |
| 31 | * direct, indirect, incidental, special, exemplary, or consequential |
| 32 | * damages (including, but not limited to, procurement of substitute |
| 33 | * goods or services; loss of use, data, or profits; or business |
| 34 | * interruption) however caused and on any theory of liability, whether |
| 35 | * in contract, strict liability, or tort (including negligence or |
| 36 | * otherwise) arising in any way out of the use of this software, even if |
| 37 | * advised of the possibility of such damage. |
| 38 | * |
| 39 | * $Id: commands.c,v 1.14 2000/11/14 20:01:23 grog Exp grog $ |
| 40 | * $FreeBSD: src/sbin/vinum/commands.c,v 1.31.2.6 2003/06/06 05:13:29 grog Exp $ |
| 41 | * $DragonFly: src/sbin/vinum/commands.c,v 1.4 2003/11/21 22:46:13 dillon Exp $ |
| 42 | */ |
| 43 | |
| 44 | #define _KERNEL_STRUCTURES |
| 45 | |
| 46 | #include <ctype.h> |
| 47 | #include <errno.h> |
| 48 | #include <fcntl.h> |
| 49 | #include <sys/mman.h> |
| 50 | #include <netdb.h> |
| 51 | #include <paths.h> |
| 52 | #include <setjmp.h> |
| 53 | #include <stdio.h> |
| 54 | #include <stdlib.h> |
| 55 | #include <string.h> |
| 56 | #include <syslog.h> |
| 57 | #include <unistd.h> |
| 58 | #include <sys/ioctl.h> |
| 59 | #include <dev/raid/vinum/vinumhdr.h> |
| 60 | #include <dev/raid/vinum/request.h> |
| 61 | #include "vext.h" |
| 62 | #include <sys/types.h> |
| 63 | #include <sys/linker.h> |
| 64 | #include <sys/module.h> |
| 65 | #include <sys/wait.h> |
| 66 | #include <readline/history.h> |
| 67 | #include <readline/readline.h> |
| 68 | #include <devstat.h> |
| 69 | |
| 70 | static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen); |
| 71 | |
| 72 | void |
| 73 | vinum_create(int argc, char *argv[], char *arg0[]) |
| 74 | { |
| 75 | int error; |
| 76 | FILE *dfd; /* file descriptor for the config file */ |
| 77 | char buffer[BUFSIZE]; /* read config file in here */ |
| 78 | char commandline[BUFSIZE]; /* issue command from here */ |
| 79 | struct _ioctl_reply *reply; |
| 80 | int ioctltype; /* for ioctl call */ |
| 81 | char tempfile[PATH_MAX]; /* name of temp file for direct editing */ |
| 82 | char *file; /* file to read */ |
| 83 | FILE *tf; /* temp file */ |
| 84 | |
| 85 | if (argc == 0) { /* no args, */ |
| 86 | char *editor; /* editor to start */ |
| 87 | int status; |
| 88 | |
| 89 | editor = getenv("EDITOR"); |
| 90 | if (editor == NULL) |
| 91 | editor = "/usr/bin/vi"; |
| 92 | sprintf(tempfile, "/var/tmp/" VINUMMOD ".create.%d", getpid()); /* create a temp file */ |
| 93 | tf = fopen(tempfile, "w"); /* open it */ |
| 94 | if (tf == NULL) { |
| 95 | fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno)); |
| 96 | return; |
| 97 | } |
| 98 | printconfig(tf, "# "); /* and put the current config it */ |
| 99 | fclose(tf); |
| 100 | sprintf(commandline, "%s %s", editor, tempfile); /* create an edit command */ |
| 101 | status = system(commandline); /* do it */ |
| 102 | if (status != 0) { |
| 103 | fprintf(stderr, "Can't edit config: status %d\n", status); |
| 104 | return; |
| 105 | } |
| 106 | file = tempfile; |
| 107 | } else if (argc == 1) |
| 108 | file = argv[0]; |
| 109 | else { |
| 110 | fprintf(stderr, "Expecting 1 parameter, not %d\n", argc); |
| 111 | return; |
| 112 | } |
| 113 | reply = (struct _ioctl_reply *) &buffer; |
| 114 | dfd = fopen(file, "r"); |
| 115 | if (dfd == NULL) { /* no go */ |
| 116 | fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno)); |
| 117 | return; |
| 118 | } |
| 119 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 120 | printf("Can't configure: %s (%d)\n", strerror(errno), errno); |
| 121 | return; |
| 122 | } |
| 123 | file_line = 0; /* start with line 1 */ |
| 124 | /* Parse the configuration, and add it to the global configuration */ |
| 125 | for (;;) { /* love this style(9) */ |
| 126 | char *configline; |
| 127 | |
| 128 | configline = fgets(buffer, BUFSIZE, dfd); |
| 129 | if (history) |
| 130 | fprintf(history, "%s", buffer); |
| 131 | |
| 132 | if (configline == NULL) { |
| 133 | if (ferror(dfd)) |
| 134 | perror("Can't read config file"); |
| 135 | break; |
| 136 | } |
| 137 | file_line++; /* count the lines */ |
| 138 | if (vflag) |
| 139 | printf("%4d: %s", file_line, buffer); |
| 140 | strcpy(commandline, buffer); /* make a copy */ |
| 141 | ioctl(superdev, VINUM_CREATE, buffer); |
| 142 | if (reply->error != 0) { /* error in config */ |
| 143 | if (!vflag) /* print this line anyway */ |
| 144 | printf("%4d: %s", file_line, commandline); |
| 145 | fprintf(stdout, "** %d %s: %s\n", |
| 146 | file_line, |
| 147 | reply->msg, |
| 148 | strerror(reply->error)); |
| 149 | |
| 150 | /* |
| 151 | * XXX at the moment, we reset the config |
| 152 | * lock on error, so try to get it again. |
| 153 | * If we fail, don't cry again. |
| 154 | */ |
| 155 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) /* can't get config? */ |
| 156 | return; |
| 157 | } |
| 158 | } |
| 159 | fclose(dfd); /* done with the config file */ |
| 160 | ioctltype = 0; /* saveconfig after update */ |
| 161 | error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ |
| 162 | if (error != 0) |
| 163 | perror("Can't save Vinum config"); |
| 164 | make_devices(); |
| 165 | listconfig(); |
| 166 | checkupdates(); /* make sure we're updating */ |
| 167 | } |
| 168 | |
| 169 | /* Read vinum config from a disk */ |
| 170 | void |
| 171 | vinum_read(int argc, char *argv[], char *arg0[]) |
| 172 | { |
| 173 | int error; |
| 174 | char buffer[BUFSIZE]; /* read config file in here */ |
| 175 | struct _ioctl_reply *reply; |
| 176 | int i; |
| 177 | |
| 178 | reply = (struct _ioctl_reply *) &buffer; |
| 179 | if (argc < 1) { /* wrong arg count */ |
| 180 | fprintf(stderr, "Usage: read drive [drive ...]\n"); |
| 181 | return; |
| 182 | } |
| 183 | strcpy(buffer, "read "); |
| 184 | for (i = 0; i < argc; i++) { /* each drive name */ |
| 185 | strcat(buffer, argv[i]); |
| 186 | strcat(buffer, " "); |
| 187 | } |
| 188 | |
| 189 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 190 | fprintf(stderr, "Can't configure: %s (%d)\n", strerror(errno), errno); |
| 191 | return; |
| 192 | } |
| 193 | ioctl(superdev, VINUM_CREATE, &buffer); |
| 194 | if (reply->error != 0) { /* error in config */ |
| 195 | fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error)); |
| 196 | error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */ |
| 197 | if (error != 0) |
| 198 | perror("Can't save Vinum config"); |
| 199 | } else { |
| 200 | error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */ |
| 201 | if (error != 0) |
| 202 | perror("Can't save Vinum config"); |
| 203 | make_devices(); |
| 204 | } |
| 205 | checkupdates(); /* make sure we're updating */ |
| 206 | } |
| 207 | |
| 208 | #ifdef VINUMDEBUG |
| 209 | void |
| 210 | vinum_debug(int argc, char *argv[], char *arg0[]) |
| 211 | { |
| 212 | struct debuginfo info; |
| 213 | |
| 214 | if (argc > 0) { |
| 215 | info.param = atoi(argv[0]); |
| 216 | info.changeit = 1; |
| 217 | } else { |
| 218 | info.changeit = 0; |
| 219 | sleep(2); /* give a chance to leave the window */ |
| 220 | } |
| 221 | ioctl(superdev, VINUM_DEBUG, (caddr_t) & info); |
| 222 | } |
| 223 | #endif |
| 224 | |
| 225 | void |
| 226 | vinum_modify(int argc, char *argv[], char *arg0[]) |
| 227 | { |
| 228 | fprintf(stderr, "Modify command is currently not implemented\n"); |
| 229 | checkupdates(); /* make sure we're updating */ |
| 230 | } |
| 231 | |
| 232 | void |
| 233 | vinum_set(int argc, char *argv[], char *arg0[]) |
| 234 | { |
| 235 | fprintf(stderr, "set is not implemented yet\n"); |
| 236 | } |
| 237 | |
| 238 | void |
| 239 | vinum_rm(int argc, char *argv[], char *arg0[]) |
| 240 | { |
| 241 | int object; |
| 242 | struct _ioctl_reply reply; |
| 243 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 244 | |
| 245 | if (argc == 0) /* start everything */ |
| 246 | fprintf(stderr, "Usage: rm object [object...]\n"); |
| 247 | else { /* start specified objects */ |
| 248 | int index; |
| 249 | enum objecttype type; |
| 250 | |
| 251 | for (index = 0; index < argc; index++) { |
| 252 | object = find_object(argv[index], &type); /* look for it */ |
| 253 | if (type == invalid_object) |
| 254 | fprintf(stderr, "Can't find object: %s\n", argv[index]); |
| 255 | else { |
| 256 | message->index = object; /* pass object number */ |
| 257 | message->type = type; /* and type of object */ |
| 258 | message->force = force; /* do we want to force the operation? */ |
| 259 | message->recurse = recurse; /* do we want to remove subordinates? */ |
| 260 | ioctl(superdev, VINUM_REMOVE, message); |
| 261 | if (reply.error != 0) { |
| 262 | fprintf(stderr, |
| 263 | "Can't remove %s: %s (%d)\n", |
| 264 | argv[index], |
| 265 | reply.msg[0] ? reply.msg : strerror(reply.error), |
| 266 | reply.error); |
| 267 | } else if (vflag) |
| 268 | fprintf(stderr, "%s removed\n", argv[index]); |
| 269 | } |
| 270 | } |
| 271 | checkupdates(); /* make sure we're updating */ |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | void |
| 276 | vinum_resetconfig(int argc, char *argv[], char *arg0[]) |
| 277 | { |
| 278 | char reply[32]; |
| 279 | int error; |
| 280 | |
| 281 | if (! isatty (STDIN_FILENO)) { |
| 282 | fprintf (stderr, "Please enter this command from a tty device\n"); |
| 283 | return; |
| 284 | } |
| 285 | printf(" WARNING! This command will completely wipe out your vinum configuration.\n" |
| 286 | " All data will be lost. If you really want to do this, enter the text\n\n" |
| 287 | " NO FUTURE\n" |
| 288 | " Enter text -> "); |
| 289 | fgets(reply, sizeof(reply), stdin); |
| 290 | if (strcmp(reply, "NO FUTURE\n")) /* changed his mind */ |
| 291 | printf("\n No change\n"); |
| 292 | else { |
| 293 | error = ioctl(superdev, VINUM_RESETCONFIG, NULL); /* trash config on disk */ |
| 294 | if (error) { |
| 295 | if (errno == EBUSY) |
| 296 | fprintf(stderr, "Can't reset configuration: objects are in use\n"); |
| 297 | else |
| 298 | perror("Can't find vinum config"); |
| 299 | } else { |
| 300 | make_devices(); /* recreate the /dev/vinum hierarchy */ |
| 301 | printf("\b Vinum configuration obliterated\n"); |
| 302 | start_daemon(); /* then restart the daemon */ |
| 303 | } |
| 304 | } |
| 305 | checkupdates(); /* make sure we're updating */ |
| 306 | } |
| 307 | |
| 308 | /* Initialize subdisks */ |
| 309 | void |
| 310 | vinum_init(int argc, char *argv[], char *arg0[]) |
| 311 | { |
| 312 | if (argc > 0) { /* initialize plexes */ |
| 313 | int objindex; |
| 314 | int objno; |
| 315 | enum objecttype type; /* type returned */ |
| 316 | |
| 317 | if (history) |
| 318 | fflush(history); /* don't let all the kids do it. */ |
| 319 | for (objindex = 0; objindex < argc; objindex++) { |
| 320 | objno = find_object(argv[objindex], &type); /* find the object */ |
| 321 | if (objno < 0) |
| 322 | printf("Can't find %s\n", argv[objindex]); |
| 323 | else { |
| 324 | switch (type) { |
| 325 | case volume_object: |
| 326 | initvol(objno); |
| 327 | break; |
| 328 | |
| 329 | case plex_object: |
| 330 | initplex(objno, argv[objindex]); |
| 331 | break; |
| 332 | |
| 333 | case sd_object: |
| 334 | initsd(objno, dowait); |
| 335 | break; |
| 336 | |
| 337 | default: |
| 338 | printf("Can't initialize %s: wrong object type\n", argv[objindex]); |
| 339 | break; |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | } |
| 344 | checkupdates(); /* make sure we're updating */ |
| 345 | } |
| 346 | |
| 347 | void |
| 348 | initvol(int volno) |
| 349 | { |
| 350 | printf("Initializing volumes is not implemented yet\n"); |
| 351 | } |
| 352 | |
| 353 | void |
| 354 | initplex(int plexno, char *name) |
| 355 | { |
| 356 | int sdno; |
| 357 | int plexfh = NULL; /* file handle for plex */ |
| 358 | pid_t pid; |
| 359 | char filename[MAXPATHLEN]; /* create a file name here */ |
| 360 | |
| 361 | /* Variables for use by children */ |
| 362 | int failed = 0; /* set if a child dies badly */ |
| 363 | |
| 364 | sprintf(filename, VINUM_DIR "/plex/%s", name); |
| 365 | if ((plexfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* got a plex, open it */ |
| 366 | /* |
| 367 | * We don't actually write anything to the |
| 368 | * plex. We open it to ensure that nobody |
| 369 | * else tries to open it while we initialize |
| 370 | * its subdisks. |
| 371 | */ |
| 372 | fprintf(stderr, "can't open plex %s: %s\n", filename, strerror(errno)); |
| 373 | return; |
| 374 | } |
| 375 | if (dowait == 0) { |
| 376 | pid = fork(); /* into the background with you */ |
| 377 | if (pid != 0) { /* I'm the parent, or we failed */ |
| 378 | if (pid < 0) /* failure */ |
| 379 | printf("Couldn't fork: %s", strerror(errno)); |
| 380 | close(plexfh); /* we don't need this any more */ |
| 381 | return; |
| 382 | } |
| 383 | } |
| 384 | /* |
| 385 | * If we get here, we're either the first-level |
| 386 | * child (if we're not waiting) or we're going |
| 387 | * to wait. |
| 388 | */ |
| 389 | for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */ |
| 390 | get_plex_sd_info(&sd, plexno, sdno); |
| 391 | initsd(sd.sdno, 0); |
| 392 | } |
| 393 | /* Now wait for them to complete */ |
| 394 | while (1) { |
| 395 | int status; |
| 396 | pid = wait(&status); |
| 397 | if (((int) pid == -1) |
| 398 | && (errno == ECHILD)) /* all gone */ |
| 399 | break; |
| 400 | if (WEXITSTATUS(status) != 0) { /* oh, oh */ |
| 401 | printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status)); |
| 402 | failed++; |
| 403 | } |
| 404 | } |
| 405 | if (failed == 0) { |
| 406 | #if 0 |
| 407 | message->index = plexno; /* pass object number */ |
| 408 | message->type = plex_object; /* and type of object */ |
| 409 | message->state = object_up; |
| 410 | message->force = 1; /* insist */ |
| 411 | ioctl(superdev, VINUM_SETSTATE, message); |
| 412 | #endif |
| 413 | syslog(LOG_INFO | LOG_KERN, "plex %s initialized", plex.name); |
| 414 | } else |
| 415 | syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died", |
| 416 | plex.name, |
| 417 | failed); |
| 418 | if (dowait == 0) /* we're the waiting child, */ |
| 419 | exit(0); /* we've done our dash */ |
| 420 | } |
| 421 | |
| 422 | /* Initialize a subdisk. */ |
| 423 | void |
| 424 | initsd(int sdno, int dowait) |
| 425 | { |
| 426 | pid_t pid; |
| 427 | struct _ioctl_reply reply; |
| 428 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 429 | char filename[MAXPATHLEN]; /* create a file name here */ |
| 430 | |
| 431 | /* Variables for use by children */ |
| 432 | int sdfh; /* and for subdisk */ |
| 433 | int initsize; /* actual size to write */ |
| 434 | int64_t sdsize; /* size of subdisk */ |
| 435 | |
| 436 | if (dowait == 0) { |
| 437 | pid = fork(); /* into the background with you */ |
| 438 | if (pid > 0) /* I'm the parent */ |
| 439 | return; |
| 440 | else if (pid < 0) { /* failure */ |
| 441 | printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno)); |
| 442 | return; |
| 443 | } |
| 444 | } |
| 445 | if (SSize != 0) { /* specified a size for init */ |
| 446 | if (SSize < 512) |
| 447 | SSize <<= DEV_BSHIFT; |
| 448 | initsize = min(SSize, MAXPLEXINITSIZE); |
| 449 | } else |
| 450 | initsize = PLEXINITSIZE; |
| 451 | openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); |
| 452 | get_sd_info(&sd, sdno); |
| 453 | sdsize = sd.sectors * DEV_BSIZE; /* size of subdisk in bytes */ |
| 454 | sprintf(filename, VINUM_DIR "/sd/%s", sd.name); |
| 455 | setproctitle("initializing %s", filename); /* show what we're doing */ |
| 456 | syslog(LOG_INFO | LOG_KERN, "initializing subdisk %s", filename); |
| 457 | if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */ |
| 458 | syslog(LOG_ERR | LOG_KERN, |
| 459 | "can't open subdisk %s: %s", |
| 460 | filename, |
| 461 | strerror(errno)); |
| 462 | exit(1); |
| 463 | } |
| 464 | /* Set the subdisk in initializing state */ |
| 465 | message->index = sd.sdno; /* pass object number */ |
| 466 | message->type = sd_object; /* and type of object */ |
| 467 | message->state = object_initializing; |
| 468 | message->verify = vflag; /* verify what we write? */ |
| 469 | message->force = 1; /* insist */ |
| 470 | ioctl(superdev, VINUM_SETSTATE, message); |
| 471 | if ((SSize > 0) /* specified a size for init */ |
| 472 | &&(SSize < 512)) |
| 473 | SSize <<= DEV_BSHIFT; |
| 474 | if (reply.error) { |
| 475 | fprintf(stderr, |
| 476 | "Can't initialize %s: %s (%d)\n", |
| 477 | filename, |
| 478 | strerror(reply.error), |
| 479 | reply.error); |
| 480 | exit(1); |
| 481 | } else { |
| 482 | do { |
| 483 | if (interval) /* pause between copies */ |
| 484 | usleep(interval * 1000); |
| 485 | message->index = sd.sdno; /* pass object number */ |
| 486 | message->type = sd_object; /* and type of object */ |
| 487 | message->state = object_up; |
| 488 | message->verify = vflag; /* verify what we write? */ |
| 489 | message->blocksize = SSize; |
| 490 | ioctl(superdev, VINUM_SETSTATE, message); |
| 491 | } |
| 492 | while (reply.error == EAGAIN); /* until we're done */ |
| 493 | if (reply.error) { |
| 494 | fprintf(stderr, |
| 495 | "Can't initialize %s: %s (%d)\n", |
| 496 | filename, |
| 497 | strerror(reply.error), |
| 498 | reply.error); |
| 499 | get_sd_info(&sd, sdno); |
| 500 | if (sd.state != sd_up) |
| 501 | /* Set the subdisk down */ |
| 502 | message->index = sd.sdno; /* pass object number */ |
| 503 | message->type = sd_object; /* and type of object */ |
| 504 | message->state = object_down; |
| 505 | message->verify = vflag; /* verify what we write? */ |
| 506 | message->force = 1; /* insist */ |
| 507 | ioctl(superdev, VINUM_SETSTATE, message); |
| 508 | } |
| 509 | } |
| 510 | printf("subdisk %s initialized\n", filename); |
| 511 | if (!dowait) |
| 512 | exit(0); |
| 513 | } |
| 514 | |
| 515 | void |
| 516 | vinum_start(int argc, char *argv[], char *arg0[]) |
| 517 | { |
| 518 | int object; |
| 519 | struct _ioctl_reply reply; |
| 520 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 521 | |
| 522 | if (argc == 0) { /* start everything */ |
| 523 | int devs = getnumdevs(); |
| 524 | struct statinfo statinfo; |
| 525 | char *namelist; |
| 526 | char *enamelist; /* end of name list */ |
| 527 | int i; |
| 528 | char **token; /* list of tokens */ |
| 529 | int tokens; /* and their number */ |
| 530 | |
| 531 | bzero(&statinfo, sizeof(struct statinfo)); |
| 532 | statinfo.dinfo = malloc(devs * sizeof(struct statinfo)); |
| 533 | namelist = malloc(devs * (DEVSTAT_NAME_LEN + 8)); |
| 534 | token = malloc((devs + 1) * sizeof(char *)); |
| 535 | if ((statinfo.dinfo == NULL) || (namelist == NULL) || (token == NULL)) { |
| 536 | fprintf(stderr, "Can't allocate memory for drive list\n"); |
| 537 | return; |
| 538 | } |
| 539 | bzero(statinfo.dinfo, sizeof(struct devinfo)); |
| 540 | |
| 541 | tokens = 0; /* no tokens yet */ |
| 542 | if (getdevs(&statinfo) < 0) { /* find out what devices we have */ |
| 543 | perror("Can't get device list"); |
| 544 | return; |
| 545 | } |
| 546 | namelist[0] = '\0'; /* start with empty namelist */ |
| 547 | enamelist = namelist; /* point to the end of the list */ |
| 548 | |
| 549 | for (i = 0; i < devs; i++) { |
| 550 | struct devstat *stat = &statinfo.dinfo->devices[i]; |
| 551 | |
| 552 | if ((((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) /* disk device */ |
| 553 | || ((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_STORARRAY)) /* storage array */ |
| 554 | &&((stat->device_type & DEVSTAT_TYPE_IF_MASK) != DEVSTAT_TYPE_IF_OTHER) /* and not md */ |
| 555 | &&((stat->device_type & DEVSTAT_TYPE_PASS) == 0) /* and not passthrough */ |
| 556 | &&((stat->device_name[0] != '\0'))) { /* and it has a name */ |
| 557 | sprintf(enamelist, "%s%s%d", _PATH_DEV, stat->device_name, stat->unit_number); |
| 558 | token[tokens] = enamelist; /* point to it */ |
| 559 | tokens++; /* one more token */ |
| 560 | enamelist = &enamelist[strlen(enamelist) + 1]; /* and start beyond the end */ |
| 561 | } |
| 562 | } |
| 563 | free(statinfo.dinfo); /* don't need the list any more */ |
| 564 | vinum_read(tokens, token, &token[0]); /* start the system */ |
| 565 | free(namelist); |
| 566 | free(token); |
| 567 | list_defective_objects(); /* and list anything that's down */ |
| 568 | } else { /* start specified objects */ |
| 569 | int index; |
| 570 | enum objecttype type; |
| 571 | |
| 572 | for (index = 0; index < argc; index++) { |
| 573 | object = find_object(argv[index], &type); /* look for it */ |
| 574 | if (type == invalid_object) |
| 575 | fprintf(stderr, "Can't find object: %s\n", argv[index]); |
| 576 | else { |
| 577 | int doit = 0; /* set to 1 if we pass our tests */ |
| 578 | switch (type) { |
| 579 | case drive_object: |
| 580 | if (drive.state == drive_up) /* already up */ |
| 581 | fprintf(stderr, "%s is already up\n", drive.label.name); |
| 582 | else |
| 583 | doit = 1; |
| 584 | break; |
| 585 | |
| 586 | case sd_object: |
| 587 | if (sd.state == sd_up) /* already up */ |
| 588 | fprintf(stderr, "%s is already up\n", sd.name); |
| 589 | else |
| 590 | doit = 1; |
| 591 | break; |
| 592 | |
| 593 | case plex_object: |
| 594 | if (plex.state == plex_up) /* already up */ |
| 595 | fprintf(stderr, "%s is already up\n", plex.name); |
| 596 | else { |
| 597 | int sdno; |
| 598 | |
| 599 | /* |
| 600 | * First, see if we can bring it up |
| 601 | * just by asking. This might happen |
| 602 | * if somebody has used setupstate on |
| 603 | * the subdisks. If we don't do this, |
| 604 | * we'll return success, but the plex |
| 605 | * won't have changed state. Note |
| 606 | * that we don't check for errors |
| 607 | * here. |
| 608 | */ |
| 609 | message->index = plex.plexno; /* pass object number */ |
| 610 | message->type = plex_object; /* it's a plex */ |
| 611 | message->state = object_up; |
| 612 | message->force = 0; /* don't force it */ |
| 613 | ioctl(superdev, VINUM_SETSTATE, message); |
| 614 | for (sdno = 0; sdno < plex.subdisks; sdno++) { |
| 615 | get_plex_sd_info(&sd, object, sdno); |
| 616 | if ((sd.state >= sd_empty) |
| 617 | && (sd.state <= sd_reviving)) { /* candidate for start */ |
| 618 | message->index = sd.sdno; /* pass object number */ |
| 619 | message->type = sd_object; /* it's a subdisk */ |
| 620 | message->state = object_up; |
| 621 | message->force = force; /* don't force it, use a larger hammer */ |
| 622 | |
| 623 | /* |
| 624 | * We don't do any checking here. |
| 625 | * The kernel module has a better |
| 626 | * understanding of these things, |
| 627 | * let it do it. |
| 628 | */ |
| 629 | if (SSize != 0) { /* specified a size for init */ |
| 630 | if (SSize < 512) |
| 631 | SSize <<= DEV_BSHIFT; |
| 632 | message->blocksize = SSize; |
| 633 | } else |
| 634 | message->blocksize = DEFAULT_REVIVE_BLOCKSIZE; |
| 635 | ioctl(superdev, VINUM_SETSTATE, message); |
| 636 | if (reply.error != 0) { |
| 637 | if (reply.error == EAGAIN) /* we're reviving */ |
| 638 | continue_revive(sd.sdno); |
| 639 | else |
| 640 | fprintf(stderr, |
| 641 | "Can't start %s: %s (%d)\n", |
| 642 | sd.name, |
| 643 | reply.msg[0] ? reply.msg : strerror(reply.error), |
| 644 | reply.error); |
| 645 | } |
| 646 | if (Verbose) |
| 647 | vinum_lsi(sd.sdno, 0); |
| 648 | } |
| 649 | } |
| 650 | } |
| 651 | break; |
| 652 | |
| 653 | case volume_object: |
| 654 | if (vol.state == volume_up) /* already up */ |
| 655 | fprintf(stderr, "%s is already up\n", vol.name); |
| 656 | else |
| 657 | doit = 1; |
| 658 | break; |
| 659 | |
| 660 | default: |
| 661 | } |
| 662 | |
| 663 | if (doit) { |
| 664 | message->index = object; /* pass object number */ |
| 665 | message->type = type; /* and type of object */ |
| 666 | message->state = object_up; |
| 667 | message->force = force; /* don't force it, use a larger hammer */ |
| 668 | |
| 669 | /* |
| 670 | * We don't do any checking here. |
| 671 | * The kernel module has a better |
| 672 | * understanding of these things, |
| 673 | * let it do it. |
| 674 | */ |
| 675 | if (SSize != 0) { /* specified a size for init or revive */ |
| 676 | if (SSize < 512) |
| 677 | SSize <<= DEV_BSHIFT; |
| 678 | message->blocksize = SSize; |
| 679 | } else |
| 680 | message->blocksize = 0; |
| 681 | ioctl(superdev, VINUM_SETSTATE, message); |
| 682 | if (reply.error != 0) { |
| 683 | if ((reply.error == EAGAIN) /* we're reviving */ |
| 684 | &&(type == sd_object)) |
| 685 | continue_revive(object); |
| 686 | else |
| 687 | fprintf(stderr, |
| 688 | "Can't start %s: %s (%d)\n", |
| 689 | argv[index], |
| 690 | reply.msg[0] ? reply.msg : strerror(reply.error), |
| 691 | reply.error); |
| 692 | } |
| 693 | if (Verbose) |
| 694 | vinum_li(object, type); |
| 695 | } |
| 696 | } |
| 697 | } |
| 698 | } |
| 699 | checkupdates(); /* make sure we're updating */ |
| 700 | } |
| 701 | |
| 702 | void |
| 703 | vinum_stop(int argc, char *argv[], char *arg0[]) |
| 704 | { |
| 705 | int object; |
| 706 | struct _ioctl_reply reply; |
| 707 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 708 | |
| 709 | if (checkupdates() && (!force)) /* not updating? */ |
| 710 | return; |
| 711 | message->force = force; /* should we force the transition? */ |
| 712 | if (argc == 0) { /* stop vinum */ |
| 713 | int fileid = 0; /* ID of Vinum kld */ |
| 714 | |
| 715 | close(superdev); /* we can't stop if we have vinum open */ |
| 716 | sleep(1); /* wait for the daemon to let go */ |
| 717 | fileid = kldfind(VINUMMOD); |
| 718 | if ((fileid < 0) /* no go */ |
| 719 | ||(kldunload(fileid) < 0)) |
| 720 | perror("Can't unload " VINUMMOD); |
| 721 | else { |
| 722 | fprintf(stderr, VINUMMOD " unloaded\n"); |
| 723 | exit(0); |
| 724 | } |
| 725 | |
| 726 | /* If we got here, the stop failed. Reopen the superdevice. */ |
| 727 | superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* reopen vinum superdevice */ |
| 728 | if (superdev < 0) { |
| 729 | perror("Can't reopen Vinum superdevice"); |
| 730 | exit(1); |
| 731 | } |
| 732 | } else { /* stop specified objects */ |
| 733 | int i; |
| 734 | enum objecttype type; |
| 735 | |
| 736 | for (i = 0; i < argc; i++) { |
| 737 | object = find_object(argv[i], &type); /* look for it */ |
| 738 | if (type == invalid_object) |
| 739 | fprintf(stderr, "Can't find object: %s\n", argv[i]); |
| 740 | else { |
| 741 | message->index = object; /* pass object number */ |
| 742 | message->type = type; /* and type of object */ |
| 743 | message->state = object_down; |
| 744 | ioctl(superdev, VINUM_SETSTATE, message); |
| 745 | if (reply.error != 0) |
| 746 | fprintf(stderr, |
| 747 | "Can't stop %s: %s (%d)\n", |
| 748 | argv[i], |
| 749 | reply.msg[0] ? reply.msg : strerror(reply.error), |
| 750 | reply.error); |
| 751 | if (Verbose) |
| 752 | vinum_li(object, type); |
| 753 | } |
| 754 | } |
| 755 | } |
| 756 | } |
| 757 | |
| 758 | void |
| 759 | vinum_label(int argc, char *argv[], char *arg0[]) |
| 760 | { |
| 761 | int object; |
| 762 | struct _ioctl_reply reply; |
| 763 | int *message = (int *) &reply; |
| 764 | |
| 765 | if (argc == 0) /* start everything */ |
| 766 | fprintf(stderr, "label: please specify one or more volume names\n"); |
| 767 | else { /* start specified objects */ |
| 768 | int i; |
| 769 | enum objecttype type; |
| 770 | |
| 771 | for (i = 0; i < argc; i++) { |
| 772 | object = find_object(argv[i], &type); /* look for it */ |
| 773 | if (type == invalid_object) |
| 774 | fprintf(stderr, "Can't find object: %s\n", argv[i]); |
| 775 | else if (type != volume_object) /* it exists, but it isn't a volume */ |
| 776 | fprintf(stderr, "%s is not a volume\n", argv[i]); |
| 777 | else { |
| 778 | message[0] = object; /* pass object number */ |
| 779 | ioctl(superdev, VINUM_LABEL, message); |
| 780 | if (reply.error != 0) |
| 781 | fprintf(stderr, |
| 782 | "Can't label %s: %s (%d)\n", |
| 783 | argv[i], |
| 784 | reply.msg[0] ? reply.msg : strerror(reply.error), |
| 785 | reply.error); |
| 786 | if (Verbose) |
| 787 | vinum_li(object, type); |
| 788 | } |
| 789 | } |
| 790 | } |
| 791 | checkupdates(); /* not updating? */ |
| 792 | } |
| 793 | |
| 794 | void |
| 795 | reset_volume_stats(int volno, int recurse) |
| 796 | { |
| 797 | struct vinum_ioctl_msg msg; |
| 798 | struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; |
| 799 | |
| 800 | msg.index = volno; |
| 801 | msg.type = volume_object; |
| 802 | /* XXX get these numbers right if we ever |
| 803 | * actually return errors */ |
| 804 | if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { |
| 805 | fprintf(stderr, "Can't reset stats for volume %d: %s\n", volno, reply->msg); |
| 806 | longjmp(command_fail, -1); |
| 807 | } else if (recurse) { |
| 808 | struct volume vol; |
| 809 | int plexno; |
| 810 | |
| 811 | get_volume_info(&vol, volno); |
| 812 | for (plexno = 0; plexno < vol.plexes; plexno++) |
| 813 | reset_plex_stats(vol.plex[plexno], recurse); |
| 814 | } |
| 815 | } |
| 816 | |
| 817 | void |
| 818 | reset_plex_stats(int plexno, int recurse) |
| 819 | { |
| 820 | struct vinum_ioctl_msg msg; |
| 821 | struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; |
| 822 | |
| 823 | msg.index = plexno; |
| 824 | msg.type = plex_object; |
| 825 | /* XXX get these numbers right if we ever |
| 826 | * actually return errors */ |
| 827 | if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { |
| 828 | fprintf(stderr, "Can't reset stats for plex %d: %s\n", plexno, reply->msg); |
| 829 | longjmp(command_fail, -1); |
| 830 | } else if (recurse) { |
| 831 | struct plex plex; |
| 832 | struct sd sd; |
| 833 | int sdno; |
| 834 | |
| 835 | get_plex_info(&plex, plexno); |
| 836 | for (sdno = 0; sdno < plex.subdisks; sdno++) { |
| 837 | get_plex_sd_info(&sd, plex.plexno, sdno); |
| 838 | reset_sd_stats(sd.sdno, recurse); |
| 839 | } |
| 840 | } |
| 841 | } |
| 842 | |
| 843 | void |
| 844 | reset_sd_stats(int sdno, int recurse) |
| 845 | { |
| 846 | struct vinum_ioctl_msg msg; |
| 847 | struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; |
| 848 | |
| 849 | msg.index = sdno; |
| 850 | msg.type = sd_object; |
| 851 | /* XXX get these numbers right if we ever |
| 852 | * actually return errors */ |
| 853 | if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { |
| 854 | fprintf(stderr, "Can't reset stats for subdisk %d: %s\n", sdno, reply->msg); |
| 855 | longjmp(command_fail, -1); |
| 856 | } else if (recurse) { |
| 857 | get_sd_info(&sd, sdno); /* get the info */ |
| 858 | reset_drive_stats(sd.driveno); /* and clear the drive */ |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | void |
| 863 | reset_drive_stats(int driveno) |
| 864 | { |
| 865 | struct vinum_ioctl_msg msg; |
| 866 | struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; |
| 867 | |
| 868 | msg.index = driveno; |
| 869 | msg.type = drive_object; |
| 870 | /* XXX get these numbers right if we ever |
| 871 | * actually return errors */ |
| 872 | if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { |
| 873 | fprintf(stderr, "Can't reset stats for drive %d: %s\n", driveno, reply->msg); |
| 874 | longjmp(command_fail, -1); |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | void |
| 879 | vinum_resetstats(int argc, char *argv[], char *argv0[]) |
| 880 | { |
| 881 | int i; |
| 882 | int objno; |
| 883 | enum objecttype type; |
| 884 | |
| 885 | if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { |
| 886 | perror("Can't get vinum config"); |
| 887 | return; |
| 888 | } |
| 889 | if (argc == 0) { |
| 890 | for (objno = 0; objno < vinum_conf.volumes_allocated; objno++) |
| 891 | reset_volume_stats(objno, 1); /* clear everything recursively */ |
| 892 | } else { |
| 893 | for (i = 0; i < argc; i++) { |
| 894 | objno = find_object(argv[i], &type); |
| 895 | if (objno >= 0) { /* not invalid */ |
| 896 | switch (type) { |
| 897 | case drive_object: |
| 898 | reset_drive_stats(objno); |
| 899 | break; |
| 900 | |
| 901 | case sd_object: |
| 902 | reset_sd_stats(objno, recurse); |
| 903 | break; |
| 904 | |
| 905 | case plex_object: |
| 906 | reset_plex_stats(objno, recurse); |
| 907 | break; |
| 908 | |
| 909 | case volume_object: |
| 910 | reset_volume_stats(objno, recurse); |
| 911 | break; |
| 912 | |
| 913 | case invalid_object: /* can't get this */ |
| 914 | break; |
| 915 | } |
| 916 | } |
| 917 | } |
| 918 | } |
| 919 | } |
| 920 | |
| 921 | /* Attach a subdisk to a plex, or a plex to a volume. |
| 922 | * attach subdisk plex [offset] [rename] |
| 923 | * attach plex volume [rename] |
| 924 | */ |
| 925 | void |
| 926 | vinum_attach(int argc, char *argv[], char *argv0[]) |
| 927 | { |
| 928 | int i; |
| 929 | enum objecttype supertype; |
| 930 | struct vinum_ioctl_msg msg; |
| 931 | struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; |
| 932 | const char *objname = argv[0]; |
| 933 | const char *supername = argv[1]; |
| 934 | int sdno = -1; |
| 935 | int plexno = -1; |
| 936 | char oldname[MAXNAME + 8]; |
| 937 | char newname[MAXNAME + 8]; |
| 938 | int rename = 0; /* set if we want to rename the object */ |
| 939 | |
| 940 | if ((argc < 2) |
| 941 | || (argc > 4)) { |
| 942 | fprintf(stderr, |
| 943 | "Usage: \tattach <subdisk> <plex> [rename] [<plexoffset>]\n" |
| 944 | "\tattach <plex> <volume> [rename]\n"); |
| 945 | return; |
| 946 | } |
| 947 | if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { |
| 948 | perror("Can't get vinum config"); |
| 949 | return; |
| 950 | } |
| 951 | msg.index = find_object(objname, &msg.type); /* find the object to attach */ |
| 952 | msg.otherobject = find_object(supername, &supertype); /* and the object to attach to */ |
| 953 | msg.force = force; /* did we specify the use of force? */ |
| 954 | msg.recurse = recurse; |
| 955 | msg.offset = -1; /* and no offset */ |
| 956 | |
| 957 | for (i = 2; i < argc; i++) { |
| 958 | if (!strcmp(argv[i], "rename")) { |
| 959 | rename = 1; |
| 960 | msg.rename = 1; /* do renaming */ |
| 961 | } else if (!isdigit(argv[i][0])) { /* not an offset */ |
| 962 | fprintf(stderr, "Unknown attribute: %s\n", supername); |
| 963 | return; |
| 964 | } else |
| 965 | msg.offset = sizespec(argv[i]); |
| 966 | } |
| 967 | |
| 968 | switch (msg.type) { |
| 969 | case sd_object: |
| 970 | find_object(argv[1], &supertype); |
| 971 | if (supertype != plex_object) { /* huh? */ |
| 972 | fprintf(stderr, "%s can only be attached to a plex\n", objname); |
| 973 | return; |
| 974 | } |
| 975 | if ((plex.organization != plex_concat) /* not a cat plex, */ |
| 976 | &&(!force)) { |
| 977 | fprintf(stderr, "Can't attach subdisks to a %s plex\n", plex_org(plex.organization)); |
| 978 | return; |
| 979 | } |
| 980 | sdno = msg.index; /* note the subdisk number for later */ |
| 981 | break; |
| 982 | |
| 983 | case plex_object: |
| 984 | find_object(argv[1], &supertype); |
| 985 | if (supertype != volume_object) { /* huh? */ |
| 986 | fprintf(stderr, "%s can only be attached to a volume\n", objname); |
| 987 | return; |
| 988 | } |
| 989 | break; |
| 990 | |
| 991 | case volume_object: |
| 992 | case drive_object: |
| 993 | fprintf(stderr, "Can only attach subdisks and plexes\n"); |
| 994 | return; |
| 995 | |
| 996 | default: |
| 997 | fprintf(stderr, "%s is not a Vinum object\n", objname); |
| 998 | return; |
| 999 | } |
| 1000 | |
| 1001 | ioctl(superdev, VINUM_ATTACH, &msg); |
| 1002 | if (reply->error != 0) { |
| 1003 | if (reply->error == EAGAIN) /* reviving */ |
| 1004 | continue_revive(sdno); /* continue the revive */ |
| 1005 | else |
| 1006 | fprintf(stderr, |
| 1007 | "Can't attach %s to %s: %s (%d)\n", |
| 1008 | objname, |
| 1009 | supername, |
| 1010 | reply->msg[0] ? reply->msg : strerror(reply->error), |
| 1011 | reply->error); |
| 1012 | } |
| 1013 | if (rename) { |
| 1014 | struct sd; |
| 1015 | struct plex; |
| 1016 | struct volume; |
| 1017 | |
| 1018 | /* we've overwritten msg with the |
| 1019 | * ioctl reply, start again */ |
| 1020 | msg.index = find_object(objname, &msg.type); /* find the object to rename */ |
| 1021 | switch (msg.type) { |
| 1022 | case sd_object: |
| 1023 | get_sd_info(&sd, msg.index); |
| 1024 | get_plex_info(&plex, sd.plexno); |
| 1025 | for (sdno = 0; sdno < plex.subdisks; sdno++) { |
| 1026 | if (plex.sdnos[sdno] == msg.index) /* found our subdisk */ |
| 1027 | break; |
| 1028 | } |
| 1029 | sprintf(newname, "%s.s%d", plex.name, sdno); |
| 1030 | sprintf(oldname, "%s", sd.name); |
| 1031 | vinum_rename_2(oldname, newname); |
| 1032 | break; |
| 1033 | |
| 1034 | case plex_object: |
| 1035 | get_plex_info(&plex, msg.index); |
| 1036 | get_volume_info(&vol, plex.volno); |
| 1037 | for (plexno = 0; plexno < vol.plexes; plexno++) { |
| 1038 | if (vol.plex[plexno] == msg.index) /* found our subdisk */ |
| 1039 | break; |
| 1040 | } |
| 1041 | sprintf(newname, "%s.p%d", vol.name, plexno); |
| 1042 | sprintf(oldname, "%s", plex.name); |
| 1043 | vinum_rename_2(oldname, newname); /* this may recurse */ |
| 1044 | break; |
| 1045 | |
| 1046 | default: /* can't get here */ |
| 1047 | } |
| 1048 | } |
| 1049 | checkupdates(); /* make sure we're updating */ |
| 1050 | } |
| 1051 | |
| 1052 | /* Detach a subdisk from a plex, or a plex from a volume. |
| 1053 | * detach subdisk plex [rename] |
| 1054 | * detach plex volume [rename] |
| 1055 | */ |
| 1056 | void |
| 1057 | vinum_detach(int argc, char *argv[], char *argv0[]) |
| 1058 | { |
| 1059 | struct vinum_ioctl_msg msg; |
| 1060 | struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; |
| 1061 | |
| 1062 | if ((argc < 1) |
| 1063 | || (argc > 2)) { |
| 1064 | fprintf(stderr, |
| 1065 | "Usage: \tdetach <subdisk> [rename]\n" |
| 1066 | "\tdetach <plex> [rename]\n"); |
| 1067 | return; |
| 1068 | } |
| 1069 | if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { |
| 1070 | perror("Can't get vinum config"); |
| 1071 | return; |
| 1072 | } |
| 1073 | msg.index = find_object(argv[0], &msg.type); /* find the object to detach */ |
| 1074 | msg.force = force; /* did we specify the use of force? */ |
| 1075 | msg.rename = 0; /* don't specify new name */ |
| 1076 | msg.recurse = recurse; /* but recurse if we have to */ |
| 1077 | |
| 1078 | /* XXX are we going to keep this? |
| 1079 | * Don't document it yet, since the |
| 1080 | * kernel side of things doesn't |
| 1081 | * implement it */ |
| 1082 | if (argc == 2) { |
| 1083 | if (!strcmp(argv[1], "rename")) |
| 1084 | msg.rename = 1; /* do renaming */ |
| 1085 | else { |
| 1086 | fprintf(stderr, "Unknown attribute: %s\n", argv[1]); |
| 1087 | return; |
| 1088 | } |
| 1089 | } |
| 1090 | if ((msg.type != sd_object) |
| 1091 | && (msg.type != plex_object)) { |
| 1092 | fprintf(stderr, "Can only detach subdisks and plexes\n"); |
| 1093 | return; |
| 1094 | } |
| 1095 | ioctl(superdev, VINUM_DETACH, &msg); |
| 1096 | if (reply->error != 0) |
| 1097 | fprintf(stderr, |
| 1098 | "Can't detach %s: %s (%d)\n", |
| 1099 | argv[0], |
| 1100 | reply->msg[0] ? reply->msg : strerror(reply->error), |
| 1101 | reply->error); |
| 1102 | checkupdates(); /* make sure we're updating */ |
| 1103 | } |
| 1104 | |
| 1105 | static void |
| 1106 | dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen) |
| 1107 | { |
| 1108 | struct _ioctl_reply *reply = (struct _ioctl_reply *) msg; |
| 1109 | |
| 1110 | if (strlen(name) > maxlen) { |
| 1111 | fprintf(stderr, "%s is too long\n", name); |
| 1112 | return; |
| 1113 | } |
| 1114 | strcpy(msg->newname, name); |
| 1115 | ioctl(superdev, VINUM_RENAME, msg); |
| 1116 | if (reply->error != 0) |
| 1117 | fprintf(stderr, |
| 1118 | "Can't rename %s to %s: %s (%d)\n", |
| 1119 | oldname, |
| 1120 | name, |
| 1121 | reply->msg[0] ? reply->msg : strerror(reply->error), |
| 1122 | reply->error); |
| 1123 | } |
| 1124 | |
| 1125 | /* Rename an object: |
| 1126 | * rename <object> "newname" |
| 1127 | */ |
| 1128 | void |
| 1129 | vinum_rename_2(char *oldname, char *newname) |
| 1130 | { |
| 1131 | struct vinum_rename_msg msg; |
| 1132 | int volno; |
| 1133 | int plexno; |
| 1134 | |
| 1135 | msg.index = find_object(oldname, &msg.type); /* find the object to rename */ |
| 1136 | msg.recurse = recurse; |
| 1137 | |
| 1138 | /* Ugh. Determine how long the name may be */ |
| 1139 | switch (msg.type) { |
| 1140 | case drive_object: |
| 1141 | dorename(&msg, oldname, newname, MAXDRIVENAME); |
| 1142 | break; |
| 1143 | |
| 1144 | case sd_object: |
| 1145 | dorename(&msg, oldname, newname, MAXSDNAME); |
| 1146 | break; |
| 1147 | |
| 1148 | case plex_object: |
| 1149 | plexno = msg.index; |
| 1150 | dorename(&msg, oldname, newname, MAXPLEXNAME); |
| 1151 | if (recurse) { |
| 1152 | int sdno; |
| 1153 | |
| 1154 | get_plex_info(&plex, plexno); /* find out who we are */ |
| 1155 | msg.type = sd_object; |
| 1156 | for (sdno = 0; sdno < plex.subdisks; sdno++) { |
| 1157 | char sdname[MAXPLEXNAME + 8]; |
| 1158 | |
| 1159 | get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ |
| 1160 | sprintf(sdname, "%s.s%d", newname, sdno); |
| 1161 | msg.index = sd.sdno; /* number of the subdisk */ |
| 1162 | dorename(&msg, sd.name, sdname, MAXSDNAME); |
| 1163 | } |
| 1164 | } |
| 1165 | break; |
| 1166 | |
| 1167 | case volume_object: |
| 1168 | volno = msg.index; |
| 1169 | dorename(&msg, oldname, newname, MAXVOLNAME); |
| 1170 | if (recurse) { |
| 1171 | int sdno; |
| 1172 | int plexno; |
| 1173 | |
| 1174 | get_volume_info(&vol, volno); /* find out who we are */ |
| 1175 | for (plexno = 0; plexno < vol.plexes; plexno++) { |
| 1176 | char plexname[MAXVOLNAME + 8]; |
| 1177 | |
| 1178 | msg.type = plex_object; |
| 1179 | sprintf(plexname, "%s.p%d", newname, plexno); |
| 1180 | msg.index = vol.plex[plexno]; /* number of the plex */ |
| 1181 | dorename(&msg, plex.name, plexname, MAXPLEXNAME); |
| 1182 | get_plex_info(&plex, vol.plex[plexno]); /* find out who we are */ |
| 1183 | msg.type = sd_object; |
| 1184 | for (sdno = 0; sdno < plex.subdisks; sdno++) { |
| 1185 | char sdname[MAXPLEXNAME + 8]; |
| 1186 | |
| 1187 | get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ |
| 1188 | sprintf(sdname, "%s.s%d", plexname, sdno); |
| 1189 | msg.index = sd.sdno; /* number of the subdisk */ |
| 1190 | dorename(&msg, sd.name, sdname, MAXSDNAME); |
| 1191 | } |
| 1192 | } |
| 1193 | } |
| 1194 | break; |
| 1195 | |
| 1196 | default: |
| 1197 | fprintf(stderr, "%s is not a Vinum object\n", oldname); |
| 1198 | return; |
| 1199 | } |
| 1200 | } |
| 1201 | |
| 1202 | void |
| 1203 | vinum_rename(int argc, char *argv[], char *argv0[]) |
| 1204 | { |
| 1205 | if (argc != 2) { |
| 1206 | fprintf(stderr, "Usage: \trename <object> <new name>\n"); |
| 1207 | return; |
| 1208 | } |
| 1209 | if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { |
| 1210 | perror("Can't get vinum config"); |
| 1211 | return; |
| 1212 | } |
| 1213 | vinum_rename_2(argv[0], argv[1]); |
| 1214 | checkupdates(); /* make sure we're updating */ |
| 1215 | } |
| 1216 | |
| 1217 | /* |
| 1218 | * Move objects: |
| 1219 | * |
| 1220 | * mv <dest> <src> ... |
| 1221 | */ |
| 1222 | void |
| 1223 | vinum_mv(int argc, char *argv[], char *argv0[]) |
| 1224 | { |
| 1225 | int i; /* loop index */ |
| 1226 | int srcobj; |
| 1227 | int destobj; |
| 1228 | enum objecttype srct; |
| 1229 | enum objecttype destt; |
| 1230 | int sdno; |
| 1231 | struct _ioctl_reply reply; |
| 1232 | struct vinum_ioctl_msg *msg = (struct vinum_ioctl_msg *) &reply; |
| 1233 | |
| 1234 | if (argc < 2) { |
| 1235 | fprintf(stderr, "Usage: \tmove <dest> <src> ...\n"); |
| 1236 | return; |
| 1237 | } |
| 1238 | /* Get current config */ |
| 1239 | if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { |
| 1240 | perror("Cannot get vinum config\n"); |
| 1241 | return; |
| 1242 | } |
| 1243 | /* Get our destination */ |
| 1244 | destobj = find_object(argv[0], &destt); |
| 1245 | if (destobj == -1) { |
| 1246 | fprintf(stderr, "Can't find %s\n", argv[0]); |
| 1247 | return; |
| 1248 | } |
| 1249 | /* Verify that the target is a drive */ |
| 1250 | if (destt != drive_object) { |
| 1251 | fprintf(stderr, "%s is not a drive\n", argv[0]); |
| 1252 | return; |
| 1253 | } |
| 1254 | for (i = 1; i < argc; i++) { /* for all the sources */ |
| 1255 | srcobj = find_object(argv[i], &srct); |
| 1256 | if (srcobj == -1) { |
| 1257 | fprintf(stderr, "Can't find %s\n", argv[i]); |
| 1258 | continue; |
| 1259 | } |
| 1260 | msg->index = destobj; |
| 1261 | switch (srct) { /* Handle the source object */ |
| 1262 | case drive_object: /* Move all subdisks on the drive to dst. */ |
| 1263 | get_drive_info(&drive, srcobj); /* get info on drive */ |
| 1264 | for (sdno = 0; sdno < vinum_conf.subdisks_allocated; ++sdno) { |
| 1265 | get_sd_info(&sd, sdno); |
| 1266 | if (sd.driveno == srcobj) { |
| 1267 | msg->index = destobj; |
| 1268 | msg->otherobject = sd.sdno; |
| 1269 | if (ioctl(superdev, VINUM_MOVE, msg) < 0) |
| 1270 | fprintf(stderr, |
| 1271 | "Can't move %s (part of %s) to %s: %s (%d)\n", |
| 1272 | sd.name, |
| 1273 | drive.label.name, |
| 1274 | argv[0], |
| 1275 | strerror(reply.error), |
| 1276 | reply.error); |
| 1277 | } |
| 1278 | } |
| 1279 | break; |
| 1280 | |
| 1281 | case sd_object: |
| 1282 | msg->otherobject = srcobj; |
| 1283 | if (ioctl(superdev, VINUM_MOVE, msg) < 0) |
| 1284 | fprintf(stderr, |
| 1285 | "Can't move %s to %s: %s (%d)\n", |
| 1286 | sd.name, |
| 1287 | argv[0], |
| 1288 | strerror(reply.error), |
| 1289 | reply.error); |
| 1290 | break; |
| 1291 | |
| 1292 | case plex_object: |
| 1293 | get_plex_info(&plex, srcobj); |
| 1294 | for (sdno = 0; sdno < plex.subdisks; ++sdno) { |
| 1295 | get_plex_sd_info(&sd, plex.plexno, sdno); |
| 1296 | msg->index = destobj; |
| 1297 | msg->otherobject = sd.sdno; |
| 1298 | if (ioctl(superdev, VINUM_MOVE, msg) < 0) |
| 1299 | fprintf(stderr, |
| 1300 | "Can't move %s (part of %s) to %s: %s (%d)\n", |
| 1301 | sd.name, |
| 1302 | plex.name, |
| 1303 | argv[0], |
| 1304 | strerror(reply.error), |
| 1305 | reply.error); |
| 1306 | } |
| 1307 | break; |
| 1308 | |
| 1309 | case volume_object: |
| 1310 | case invalid_object: |
| 1311 | default: |
| 1312 | fprintf(stderr, "Can't move %s (inappropriate object).\n", argv[i]); |
| 1313 | break; |
| 1314 | } |
| 1315 | if (reply.error) |
| 1316 | fprintf(stderr, |
| 1317 | "Can't move %s to %s: %s (%d)\n", |
| 1318 | argv[i], |
| 1319 | argv[0], |
| 1320 | strerror(reply.error), |
| 1321 | reply.error); |
| 1322 | } |
| 1323 | checkupdates(); /* make sure we're updating */ |
| 1324 | } |
| 1325 | |
| 1326 | /* |
| 1327 | * Replace objects. Not implemented, may never be. |
| 1328 | */ |
| 1329 | void |
| 1330 | vinum_replace(int argc, char *argv[], char *argv0[]) |
| 1331 | { |
| 1332 | fprintf(stderr, "'replace' not implemented yet. Use 'move' instead\n"); |
| 1333 | } |
| 1334 | |
| 1335 | /* Primitive help function */ |
| 1336 | void |
| 1337 | vinum_help(int argc, char *argv[], char *argv0[]) |
| 1338 | { |
| 1339 | char commands[] = |
| 1340 | { |
| 1341 | "COMMANDS\n" |
| 1342 | "create [-f description-file]\n" |
| 1343 | " Create a volume as described in description-file\n" |
| 1344 | "attach plex volume [rename]\n" |
| 1345 | "attach subdisk plex [offset] [rename]\n" |
| 1346 | " Attach a plex to a volume, or a subdisk to a plex.\n" |
| 1347 | "debug\n" |
| 1348 | " Cause the volume manager to enter the kernel debugger.\n" |
| 1349 | "debug flags\n" |
| 1350 | " Set debugging flags.\n" |
| 1351 | "detach [plex | subdisk]\n" |
| 1352 | " Detach a plex or subdisk from the volume or plex to which it is\n" |
| 1353 | " attached.\n" |
| 1354 | "info [-v]\n" |
| 1355 | " List information about volume manager state.\n" |
| 1356 | "init [-v] [-w] plex\n" |
| 1357 | " Initialize a plex by writing zeroes to all its subdisks.\n" |
| 1358 | "label volume\n" |
| 1359 | " Create a volume label\n" |
| 1360 | "list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n" |
| 1361 | " List information about specified objects\n" |
| 1362 | "l [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n" |
| 1363 | " List information about specified objects (alternative to\n" |
| 1364 | " list command)\n" |
| 1365 | "ld [-r] [-s] [-v] [-V] [volume]\n" |
| 1366 | " List information about drives\n" |
| 1367 | "ls [-r] [-s] [-v] [-V] [subdisk]\n" |
| 1368 | " List information about subdisks\n" |
| 1369 | "lp [-r] [-s] [-v] [-V] [plex]\n" |
| 1370 | " List information about plexes\n" |
| 1371 | "lv [-r] [-s] [-v] [-V] [volume]\n" |
| 1372 | " List information about volumes\n" |
| 1373 | "printconfig [file]\n" |
| 1374 | " Write a copy of the current configuration to file.\n" |
| 1375 | "makedev\n" |
| 1376 | " Remake the device nodes in " _PATH_DEV "vinum.\n" |
| 1377 | "move drive [subdisk | plex | drive]\n" |
| 1378 | " Move the subdisks of the specified object(s) to drive.\n" |
| 1379 | "quit\n" |
| 1380 | " Exit the vinum program when running in interactive mode. Nor-\n" |
| 1381 | " mally this would be done by entering the EOF character.\n" |
| 1382 | "read disk [disk...]\n" |
| 1383 | " Read the vinum configuration from the specified disks.\n" |
| 1384 | "rename [-r] [drive | subdisk | plex | volume] newname\n" |
| 1385 | " Change the name of the specified object.\n" |
| 1386 | "resetconfig\n" |
| 1387 | " Reset the complete vinum configuration.\n" |
| 1388 | "resetstats [-r] [volume | plex | subdisk]\n" |
| 1389 | " Reset statistisc counters for the specified objects, or for all\n" |
| 1390 | " objects if none are specified.\n" |
| 1391 | "rm [-f] [-r] volume | plex | subdisk\n" |
| 1392 | " Remove an object\n" |
| 1393 | "saveconfig\n" |
| 1394 | " Save vinum configuration to disk.\n" |
| 1395 | "setdaemon [value]\n" |
| 1396 | " Set daemon configuration.\n" |
| 1397 | "start\n" |
| 1398 | " Read configuration from all vinum drives.\n" |
| 1399 | "start [volume | plex | subdisk]\n" |
| 1400 | " Allow the system to access the objects\n" |
| 1401 | "stop [-f] [volume | plex | subdisk]\n" |
| 1402 | " Terminate access to the objects, or stop vinum if no parameters\n" |
| 1403 | " are specified.\n" |
| 1404 | }; |
| 1405 | puts(commands); |
| 1406 | } |
| 1407 | |
| 1408 | /* Set daemon options. |
| 1409 | * XXX quick and dirty: use a bitmap, which requires |
| 1410 | * knowing which bit does what. FIXME */ |
| 1411 | void |
| 1412 | vinum_setdaemon(int argc, char *argv[], char *argv0[]) |
| 1413 | { |
| 1414 | int options; |
| 1415 | |
| 1416 | switch (argc) { |
| 1417 | case 0: |
| 1418 | if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0) |
| 1419 | fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno); |
| 1420 | else |
| 1421 | printf("Options mask: %d\n", options); |
| 1422 | break; |
| 1423 | |
| 1424 | case 1: |
| 1425 | options = atoi(argv[0]); |
| 1426 | if (ioctl(superdev, VINUM_SETDAEMON, &options) < 0) |
| 1427 | fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno); |
| 1428 | break; |
| 1429 | |
| 1430 | default: |
| 1431 | fprintf(stderr, "Usage: \tsetdaemon [<bitmask>]\n"); |
| 1432 | } |
| 1433 | checkupdates(); /* make sure we're updating */ |
| 1434 | } |
| 1435 | |
| 1436 | int |
| 1437 | checkupdates() |
| 1438 | { |
| 1439 | int options; |
| 1440 | |
| 1441 | if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0) |
| 1442 | fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno); |
| 1443 | if (options & daemon_noupdate) { |
| 1444 | fprintf(stderr, "*** Warning: configuration updates are disabled. ***\n"); |
| 1445 | return 1; |
| 1446 | } else |
| 1447 | return 0; |
| 1448 | } |
| 1449 | |
| 1450 | /* Save config info */ |
| 1451 | void |
| 1452 | vinum_saveconfig(int argc, char *argv[], char *argv0[]) |
| 1453 | { |
| 1454 | int ioctltype; |
| 1455 | |
| 1456 | if (argc != 0) { |
| 1457 | printf("Usage: saveconfig\n"); |
| 1458 | return; |
| 1459 | } |
| 1460 | ioctltype = 1; /* user saveconfig */ |
| 1461 | if (ioctl(superdev, VINUM_SAVECONFIG, &ioctltype) < 0) |
| 1462 | fprintf(stderr, "Can't save configuration: %s (%d)\n", strerror(errno), errno); |
| 1463 | checkupdates(); /* make sure we're updating */ |
| 1464 | } |
| 1465 | |
| 1466 | /* |
| 1467 | * Create a volume name for the quick and dirty |
| 1468 | * commands. It will be of the form "vinum#", |
| 1469 | * where # is a small positive number. |
| 1470 | */ |
| 1471 | void |
| 1472 | genvolname() |
| 1473 | { |
| 1474 | int v; /* volume number */ |
| 1475 | static char volumename[MAXVOLNAME]; /* name to create */ |
| 1476 | enum objecttype type; |
| 1477 | |
| 1478 | objectname = volumename; /* point to it */ |
| 1479 | for (v = 0;; v++) { |
| 1480 | sprintf(objectname, "vinum%d", v); /* create the name */ |
| 1481 | if (find_object(objectname, &type) == -1) /* does it exist? */ |
| 1482 | return; /* no, it's ours */ |
| 1483 | } |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 | * Create a drive for the quick and dirty |
| 1488 | * commands. The name will be of the form |
| 1489 | * vinumdrive#, where # is a small positive |
| 1490 | * number. Return the name of the drive. |
| 1491 | */ |
| 1492 | struct drive * |
| 1493 | create_drive(char *devicename) |
| 1494 | { |
| 1495 | int d; /* volume number */ |
| 1496 | static char drivename[MAXDRIVENAME]; /* name to create */ |
| 1497 | enum objecttype type; |
| 1498 | struct _ioctl_reply *reply; |
| 1499 | |
| 1500 | /* |
| 1501 | * We're never likely to get anything |
| 1502 | * like 10000 drives. The only reason for |
| 1503 | * this limit is to stop the thing |
| 1504 | * looping if we have a bug somewhere. |
| 1505 | */ |
| 1506 | for (d = 0; d < 100000; d++) { /* look for a free drive number */ |
| 1507 | sprintf(drivename, "vinumdrive%d", d); /* create the name */ |
| 1508 | if (find_object(drivename, &type) == -1) { /* does it exist? */ |
| 1509 | char command[MAXDRIVENAME * 2]; |
| 1510 | |
| 1511 | sprintf(command, "drive %s device %s", drivename, devicename); /* create a create command */ |
| 1512 | if (vflag) |
| 1513 | printf("drive %s device %s\n", drivename, devicename); /* create a create command */ |
| 1514 | ioctl(superdev, VINUM_CREATE, command); |
| 1515 | reply = (struct _ioctl_reply *) &command; |
| 1516 | if (reply->error != 0) { /* error in config */ |
| 1517 | if (reply->msg[0]) |
| 1518 | fprintf(stderr, |
| 1519 | "Can't create drive %s, device %s: %s\n", |
| 1520 | drivename, |
| 1521 | devicename, |
| 1522 | reply->msg); |
| 1523 | else |
| 1524 | fprintf(stderr, |
| 1525 | "Can't create drive %s, device %s: %s (%d)\n", |
| 1526 | drivename, |
| 1527 | devicename, |
| 1528 | strerror(reply->error), |
| 1529 | reply->error); |
| 1530 | longjmp(command_fail, -1); /* give up */ |
| 1531 | } |
| 1532 | find_object(drivename, &type); |
| 1533 | return &drive; /* return the name of the drive */ |
| 1534 | } |
| 1535 | } |
| 1536 | fprintf(stderr, "Can't generate a drive name\n"); |
| 1537 | /* NOTREACHED */ |
| 1538 | return NULL; |
| 1539 | } |
| 1540 | |
| 1541 | /* |
| 1542 | * Create a volume with a single concatenated plex from |
| 1543 | * as much space as we can get on the specified drives. |
| 1544 | * If the drives aren't Vinum drives, make them so. |
| 1545 | */ |
| 1546 | void |
| 1547 | vinum_concat(int argc, char *argv[], char *argv0[]) |
| 1548 | { |
| 1549 | int o; /* object number */ |
| 1550 | char buffer[BUFSIZE]; |
| 1551 | struct drive *drive; /* drive we're currently looking at */ |
| 1552 | struct _ioctl_reply *reply; |
| 1553 | int ioctltype; |
| 1554 | int error; |
| 1555 | enum objecttype type; |
| 1556 | |
| 1557 | reply = (struct _ioctl_reply *) &buffer; |
| 1558 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 1559 | printf("Can't configure: %s (%d)\n", strerror(errno), errno); |
| 1560 | return; |
| 1561 | } |
| 1562 | if (!objectname) /* we need a name for our object */ |
| 1563 | genvolname(); |
| 1564 | sprintf(buffer, "volume %s", objectname); |
| 1565 | if (vflag) |
| 1566 | printf("volume %s\n", objectname); |
| 1567 | ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ |
| 1568 | if (reply->error != 0) { /* error in config */ |
| 1569 | if (reply->msg[0]) |
| 1570 | fprintf(stderr, |
| 1571 | "Can't create volume %s: %s\n", |
| 1572 | objectname, |
| 1573 | reply->msg); |
| 1574 | else |
| 1575 | fprintf(stderr, |
| 1576 | "Can't create volume %s: %s (%d)\n", |
| 1577 | objectname, |
| 1578 | strerror(reply->error), |
| 1579 | reply->error); |
| 1580 | longjmp(command_fail, -1); /* give up */ |
| 1581 | } |
| 1582 | sprintf(buffer, "plex name %s.p0 org concat", objectname); |
| 1583 | if (vflag) |
| 1584 | printf(" plex name %s.p0 org concat\n", objectname); |
| 1585 | ioctl(superdev, VINUM_CREATE, buffer); |
| 1586 | if (reply->error != 0) { /* error in config */ |
| 1587 | if (reply->msg[0]) |
| 1588 | fprintf(stderr, |
| 1589 | "Can't create plex %s.p0: %s\n", |
| 1590 | objectname, |
| 1591 | reply->msg); |
| 1592 | else |
| 1593 | fprintf(stderr, |
| 1594 | "Can't create plex %s.p0: %s (%d)\n", |
| 1595 | objectname, |
| 1596 | strerror(reply->error), |
| 1597 | reply->error); |
| 1598 | longjmp(command_fail, -1); /* give up */ |
| 1599 | } |
| 1600 | for (o = 0; o < argc; o++) { |
| 1601 | if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ |
| 1602 | drive = create_drive(argv[o]); /* create it */ |
| 1603 | sprintf(buffer, "sd name %s.p0.s%d drive %s size 0", objectname, o, drive->label.name); |
| 1604 | if (vflag) |
| 1605 | printf(" sd name %s.p0.s%d drive %s size 0\n", objectname, o, drive->label.name); |
| 1606 | ioctl(superdev, VINUM_CREATE, buffer); |
| 1607 | if (reply->error != 0) { /* error in config */ |
| 1608 | if (reply->msg[0]) |
| 1609 | fprintf(stderr, |
| 1610 | "Can't create subdisk %s.p0.s%d: %s\n", |
| 1611 | objectname, |
| 1612 | o, |
| 1613 | reply->msg); |
| 1614 | else |
| 1615 | fprintf(stderr, |
| 1616 | "Can't create subdisk %s.p0.s%d: %s (%d)\n", |
| 1617 | objectname, |
| 1618 | o, |
| 1619 | strerror(reply->error), |
| 1620 | reply->error); |
| 1621 | longjmp(command_fail, -1); /* give up */ |
| 1622 | } |
| 1623 | } |
| 1624 | |
| 1625 | /* done, save the config */ |
| 1626 | ioctltype = 0; /* saveconfig after update */ |
| 1627 | error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ |
| 1628 | if (error != 0) |
| 1629 | perror("Can't save Vinum config"); |
| 1630 | find_object(objectname, &type); /* find the index of the volume */ |
| 1631 | make_vol_dev(vol.volno, 1); /* and create the devices */ |
| 1632 | if (vflag) { |
| 1633 | vflag--; /* XXX don't give too much detail */ |
| 1634 | find_object(objectname, &type); /* point to the volume */ |
| 1635 | vinum_lvi(vol.volno, 1); /* and print info about it */ |
| 1636 | } |
| 1637 | } |
| 1638 | |
| 1639 | |
| 1640 | /* |
| 1641 | * Create a volume with a single striped plex from |
| 1642 | * as much space as we can get on the specified drives. |
| 1643 | * If the drives aren't Vinum drives, make them so. |
| 1644 | */ |
| 1645 | void |
| 1646 | vinum_stripe(int argc, char *argv[], char *argv0[]) |
| 1647 | { |
| 1648 | int o; /* object number */ |
| 1649 | char buffer[BUFSIZE]; |
| 1650 | struct drive *drive; /* drive we're currently looking at */ |
| 1651 | struct _ioctl_reply *reply; |
| 1652 | int ioctltype; |
| 1653 | int error; |
| 1654 | enum objecttype type; |
| 1655 | off_t maxsize; |
| 1656 | int fe; /* freelist entry index */ |
| 1657 | struct drive_freelist freelist; |
| 1658 | struct ferq { /* request to pass to ioctl */ |
| 1659 | int driveno; |
| 1660 | int fe; |
| 1661 | } *ferq = (struct ferq *) &freelist; |
| 1662 | u_int64_t bigchunk; /* biggest chunk in freelist */ |
| 1663 | |
| 1664 | maxsize = QUAD_MAX; |
| 1665 | reply = (struct _ioctl_reply *) &buffer; |
| 1666 | |
| 1667 | /* |
| 1668 | * First, check our drives. |
| 1669 | */ |
| 1670 | if (argc < 2) { |
| 1671 | fprintf(stderr, "You need at least two drives to create a striped plex\n"); |
| 1672 | return; |
| 1673 | } |
| 1674 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 1675 | printf("Can't configure: %s (%d)\n", strerror(errno), errno); |
| 1676 | return; |
| 1677 | } |
| 1678 | if (!objectname) /* we need a name for our object */ |
| 1679 | genvolname(); |
| 1680 | for (o = 0; o < argc; o++) { |
| 1681 | if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ |
| 1682 | drive = create_drive(argv[o]); /* create it */ |
| 1683 | /* Now find the largest chunk available on the drive */ |
| 1684 | bigchunk = 0; /* ain't found nothin' yet */ |
| 1685 | for (fe = 0; fe < drive->freelist_entries; fe++) { |
| 1686 | ferq->driveno = drive->driveno; |
| 1687 | ferq->fe = fe; |
| 1688 | if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { |
| 1689 | fprintf(stderr, |
| 1690 | "Can't get free list element %d: %s\n", |
| 1691 | fe, |
| 1692 | strerror(errno)); |
| 1693 | longjmp(command_fail, -1); |
| 1694 | } |
| 1695 | bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ |
| 1696 | } |
| 1697 | maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ |
| 1698 | } |
| 1699 | |
| 1700 | /* Now create the volume */ |
| 1701 | sprintf(buffer, "volume %s", objectname); |
| 1702 | if (vflag) |
| 1703 | printf("volume %s\n", objectname); |
| 1704 | ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ |
| 1705 | if (reply->error != 0) { /* error in config */ |
| 1706 | if (reply->msg[0]) |
| 1707 | fprintf(stderr, |
| 1708 | "Can't create volume %s: %s\n", |
| 1709 | objectname, |
| 1710 | reply->msg); |
| 1711 | else |
| 1712 | fprintf(stderr, |
| 1713 | "Can't create volume %s: %s (%d)\n", |
| 1714 | objectname, |
| 1715 | strerror(reply->error), |
| 1716 | reply->error); |
| 1717 | longjmp(command_fail, -1); /* give up */ |
| 1718 | } |
| 1719 | sprintf(buffer, "plex name %s.p0 org striped 256k", objectname); |
| 1720 | if (vflag) |
| 1721 | printf(" plex name %s.p0 org striped 256k\n", objectname); |
| 1722 | ioctl(superdev, VINUM_CREATE, buffer); |
| 1723 | if (reply->error != 0) { /* error in config */ |
| 1724 | if (reply->msg[0]) |
| 1725 | fprintf(stderr, |
| 1726 | "Can't create plex %s.p0: %s\n", |
| 1727 | objectname, |
| 1728 | reply->msg); |
| 1729 | else |
| 1730 | fprintf(stderr, |
| 1731 | "Can't create plex %s.p0: %s (%d)\n", |
| 1732 | objectname, |
| 1733 | strerror(reply->error), |
| 1734 | reply->error); |
| 1735 | longjmp(command_fail, -1); /* give up */ |
| 1736 | } |
| 1737 | for (o = 0; o < argc; o++) { |
| 1738 | drive = find_drive_by_devname(argv[o]); /* we know it exists... */ |
| 1739 | sprintf(buffer, |
| 1740 | "sd name %s.p0.s%d drive %s size %lldb", |
| 1741 | objectname, |
| 1742 | o, |
| 1743 | drive->label.name, |
| 1744 | (long long) maxsize); |
| 1745 | if (vflag) |
| 1746 | printf(" sd name %s.p0.s%d drive %s size %lldb\n", |
| 1747 | objectname, |
| 1748 | o, |
| 1749 | drive->label.name, |
| 1750 | (long long) maxsize); |
| 1751 | ioctl(superdev, VINUM_CREATE, buffer); |
| 1752 | if (reply->error != 0) { /* error in config */ |
| 1753 | if (reply->msg[0]) |
| 1754 | fprintf(stderr, |
| 1755 | "Can't create subdisk %s.p0.s%d: %s\n", |
| 1756 | objectname, |
| 1757 | o, |
| 1758 | reply->msg); |
| 1759 | else |
| 1760 | fprintf(stderr, |
| 1761 | "Can't create subdisk %s.p0.s%d: %s (%d)\n", |
| 1762 | objectname, |
| 1763 | o, |
| 1764 | strerror(reply->error), |
| 1765 | reply->error); |
| 1766 | longjmp(command_fail, -1); /* give up */ |
| 1767 | } |
| 1768 | } |
| 1769 | |
| 1770 | /* done, save the config */ |
| 1771 | ioctltype = 0; /* saveconfig after update */ |
| 1772 | error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ |
| 1773 | if (error != 0) |
| 1774 | perror("Can't save Vinum config"); |
| 1775 | find_object(objectname, &type); /* find the index of the volume */ |
| 1776 | make_vol_dev(vol.volno, 1); /* and create the devices */ |
| 1777 | if (vflag) { |
| 1778 | vflag--; /* XXX don't give too much detail */ |
| 1779 | find_object(objectname, &type); /* point to the volume */ |
| 1780 | vinum_lvi(vol.volno, 1); /* and print info about it */ |
| 1781 | } |
| 1782 | } |
| 1783 | |
| 1784 | /* |
| 1785 | * Create a volume with a single RAID-4 plex from |
| 1786 | * as much space as we can get on the specified drives. |
| 1787 | * If the drives aren't Vinum drives, make them so. |
| 1788 | */ |
| 1789 | void |
| 1790 | vinum_raid4(int argc, char *argv[], char *argv0[]) |
| 1791 | { |
| 1792 | int o; /* object number */ |
| 1793 | char buffer[BUFSIZE]; |
| 1794 | struct drive *drive; /* drive we're currently looking at */ |
| 1795 | struct _ioctl_reply *reply; |
| 1796 | int ioctltype; |
| 1797 | int error; |
| 1798 | enum objecttype type; |
| 1799 | off_t maxsize; |
| 1800 | int fe; /* freelist entry index */ |
| 1801 | struct drive_freelist freelist; |
| 1802 | struct ferq { /* request to pass to ioctl */ |
| 1803 | int driveno; |
| 1804 | int fe; |
| 1805 | } *ferq = (struct ferq *) &freelist; |
| 1806 | u_int64_t bigchunk; /* biggest chunk in freelist */ |
| 1807 | |
| 1808 | maxsize = QUAD_MAX; |
| 1809 | reply = (struct _ioctl_reply *) &buffer; |
| 1810 | |
| 1811 | /* |
| 1812 | * First, check our drives. |
| 1813 | */ |
| 1814 | if (argc < 3) { |
| 1815 | fprintf(stderr, "You need at least three drives to create a RAID-4 plex\n"); |
| 1816 | return; |
| 1817 | } |
| 1818 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 1819 | printf("Can't configure: %s (%d)\n", strerror(errno), errno); |
| 1820 | return; |
| 1821 | } |
| 1822 | if (!objectname) /* we need a name for our object */ |
| 1823 | genvolname(); |
| 1824 | for (o = 0; o < argc; o++) { |
| 1825 | if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ |
| 1826 | drive = create_drive(argv[o]); /* create it */ |
| 1827 | /* Now find the largest chunk available on the drive */ |
| 1828 | bigchunk = 0; /* ain't found nothin' yet */ |
| 1829 | for (fe = 0; fe < drive->freelist_entries; fe++) { |
| 1830 | ferq->driveno = drive->driveno; |
| 1831 | ferq->fe = fe; |
| 1832 | if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { |
| 1833 | fprintf(stderr, |
| 1834 | "Can't get free list element %d: %s\n", |
| 1835 | fe, |
| 1836 | strerror(errno)); |
| 1837 | longjmp(command_fail, -1); |
| 1838 | } |
| 1839 | bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ |
| 1840 | } |
| 1841 | maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ |
| 1842 | } |
| 1843 | |
| 1844 | /* Now create the volume */ |
| 1845 | sprintf(buffer, "volume %s", objectname); |
| 1846 | if (vflag) |
| 1847 | printf("volume %s\n", objectname); |
| 1848 | ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ |
| 1849 | if (reply->error != 0) { /* error in config */ |
| 1850 | if (reply->msg[0]) |
| 1851 | fprintf(stderr, |
| 1852 | "Can't create volume %s: %s\n", |
| 1853 | objectname, |
| 1854 | reply->msg); |
| 1855 | else |
| 1856 | fprintf(stderr, |
| 1857 | "Can't create volume %s: %s (%d)\n", |
| 1858 | objectname, |
| 1859 | strerror(reply->error), |
| 1860 | reply->error); |
| 1861 | longjmp(command_fail, -1); /* give up */ |
| 1862 | } |
| 1863 | sprintf(buffer, "plex name %s.p0 org raid4 256k", objectname); |
| 1864 | if (vflag) |
| 1865 | printf(" plex name %s.p0 org raid4 256k\n", objectname); |
| 1866 | ioctl(superdev, VINUM_CREATE, buffer); |
| 1867 | if (reply->error != 0) { /* error in config */ |
| 1868 | if (reply->msg[0]) |
| 1869 | fprintf(stderr, |
| 1870 | "Can't create plex %s.p0: %s\n", |
| 1871 | objectname, |
| 1872 | reply->msg); |
| 1873 | else |
| 1874 | fprintf(stderr, |
| 1875 | "Can't create plex %s.p0: %s (%d)\n", |
| 1876 | objectname, |
| 1877 | strerror(reply->error), |
| 1878 | reply->error); |
| 1879 | longjmp(command_fail, -1); /* give up */ |
| 1880 | } |
| 1881 | for (o = 0; o < argc; o++) { |
| 1882 | drive = find_drive_by_devname(argv[o]); /* we know it exists... */ |
| 1883 | sprintf(buffer, |
| 1884 | "sd name %s.p0.s%d drive %s size %lldb", |
| 1885 | objectname, |
| 1886 | o, |
| 1887 | drive->label.name, |
| 1888 | (long long) maxsize); |
| 1889 | if (vflag) |
| 1890 | printf(" sd name %s.p0.s%d drive %s size %lldb\n", |
| 1891 | objectname, |
| 1892 | o, |
| 1893 | drive->label.name, |
| 1894 | (long long) maxsize); |
| 1895 | ioctl(superdev, VINUM_CREATE, buffer); |
| 1896 | if (reply->error != 0) { /* error in config */ |
| 1897 | if (reply->msg[0]) |
| 1898 | fprintf(stderr, |
| 1899 | "Can't create subdisk %s.p0.s%d: %s\n", |
| 1900 | objectname, |
| 1901 | o, |
| 1902 | reply->msg); |
| 1903 | else |
| 1904 | fprintf(stderr, |
| 1905 | "Can't create subdisk %s.p0.s%d: %s (%d)\n", |
| 1906 | objectname, |
| 1907 | o, |
| 1908 | strerror(reply->error), |
| 1909 | reply->error); |
| 1910 | longjmp(command_fail, -1); /* give up */ |
| 1911 | } |
| 1912 | } |
| 1913 | |
| 1914 | /* done, save the config */ |
| 1915 | ioctltype = 0; /* saveconfig after update */ |
| 1916 | error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ |
| 1917 | if (error != 0) |
| 1918 | perror("Can't save Vinum config"); |
| 1919 | find_object(objectname, &type); /* find the index of the volume */ |
| 1920 | make_vol_dev(vol.volno, 1); /* and create the devices */ |
| 1921 | if (vflag) { |
| 1922 | vflag--; /* XXX don't give too much detail */ |
| 1923 | find_object(objectname, &type); /* point to the volume */ |
| 1924 | vinum_lvi(vol.volno, 1); /* and print info about it */ |
| 1925 | } |
| 1926 | } |
| 1927 | |
| 1928 | /* |
| 1929 | * Create a volume with a single RAID-4 plex from |
| 1930 | * as much space as we can get on the specified drives. |
| 1931 | * If the drives aren't Vinum drives, make them so. |
| 1932 | */ |
| 1933 | void |
| 1934 | vinum_raid5(int argc, char *argv[], char *argv0[]) |
| 1935 | { |
| 1936 | int o; /* object number */ |
| 1937 | char buffer[BUFSIZE]; |
| 1938 | struct drive *drive; /* drive we're currently looking at */ |
| 1939 | struct _ioctl_reply *reply; |
| 1940 | int ioctltype; |
| 1941 | int error; |
| 1942 | enum objecttype type; |
| 1943 | off_t maxsize; |
| 1944 | int fe; /* freelist entry index */ |
| 1945 | struct drive_freelist freelist; |
| 1946 | struct ferq { /* request to pass to ioctl */ |
| 1947 | int driveno; |
| 1948 | int fe; |
| 1949 | } *ferq = (struct ferq *) &freelist; |
| 1950 | u_int64_t bigchunk; /* biggest chunk in freelist */ |
| 1951 | |
| 1952 | maxsize = QUAD_MAX; |
| 1953 | reply = (struct _ioctl_reply *) &buffer; |
| 1954 | |
| 1955 | /* |
| 1956 | * First, check our drives. |
| 1957 | */ |
| 1958 | if (argc < 3) { |
| 1959 | fprintf(stderr, "You need at least three drives to create a RAID-5 plex\n"); |
| 1960 | return; |
| 1961 | } |
| 1962 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 1963 | printf("Can't configure: %s (%d)\n", strerror(errno), errno); |
| 1964 | return; |
| 1965 | } |
| 1966 | if (!objectname) /* we need a name for our object */ |
| 1967 | genvolname(); |
| 1968 | for (o = 0; o < argc; o++) { |
| 1969 | if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ |
| 1970 | drive = create_drive(argv[o]); /* create it */ |
| 1971 | /* Now find the largest chunk available on the drive */ |
| 1972 | bigchunk = 0; /* ain't found nothin' yet */ |
| 1973 | for (fe = 0; fe < drive->freelist_entries; fe++) { |
| 1974 | ferq->driveno = drive->driveno; |
| 1975 | ferq->fe = fe; |
| 1976 | if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { |
| 1977 | fprintf(stderr, |
| 1978 | "Can't get free list element %d: %s\n", |
| 1979 | fe, |
| 1980 | strerror(errno)); |
| 1981 | longjmp(command_fail, -1); |
| 1982 | } |
| 1983 | bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ |
| 1984 | } |
| 1985 | maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ |
| 1986 | } |
| 1987 | |
| 1988 | /* Now create the volume */ |
| 1989 | sprintf(buffer, "volume %s", objectname); |
| 1990 | if (vflag) |
| 1991 | printf("volume %s\n", objectname); |
| 1992 | ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ |
| 1993 | if (reply->error != 0) { /* error in config */ |
| 1994 | if (reply->msg[0]) |
| 1995 | fprintf(stderr, |
| 1996 | "Can't create volume %s: %s\n", |
| 1997 | objectname, |
| 1998 | reply->msg); |
| 1999 | else |
| 2000 | fprintf(stderr, |
| 2001 | "Can't create volume %s: %s (%d)\n", |
| 2002 | objectname, |
| 2003 | strerror(reply->error), |
| 2004 | reply->error); |
| 2005 | longjmp(command_fail, -1); /* give up */ |
| 2006 | } |
| 2007 | sprintf(buffer, "plex name %s.p0 org raid5 256k", objectname); |
| 2008 | if (vflag) |
| 2009 | printf(" plex name %s.p0 org raid5 256k\n", objectname); |
| 2010 | ioctl(superdev, VINUM_CREATE, buffer); |
| 2011 | if (reply->error != 0) { /* error in config */ |
| 2012 | if (reply->msg[0]) |
| 2013 | fprintf(stderr, |
| 2014 | "Can't create plex %s.p0: %s\n", |
| 2015 | objectname, |
| 2016 | reply->msg); |
| 2017 | else |
| 2018 | fprintf(stderr, |
| 2019 | "Can't create plex %s.p0: %s (%d)\n", |
| 2020 | objectname, |
| 2021 | strerror(reply->error), |
| 2022 | reply->error); |
| 2023 | longjmp(command_fail, -1); /* give up */ |
| 2024 | } |
| 2025 | for (o = 0; o < argc; o++) { |
| 2026 | drive = find_drive_by_devname(argv[o]); /* we know it exists... */ |
| 2027 | sprintf(buffer, |
| 2028 | "sd name %s.p0.s%d drive %s size %lldb", |
| 2029 | objectname, |
| 2030 | o, |
| 2031 | drive->label.name, |
| 2032 | (long long) maxsize); |
| 2033 | if (vflag) |
| 2034 | printf(" sd name %s.p0.s%d drive %s size %lldb\n", |
| 2035 | objectname, |
| 2036 | o, |
| 2037 | drive->label.name, |
| 2038 | (long long) maxsize); |
| 2039 | ioctl(superdev, VINUM_CREATE, buffer); |
| 2040 | if (reply->error != 0) { /* error in config */ |
| 2041 | if (reply->msg[0]) |
| 2042 | fprintf(stderr, |
| 2043 | "Can't create subdisk %s.p0.s%d: %s\n", |
| 2044 | objectname, |
| 2045 | o, |
| 2046 | reply->msg); |
| 2047 | else |
| 2048 | fprintf(stderr, |
| 2049 | "Can't create subdisk %s.p0.s%d: %s (%d)\n", |
| 2050 | objectname, |
| 2051 | o, |
| 2052 | strerror(reply->error), |
| 2053 | reply->error); |
| 2054 | longjmp(command_fail, -1); /* give up */ |
| 2055 | } |
| 2056 | } |
| 2057 | |
| 2058 | /* done, save the config */ |
| 2059 | ioctltype = 0; /* saveconfig after update */ |
| 2060 | error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ |
| 2061 | if (error != 0) |
| 2062 | perror("Can't save Vinum config"); |
| 2063 | find_object(objectname, &type); /* find the index of the volume */ |
| 2064 | make_vol_dev(vol.volno, 1); /* and create the devices */ |
| 2065 | if (vflag) { |
| 2066 | vflag--; /* XXX don't give too much detail */ |
| 2067 | find_object(objectname, &type); /* point to the volume */ |
| 2068 | vinum_lvi(vol.volno, 1); /* and print info about it */ |
| 2069 | } |
| 2070 | } |
| 2071 | |
| 2072 | /* |
| 2073 | * Create a volume with a two plexes from as much space |
| 2074 | * as we can get on the specified drives. If the |
| 2075 | * drives aren't Vinum drives, make them so. |
| 2076 | * |
| 2077 | * The number of drives must be even, and at least 4 |
| 2078 | * for a striped plex. Specify striped plexes with the |
| 2079 | * -s flag; otherwise they will be concatenated. It's |
| 2080 | * possible that the two plexes may differ in length. |
| 2081 | */ |
| 2082 | void |
| 2083 | vinum_mirror(int argc, char *argv[], char *argv0[]) |
| 2084 | { |
| 2085 | int o; /* object number */ |
| 2086 | int p; /* plex number */ |
| 2087 | char buffer[BUFSIZE]; |
| 2088 | struct drive *drive; /* drive we're currently looking at */ |
| 2089 | struct _ioctl_reply *reply; |
| 2090 | int ioctltype; |
| 2091 | int error; |
| 2092 | enum objecttype type; |
| 2093 | off_t maxsize[2]; /* maximum subdisk size for striped plexes */ |
| 2094 | int fe; /* freelist entry index */ |
| 2095 | struct drive_freelist freelist; |
| 2096 | struct ferq { /* request to pass to ioctl */ |
| 2097 | int driveno; |
| 2098 | int fe; |
| 2099 | } *ferq = (struct ferq *) &freelist; |
| 2100 | u_int64_t bigchunk; /* biggest chunk in freelist */ |
| 2101 | |
| 2102 | if (sflag) /* striped, */ |
| 2103 | maxsize[0] = maxsize[1] = QUAD_MAX; /* we need to calculate sd size */ |
| 2104 | else |
| 2105 | maxsize[0] = maxsize[1] = 0; /* let the kernel routines do it */ |
| 2106 | |
| 2107 | reply = (struct _ioctl_reply *) &buffer; |
| 2108 | |
| 2109 | /* |
| 2110 | * First, check our drives. |
| 2111 | */ |
| 2112 | if (argc & 1) { |
| 2113 | fprintf(stderr, "You need an even number of drives to create a mirrored volume\n"); |
| 2114 | return; |
| 2115 | } |
| 2116 | if (sflag && (argc < 4)) { |
| 2117 | fprintf(stderr, "You need at least 4 drives to create a mirrored, striped volume\n"); |
| 2118 | return; |
| 2119 | } |
| 2120 | if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ |
| 2121 | printf("Can't configure: %s (%d)\n", strerror(errno), errno); |
| 2122 | return; |
| 2123 | } |
| 2124 | if (!objectname) /* we need a name for our object */ |
| 2125 | genvolname(); |
| 2126 | for (o = 0; o < argc; o++) { |
| 2127 | if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ |
| 2128 | drive = create_drive(argv[o]); /* create it */ |
| 2129 | if (sflag) { /* striping, */ |
| 2130 | /* Find the largest chunk available on the drive */ |
| 2131 | bigchunk = 0; /* ain't found nothin' yet */ |
| 2132 | for (fe = 0; fe < drive->freelist_entries; fe++) { |
| 2133 | ferq->driveno = drive->driveno; |
| 2134 | ferq->fe = fe; |
| 2135 | if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { |
| 2136 | fprintf(stderr, |
| 2137 | "Can't get free list element %d: %s\n", |
| 2138 | fe, |
| 2139 | strerror(errno)); |
| 2140 | longjmp(command_fail, -1); |
| 2141 | } |
| 2142 | bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ |
| 2143 | } |
| 2144 | maxsize[o & 1] = min(maxsize[o & 1], bigchunk); /* get the maximum size of a subdisk */ |
| 2145 | } |
| 2146 | } |
| 2147 | |
| 2148 | /* Now create the volume */ |
| 2149 | sprintf(buffer, "volume %s setupstate", objectname); |
| 2150 | if (vflag) |
| 2151 | printf("volume %s setupstate\n", objectname); |
| 2152 | ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ |
| 2153 | if (reply->error != 0) { /* error in config */ |
| 2154 | if (reply->msg[0]) |
| 2155 | fprintf(stderr, |
| 2156 | "Can't create volume %s: %s\n", |
| 2157 | objectname, |
| 2158 | reply->msg); |
| 2159 | else |
| 2160 | fprintf(stderr, |
| 2161 | "Can't create volume %s: %s (%d)\n", |
| 2162 | objectname, |
| 2163 | strerror(reply->error), |
| 2164 | reply->error); |
| 2165 | longjmp(command_fail, -1); /* give up */ |
| 2166 | } |
| 2167 | for (p = 0; p < 2; p++) { /* create each plex */ |
| 2168 | if (sflag) { |
| 2169 | sprintf(buffer, "plex name %s.p%d org striped 256k", objectname, p); |
| 2170 | if (vflag) |
| 2171 | printf(" plex name %s.p%d org striped 256k\n", objectname, p); |
| 2172 | } else { /* concat */ |
| 2173 | sprintf(buffer, "plex name %s.p%d org concat", objectname, p); |
| 2174 | if (vflag) |
| 2175 | printf(" plex name %s.p%d org concat\n", objectname, p); |
| 2176 | } |
| 2177 | ioctl(superdev, VINUM_CREATE, buffer); |
| 2178 | if (reply->error != 0) { /* error in config */ |
| 2179 | if (reply->msg[0]) |
| 2180 | fprintf(stderr, |
| 2181 | "Can't create plex %s.p%d: %s\n", |
| 2182 | objectname, |
| 2183 | p, |
| 2184 | reply->msg); |
| 2185 | else |
| 2186 | fprintf(stderr, |
| 2187 | "Can't create plex %s.p%d: %s (%d)\n", |
| 2188 | objectname, |
| 2189 | p, |
| 2190 | strerror(reply->error), |
| 2191 | reply->error); |
| 2192 | longjmp(command_fail, -1); /* give up */ |
| 2193 | } |
| 2194 | /* Now look at the subdisks */ |
| 2195 | for (o = p; o < argc; o += 2) { /* every second one */ |
| 2196 | drive = find_drive_by_devname(argv[o]); /* we know it exists... */ |
| 2197 | sprintf(buffer, |
| 2198 | "sd name %s.p%d.s%d drive %s size %lldb", |
| 2199 | objectname, |
| 2200 | p, |
| 2201 | o >> 1, |
| 2202 | drive->label.name, |
| 2203 | (long long) maxsize[p]); |
| 2204 | if (vflag) |
| 2205 | printf(" sd name %s.p%d.s%d drive %s size %lldb\n", |
| 2206 | objectname, |
| 2207 | p, |
| 2208 | o >> 1, |
| 2209 | drive->label.name, |
| 2210 | (long long) maxsize[p]); |
| 2211 | ioctl(superdev, VINUM_CREATE, buffer); |
| 2212 | if (reply->error != 0) { /* error in config */ |
| 2213 | if (reply->msg[0]) |
| 2214 | fprintf(stderr, |
| 2215 | "Can't create subdisk %s.p%d.s%d: %s\n", |
| 2216 | objectname, |
| 2217 | p, |
| 2218 | o >> 1, |
| 2219 | reply->msg); |
| 2220 | else |
| 2221 | fprintf(stderr, |
| 2222 | "Can't create subdisk %s.p%d.s%d: %s (%d)\n", |
| 2223 | objectname, |
| 2224 | p, |
| 2225 | o >> 1, |
| 2226 | strerror(reply->error), |
| 2227 | reply->error); |
| 2228 | longjmp(command_fail, -1); /* give up */ |
| 2229 | } |
| 2230 | } |
| 2231 | } |
| 2232 | |
| 2233 | /* done, save the config */ |
| 2234 | ioctltype = 0; /* saveconfig after update */ |
| 2235 | error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ |
| 2236 | if (error != 0) |
| 2237 | perror("Can't save Vinum config"); |
| 2238 | find_object(objectname, &type); /* find the index of the volume */ |
| 2239 | make_vol_dev(vol.volno, 1); /* and create the devices */ |
| 2240 | if (vflag) { |
| 2241 | vflag--; /* XXX don't give too much detail */ |
| 2242 | sflag = 0; /* no stats, please */ |
| 2243 | find_object(objectname, &type); /* point to the volume */ |
| 2244 | vinum_lvi(vol.volno, 1); /* and print info about it */ |
| 2245 | } |
| 2246 | } |
| 2247 | |
| 2248 | void |
| 2249 | vinum_readpol(int argc, char *argv[], char *argv0[]) |
| 2250 | { |
| 2251 | int object; |
| 2252 | struct _ioctl_reply reply; |
| 2253 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 2254 | enum objecttype type; |
| 2255 | struct plex plex; |
| 2256 | struct volume vol; |
| 2257 | int plexno; |
| 2258 | |
| 2259 | if (argc == 0) { /* start everything */ |
| 2260 | fprintf(stderr, "Usage: readpol <volume> <plex>|round\n"); |
| 2261 | return; |
| 2262 | } |
| 2263 | object = find_object(argv[1], &type); /* look for it */ |
| 2264 | if (type != volume_object) { |
| 2265 | fprintf(stderr, "%s is not a volume\n", argv[1]); |
| 2266 | return; |
| 2267 | } |
| 2268 | get_volume_info(&vol, object); |
| 2269 | if (strcmp(argv[2], "round")) { /* not 'round' */ |
| 2270 | object = find_object(argv[2], &type); /* look for it */ |
| 2271 | if (type != plex_object) { |
| 2272 | fprintf(stderr, "%s is not a plex\n", argv[2]); |
| 2273 | return; |
| 2274 | } |
| 2275 | get_plex_info(&plex, object); |
| 2276 | plexno = plex.plexno; |
| 2277 | } else /* round */ |
| 2278 | plexno = -1; |
| 2279 | |
| 2280 | /* Set the value */ |
| 2281 | message->index = vol.volno; |
| 2282 | message->otherobject = plexno; |
| 2283 | if (ioctl(superdev, VINUM_READPOL, message) < 0) |
| 2284 | fprintf(stderr, "Can't set read policy: %s (%d)\n", strerror(errno), errno); |
| 2285 | if (vflag) |
| 2286 | vinum_lpi(plexno, recurse); |
| 2287 | } |
| 2288 | |
| 2289 | /* |
| 2290 | * Brute force set state function. Don't look at |
| 2291 | * any dependencies, just do it. |
| 2292 | */ |
| 2293 | void |
| 2294 | vinum_setstate(int argc, char *argv[], char *argv0[]) |
| 2295 | { |
| 2296 | int object; |
| 2297 | struct _ioctl_reply reply; |
| 2298 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 2299 | int index; |
| 2300 | enum objecttype type; |
| 2301 | int state; |
| 2302 | |
| 2303 | for (index = 1; index < argc; index++) { |
| 2304 | object = find_object(argv[index], &type); /* look for it */ |
| 2305 | if (type == invalid_object) |
| 2306 | fprintf(stderr, "Can't find object: %s\n", argv[index]); |
| 2307 | else { |
| 2308 | int doit = 0; /* set to 1 if we pass our tests */ |
| 2309 | switch (type) { |
| 2310 | case drive_object: |
| 2311 | state = DriveState(argv[0]); /* get the state */ |
| 2312 | if (drive.state == state) /* already in that state */ |
| 2313 | fprintf(stderr, "%s is already %s\n", drive.label.name, argv[0]); |
| 2314 | else |
| 2315 | doit = 1; |
| 2316 | break; |
| 2317 | |
| 2318 | case sd_object: |
| 2319 | state = SdState(argv[0]); /* get the state */ |
| 2320 | if (sd.state == state) /* already in that state */ |
| 2321 | fprintf(stderr, "%s is already %s\n", sd.name, argv[0]); |
| 2322 | else |
| 2323 | doit = 1; |
| 2324 | break; |
| 2325 | |
| 2326 | case plex_object: |
| 2327 | state = PlexState(argv[0]); /* get the state */ |
| 2328 | if (plex.state == state) /* already in that state */ |
| 2329 | fprintf(stderr, "%s is already %s\n", plex.name, argv[0]); |
| 2330 | else |
| 2331 | doit = 1; |
| 2332 | break; |
| 2333 | |
| 2334 | case volume_object: |
| 2335 | state = VolState(argv[0]); /* get the state */ |
| 2336 | if (vol.state == state) /* already in that state */ |
| 2337 | fprintf(stderr, "%s is already %s\n", vol.name, argv[0]); |
| 2338 | else |
| 2339 | doit = 1; |
| 2340 | break; |
| 2341 | |
| 2342 | default: |
| 2343 | state = 0; /* to keep the compiler happy */ |
| 2344 | } |
| 2345 | |
| 2346 | if (state == -1) |
| 2347 | fprintf(stderr, "Invalid state for object: %s\n", argv[0]); |
| 2348 | else if (doit) { |
| 2349 | message->index = object; /* pass object number */ |
| 2350 | message->type = type; /* and type of object */ |
| 2351 | message->state = state; |
| 2352 | message->force = force; /* don't force it, use a larger hammer */ |
| 2353 | ioctl(superdev, VINUM_SETSTATE_FORCE, message); |
| 2354 | if (reply.error != 0) |
| 2355 | fprintf(stderr, |
| 2356 | "Can't start %s: %s (%d)\n", |
| 2357 | argv[index], |
| 2358 | reply.msg[0] ? reply.msg : strerror(reply.error), |
| 2359 | reply.error); |
| 2360 | if (Verbose) |
| 2361 | vinum_li(object, type); |
| 2362 | } |
| 2363 | } |
| 2364 | } |
| 2365 | } |
| 2366 | |
| 2367 | void |
| 2368 | vinum_checkparity(int argc, char *argv[], char *argv0[]) |
| 2369 | { |
| 2370 | Verbose = vflag; /* accept -v for verbose */ |
| 2371 | if (argc == 0) /* no parameters? */ |
| 2372 | fprintf(stderr, "Usage: checkparity object [object...]\n"); |
| 2373 | else |
| 2374 | parityops(argc, argv, checkparity); |
| 2375 | } |
| 2376 | |
| 2377 | void |
| 2378 | vinum_rebuildparity(int argc, char *argv[], char *argv0[]) |
| 2379 | { |
| 2380 | if (argc == 0) /* no parameters? */ |
| 2381 | fprintf(stderr, "Usage: rebuildparity object [object...]\n"); |
| 2382 | else |
| 2383 | parityops(argc, argv, vflag ? rebuildandcheckparity : rebuildparity); |
| 2384 | } |
| 2385 | |
| 2386 | /* |
| 2387 | * Common code for rebuildparity and checkparity. |
| 2388 | * We bend the meanings of some flags here: |
| 2389 | * |
| 2390 | * -v: Report incorrect parity on rebuild. |
| 2391 | * -V: Show running count of position being checked. |
| 2392 | * -f: Start from beginning of the plex. |
| 2393 | */ |
| 2394 | void |
| 2395 | parityops(int argc, char *argv[], enum parityop op) |
| 2396 | { |
| 2397 | int object; |
| 2398 | struct plex plex; |
| 2399 | struct _ioctl_reply reply; |
| 2400 | struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; |
| 2401 | int index; |
| 2402 | enum objecttype type; |
| 2403 | char *msg; |
| 2404 | off_t block; |
| 2405 | |
| 2406 | if (op == checkparity) |
| 2407 | msg = "Checking"; |
| 2408 | else |
| 2409 | msg = "Rebuilding"; |
| 2410 | for (index = 0; index < argc; index++) { |
| 2411 | object = find_object(argv[index], &type); /* look for it */ |
| 2412 | if (type != plex_object) |
| 2413 | fprintf(stderr, "%s is not a plex\n", argv[index]); |
| 2414 | else { |
| 2415 | get_plex_info(&plex, object); |
| 2416 | if (!isparity((&plex))) |
| 2417 | fprintf(stderr, "%s is not a RAID-4 or RAID-5 plex\n", argv[index]); |
| 2418 | else { |
| 2419 | do { |
| 2420 | message->index = object; /* pass object number */ |
| 2421 | message->type = type; /* and type of object */ |
| 2422 | message->op = op; /* what to do */ |
| 2423 | if (force) |
| 2424 | message->offset = 0; /* start at the beginning */ |
| 2425 | else |
| 2426 | message->offset = plex.checkblock; /* continue where we left off */ |
| 2427 | force = 0; /* don't reset after the first time */ |
| 2428 | ioctl(superdev, VINUM_PARITYOP, message); |
| 2429 | get_plex_info(&plex, object); |
| 2430 | if (Verbose) { |
| 2431 | block = (plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1); |
| 2432 | if (block != 0) |
| 2433 | printf("\r%s at %s (%d%%) ", |
| 2434 | msg, |
| 2435 | roughlength(block, 1), |
| 2436 | ((int) (block * 100 / plex.length) >> DEV_BSHIFT)); |
| 2437 | if ((reply.error == EAGAIN) |
| 2438 | && (reply.msg[0])) /* got a comment back */ |
| 2439 | fputs(reply.msg, stderr); /* show it */ |
| 2440 | fflush(stdout); |
| 2441 | } |
| 2442 | } |
| 2443 | while (reply.error == EAGAIN); |
| 2444 | if (reply.error != 0) { |
| 2445 | if (reply.msg[0]) |
| 2446 | fputs(reply.msg, stderr); |
| 2447 | else |
| 2448 | fprintf(stderr, |
| 2449 | "%s failed: %s\n", |
| 2450 | msg, |
| 2451 | strerror(reply.error)); |
| 2452 | } else if (Verbose) { |
| 2453 | if (op == checkparity) |
| 2454 | fprintf(stderr, "%s has correct parity\n", argv[index]); |
| 2455 | else |
| 2456 | fprintf(stderr, "Rebuilt parity on %s\n", argv[index]); |
| 2457 | } |
| 2458 | } |
| 2459 | } |
| 2460 | } |
| 2461 | } |
| 2462 | |
| 2463 | /* Local Variables: */ |
| 2464 | /* fill-column: 50 */ |
| 2465 | /* End: */ |