1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sbin/hammer/cmd_pseudofs.c,v 1.12 2008/10/08 21:01:54 thomas Exp $ 35 */ 36 37 #include <libgen.h> 38 39 #include "hammer.h" 40 41 static int scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path); 42 static void parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd); 43 static void init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave); 44 static void pseudofs_usage(int code); 45 static int timetosecs(char *str); 46 47 void 48 clrpfs(struct hammer_ioc_pseudofs_rw *pfs, hammer_pseudofs_data_t pfsd, 49 int pfs_id) 50 { 51 bzero(pfs, sizeof(*pfs)); 52 53 if (pfsd) 54 pfs->ondisk = pfsd; 55 else 56 pfs->ondisk = malloc(sizeof(*pfs->ondisk)); 57 bzero(pfs->ondisk, sizeof(*pfs->ondisk)); 58 59 pfs->pfs_id = pfs_id; 60 pfs->bytes = sizeof(*pfs->ondisk); 61 pfs->version = HAMMER_IOC_PSEUDOFS_VERSION; 62 } 63 64 /* 65 * If path is a symlink, return strdup'd path. 66 * If it's a directory via symlink, strip trailing / 67 * from strdup'd path and return the symlink. 68 */ 69 static char* 70 getlink(const char *path) 71 { 72 int i; 73 char *linkpath; 74 struct stat st; 75 76 if (lstat(path, &st)) 77 return(NULL); 78 linkpath = strdup(path); 79 80 if (S_ISDIR(st.st_mode)) { 81 i = strlen(linkpath) - 1; 82 while (i > 0 && linkpath[i] == '/') 83 linkpath[i--] = 0; 84 lstat(linkpath, &st); 85 } 86 if (S_ISLNK(st.st_mode)) { 87 return(linkpath); 88 } 89 90 free(linkpath); 91 return(NULL); 92 } 93 94 /* 95 * Calculate the PFS id given a path to a file/directory or 96 * a @@%llx:%d softlink. 97 */ 98 int 99 getpfs(struct hammer_ioc_pseudofs_rw *pfs, const char *path) 100 { 101 int fd; 102 103 clrpfs(pfs, NULL, -1); 104 105 /* 106 * Extract the PFS id. 107 * dirname(path) is supposed to be a directory in PFS#0. 108 */ 109 if (scanpfsid(pfs, path) == 0) { 110 path = dirname(path); /* strips trailing / first if any */ 111 } 112 113 /* 114 * Open the path regardless of scanpfsid() result, since some 115 * commands can take a regular file/directory (e.g. pfs-status). 116 */ 117 fd = open(path, O_RDONLY); 118 if (fd < 0) { 119 fprintf(stderr, "Failed to open %s\n", path); 120 exit(1); 121 } 122 123 /* 124 * If pfs.pfs_id has been set to non -1, the file descriptor fd 125 * could be any fd of HAMMER inodes since HAMMERIOC_GET_PSEUDOFS 126 * doesn't depend on inode attributes if it's set to a valid id. 127 */ 128 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs) < 0) { 129 fprintf(stderr, "Cannot access %s: %s\n", 130 path, strerror(errno)); 131 exit(1); 132 } 133 return(fd); 134 } 135 136 /* 137 * Extract the PFS id from path. 138 */ 139 static int 140 scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path) 141 { 142 char *linkpath; 143 char buf[64]; 144 uintmax_t dummy_tid; 145 struct stat st; 146 147 if (stat(path, &st)) { 148 /* possibly slave PFS */ 149 } else if (S_ISDIR(st.st_mode)) { 150 /* possibly master or slave PFS */ 151 } else { 152 return(-1); /* neither */ 153 } 154 155 linkpath = getlink(path); 156 if (linkpath) { 157 /* 158 * Read the symlink assuming it's a link to PFS. 159 */ 160 bzero(buf, sizeof(buf)); 161 if (readlink(linkpath, buf, sizeof(buf) - 1) < 0) { 162 free(linkpath); 163 return(-1); 164 } 165 free(linkpath); 166 path = buf; 167 } 168 169 /* 170 * The symlink created by pfs-master|slave is just a symlink. 171 * One could happen to remove a symlink and relink PFS as 172 * # ln -s ./@@-1:00001 ./link 173 * which results PFS having something extra before @@. 174 * One could also directly use the PFS and results the same. 175 * Get rid of it before we extract the PFS id. 176 */ 177 if (strchr(path, '/')) { 178 path = basename(path); /* strips trailing / first if any */ 179 if (path == NULL) 180 err(1, "basename"); 181 } 182 183 /* 184 * Test and extract the PFS id from the link. 185 * "@@%jx:%d" convers both "@@-1:%05d" format for master PFS 186 * and "@@0x%016jx:%05d" format for slave PFS. 187 */ 188 if (sscanf(path, "@@%jx:%d", &dummy_tid, &pfs->pfs_id) == 2) { 189 assert(pfs->pfs_id > 0); 190 return(0); 191 } 192 193 return(-1); 194 } 195 196 void 197 relpfs(int fd, struct hammer_ioc_pseudofs_rw *pfs) 198 { 199 if (fd >= 0) 200 close(fd); 201 if (pfs->ondisk) { 202 free(pfs->ondisk); 203 pfs->ondisk = NULL; 204 } 205 } 206 207 static void 208 print_pfs_status(char *path) 209 { 210 struct hammer_ioc_pseudofs_rw pfs; 211 int fd; 212 213 fd = getpfs(&pfs, path); 214 printf("%s\t", path); 215 if (fd < 0 || ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 216 printf("Invalid PFS path %s\n", path); 217 } else { 218 printf("PFS#%d {\n", pfs.pfs_id); 219 dump_pfsd(pfs.ondisk, fd); 220 printf("}\n"); 221 } 222 if (fd >= 0) 223 close(fd); 224 if (pfs.ondisk) 225 free(pfs.ondisk); 226 relpfs(fd, &pfs); 227 } 228 229 void 230 hammer_cmd_pseudofs_status(char **av, int ac) 231 { 232 int i; 233 234 if (ac == 0) { 235 char buf[2] = "."; /* can't be readonly string */ 236 print_pfs_status(buf); 237 return; 238 } 239 240 for (i = 0; i < ac; ++i) 241 print_pfs_status(av[i]); 242 } 243 244 void 245 hammer_cmd_pseudofs_create(char **av, int ac, int is_slave) 246 { 247 struct hammer_ioc_pseudofs_rw pfs; 248 struct hammer_pseudofs_data pfsd; 249 struct stat st; 250 const char *path; 251 char *dirpath; 252 char *linkpath; 253 int pfs_id; 254 int fd; 255 int error; 256 257 if (ac == 0) 258 pseudofs_usage(1); 259 path = av[0]; 260 if (lstat(path, &st) == 0) { 261 fprintf(stderr, "Cannot create %s, file exists!\n", path); 262 exit(1); 263 } else if (path[strlen(path) - 1] == '/') { 264 fprintf(stderr, "Invalid PFS path %s with trailing /\n", path); 265 exit(1); 266 } 267 268 /* 269 * Figure out the directory prefix, taking care of degenerate 270 * cases. 271 */ 272 dirpath = dirname(path); 273 fd = open(dirpath, O_RDONLY); 274 if (fd < 0) { 275 fprintf(stderr, "Cannot open directory %s\n", dirpath); 276 exit(1); 277 } 278 279 /* 280 * Avoid foot-shooting. Don't let the user create a PFS 281 * softlink via a PFS. PFS softlinks may only be accessed 282 * via the master filesystem. Checking it here ensures 283 * other PFS commands access PFS under the master filesystem. 284 */ 285 clrpfs(&pfs, &pfsd, -1); 286 287 ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs); 288 if (pfs.pfs_id != HAMMER_ROOT_PFSID) { 289 fprintf(stderr, 290 "You are attempting to access a PFS softlink " 291 "from a PFS. It may not represent the PFS\n" 292 "on the main filesystem mount that you " 293 "expect! You may only access PFS softlinks\n" 294 "via the main filesystem mount!\n"); 295 exit(1); 296 } 297 298 error = 0; 299 for (pfs_id = 0; pfs_id < HAMMER_MAX_PFS; ++pfs_id) { 300 clrpfs(&pfs, &pfsd, pfs_id); 301 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { 302 error = errno; 303 break; 304 } 305 } 306 if (pfs_id == HAMMER_MAX_PFS) { 307 fprintf(stderr, "Cannot create %s, all PFSs in use\n", path); 308 exit(1); 309 } else if (pfs_id == HAMMER_ROOT_PFSID) { 310 fprintf(stderr, "Fatal error: PFS#%d must exist\n", 311 HAMMER_ROOT_PFSID); 312 exit(1); 313 } 314 315 if (error != ENOENT) { 316 fprintf(stderr, "Cannot create %s, got %s during scan\n", 317 path, strerror(error)); 318 exit(1); 319 } 320 321 /* 322 * Create the new PFS 323 */ 324 printf("Creating PFS#%d\t", pfs_id); 325 clrpfs(&pfs, &pfsd, pfs_id); 326 init_pfsd(&pfsd, is_slave); 327 328 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 329 printf("failed: %s\n", strerror(errno)); 330 } else { 331 /* special symlink, must be exactly 10 characters */ 332 asprintf(&linkpath, "@@PFS%05d", pfs_id); 333 if (symlink(linkpath, path) < 0) { 334 printf("failed: cannot create symlink: %s\n", 335 strerror(errno)); 336 } else { 337 printf("succeeded!\n"); 338 hammer_cmd_pseudofs_update(av, ac); 339 } 340 } 341 free(dirpath); 342 close(fd); 343 } 344 345 void 346 hammer_cmd_pseudofs_destroy(char **av, int ac) 347 { 348 struct hammer_ioc_pseudofs_rw pfs; 349 char *linkpath; 350 int fd; 351 int i; 352 353 if (ac == 0) 354 pseudofs_usage(1); 355 fd = getpfs(&pfs, av[0]); 356 357 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 358 fprintf(stderr, "You cannot destroy PFS#0\n"); 359 exit(1); 360 } 361 printf("You have requested that PFS#%d (%s) be destroyed\n", 362 pfs.pfs_id, pfs.ondisk->label); 363 printf("This will irrevocably destroy all data on this PFS!!!!!\n"); 364 printf("Do you really want to do this? "); 365 fflush(stdout); 366 if (getyn() == 0) { 367 fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); 368 exit(1); 369 } 370 371 if (hammer_is_pfs_master(pfs.ondisk)) { 372 printf("This PFS is currently setup as a MASTER!\n"); 373 printf("Are you absolutely sure you want to destroy it? "); 374 fflush(stdout); 375 if (getyn() == 0) { 376 fprintf(stderr, "No action taken on PFS#%d\n", 377 pfs.pfs_id); 378 exit(1); 379 } 380 } 381 382 printf("Destroying PFS#%d (%s)", pfs.pfs_id, pfs.ondisk->label); 383 if (DebugOpt) { 384 printf("\n"); 385 } else { 386 printf(" in"); 387 for (i = 5; i; --i) { 388 printf(" %d", i); 389 fflush(stdout); 390 sleep(1); 391 } 392 printf(".. starting destruction pass\n"); 393 } 394 395 /* 396 * Remove the softlink on success. 397 */ 398 if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) { 399 printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id); 400 linkpath = getlink(av[0]); 401 if (linkpath) { 402 if (remove(linkpath) < 0) { 403 fprintf(stderr, 404 "Unable to remove softlink %s: %s\n", 405 linkpath, strerror(errno)); 406 /* exit status 0 anyway */ 407 } 408 free(linkpath); 409 } 410 } else { 411 printf("pfs-destroy of PFS#%d failed: %s\n", 412 pfs.pfs_id, strerror(errno)); 413 } 414 relpfs(fd, &pfs); 415 } 416 417 void 418 hammer_cmd_pseudofs_upgrade(char **av, int ac) 419 { 420 struct hammer_ioc_pseudofs_rw pfs; 421 int fd; 422 423 if (ac == 0) 424 pseudofs_usage(1); 425 fd = getpfs(&pfs, av[0]); 426 427 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 428 fprintf(stderr, "You cannot upgrade PFS#0" 429 " (It should already be a master)\n"); 430 exit(1); 431 } else if (hammer_is_pfs_master(pfs.ondisk)) { 432 printf("It is already a master\n"); 433 exit(1); 434 } 435 436 if (ioctl(fd, HAMMERIOC_UPG_PSEUDOFS, &pfs) == 0) { 437 printf("pfs-upgrade of PFS#%d (%s) succeeded\n", 438 pfs.pfs_id, pfs.ondisk->label); 439 } else { 440 fprintf(stderr, "pfs-upgrade of PFS#%d (%s) failed: %s\n", 441 pfs.pfs_id, pfs.ondisk->label, strerror(errno)); 442 } 443 relpfs(fd, &pfs); 444 } 445 446 void 447 hammer_cmd_pseudofs_downgrade(char **av, int ac) 448 { 449 struct hammer_ioc_pseudofs_rw pfs; 450 int fd; 451 452 if (ac == 0) 453 pseudofs_usage(1); 454 fd = getpfs(&pfs, av[0]); 455 456 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 457 fprintf(stderr, "You cannot downgrade PFS#0\n"); 458 exit(1); 459 } else if (hammer_is_pfs_slave(pfs.ondisk)) { 460 printf("It is already a slave\n"); 461 exit(1); 462 } 463 464 if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) { 465 printf("pfs-downgrade of PFS#%d (%s) succeeded\n", 466 pfs.pfs_id, pfs.ondisk->label); 467 } else { 468 fprintf(stderr, "pfs-downgrade of PFS#%d (%s) failed: %s\n", 469 pfs.pfs_id, pfs.ondisk->label, strerror(errno)); 470 } 471 relpfs(fd, &pfs); 472 } 473 474 void 475 hammer_cmd_pseudofs_update(char **av, int ac) 476 { 477 struct hammer_ioc_pseudofs_rw pfs; 478 int fd; 479 480 if (ac == 0) 481 pseudofs_usage(1); 482 fd = getpfs(&pfs, av[0]); 483 484 printf("%s\n", av[0]); 485 fflush(stdout); 486 487 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 488 parse_pfsd_options(av + 1, ac - 1, pfs.ondisk); 489 if (hammer_is_pfs_slave(pfs.ondisk) && 490 pfs.pfs_id == HAMMER_ROOT_PFSID) { 491 printf("The real mount point cannot be made a PFS " 492 "slave, only PFS sub-directories can be made " 493 "slaves\n"); 494 exit(1); 495 } 496 pfs.bytes = sizeof(*pfs.ondisk); 497 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) { 498 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 499 dump_pfsd(pfs.ondisk, fd); 500 } else { 501 printf("Unable to retrieve PFS configuration " 502 "after successful update: %s\n", 503 strerror(errno)); 504 exit(1); 505 } 506 } else { 507 printf("Unable to adjust PFS configuration: %s\n", 508 strerror(errno)); 509 exit(1); 510 } 511 } 512 relpfs(fd, &pfs); 513 } 514 515 static void 516 init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave) 517 { 518 uint32_t status; 519 520 bzero(pfsd, sizeof(*pfsd)); 521 pfsd->sync_beg_tid = 1; 522 pfsd->sync_end_tid = 1; 523 pfsd->sync_beg_ts = 0; 524 pfsd->sync_end_ts = 0; 525 uuid_create(&pfsd->shared_uuid, &status); 526 uuid_create(&pfsd->unique_uuid, &status); 527 if (is_slave) 528 pfsd->mirror_flags |= HAMMER_PFSD_SLAVE; 529 } 530 531 void 532 dump_pfsd(hammer_pseudofs_data_t pfsd, int fd) 533 { 534 struct hammer_ioc_version version; 535 uint32_t status; 536 char *str = NULL; 537 538 printf(" sync-beg-tid=0x%016jx\n", (uintmax_t)pfsd->sync_beg_tid); 539 printf(" sync-end-tid=0x%016jx\n", (uintmax_t)pfsd->sync_end_tid); 540 uuid_to_string(&pfsd->shared_uuid, &str, &status); 541 printf(" shared-uuid=%s\n", str); 542 free(str); 543 uuid_to_string(&pfsd->unique_uuid, &str, &status); 544 printf(" unique-uuid=%s\n", str); 545 free(str); 546 printf(" label=\"%s\"\n", pfsd->label); 547 if (pfsd->snapshots[0]) 548 printf(" snapshots=\"%s\"\n", pfsd->snapshots); 549 if (pfsd->prune_min < (60 * 60 * 24)) { 550 printf(" prune-min=%02d:%02d:%02d\n", 551 pfsd->prune_min / 60 / 60 % 24, 552 pfsd->prune_min / 60 % 60, 553 pfsd->prune_min % 60); 554 } else if (pfsd->prune_min % (60 * 60 * 24)) { 555 printf(" prune-min=%dd/%02d:%02d:%02d\n", 556 pfsd->prune_min / 60 / 60 / 24, 557 pfsd->prune_min / 60 / 60 % 24, 558 pfsd->prune_min / 60 % 60, 559 pfsd->prune_min % 60); 560 } else { 561 printf(" prune-min=%dd\n", pfsd->prune_min / 60 / 60 / 24); 562 } 563 564 if (hammer_is_pfs_slave(pfsd)) { 565 printf(" operating as a SLAVE\n"); 566 } else { 567 printf(" operating as a MASTER\n"); 568 } 569 570 /* 571 * Snapshots directory cannot be shown when there is no fd since 572 * hammer version can't be retrieved. mirror-dump passes -1 because 573 * its input came from mirror-read output thus no path is available 574 * to open(2). 575 */ 576 if (fd >= 0 && pfsd->snapshots[0] == 0) { 577 bzero(&version, sizeof(version)); 578 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) 579 return; 580 if (version.cur_version < 3) { 581 if (hammer_is_pfs_slave(pfsd)) { 582 printf(" snapshots directory not set for " 583 "slave\n"); 584 } else { 585 printf(" snapshots directory for master " 586 "defaults to <pfs>/snapshots\n"); 587 } 588 } else { 589 printf(" snapshots directory defaults to " 590 "/var/hammer/<pfs>\n"); 591 } 592 } 593 } 594 595 static void 596 parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd) 597 { 598 char *cmd; 599 char *ptr; 600 int len; 601 uint32_t status; 602 603 while (ac) { 604 cmd = *av; 605 if ((ptr = strchr(cmd, '=')) != NULL) 606 *ptr++ = 0; 607 608 /* 609 * Basic assignment value test 610 */ 611 if (ptr == NULL) { 612 fprintf(stderr, 613 "option %s requires an assignment\n", 614 cmd); 615 exit(1); 616 } 617 618 status = uuid_s_ok; 619 if (strcmp(cmd, "sync-beg-tid") == 0) { 620 pfsd->sync_beg_tid = strtoull(ptr, NULL, 16); 621 } else if (strcmp(cmd, "sync-end-tid") == 0) { 622 pfsd->sync_end_tid = strtoull(ptr, NULL, 16); 623 } else if (strcmp(cmd, "shared-uuid") == 0) { 624 uuid_from_string(ptr, &pfsd->shared_uuid, &status); 625 } else if (strcmp(cmd, "unique-uuid") == 0) { 626 uuid_from_string(ptr, &pfsd->unique_uuid, &status); 627 } else if (strcmp(cmd, "label") == 0) { 628 len = strlen(ptr); 629 if (ptr[0] == '"' && ptr[len-1] == '"') { 630 ptr[len-1] = 0; 631 ++ptr; 632 } else if (ptr[0] == '"') { 633 fprintf(stderr, 634 "option %s: malformed string\n", 635 cmd); 636 exit(1); 637 } 638 snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr); 639 } else if (strcmp(cmd, "snapshots") == 0) { 640 len = strlen(ptr); 641 if (ptr[0] != '/') { 642 fprintf(stderr, 643 "option %s: '%s' must be an " 644 "absolute path\n", cmd, ptr); 645 if (ptr[0] == 0) { 646 fprintf(stderr, 647 "use 'snapshots-clear' " 648 "to unset snapshots dir\n"); 649 } 650 exit(1); 651 } 652 if (len >= (int)sizeof(pfsd->snapshots)) { 653 fprintf(stderr, 654 "option %s: path too long, %d " 655 "character limit\n", cmd, len); 656 exit(1); 657 } 658 snprintf(pfsd->snapshots, sizeof(pfsd->snapshots), 659 "%s", ptr); 660 } else if (strcmp(cmd, "snapshots-clear") == 0) { 661 pfsd->snapshots[0] = 0; 662 } else if (strcmp(cmd, "prune-min") == 0) { 663 pfsd->prune_min = timetosecs(ptr); 664 if (pfsd->prune_min < 0) { 665 fprintf(stderr, 666 "option %s: illegal time spec, " 667 "use Nd or [Nd/]hh[:mm[:ss]]\n", ptr); 668 exit(1); 669 } 670 } else { 671 fprintf(stderr, "invalid option: %s\n", cmd); 672 exit(1); 673 } 674 if (status != uuid_s_ok) { 675 fprintf(stderr, "option %s: error parsing uuid %s\n", 676 cmd, ptr); 677 exit(1); 678 } 679 --ac; 680 ++av; 681 } 682 } 683 684 static 685 void 686 pseudofs_usage(int code) 687 { 688 fprintf(stderr, 689 "hammer pfs-status <dirpath> ...\n" 690 "hammer pfs-master <dirpath> [options]\n" 691 "hammer pfs-slave <dirpath> [options]\n" 692 "hammer pfs-update <dirpath> [options]\n" 693 "hammer pfs-upgrade <dirpath>\n" 694 "hammer pfs-downgrade <dirpath>\n" 695 "hammer pfs-destroy <dirpath>\n" 696 "\n" 697 "options:\n" 698 " sync-beg-tid=0x16llx\n" 699 " sync-end-tid=0x16llx\n" 700 " shared-uuid=0x16llx\n" 701 " unique-uuid=0x16llx\n" 702 " label=\"string\"\n" 703 " snapshots=\"/path\"\n" 704 " snapshots-clear\n" 705 " prune-min=Nd\n" 706 " prune-min=[Nd/]hh[:mm[:ss]]\n" 707 ); 708 exit(code); 709 } 710 711 /* 712 * Convert time in the form [Nd/]hh[:mm[:ss]] to seconds. 713 * 714 * Return -1 if a parse error occurs. 715 * Return 0x7FFFFFFF if the time exceeds the maximum allowed. 716 */ 717 static 718 int 719 timetosecs(char *str) 720 { 721 int days = 0; 722 int hrs = 0; 723 int mins = 0; 724 int secs = 0; 725 int n; 726 long long v; 727 char *ptr; 728 729 n = strtol(str, &ptr, 10); 730 if (n < 0) 731 return(-1); 732 if (*ptr == 'd') { 733 days = n; 734 ++ptr; 735 if (*ptr == '/') 736 n = strtol(ptr + 1, &ptr, 10); 737 else 738 n = 0; 739 } 740 if (n < 0) 741 return(-1); 742 hrs = n; 743 if (*ptr == ':') { 744 n = strtol(ptr + 1, &ptr, 10); 745 if (n < 0) 746 return(-1); 747 mins = n; 748 if (*ptr == ':') { 749 n = strtol(ptr + 1, &ptr, 10); 750 if (n < 0) 751 return(-1); 752 secs = n; 753 } 754 } 755 if (*ptr) 756 return(-1); 757 v = days * 24 * 60 * 60 + hrs * 60 * 60 + mins * 60 + secs; 758 if (v > 0x7FFFFFFF) 759 v = 0x7FFFFFFF; 760 return((int)v); 761 } 762