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