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