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