1 /* 2 * Copyright (c) 2007 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/hammer.c,v 1.44 2008/11/13 02:04:27 dillon Exp $ 35 */ 36 37 #include "hammer.h" 38 #include <signal.h> 39 #include <math.h> 40 #include <fstab.h> 41 42 static void hammer_parsedevs(const char *blkdevs); 43 static void sigalrm(int signo); 44 static void sigintr(int signo); 45 static void usage(int exit_code); 46 47 int RecurseOpt; 48 int VerboseOpt; 49 int QuietOpt; 50 int NoSyncOpt; 51 int TwoWayPipeOpt; 52 int TimeoutOpt; 53 int DelayOpt = 5; 54 char *SshPort; 55 int ForceYesOpt = 0; 56 int CompressOpt; 57 int ForceOpt; 58 int RunningIoctl; 59 int DidInterrupt; 60 int BulkOpt; 61 int AllPFS; 62 u_int64_t BandwidthOpt; 63 u_int64_t SplitupOpt = 4ULL * 1024ULL * 1024ULL * 1024ULL; 64 u_int64_t MemoryLimit = 1024LLU * 1024 * 1024; 65 const char *SplitupOptStr; 66 const char *CyclePath; 67 const char *LinkPath; 68 const char *RestrictTarget; 69 70 int 71 main(int ac, char **av) 72 { 73 char *blkdevs = NULL; 74 char *ptr; 75 char *restrictcmd = NULL; 76 u_int32_t status; 77 int ch; 78 int cacheSize = 0; 79 80 while ((ch = getopt(ac, av, 81 "b:c:de:hf:i:m:p:qrs:t:v2yABC:FR:S:T:X")) != -1) { 82 switch(ch) { 83 case '2': 84 TwoWayPipeOpt = 1; 85 break; 86 case 'y': 87 ForceYesOpt = 1; 88 break; 89 case 'b': 90 BandwidthOpt = strtoull(optarg, &ptr, 0); 91 switch(*ptr) { 92 case 'g': 93 case 'G': 94 BandwidthOpt *= 1024; 95 /* fall through */ 96 case 'm': 97 case 'M': 98 BandwidthOpt *= 1024; 99 /* fall through */ 100 case 'k': 101 case 'K': 102 BandwidthOpt *= 1024; 103 break; 104 case '\0': 105 /* bytes per second if no suffix */ 106 break; 107 default: 108 usage(1); 109 } 110 break; 111 case 'S': 112 SplitupOptStr = strdup(optarg); 113 SplitupOpt = strtoull(optarg, &ptr, 0); 114 switch(*ptr) { 115 case 'g': 116 case 'G': 117 SplitupOpt *= 1024; 118 /* fall through */ 119 case 'm': 120 case 'M': 121 SplitupOpt *= 1024; 122 /* fall through */ 123 case 'k': 124 case 'K': 125 SplitupOpt *= 1024; 126 break; 127 case '\0': 128 /* bytes per second if no suffix */ 129 break; 130 default: 131 usage(1); 132 } 133 break; 134 case 'c': 135 CyclePath = optarg; 136 break; 137 case 'd': 138 ++DebugOpt; 139 break; 140 case 'e': 141 ScoreBoardFile = optarg; 142 break; 143 case 'h': 144 usage(0); 145 /* not reached */ 146 case 'i': 147 DelayOpt = strtol(optarg, NULL, 0); 148 break; 149 case 'm': 150 MemoryLimit = strtouq(optarg, &ptr, 0); 151 switch(*ptr) { 152 case 't': 153 case 'T': 154 MemoryLimit *= 1024; 155 /* fall through */ 156 case 'g': 157 case 'G': 158 MemoryLimit *= 1024; 159 /* fall through */ 160 case 'm': 161 case 'M': 162 MemoryLimit *= 1024; 163 /* fall through */ 164 case 'k': 165 case 'K': 166 MemoryLimit *= 1024; 167 /* fall through */ 168 default: 169 break; 170 } 171 172 /* minimum limit */ 173 if (MemoryLimit < 1024 * 1024) 174 MemoryLimit = 1024 * 1024; 175 break; 176 case 'p': 177 SshPort = optarg; 178 break; 179 case 'r': 180 RecurseOpt = 1; 181 break; 182 case 'f': 183 blkdevs = optarg; 184 break; 185 case 's': 186 LinkPath = optarg; 187 break; 188 case 't': 189 TimeoutOpt = strtol(optarg, NULL, 0); 190 break; 191 case 'v': 192 if (QuietOpt > 0) 193 --QuietOpt; 194 else 195 ++VerboseOpt; 196 break; 197 case 'q': 198 if (VerboseOpt > 0) 199 --VerboseOpt; 200 else 201 ++QuietOpt; 202 break; 203 case 'A': 204 AllPFS = 1; 205 break; 206 case 'B': 207 BulkOpt = 1; 208 break; 209 case 'C': 210 cacheSize = strtol(optarg, &ptr, 0); 211 switch(*ptr) { 212 case 'm': 213 case 'M': 214 cacheSize *= 1024; 215 /* fall through */ 216 case 'k': 217 case 'K': 218 cacheSize *= 1024; 219 ++ptr; 220 break; 221 case '\0': 222 case ':': 223 /* bytes if no suffix */ 224 break; 225 default: 226 usage(1); 227 } 228 if (*ptr == ':') { 229 UseReadAhead = strtol(ptr + 1, NULL, 0); 230 UseReadBehind = -UseReadAhead; 231 } 232 if (cacheSize < 1024 * 1024) 233 cacheSize = 1024 * 1024; 234 if (UseReadAhead < 0) 235 usage(1); 236 if (UseReadAhead * HAMMER_BUFSIZE / cacheSize / 16) { 237 UseReadAhead = cacheSize / 16 / HAMMER_BUFSIZE; 238 UseReadBehind = -UseReadAhead; 239 } 240 hammer_cache_set(cacheSize); 241 break; 242 case 'F': 243 ForceOpt = 1; 244 break; 245 case 'R': 246 if (restrictcmd == NULL) 247 restrictcmd = optarg; 248 break; 249 case 'T': 250 if (RestrictTarget == NULL) 251 RestrictTarget = optarg; 252 break; 253 case 'X': 254 CompressOpt = 1; 255 break; 256 default: 257 usage(1); 258 /* not reached */ 259 } 260 } 261 ac -= optind; 262 av += optind; 263 if (ac < 1) { 264 usage(1); 265 /* not reached */ 266 } 267 268 signal(SIGALRM, sigalrm); 269 signal(SIGINT, sigintr); 270 271 /* 272 * Check command restriction (used by hammer ssh-remote). Several 273 * commands may be iterated with a comma. 274 */ 275 if (restrictcmd) { 276 char *elm; 277 278 ptr = strdup(restrictcmd); 279 while ((elm = strsep(&ptr, ",")) != NULL) { 280 if (strcmp(av[0], elm) == 0) 281 break; 282 } 283 if (elm == NULL) { 284 fprintf(stderr, "hammer-remote: request does not match " 285 "restricted command\n"); 286 exit(1); 287 } 288 free(ptr); 289 } 290 291 /* 292 * Parse commands 293 */ 294 if (strcmp(av[0], "synctid") == 0) { 295 hammer_cmd_synctid(av + 1, ac - 1); 296 exit(0); 297 } 298 if (strcmp(av[0], "namekey2") == 0) { 299 int64_t key; 300 int32_t crcx; 301 int len; 302 const char *aname = av[1]; 303 304 if (aname == NULL) 305 usage(1); 306 len = strlen(aname); 307 key = (u_int32_t)crc32(aname, len) & 0xFFFFFFFEU; 308 309 switch(len) { 310 default: 311 crcx = crc32(aname + 3, len - 5); 312 crcx = crcx ^ (crcx >> 6) ^ (crcx >> 12); 313 key |= (int64_t)(crcx & 0x3F) << 42; 314 /* fall through */ 315 case 5: 316 case 4: 317 /* fall through */ 318 case 3: 319 key |= ((int64_t)(aname[2] & 0x1F) << 48); 320 /* fall through */ 321 case 2: 322 key |= ((int64_t)(aname[1] & 0x1F) << 53) | 323 ((int64_t)(aname[len-2] & 0x1F) << 37); 324 /* fall through */ 325 case 1: 326 key |= ((int64_t)(aname[0] & 0x1F) << 58) | 327 ((int64_t)(aname[len-1] & 0x1F) << 32); 328 /* fall through */ 329 case 0: 330 break; 331 } 332 if (key == 0) 333 key |= 0x100000000LL; 334 printf("0x%016jx\n", (uintmax_t)key); 335 exit(0); 336 } 337 if (strcmp(av[0], "namekey1") == 0) { 338 int64_t key; 339 340 if (av[1] == NULL) 341 usage(1); 342 key = (int64_t)(crc32(av[1], strlen(av[1])) & 0x7FFFFFFF) << 32; 343 if (key == 0) 344 key |= 0x100000000LL; 345 printf("0x%016jx\n", (uintmax_t)key); 346 exit(0); 347 } 348 if (strcmp(av[0], "namekey32") == 0) { 349 int32_t key; 350 351 if (av[1] == NULL) 352 usage(1); 353 key = crc32(av[1], strlen(av[1])) & 0x7FFFFFFF; 354 if (key == 0) 355 ++key; 356 printf("0x%08x\n", key); 357 exit(0); 358 } 359 if (strcmp(av[0], "pfs-status") == 0) { 360 hammer_cmd_pseudofs_status(av + 1, ac - 1); 361 exit(0); 362 } 363 if (strcmp(av[0], "pfs-master") == 0) { 364 hammer_cmd_pseudofs_create(av + 1, ac - 1, 0); 365 exit(0); 366 } 367 if (strcmp(av[0], "pfs-slave") == 0) { 368 hammer_cmd_pseudofs_create(av + 1, ac - 1, 1); 369 exit(0); 370 } 371 if (strcmp(av[0], "pfs-update") == 0) { 372 hammer_cmd_pseudofs_update(av + 1, ac - 1); 373 exit(0); 374 } 375 if (strcmp(av[0], "pfs-upgrade") == 0) { 376 hammer_cmd_pseudofs_upgrade(av + 1, ac - 1); 377 exit(0); 378 } 379 if (strcmp(av[0], "pfs-downgrade") == 0) { 380 hammer_cmd_pseudofs_downgrade(av + 1, ac - 1); 381 exit(0); 382 } 383 if (strcmp(av[0], "pfs-destroy") == 0) { 384 hammer_cmd_pseudofs_destroy(av + 1, ac - 1); 385 exit(0); 386 } 387 if (strcmp(av[0], "prune") == 0) { 388 hammer_cmd_softprune(av + 1, ac - 1, 0); 389 exit(0); 390 } 391 if (strcmp(av[0], "config") == 0) { 392 hammer_cmd_config(av + 1, ac - 1); 393 exit(0); 394 } 395 if (strcmp(av[0], "viconfig") == 0) { 396 hammer_cmd_viconfig(av + 1, ac - 1); 397 exit(0); 398 } 399 if (strcmp(av[0], "cleanup") == 0) { 400 hammer_cmd_cleanup(av + 1, ac - 1); 401 exit(0); 402 } 403 if (strcmp(av[0], "info") == 0) { 404 hammer_cmd_info(av + 1, ac - 1); 405 exit(0); 406 } 407 if (strcmp(av[0], "prune-everything") == 0) { 408 hammer_cmd_softprune(av + 1, ac - 1, 1); 409 exit(0); 410 } 411 if (strcmp(av[0], "ssh-remote") == 0) { 412 if (ac != 3) 413 usage(1); 414 hammer_cmd_sshremote(av[1], av[2]); 415 exit(0); 416 } 417 if (strcmp(av[0], "snap") == 0) { 418 hammer_cmd_snap(av + 1, ac - 1, 0, 1); 419 exit(0); 420 } 421 if (strcmp(av[0], "snaplo") == 0) { 422 hammer_cmd_snap(av + 1, ac - 1, 0, 0); 423 exit(0); 424 } 425 if (strcmp(av[0], "snapq") == 0) { 426 hammer_cmd_snap(av + 1, ac - 1, 1, 0); 427 exit(0); 428 } 429 if (strcmp(av[0], "snapls") == 0) { 430 hammer_cmd_snapls(av + 1, ac - 1); 431 exit(0); 432 } 433 if (strcmp(av[0], "snaprm") == 0) { 434 hammer_cmd_snaprm(av + 1, ac - 1); 435 exit(0); 436 } 437 if (strcmp(av[0], "snapshot") == 0) { 438 hammer_cmd_snapshot(av + 1, ac - 1); 439 exit(0); 440 } 441 if (strcmp(av[0], "bstats") == 0) { 442 hammer_cmd_bstats(av + 1, ac - 1); 443 exit(0); 444 } 445 if (strcmp(av[0], "iostats") == 0) { 446 hammer_cmd_iostats(av + 1, ac - 1); 447 exit(0); 448 } 449 450 if (strncmp(av[0], "history", 7) == 0) { 451 hammer_cmd_history(av[0] + 7, av + 1, ac - 1); 452 exit(0); 453 } 454 if (strcmp(av[0], "rebalance") == 0) { 455 signal(SIGINT, sigalrm); 456 hammer_cmd_rebalance(av + 1, ac - 1); 457 exit(0); 458 } 459 if (strncmp(av[0], "reblock", 7) == 0) { 460 signal(SIGINT, sigalrm); 461 if (strcmp(av[0], "reblock") == 0) 462 hammer_cmd_reblock(av + 1, ac - 1, -1); 463 else if (strcmp(av[0], "reblock-btree") == 0) 464 hammer_cmd_reblock(av + 1, ac - 1, HAMMER_IOC_DO_BTREE); 465 else if (strcmp(av[0], "reblock-inodes") == 0) 466 hammer_cmd_reblock(av + 1, ac - 1, HAMMER_IOC_DO_INODES); 467 else if (strcmp(av[0], "reblock-dirs") == 0) 468 hammer_cmd_reblock(av + 1, ac - 1, HAMMER_IOC_DO_DIRS); 469 else if (strcmp(av[0], "reblock-data") == 0) 470 hammer_cmd_reblock(av + 1, ac - 1, HAMMER_IOC_DO_DATA); 471 else 472 usage(1); 473 exit(0); 474 } 475 if (strncmp(av[0], "mirror", 6) == 0) { 476 if (strcmp(av[0], "mirror-read") == 0) 477 hammer_cmd_mirror_read(av + 1, ac - 1, 0); 478 else if (strcmp(av[0], "mirror-read-stream") == 0) 479 hammer_cmd_mirror_read(av + 1, ac - 1, 1); 480 else if (strcmp(av[0], "mirror-write") == 0) 481 hammer_cmd_mirror_write(av + 1, ac - 1); 482 else if (strcmp(av[0], "mirror-copy") == 0) 483 hammer_cmd_mirror_copy(av + 1, ac - 1, 0); 484 else if (strcmp(av[0], "mirror-stream") == 0) 485 hammer_cmd_mirror_copy(av + 1, ac - 1, 1); 486 else if (strcmp(av[0], "mirror-dump") == 0) 487 hammer_cmd_mirror_dump(av + 1, ac - 1); 488 else 489 usage(1); 490 exit(0); 491 } 492 if (strcmp(av[0], "dedup-simulate") == 0) { 493 hammer_cmd_dedup_simulate(av + 1, ac - 1); 494 exit(0); 495 } 496 if (strcmp(av[0], "dedup") == 0) { 497 hammer_cmd_dedup(av + 1, ac - 1); 498 exit(0); 499 } 500 if (strcmp(av[0], "version") == 0) { 501 hammer_cmd_get_version(av + 1, ac - 1); 502 exit(0); 503 } 504 if (strcmp(av[0], "version-upgrade") == 0) { 505 hammer_cmd_set_version(av + 1, ac - 1); 506 exit(0); 507 } 508 if (strcmp(av[0], "volume-add") == 0) { 509 hammer_cmd_volume_add(av + 1, ac - 1); 510 exit(0); 511 } 512 if (strcmp(av[0], "volume-del") == 0) { 513 hammer_cmd_volume_del(av + 1, ac - 1); 514 exit(0); 515 } 516 if (strcmp(av[0], "volume-list") == 0) { 517 hammer_cmd_volume_list(av + 1, ac - 1); 518 exit(0); 519 } 520 521 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status); 522 if (status != uuid_s_ok) { 523 errx(1, "uuids file does not have the DragonFly " 524 "HAMMER filesystem type"); 525 } 526 527 if (strcmp(av[0], "show") == 0) { 528 const char *arg = NULL; 529 int filter = -1; 530 531 hammer_parsedevs(blkdevs); 532 if (ac > 1) 533 arg = av[1]; 534 if (ac > 2) { 535 if (strcmp(av[2], "filter") == 0) 536 filter = 1; 537 else if (strcmp(av[2], "nofilter") == 0) 538 filter = 0; 539 } 540 hammer_cmd_show(-1, arg, filter, 0, NULL, NULL); 541 exit(0); 542 } 543 if (strcmp(av[0], "show-undo") == 0) { 544 hammer_parsedevs(blkdevs); 545 hammer_cmd_show_undo(); 546 exit(0); 547 } 548 if (strcmp(av[0], "recover") == 0) { 549 hammer_parsedevs(blkdevs); 550 if (ac <= 1) 551 errx(1, "hammer recover required target directory"); 552 hammer_cmd_recover(av[1]); 553 exit(0); 554 } 555 if (strcmp(av[0], "blockmap") == 0) { 556 hammer_parsedevs(blkdevs); 557 hammer_cmd_blockmap(); 558 exit(0); 559 } 560 if (strcmp(av[0], "checkmap") == 0) { 561 hammer_parsedevs(blkdevs); 562 hammer_cmd_checkmap(); 563 exit(0); 564 } 565 usage(1); 566 /* not reached */ 567 return(0); 568 } 569 570 /* 571 * Parse the device specification. 572 * 573 * Multi-volume hammer devices are colon-separated. Each element 574 * may be further expanded via /etc/devtab. One may also specify 575 * a single element which is expanded into multiple elements via 576 * /etc/devtab. 577 */ 578 static 579 void 580 hammer_parsedevs(const char *blkdevs) 581 { 582 char *copy; 583 char *volname; 584 585 if (blkdevs == NULL) { 586 errx(1, "A -f blkdevs specification is required " 587 "for this command"); 588 } 589 590 copy = strdup(blkdevs); 591 while ((volname = copy) != NULL) { 592 if ((copy = strchr(copy, ':')) != NULL) 593 *copy++ = 0; 594 volname = getdevpath(volname, 0); 595 if (strchr(volname, ':')) 596 hammer_parsedevs(volname); 597 else 598 setup_volume(-1, volname, 0, O_RDONLY); 599 free(volname); 600 } 601 free(copy); 602 } 603 604 static 605 void 606 sigalrm(int signo __unused) 607 { 608 /* do nothing (interrupts HAMMER ioctl) */ 609 } 610 611 static 612 void 613 sigintr(int signo __unused) 614 { 615 if (RunningIoctl == 0) 616 _exit(1); 617 DidInterrupt = 1; 618 /* do nothing (interrupts HAMMER ioctl) */ 619 } 620 621 static 622 void 623 usage(int exit_code) 624 { 625 fprintf(stderr, 626 "hammer -h\n" 627 "hammer [-2ABqrvXy] [-b bandwidth] [-C cachesize[:readahead]] [-c cyclefile]\n" 628 " [-f blkdevs] [-i delay] [-t seconds] [-S splitup]\n" 629 " command [argument ...]\n" 630 "hammer synctid <filesystem> [quick]\n" 631 "hammer bstats [interval]\n" 632 "hammer iostats [interval]\n" 633 "hammer history[@offset[,len]] <file> ...\n" 634 "hammer namekey1 <path>\n" 635 "hammer namekey2 <path>\n" 636 "hammer namekey32 <path>\n" 637 "hammer cleanup [<filesystem> ...]\n" 638 "hammer info [<dirpath> ...]\n" 639 "hammer snapshot [<filesystem>] <snapshot-dir>\n" 640 "hammer snapshot <filesystem> <snapshot-dir> [<note>]\n" 641 "hammer prune <softlink-dir>\n" 642 "hammer prune-everything <filesystem>\n" 643 "hammer rebalance <filesystem> [saturation_percentage]\n" 644 "hammer reblock[-btree|-inodes|-dirs|-data] " 645 "<filesystem> [fill_percentage]\n" 646 "hammer pfs-status <dirpath> ...\n" 647 "hammer pfs-master <dirpath> [options]\n" 648 "hammer pfs-slave <dirpath> [options]\n" 649 "hammer pfs-update <dirpath> [options]\n" 650 "hammer pfs-upgrade <dirpath>\n" 651 "hammer pfs-downgrade <dirpath>\n" 652 "hammer pfs-destroy <dirpath>\n" 653 "hammer mirror-read <filesystem> [begin-tid]\n" 654 "hammer mirror-read-stream <filesystem> [begin-tid]\n" 655 "hammer mirror-write <filesystem>\n" 656 "hammer mirror-dump [header]\n" 657 "hammer mirror-copy [[user@]host:]<filesystem>" 658 " [[user@]host:]<filesystem>\n" 659 "hammer mirror-stream [[user@]host:]<filesystem>" 660 " [[user@]host:]<filesystem>\n" 661 "hammer ssh-remote command filesystem\n" 662 "hammer version <filesystem>\n" 663 "hammer version-upgrade <filesystem> <version> [force]\n" 664 "hammer volume-add <device> <filesystem>\n" 665 "hammer volume-del <device> <filesystem>\n" 666 "hammer volume-list <filesystem>\n" 667 ); 668 669 fprintf(stderr, "\nHAMMER utility version 3+ commands:\n"); 670 671 fprintf(stderr, 672 "hammer config [<filesystem> [<configfile>]]\n" 673 "hammer viconfig [<filesystem>]\n" 674 "hammer snap <path> [<note>]\n" 675 "hammer snaplo <path> [<note>]\n" 676 "hammer snapq <dir> [<note>]\n" 677 "hammer snaprm <path> ...\n" 678 "hammer snaprm <transid> ...\n" 679 "hammer snaprm <filesystem> <transid> ...\n" 680 "hammer snapls [<path> ...]\n" 681 ); 682 683 fprintf(stderr, "\nHAMMER utility version 4+ commands:\n"); 684 685 fprintf(stderr, 686 "hammer -f blkdevs blockmap\n" 687 "hammer -f blkdevs checkmap\n" 688 "hammer -f blkdevs [-qqq] show [lo:objid]\n" 689 "hammer -f blkdevs show-undo\n" 690 "hammer -f blkdevs recover <target_dir>\n" 691 ); 692 693 fprintf(stderr, "\nHAMMER utility version 5+ commands:\n"); 694 695 fprintf(stderr, 696 "hammer dedup-simulate <filesystem>\n" 697 "hammer dedup <filesystem>\n" 698 ); 699 700 exit(exit_code); 701 } 702 703