1 /* 2 * Copyright (c) 2011-2012 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "hammer2.h" 37 38 int DebugOpt; 39 int VerboseOpt; 40 int QuietOpt; 41 int NormalExit = 1; /* if set to 0 main() has to pthread_exit() */ 42 int RecurseOpt; 43 int ForceOpt; 44 size_t MemOpt; 45 46 static void usage(int code); 47 48 int 49 main(int ac, char **av) 50 { 51 const char *sel_path = NULL; 52 const char *uuid_str = NULL; 53 const char *arg; 54 char *opt; 55 int pfs_type = HAMMER2_PFSTYPE_NONE; 56 int all_opt = 0; 57 int ecode = 0; 58 int ch; 59 60 srandomdev(); 61 signal(SIGPIPE, SIG_IGN); 62 dmsg_crypto_setup(); 63 64 /* 65 * Core options 66 */ 67 while ((ch = getopt(ac, av, "adfm:rqs:t:u:v")) != -1) { 68 switch(ch) { 69 case 'a': 70 all_opt = 1; 71 break; 72 case 'd': 73 if (DebugOpt) 74 ++DMsgDebugOpt; 75 DebugOpt = 1; 76 break; 77 case 'f': 78 ForceOpt = 1; 79 break; 80 case 'm': 81 MemOpt = strtoul(optarg, &opt, 0); 82 switch(*opt) { 83 case 'g': 84 case 'G': 85 MemOpt *= 1024; 86 /* FALLTHROUGH */ 87 case 'm': 88 case 'M': 89 MemOpt *= 1024; 90 /* FALLTHROUGH */ 91 case 'k': 92 case 'K': 93 MemOpt *= 1024; 94 break; 95 case 0: 96 break; 97 default: 98 fprintf(stderr, "-m: unrecognized suffix\n"); 99 usage(1); 100 break; 101 } 102 break; 103 case 'r': 104 RecurseOpt = 1; 105 break; 106 case 's': 107 sel_path = optarg; 108 break; 109 case 't': 110 /* 111 * set node type for mkpfs 112 */ 113 if (strcasecmp(optarg, "CACHE") == 0) { 114 pfs_type = HAMMER2_PFSTYPE_CACHE; 115 } else if (strcasecmp(optarg, "DUMMY") == 0) { 116 pfs_type = HAMMER2_PFSTYPE_DUMMY; 117 } else if (strcasecmp(optarg, "SLAVE") == 0) { 118 pfs_type = HAMMER2_PFSTYPE_SLAVE; 119 } else if (strcasecmp(optarg, "SOFT_SLAVE") == 0) { 120 pfs_type = HAMMER2_PFSTYPE_SOFT_SLAVE; 121 } else if (strcasecmp(optarg, "SOFT_MASTER") == 0) { 122 pfs_type = HAMMER2_PFSTYPE_SOFT_MASTER; 123 } else if (strcasecmp(optarg, "MASTER") == 0) { 124 pfs_type = HAMMER2_PFSTYPE_MASTER; 125 } else { 126 fprintf(stderr, "-t: Unrecognized node type\n"); 127 usage(1); 128 } 129 break; 130 case 'u': 131 /* 132 * set uuid for mkpfs, else one will be generated 133 * (required for all except the MASTER node_type) 134 */ 135 uuid_str = optarg; 136 break; 137 case 'v': 138 if (QuietOpt) 139 --QuietOpt; 140 else 141 ++VerboseOpt; 142 break; 143 case 'q': 144 if (VerboseOpt) 145 --VerboseOpt; 146 else 147 ++QuietOpt; 148 break; 149 default: 150 fprintf(stderr, "Unknown option: %c\n", ch); 151 usage(1); 152 /* not reached */ 153 break; 154 } 155 } 156 157 /* 158 * Adjust, then process the command 159 */ 160 ac -= optind; 161 av += optind; 162 if (ac < 1) { 163 fprintf(stderr, "Missing command\n"); 164 usage(1); 165 /* not reached */ 166 } 167 168 if (strcmp(av[0], "connect") == 0) { 169 /* 170 * Add cluster connection 171 */ 172 if (ac < 2) { 173 fprintf(stderr, "connect: missing argument\n"); 174 usage(1); 175 } 176 ecode = cmd_remote_connect(sel_path, av[1]); 177 } else if (strcmp(av[0], "dumpchain") == 0) { 178 if (ac < 2) 179 ecode = cmd_dumpchain(".", (u_int)-1); 180 else if (ac < 3) 181 ecode = cmd_dumpchain(av[1], (u_int)-1); 182 else 183 ecode = cmd_dumpchain(av[1], 184 (u_int)strtoul(av[2], NULL, 0)); 185 } else if (strcmp(av[0], "debugspan") == 0) { 186 /* 187 * Debug connection to the target hammer2 service and run 188 * the CONN/SPAN protocol. 189 */ 190 if (ac < 2) { 191 fprintf(stderr, "debugspan: requires hostname\n"); 192 usage(1); 193 } 194 ecode = cmd_debugspan(av[1]); 195 } else if (strcmp(av[0], "disconnect") == 0) { 196 /* 197 * Remove cluster connection 198 */ 199 if (ac < 2) { 200 fprintf(stderr, "disconnect: missing argument\n"); 201 usage(1); 202 } 203 ecode = cmd_remote_disconnect(sel_path, av[1]); 204 } else if (strcmp(av[0], "destroy") == 0) { 205 if (ac < 2) { 206 fprintf(stderr, 207 "Specify one or more paths to destroy\n"); 208 } 209 ecode = cmd_destroy_path(ac - 1, (const char **)(void *)&av[1]); 210 } else if (strcmp(av[0], "hash") == 0) { 211 ecode = cmd_hash(ac - 1, (const char **)(void *)&av[1]); 212 } else if (strcmp(av[0], "info") == 0) { 213 ecode = cmd_info(ac - 1, (const char **)(void *)&av[1]); 214 } else if (strcmp(av[0], "mountall") == 0) { 215 ecode = cmd_mountall(ac - 1, (const char **)(void *)&av[1]); 216 } else if (strcmp(av[0], "status") == 0) { 217 /* 218 * Get status of PFS and its connections (-a for all PFSs) 219 */ 220 if (ac < 2) { 221 ecode = cmd_remote_status(sel_path, all_opt); 222 } else { 223 int i; 224 225 for (i = 1; i < ac; ++i) 226 ecode = cmd_remote_status(av[i], all_opt); 227 } 228 } else if (strcmp(av[0], "pfs-clid") == 0) { 229 /* 230 * Print cluster id (uuid) for specific PFS 231 */ 232 if (ac < 2) { 233 fprintf(stderr, "pfs-clid: requires name\n"); 234 usage(1); 235 } 236 ecode = cmd_pfs_getid(sel_path, av[1], 0); 237 } else if (strcmp(av[0], "pfs-fsid") == 0) { 238 /* 239 * Print private id (uuid) for specific PFS 240 */ 241 if (ac < 2) { 242 fprintf(stderr, "pfs-fsid: requires name\n"); 243 usage(1); 244 } 245 ecode = cmd_pfs_getid(sel_path, av[1], 1); 246 } else if (strcmp(av[0], "pfs-list") == 0) { 247 /* 248 * List all PFSs 249 */ 250 if (ac >= 2) { 251 ecode = cmd_pfs_list(ac - 1, 252 (const char **)(void *)&av[1]); 253 } else { 254 ecode = cmd_pfs_list(1, &sel_path); 255 } 256 } else if (strcmp(av[0], "pfs-create") == 0) { 257 /* 258 * Create new PFS using pfs_type 259 */ 260 if (ac < 2) { 261 fprintf(stderr, "pfs-create: requires name\n"); 262 usage(1); 263 } 264 ecode = cmd_pfs_create(sel_path, av[1], pfs_type, uuid_str); 265 } else if (strcmp(av[0], "pfs-delete") == 0) { 266 /* 267 * Delete a PFS by name 268 */ 269 if (ac < 2) { 270 fprintf(stderr, "pfs-delete: requires name\n"); 271 usage(1); 272 } 273 ecode = cmd_pfs_delete(sel_path, av[1]); 274 } else if (strcmp(av[0], "snapshot") == 0) { 275 /* 276 * Create snapshot with optional pfs-type and optional 277 * label override. 278 */ 279 if (ac > 3) { 280 fprintf(stderr, "pfs-snapshot: too many arguments\n"); 281 usage(1); 282 } 283 switch(ac) { 284 case 1: 285 ecode = cmd_pfs_snapshot(sel_path, NULL, NULL); 286 break; 287 case 2: 288 ecode = cmd_pfs_snapshot(sel_path, av[1], NULL); 289 break; 290 case 3: 291 ecode = cmd_pfs_snapshot(sel_path, av[1], av[2]); 292 break; 293 } 294 } else if (strcmp(av[0], "service") == 0) { 295 /* 296 * Start the service daemon. This daemon accepts 297 * connections from local and remote clients, handles 298 * the security handshake, and manages the core messaging 299 * protocol. 300 */ 301 ecode = cmd_service(); 302 } else if (strcmp(av[0], "stat") == 0) { 303 ecode = cmd_stat(ac - 1, (const char **)(void *)&av[1]); 304 } else if (strcmp(av[0], "leaf") == 0) { 305 /* 306 * Start the management daemon for a specific PFS. 307 * 308 * This will typically connect to the local master node 309 * daemon, register the PFS, and then pass its side of 310 * the socket descriptor to the kernel HAMMER2 VFS via an 311 * ioctl(). The process and/or thread context remains in the 312 * kernel until the PFS is unmounted or the connection is 313 * lost, then returns from the ioctl. 314 * 315 * It is possible to connect directly to a remote master node 316 * instead of the local master node in situations where 317 * encryption is not desired or no local master node is 318 * desired. This is not recommended because it represents 319 * a single point of failure for the PFS's communications. 320 * 321 * Direct kernel<->kernel communication between HAMMER2 VFSs 322 * is theoretically possible for directly-connected 323 * registrations (i.e. where the spanning tree is degenerate), 324 * but not recommended. We specifically try to reduce the 325 * complexity of the HAMMER2 VFS kernel code. 326 */ 327 ecode = cmd_leaf(sel_path); 328 } else if (strcmp(av[0], "shell") == 0) { 329 /* 330 * Connect to the command line monitor in the hammer2 master 331 * node for the machine using HAMMER2_DBG_SHELL messages. 332 */ 333 ecode = cmd_shell((ac < 2) ? NULL : av[1]); 334 } else if (strcmp(av[0], "rsainit") == 0) { 335 /* 336 * Initialize a RSA keypair. If no target directory is 337 * specified we default to "/etc/hammer2". 338 */ 339 arg = (ac < 2) ? HAMMER2_DEFAULT_DIR : av[1]; 340 ecode = cmd_rsainit(arg); 341 } else if (strcmp(av[0], "rsaenc") == 0) { 342 /* 343 * Encrypt the input symmetrically by running it through 344 * the specified public and/or private key files. 345 * 346 * If no key files are specified data is encoded using 347 * "/etc/hammer2/rsa.pub". 348 * 349 * WARNING: no padding is added, data stream must contain 350 * random padding for this to be secure. 351 * 352 * Used for debugging only 353 */ 354 if (ac == 1) { 355 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.pub"; 356 ecode = cmd_rsaenc(&rsapath, 1); 357 } else { 358 ecode = cmd_rsaenc((const char **)(void *)&av[1], 359 ac - 1); 360 } 361 } else if (strcmp(av[0], "rsadec") == 0) { 362 /* 363 * Decrypt the input symmetrically by running it through 364 * the specified public and/or private key files. 365 * 366 * If no key files are specified data is decoded using 367 * "/etc/hammer2/rsa.prv". 368 * 369 * WARNING: no padding is added, data stream must contain 370 * random padding for this to be secure. 371 * 372 * Used for debugging only 373 */ 374 if (ac == 1) { 375 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.prv"; 376 ecode = cmd_rsadec(&rsapath, 1); 377 } else { 378 ecode = cmd_rsadec((const char **)(void *)&av[1], 379 ac - 1); 380 } 381 } else if (strcmp(av[0], "show") == 0) { 382 /* 383 * Raw dump of filesystem. Use -v to check all crc's, and 384 * -vv to dump bulk file data. 385 */ 386 if (ac != 2) { 387 fprintf(stderr, "show: requires device path\n"); 388 usage(1); 389 } else { 390 cmd_show(av[1], 0); 391 } 392 } else if (strcmp(av[0], "freemap") == 0) { 393 /* 394 * Raw dump of freemap. Use -v to check all crc's, and 395 * -vv to dump bulk file data. 396 */ 397 if (ac != 2) { 398 fprintf(stderr, "freemap: requires device path\n"); 399 usage(1); 400 } else { 401 cmd_show(av[1], 1); 402 } 403 } else if (strcmp(av[0], "setcomp") == 0) { 404 if (ac < 3) { 405 /* 406 * Missing compression method and at least one 407 * path. 408 */ 409 fprintf(stderr, 410 "setcomp: requires compression method and" 411 "directory/file path\n"); 412 usage(1); 413 } else { 414 /* 415 * Multiple paths may be specified 416 */ 417 ecode = cmd_setcomp(av[1], &av[2]); 418 } 419 } else if (strcmp(av[0], "setcheck") == 0) { 420 if (ac < 3) { 421 /* 422 * Missing compression method and at least one 423 * path. 424 */ 425 fprintf(stderr, 426 "setcheck: requires check code method and" 427 "directory/file path\n"); 428 usage(1); 429 } else { 430 /* 431 * Multiple paths may be specified 432 */ 433 ecode = cmd_setcheck(av[1], &av[2]); 434 } 435 } else if (strcmp(av[0], "clrcheck") == 0) { 436 ecode = cmd_setcheck("none", &av[1]); 437 } else if (strcmp(av[0], "setcrc32") == 0) { 438 ecode = cmd_setcheck("crc32", &av[1]); 439 } else if (strcmp(av[0], "setxxhash64") == 0) { 440 ecode = cmd_setcheck("xxhash64", &av[1]); 441 } else if (strcmp(av[0], "setsha192") == 0) { 442 ecode = cmd_setcheck("sha192", &av[1]); 443 } else if (strcmp(av[0], "printinode") == 0) { 444 if (ac != 2) { 445 fprintf(stderr, 446 "printinode: requires directory/file path\n"); 447 usage(1); 448 } else { 449 print_inode(av[1]); 450 } 451 } else if (strcmp(av[0], "bulkfree") == 0) { 452 if (ac != 2) { 453 fprintf(stderr, 454 "bulkfree: requires path to mount\n"); 455 usage(1); 456 } else { 457 ecode = cmd_bulkfree(av[1]); 458 } 459 #if 0 460 } else if (strcmp(av[0], "bulkfree-async") == 0) { 461 if (ac != 2) { 462 fprintf(stderr, 463 "bulkfree-async: requires path to mount\n"); 464 usage(1); 465 } else { 466 ecode = cmd_bulkfree_async(av[1]); 467 } 468 #endif 469 } else if (strcmp(av[0], "cleanup") == 0) { 470 ecode = cmd_cleanup(av[1]); /* can be NULL */ 471 } else { 472 fprintf(stderr, "Unrecognized command: %s\n", av[0]); 473 usage(1); 474 } 475 476 /* 477 * In DebugMode we may wind up starting several pthreads in the 478 * original process, in which case we have to let them run and 479 * not actually exit. 480 */ 481 if (NormalExit) { 482 return (ecode); 483 } else { 484 pthread_exit(NULL); 485 _exit(2); /* NOT REACHED */ 486 } 487 } 488 489 static 490 void 491 usage(int code) 492 { 493 fprintf(stderr, 494 "hammer2 [options] command...\n" 495 " -s path Select filesystem\n" 496 " -t type PFS type for pfs-create\n" 497 " -u uuid uuid for pfs-create\n" 498 " -m mem[k,m,g] buffer memory (bulkfree)\n" 499 "\n" 500 " cleanup [<path>...] " 501 "Run cleanup passes\n" 502 " connect <target> " 503 "Add cluster link\n" 504 " debugspan <target> " 505 "Debug spanning tree\n" 506 " dumpchain <path> [chnflags] " 507 "Dump in-memory chain topo from\n" 508 "NOTE: ONFLUSH flag is 0x200\n" 509 " destroy <path>* " 510 "Destroy a directory entry (only use if inode bad)\n" 511 " disconnect <target> " 512 "Del cluster link\n" 513 " hash filename* " 514 "Print directory hash\n" 515 " info [devpath...] " 516 "Info on all offline or online H2 partitions\n" 517 " mountall [devpath...] " 518 "Mount @LOCAL for all H2 partitions\n" 519 " status [<path>...] " 520 "Report active cluster status\n" 521 " pfs-list [<path>...] " 522 "List PFSs\n" 523 " pfs-clid <label> " 524 "Print cluster id for specific PFS\n" 525 " pfs-fsid <label> " 526 "Print private id for specific PFS\n" 527 " pfs-create <label> " 528 "Create a PFS\n" 529 " pfs-delete <label> " 530 "Destroy a PFS\n" 531 " snapshot <path> [<label>] " 532 "Snapshot a PFS or directory\n" 533 " service " 534 "Start service daemon\n" 535 " stat [<path>] " 536 "Return inode quota & config\n" 537 " leaf " 538 "Start pfs leaf daemon\n" 539 " shell [<host>] " 540 "Connect to debug shell\n" 541 " debugspan <target> " 542 "Connect to target, run CONN/SPAN\n" 543 " rsainit " 544 "Initialize rsa fields\n" 545 " show devpath " 546 "Raw hammer2 media dump\n" 547 " freemap devpath " 548 "Raw hammer2 media dump\n" 549 " setcomp comp[:level] path... " 550 "Set comp algo {none, autozero, lz4, zlib} & level\n" 551 " setcheck check path... " 552 "Set check algo {none, crc32, xxhash64, sha192}\n" 553 " clrcheck path... " 554 "Clear check code override\n" 555 " setcrc32 path... " 556 "Set check algo to crc32\n" 557 " setxxhash64 path... " 558 "Set check algo to xxhash64\n" 559 " setsha192 path... " 560 "Set check algo to sha192\n" 561 " bulkfree path... " 562 "Run bulkfree pass\n" 563 #if 0 564 " bulkfree-async path... " 565 "Run bulkfree pass asynchronously\n" 566 #endif 567 ); 568 exit(code); 569 } 570