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 ecode = cmd_pfs_list(sel_path); 204 } else if (strcmp(av[0], "pfs-create") == 0) { 205 /* 206 * Create new PFS using pfs_type 207 */ 208 if (ac < 2) { 209 fprintf(stderr, "pfs-create: requires name\n"); 210 usage(1); 211 } 212 ecode = cmd_pfs_create(sel_path, av[1], pfs_type, uuid_str); 213 } else if (strcmp(av[0], "pfs-delete") == 0) { 214 /* 215 * Delete a PFS by name 216 */ 217 if (ac < 2) { 218 fprintf(stderr, "pfs-delete: requires name\n"); 219 usage(1); 220 } 221 ecode = cmd_pfs_delete(sel_path, av[1]); 222 } else if (strcmp(av[0], "snapshot") == 0) { 223 /* 224 * Create snapshot with optional pfs-type and optional 225 * label override. 226 */ 227 if (ac > 2) { 228 fprintf(stderr, "pfs-snapshot: too many arguments\n"); 229 usage(1); 230 } 231 if (ac != 2) 232 ecode = cmd_pfs_snapshot(sel_path, NULL); 233 else 234 ecode = cmd_pfs_snapshot(sel_path, av[1]); 235 } else if (strcmp(av[0], "service") == 0) { 236 /* 237 * Start the service daemon. This daemon accepts 238 * connections from local and remote clients, handles 239 * the security handshake, and manages the core messaging 240 * protocol. 241 */ 242 ecode = cmd_service(); 243 } else if (strcmp(av[0], "stat") == 0) { 244 ecode = cmd_stat(ac - 1, (const char **)(void *)&av[1]); 245 } else if (strcmp(av[0], "leaf") == 0) { 246 /* 247 * Start the management daemon for a specific PFS. 248 * 249 * This will typically connect to the local master node 250 * daemon, register the PFS, and then pass its side of 251 * the socket descriptor to the kernel HAMMER2 VFS via an 252 * ioctl(). The process and/or thread context remains in the 253 * kernel until the PFS is unmounted or the connection is 254 * lost, then returns from the ioctl. 255 * 256 * It is possible to connect directly to a remote master node 257 * instead of the local master node in situations where 258 * encryption is not desired or no local master node is 259 * desired. This is not recommended because it represents 260 * a single point of failure for the PFS's communications. 261 * 262 * Direct kernel<->kernel communication between HAMMER2 VFSs 263 * is theoretically possible for directly-connected 264 * registrations (i.e. where the spanning tree is degenerate), 265 * but not recommended. We specifically try to reduce the 266 * complexity of the HAMMER2 VFS kernel code. 267 */ 268 ecode = cmd_leaf(sel_path); 269 } else if (strcmp(av[0], "shell") == 0) { 270 /* 271 * Connect to the command line monitor in the hammer2 master 272 * node for the machine using HAMMER2_DBG_SHELL messages. 273 */ 274 ecode = cmd_shell((ac < 2) ? NULL : av[1]); 275 } else if (strcmp(av[0], "rsainit") == 0) { 276 /* 277 * Initialize a RSA keypair. If no target directory is 278 * specified we default to "/etc/hammer2". 279 */ 280 arg = (ac < 2) ? HAMMER2_DEFAULT_DIR : av[1]; 281 ecode = cmd_rsainit(arg); 282 } else if (strcmp(av[0], "rsaenc") == 0) { 283 /* 284 * Encrypt the input symmetrically by running it through 285 * the specified public and/or private key files. 286 * 287 * If no key files are specified data is encoded using 288 * "/etc/hammer2/rsa.pub". 289 * 290 * WARNING: no padding is added, data stream must contain 291 * random padding for this to be secure. 292 * 293 * Used for debugging only 294 */ 295 if (ac == 1) { 296 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.pub"; 297 ecode = cmd_rsaenc(&rsapath, 1); 298 } else { 299 ecode = cmd_rsaenc((const char **)(void *)&av[1], 300 ac - 1); 301 } 302 } else if (strcmp(av[0], "rsadec") == 0) { 303 /* 304 * Decrypt the input symmetrically by running it through 305 * the specified public and/or private key files. 306 * 307 * If no key files are specified data is decoded using 308 * "/etc/hammer2/rsa.prv". 309 * 310 * WARNING: no padding is added, data stream must contain 311 * random padding for this to be secure. 312 * 313 * Used for debugging only 314 */ 315 if (ac == 1) { 316 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.prv"; 317 ecode = cmd_rsadec(&rsapath, 1); 318 } else { 319 ecode = cmd_rsadec((const char **)(void *)&av[1], 320 ac - 1); 321 } 322 } else if (strcmp(av[0], "show") == 0) { 323 /* 324 * Raw dump of filesystem. Use -v to check all crc's, and 325 * -vv to dump bulk file data. 326 */ 327 if (ac != 2) { 328 fprintf(stderr, "show: requires device path\n"); 329 usage(1); 330 } else { 331 cmd_show(av[1], 0); 332 } 333 } else if (strcmp(av[0], "freemap") == 0) { 334 /* 335 * Raw dump of freemap. Use -v to check all crc's, and 336 * -vv to dump bulk file data. 337 */ 338 if (ac != 2) { 339 fprintf(stderr, "freemap: requires device path\n"); 340 usage(1); 341 } else { 342 cmd_show(av[1], 1); 343 } 344 } else if (strcmp(av[0], "setcomp") == 0) { 345 if (ac < 3) { 346 /* 347 * Missing compression method and at least one 348 * path. 349 */ 350 fprintf(stderr, 351 "setcomp: requires compression method and" 352 "directory/file path\n"); 353 usage(1); 354 } else { 355 /* 356 * Multiple paths may be specified 357 */ 358 ecode = cmd_setcomp(av[1], &av[2]); 359 } 360 } else if (strcmp(av[0], "printinode") == 0) { 361 if (ac != 2) { 362 fprintf(stderr, 363 "printinode: requires directory/file path\n"); 364 usage(1); 365 } 366 else 367 print_inode(av[1]); 368 } else { 369 fprintf(stderr, "Unrecognized command: %s\n", av[0]); 370 usage(1); 371 } 372 373 /* 374 * In DebugMode we may wind up starting several pthreads in the 375 * original process, in which case we have to let them run and 376 * not actually exit. 377 */ 378 if (NormalExit) { 379 return (ecode); 380 } else { 381 pthread_exit(NULL); 382 _exit(2); /* NOT REACHED */ 383 } 384 } 385 386 static 387 void 388 usage(int code) 389 { 390 fprintf(stderr, 391 "hammer2 [-s path] command...\n" 392 " -s path Select filesystem\n" 393 " -t type PFS type for pfs-create\n" 394 " -u uuid uuid for pfs-create\n" 395 "\n" 396 " connect <target> " 397 "Add cluster link\n" 398 " disconnect <target> " 399 "Del cluster link\n" 400 " hash filename* " 401 "Print directory hash\n" 402 " status " 403 "Report cluster status\n" 404 " pfs-list " 405 "List PFSs\n" 406 " pfs-clid <label> " 407 "Print cluster id for specific PFS\n" 408 " pfs-fsid <label> " 409 "Print private id for specific PFS\n" 410 " pfs-create <label> " 411 "Create a PFS\n" 412 " pfs-delete <label> " 413 "Destroy a PFS\n" 414 " snapshot [<label>] " 415 "Snapshot a PFS\n" 416 " service " 417 "Start service daemon\n" 418 " stat [<path>] " 419 "Return inode quota & config\n" 420 " leaf " 421 "Start pfs leaf daemon\n" 422 " shell [<host>] " 423 "Connect to debug shell\n" 424 " debugspan <target> " 425 "Connect to target, run CONN/SPAN\n" 426 " rsainit " 427 "Initialize rsa fields\n" 428 " show devpath " 429 "Raw hammer2 media dump\n" 430 " freemap devpath " 431 "Raw hammer2 media dump\n" 432 " setcomp comp[:level] path " 433 "Set compression {none, autozero, lz4, zlib} & level\n" 434 ); 435 exit(code); 436 } 437