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 "hammer.h" 38 39 static int scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path); 40 static void parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd); 41 static void init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave); 42 static void pseudofs_usage(int code); 43 static int timetosecs(char *str); 44 45 void 46 clrpfs(struct hammer_ioc_pseudofs_rw *pfs, hammer_pseudofs_data_t pfsd, 47 int pfs_id) 48 { 49 bzero(pfs, sizeof(*pfs)); 50 51 if (pfsd) 52 pfs->ondisk = pfsd; 53 else 54 pfs->ondisk = malloc(sizeof(*pfs->ondisk)); 55 bzero(pfs->ondisk, sizeof(*pfs->ondisk)); 56 57 pfs->pfs_id = pfs_id; 58 pfs->bytes = sizeof(*pfs->ondisk); 59 pfs->version = HAMMER_IOC_PSEUDOFS_VERSION; 60 } 61 62 /* 63 * If path is a symlink, return strdup'd path. 64 * If it's a directory via symlink, strip trailing / 65 * from strdup'd path and return the symlink. 66 */ 67 static 68 char* 69 getlink(const char *path) 70 { 71 int i; 72 char *linkpath; 73 struct stat st; 74 75 if (lstat(path, &st)) 76 return(NULL); 77 linkpath = strdup(path); 78 79 if (S_ISDIR(st.st_mode)) { 80 i = strlen(linkpath) - 1; 81 while (i > 0 && linkpath[i] == '/') 82 linkpath[i--] = 0; 83 lstat(linkpath, &st); 84 } 85 if (S_ISLNK(st.st_mode)) 86 return(linkpath); 87 88 free(linkpath); 89 return(NULL); 90 } 91 92 /* 93 * Calculate the PFS id given a path to a file/directory or 94 * a @@%llx:%d softlink. 95 */ 96 int 97 getpfs(struct hammer_ioc_pseudofs_rw *pfs, const char *path) 98 { 99 int fd; 100 101 clrpfs(pfs, NULL, -1); 102 103 /* 104 * Extract the PFS id. 105 * dirname(path) is supposed to be a directory in root PFS. 106 */ 107 if (scanpfsid(pfs, path) == 0) 108 path = dirname(path); /* strips trailing / first if any */ 109 110 /* 111 * Open the path regardless of scanpfsid() result, since some 112 * commands can take a regular file/directory (e.g. pfs-status). 113 */ 114 fd = open(path, O_RDONLY); 115 if (fd < 0) { 116 err(1, "Failed to open %s", path); 117 /* not reached */ 118 } 119 120 /* 121 * If pfs.pfs_id has been set to non -1, the file descriptor fd 122 * could be any fd of HAMMER inodes since HAMMERIOC_GET_PSEUDOFS 123 * doesn't depend on inode attributes if it's set to a valid id. 124 */ 125 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, pfs) < 0) { 126 err(1, "Cannot access %s", path); 127 /* not reached */ 128 } 129 130 return(fd); 131 } 132 133 /* 134 * Extract the PFS id from path. 135 */ 136 static 137 int 138 scanpfsid(struct hammer_ioc_pseudofs_rw *pfs, const char *path) 139 { 140 char *linkpath; 141 char buf[64]; 142 uintmax_t dummy_tid; 143 struct stat st; 144 145 if (stat(path, &st)) 146 ; /* possibly slave PFS */ 147 else if (S_ISDIR(st.st_mode)) 148 ; /* possibly master or slave PFS */ 149 else 150 return(-1); /* neither */ 151 152 linkpath = getlink(path); 153 if (linkpath) { 154 /* 155 * Read the symlink assuming it's a link to PFS. 156 */ 157 bzero(buf, sizeof(buf)); 158 if (readlink(linkpath, buf, sizeof(buf) - 1) < 0) { 159 free(linkpath); 160 return(-1); 161 } 162 free(linkpath); 163 path = buf; 164 } 165 166 /* 167 * The symlink created by pfs-master|slave is just a symlink. 168 * One could happen to remove a symlink and relink PFS as 169 * # ln -s ./@@-1:00001 ./link 170 * which results PFS having something extra before @@. 171 * One could also directly use the PFS and results the same. 172 * Get rid of it before we extract the PFS id. 173 */ 174 if (strchr(path, '/')) { 175 path = basename(path); /* strips trailing / first if any */ 176 if (path == NULL) { 177 err(1, "basename"); 178 /* not reached */ 179 } 180 } 181 182 /* 183 * Test and extract the PFS id from the link. 184 * "@@%jx:%d" covers both "@@-1:%05d" format for master PFS 185 * and "@@0x%016jx:%05d" format for slave PFS. 186 */ 187 if (sscanf(path, "@@%jx:%d", &dummy_tid, &pfs->pfs_id) == 2) { 188 assert(pfs->pfs_id > 0); 189 return(0); 190 } 191 192 return(-1); 193 } 194 195 void 196 relpfs(int fd, struct hammer_ioc_pseudofs_rw *pfs) 197 { 198 if (fd >= 0) 199 close(fd); 200 if (pfs->ondisk) { 201 free(pfs->ondisk); 202 pfs->ondisk = NULL; 203 } 204 } 205 206 static 207 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 256 if (ac == 0) { 257 pseudofs_usage(1); 258 /* not reached */ 259 } 260 path = av[0]; 261 if (lstat(path, &st) == 0) { 262 errx(1, "Cannot create %s, file exists!", path); 263 /* not reached */ 264 } else if (path[strlen(path) - 1] == '/') { 265 errx(1, "Invalid PFS path %s with trailing /", path); 266 /* not reached */ 267 } 268 269 /* 270 * Figure out the directory prefix, taking care of degenerate 271 * cases. 272 */ 273 dirpath = dirname(path); 274 fd = open(dirpath, O_RDONLY); 275 if (fd < 0) { 276 err(1, "Cannot open directory %s", dirpath); 277 /* not reached */ 278 } 279 280 /* 281 * Avoid foot-shooting. Don't let the user create a PFS 282 * softlink via a PFS. PFS softlinks may only be accessed 283 * via the master filesystem. Checking it here ensures 284 * other PFS commands access PFS under the master filesystem. 285 */ 286 clrpfs(&pfs, &pfsd, -1); 287 288 ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs); 289 if (pfs.pfs_id != HAMMER_ROOT_PFSID) { 290 fprintf(stderr, 291 "You are attempting to access a PFS softlink " 292 "from a PFS. It may not represent the PFS\n" 293 "on the main filesystem mount that you " 294 "expect! You may only access PFS softlinks\n" 295 "via the main filesystem mount!\n"); 296 exit(1); 297 } 298 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 if (errno != ENOENT) { 303 err(1, "Cannot create %s", path); 304 /* not reached */ 305 } 306 break; 307 } 308 } 309 if (pfs_id == HAMMER_MAX_PFS) { 310 errx(1, "Cannot create %s, all PFSs in use", path); 311 /* not reached */ 312 } else if (pfs_id == HAMMER_ROOT_PFSID) { 313 errx(1, "Fatal error: PFS#%d must exist", HAMMER_ROOT_PFSID); 314 /* not reached */ 315 } 316 317 /* 318 * Create the new PFS 319 */ 320 printf("Creating PFS#%d\t", pfs_id); 321 clrpfs(&pfs, &pfsd, pfs_id); 322 init_pfsd(&pfsd, is_slave); 323 324 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { 325 printf("failed: %s\n", strerror(errno)); 326 } else { 327 /* special symlink, must be exactly 10 characters */ 328 asprintf(&linkpath, "@@PFS%05d", pfs_id); 329 if (symlink(linkpath, path) < 0) { 330 printf("failed: cannot create symlink: %s\n", 331 strerror(errno)); 332 } else { 333 printf("succeeded!\n"); 334 hammer_cmd_pseudofs_update(av, ac); 335 } 336 } 337 free(dirpath); 338 close(fd); 339 } 340 341 void 342 hammer_cmd_pseudofs_destroy(char **av, int ac) 343 { 344 struct hammer_ioc_pseudofs_rw pfs; 345 char *linkpath; 346 int fd; 347 int i; 348 349 if (ac == 0) { 350 pseudofs_usage(1); 351 /* not reached */ 352 } 353 fd = getpfs(&pfs, av[0]); 354 355 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 356 errx(1, "You cannot destroy PFS#%d", HAMMER_ROOT_PFSID); 357 /* not reached */ 358 } 359 360 printf("You have requested that PFS#%d (%s) be destroyed\n", 361 pfs.pfs_id, pfs.ondisk->label); 362 printf("This will irrevocably destroy all data on this PFS!!!!!\n"); 363 printf("Do you really want to do this? [y/n] "); 364 fflush(stdout); 365 if (getyn() == 0) { 366 errx(1, "No action taken on PFS#%d", pfs.pfs_id); 367 /* not reached */ 368 } 369 370 if (hammer_is_pfs_master(pfs.ondisk)) { 371 printf("This PFS is currently setup as a MASTER!\n"); 372 printf("Are you absolutely sure you want to destroy it? [y/n] "); 373 fflush(stdout); 374 if (getyn() == 0) { 375 errx(1, "No action taken on PFS#%d", pfs.pfs_id); 376 /* not reached */ 377 } 378 } 379 380 printf("Destroying PFS#%d (%s)", pfs.pfs_id, pfs.ondisk->label); 381 if (DebugOpt) { 382 printf("\n"); 383 } else { 384 printf(" in"); 385 for (i = 5; i; --i) { 386 printf(" %d", i); 387 fflush(stdout); 388 sleep(1); 389 } 390 printf(".. starting destruction pass\n"); 391 } 392 393 /* 394 * Remove the softlink on success. 395 */ 396 if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) { 397 printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id); 398 linkpath = getlink(av[0]); 399 if (linkpath) { 400 if (remove(linkpath) < 0) { 401 err(1, "Unable to remove softlink %s", linkpath); 402 /* not reached */ 403 } 404 free(linkpath); 405 } 406 } else { 407 printf("pfs-destroy of PFS#%d failed: %s\n", 408 pfs.pfs_id, strerror(errno)); 409 } 410 relpfs(fd, &pfs); 411 } 412 413 void 414 hammer_cmd_pseudofs_upgrade(char **av, int ac) 415 { 416 struct hammer_ioc_pseudofs_rw pfs; 417 int fd; 418 419 if (ac == 0) { 420 pseudofs_usage(1); 421 /* not reached */ 422 } 423 fd = getpfs(&pfs, av[0]); 424 425 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 426 errx(1, "You cannot upgrade PFS#%d" 427 " (It should already be a master)", 428 HAMMER_ROOT_PFSID); 429 /* not reached */ 430 } else if (hammer_is_pfs_master(pfs.ondisk)) { 431 errx(1, "It is already a master"); 432 /* not reached */ 433 } 434 435 if (ioctl(fd, HAMMERIOC_UPG_PSEUDOFS, &pfs) == 0) { 436 printf("pfs-upgrade of PFS#%d (%s) succeeded\n", 437 pfs.pfs_id, pfs.ondisk->label); 438 } else { 439 err(1, "pfs-upgrade of PFS#%d (%s) failed", 440 pfs.pfs_id, pfs.ondisk->label); 441 /* not reached */ 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 /* not reached */ 455 } 456 fd = getpfs(&pfs, av[0]); 457 458 if (pfs.pfs_id == HAMMER_ROOT_PFSID) { 459 errx(1, "You cannot downgrade PFS#%d", HAMMER_ROOT_PFSID); 460 /* not reached */ 461 } else if (hammer_is_pfs_slave(pfs.ondisk)) { 462 errx(1, "It is already a slave"); 463 /* not reached */ 464 } 465 466 if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) { 467 printf("pfs-downgrade of PFS#%d (%s) succeeded\n", 468 pfs.pfs_id, pfs.ondisk->label); 469 } else { 470 err(1, "pfs-downgrade of PFS#%d (%s) failed", 471 pfs.pfs_id, pfs.ondisk->label); 472 /* not reached */ 473 } 474 relpfs(fd, &pfs); 475 } 476 477 void 478 hammer_cmd_pseudofs_update(char **av, int ac) 479 { 480 struct hammer_ioc_pseudofs_rw pfs; 481 int fd; 482 483 if (ac == 0) { 484 pseudofs_usage(1); 485 /* not reached */ 486 } 487 fd = getpfs(&pfs, av[0]); 488 489 printf("%s\n", av[0]); 490 fflush(stdout); 491 492 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 493 parse_pfsd_options(av + 1, ac - 1, pfs.ondisk); 494 if (hammer_is_pfs_slave(pfs.ondisk) && 495 pfs.pfs_id == HAMMER_ROOT_PFSID) { 496 errx(1, "The real mount point cannot be made a PFS " 497 "slave, only PFS sub-directories can be made " 498 "slaves"); 499 /* not reached */ 500 } 501 pfs.bytes = sizeof(*pfs.ondisk); 502 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) { 503 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { 504 dump_pfsd(pfs.ondisk, fd); 505 } else { 506 err(1, "Unable to retrieve PFS configuration " 507 "after successful update"); 508 /* not reached */ 509 } 510 } else { 511 err(1, "Unable to adjust PFS configuration"); 512 /* not reached */ 513 } 514 } 515 relpfs(fd, &pfs); 516 } 517 518 static 519 void 520 init_pfsd(hammer_pseudofs_data_t pfsd, int is_slave) 521 { 522 bzero(pfsd, sizeof(*pfsd)); 523 pfsd->sync_beg_tid = 1; 524 pfsd->sync_end_tid = 1; 525 pfsd->sync_beg_ts = 0; 526 pfsd->sync_end_ts = 0; 527 hammer_uuid_create(&pfsd->shared_uuid); 528 hammer_uuid_create(&pfsd->unique_uuid); 529 if (is_slave) 530 pfsd->mirror_flags |= HAMMER_PFSD_SLAVE; 531 } 532 533 void 534 dump_pfsd(hammer_pseudofs_data_t pfsd, int fd) 535 { 536 struct hammer_ioc_version version; 537 char *str = NULL; 538 539 printf(" sync-beg-tid=0x%016jx\n", (uintmax_t)pfsd->sync_beg_tid); 540 printf(" sync-end-tid=0x%016jx\n", (uintmax_t)pfsd->sync_end_tid); 541 hammer_uuid_to_string(&pfsd->shared_uuid, &str); 542 printf(" shared-uuid=%s\n", str); 543 free(str); 544 hammer_uuid_to_string(&pfsd->unique_uuid, &str); 545 printf(" unique-uuid=%s\n", str); 546 free(str); 547 printf(" label=\"%s\"\n", pfsd->label); 548 if (pfsd->snapshots[0]) 549 printf(" snapshots=\"%s\"\n", pfsd->snapshots); 550 if (pfsd->prune_min < (60 * 60 * 24)) { 551 printf(" prune-min=%02d:%02d:%02d\n", 552 pfsd->prune_min / 60 / 60 % 24, 553 pfsd->prune_min / 60 % 60, 554 pfsd->prune_min % 60); 555 } else if (pfsd->prune_min % (60 * 60 * 24)) { 556 printf(" prune-min=%dd/%02d:%02d:%02d\n", 557 pfsd->prune_min / 60 / 60 / 24, 558 pfsd->prune_min / 60 / 60 % 24, 559 pfsd->prune_min / 60 % 60, 560 pfsd->prune_min % 60); 561 } else { 562 printf(" prune-min=%dd\n", pfsd->prune_min / 60 / 60 / 24); 563 } 564 565 if (hammer_is_pfs_slave(pfsd)) 566 printf(" operating as a SLAVE\n"); 567 else 568 printf(" operating as a MASTER\n"); 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 HammerVersion = version.cur_version; 581 if (version.cur_version < 3) { 582 if (hammer_is_pfs_slave(pfsd)) { 583 printf(" snapshots directory not set for " 584 "slave\n"); 585 } else { 586 printf(" snapshots directory for master " 587 "defaults to <pfs>/snapshots\n"); 588 } 589 } else { 590 printf(" snapshots directory defaults to " 591 "/var/hammer/<pfs>\n"); 592 } 593 } 594 } 595 596 static 597 void 598 parse_pfsd_options(char **av, int ac, hammer_pseudofs_data_t pfsd) 599 { 600 char *cmd; 601 char *ptr; 602 int len; 603 604 while (ac) { 605 cmd = *av; 606 if ((ptr = strchr(cmd, '=')) != NULL) 607 *ptr++ = 0; 608 609 /* 610 * Basic assignment value test 611 */ 612 if (ptr == NULL) { 613 errx(1, "option %s requires an assignment", cmd); 614 /* not reached */ 615 } 616 617 if (strcmp(cmd, "sync-beg-tid") == 0) { 618 pfsd->sync_beg_tid = strtoull(ptr, NULL, 16); 619 } else if (strcmp(cmd, "sync-end-tid") == 0) { 620 pfsd->sync_end_tid = strtoull(ptr, NULL, 16); 621 } else if (strcmp(cmd, "shared-uuid") == 0) { 622 if (hammer_uuid_from_string(ptr, &pfsd->shared_uuid)) { 623 errx(1, "option %s: error parsing uuid %s", 624 cmd, ptr); 625 /* not reached */ 626 } 627 } else if (strcmp(cmd, "unique-uuid") == 0) { 628 if (hammer_uuid_from_string(ptr, &pfsd->unique_uuid)) { 629 errx(1, "option %s: error parsing uuid %s", 630 cmd, ptr); 631 /* not reached */ 632 } 633 } else if (strcmp(cmd, "label") == 0) { 634 len = strlen(ptr); 635 if (ptr[0] == '"' && ptr[len-1] == '"') { 636 ptr[len-1] = 0; 637 ++ptr; 638 } else if (ptr[0] == '"') { 639 errx(1, "option %s: malformed string", cmd); 640 /* not reached */ 641 } 642 snprintf(pfsd->label, sizeof(pfsd->label), "%s", ptr); 643 } else if (strcmp(cmd, "snapshots") == 0) { 644 len = strlen(ptr); 645 if (ptr[0] != '/') { 646 fprintf(stderr, 647 "option %s: '%s' must be an " 648 "absolute path\n", cmd, ptr); 649 if (ptr[0] == 0) { 650 fprintf(stderr, 651 "use 'snapshots-clear' " 652 "to unset snapshots dir\n"); 653 } 654 exit(1); 655 } 656 if (len >= (int)sizeof(pfsd->snapshots)) { 657 errx(1, "option %s: path too long, %d " 658 "character limit", cmd, len); 659 /* not reached */ 660 } 661 snprintf(pfsd->snapshots, sizeof(pfsd->snapshots), 662 "%s", ptr); 663 } else if (strcmp(cmd, "snapshots-clear") == 0) { 664 pfsd->snapshots[0] = 0; 665 } else if (strcmp(cmd, "prune-min") == 0) { 666 pfsd->prune_min = timetosecs(ptr); 667 if (pfsd->prune_min < 0) { 668 errx(1, "option %s: illegal time spec, " 669 "use Nd or [Nd/]hh[:mm[:ss]]", ptr); 670 /* not reached */ 671 } 672 } else { 673 errx(1, "invalid option: %s", cmd); 674 /* not reached */ 675 } 676 --ac; 677 ++av; 678 } 679 } 680 681 static 682 void 683 pseudofs_usage(int code) 684 { 685 fprintf(stderr, 686 "hammer pfs-status <dirpath> ...\n" 687 "hammer pfs-master <dirpath> [options]\n" 688 "hammer pfs-slave <dirpath> [options]\n" 689 "hammer pfs-update <dirpath> [options]\n" 690 "hammer pfs-upgrade <dirpath>\n" 691 "hammer pfs-downgrade <dirpath>\n" 692 "hammer pfs-destroy <dirpath>\n" 693 "\n" 694 "options:\n" 695 " sync-beg-tid=0x16llx\n" 696 " sync-end-tid=0x16llx\n" 697 " shared-uuid=0x16llx\n" 698 " unique-uuid=0x16llx\n" 699 " label=\"string\"\n" 700 " snapshots=\"/path\"\n" 701 " snapshots-clear\n" 702 " prune-min=Nd\n" 703 " prune-min=[Nd/]hh[:mm[:ss]]\n" 704 ); 705 exit(code); 706 } 707 708 /* 709 * Convert time in the form [Nd/]hh[:mm[:ss]] to seconds. 710 * 711 * Return -1 if a parse error occurs. 712 * Return 0x7FFFFFFF if the time exceeds the maximum allowed. 713 */ 714 static 715 int 716 timetosecs(char *str) 717 { 718 int days = 0; 719 int hrs = 0; 720 int mins = 0; 721 int secs = 0; 722 int n; 723 long long v; 724 char *ptr; 725 726 n = strtol(str, &ptr, 10); 727 if (n < 0) 728 return(-1); 729 if (*ptr == 'd') { 730 days = n; 731 ++ptr; 732 if (*ptr == '/') 733 n = strtol(ptr + 1, &ptr, 10); 734 else 735 n = 0; 736 } 737 if (n < 0) 738 return(-1); 739 hrs = n; 740 if (*ptr == ':') { 741 n = strtol(ptr + 1, &ptr, 10); 742 if (n < 0) 743 return(-1); 744 mins = n; 745 if (*ptr == ':') { 746 n = strtol(ptr + 1, &ptr, 10); 747 if (n < 0) 748 return(-1); 749 secs = n; 750 } 751 } 752 if (*ptr) 753 return(-1); 754 v = days * 24 * 60 * 60 + hrs * 60 * 60 + mins * 60 + secs; 755 if (v > 0x7FFFFFFF) 756 v = 0x7FFFFFFF; 757 return((int)v); 758 } 759