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 static void usage(int code); 39 40 int DebugOpt; 41 int VerboseOpt; 42 int QuietOpt; 43 int NormalExit = 1; /* if set to 0 main() has to pthread_exit() */ 44 45 int 46 main(int ac, char **av) 47 { 48 const char *sel_path = NULL; 49 const char *uuid_str = NULL; 50 const char *arg; 51 int pfs_type = HAMMER2_PFSTYPE_NONE; 52 int all_opt = 0; 53 int ecode = 0; 54 int ch; 55 56 srandomdev(); 57 signal(SIGPIPE, SIG_IGN); 58 dmsg_crypto_setup(); 59 60 /* 61 * Core options 62 */ 63 while ((ch = getopt(ac, av, "adqs:t:u:v")) != -1) { 64 switch(ch) { 65 case 'a': 66 all_opt = 1; 67 break; 68 case 'd': 69 DebugOpt = 1; 70 break; 71 case 's': 72 sel_path = optarg; 73 break; 74 case 't': 75 /* 76 * set node type for mkpfs 77 */ 78 if (strcasecmp(optarg, "ADMIN") == 0) { 79 pfs_type = HAMMER2_PFSTYPE_ADMIN; 80 } else if (strcasecmp(optarg, "CACHE") == 0) { 81 pfs_type = HAMMER2_PFSTYPE_CACHE; 82 } else if (strcasecmp(optarg, "COPY") == 0) { 83 pfs_type = HAMMER2_PFSTYPE_COPY; 84 } else if (strcasecmp(optarg, "SLAVE") == 0) { 85 pfs_type = HAMMER2_PFSTYPE_SLAVE; 86 } else if (strcasecmp(optarg, "SOFT_SLAVE") == 0) { 87 pfs_type = HAMMER2_PFSTYPE_SOFT_SLAVE; 88 } else if (strcasecmp(optarg, "SOFT_MASTER") == 0) { 89 pfs_type = HAMMER2_PFSTYPE_SOFT_MASTER; 90 } else if (strcasecmp(optarg, "MASTER") == 0) { 91 pfs_type = HAMMER2_PFSTYPE_MASTER; 92 } else { 93 fprintf(stderr, "-t: Unrecognized node type\n"); 94 usage(1); 95 } 96 break; 97 case 'u': 98 /* 99 * set uuid for mkpfs, else one will be generated 100 * (required for all except the MASTER node_type) 101 */ 102 uuid_str = optarg; 103 break; 104 case 'v': 105 if (QuietOpt) 106 --QuietOpt; 107 else 108 ++VerboseOpt; 109 break; 110 case 'q': 111 if (VerboseOpt) 112 --VerboseOpt; 113 else 114 ++QuietOpt; 115 break; 116 default: 117 fprintf(stderr, "Unknown option: %c\n", ch); 118 usage(1); 119 /* not reached */ 120 break; 121 } 122 } 123 124 /* 125 * Adjust, then process the command 126 */ 127 ac -= optind; 128 av += optind; 129 if (ac < 1) { 130 fprintf(stderr, "Missing command\n"); 131 usage(1); 132 /* not reached */ 133 } 134 135 if (strcmp(av[0], "connect") == 0) { 136 /* 137 * Add cluster connection 138 */ 139 if (ac < 2) { 140 fprintf(stderr, "connect: missing argument\n"); 141 usage(1); 142 } 143 ecode = cmd_remote_connect(sel_path, av[1]); 144 } else if (strcmp(av[0], "debugspan") == 0) { 145 /* 146 * Debug connection to the target hammer2 service and run 147 * the CONN/SPAN protocol. 148 */ 149 if (ac < 2) { 150 fprintf(stderr, "debugspan: requires hostname\n"); 151 usage(1); 152 } 153 ecode = cmd_debugspan(av[1]); 154 } else if (strcmp(av[0], "disconnect") == 0) { 155 /* 156 * Remove cluster connection 157 */ 158 if (ac < 2) { 159 fprintf(stderr, "disconnect: missing argument\n"); 160 usage(1); 161 } 162 ecode = cmd_remote_disconnect(sel_path, av[1]); 163 } else if (strcmp(av[0], "hash") == 0) { 164 ecode = cmd_hash(ac - 1, (const char **)(void *)&av[1]); 165 } else if (strcmp(av[0], "status") == 0) { 166 /* 167 * Get status of PFS and its connections (-a for all PFSs) 168 */ 169 ecode = cmd_remote_status(sel_path, all_opt); 170 } else if (strcmp(av[0], "pfs-clid") == 0) { 171 /* 172 * Print cluster id (uuid) for specific PFS 173 */ 174 if (ac < 2) { 175 fprintf(stderr, "pfs-clid: requires name\n"); 176 usage(1); 177 } 178 ecode = cmd_pfs_getid(sel_path, av[1], 0); 179 } else if (strcmp(av[0], "pfs-fsid") == 0) { 180 /* 181 * Print private id (uuid) for specific PFS 182 */ 183 if (ac < 2) { 184 fprintf(stderr, "pfs-fsid: requires name\n"); 185 usage(1); 186 } 187 ecode = cmd_pfs_getid(sel_path, av[1], 1); 188 } else if (strcmp(av[0], "pfs-list") == 0) { 189 /* 190 * List all PFSs 191 */ 192 ecode = cmd_pfs_list(sel_path); 193 } else if (strcmp(av[0], "pfs-create") == 0) { 194 /* 195 * Create new PFS using pfs_type 196 */ 197 if (ac < 2) { 198 fprintf(stderr, "pfs-create: requires name\n"); 199 usage(1); 200 } 201 ecode = cmd_pfs_create(sel_path, av[1], pfs_type, uuid_str); 202 } else if (strcmp(av[0], "pfs-delete") == 0) { 203 /* 204 * Delete a PFS by name 205 */ 206 if (ac < 2) { 207 fprintf(stderr, "pfs-delete: requires name\n"); 208 usage(1); 209 } 210 ecode = cmd_pfs_delete(sel_path, av[1]); 211 } else if (strcmp(av[0], "snapshot") == 0) { 212 /* 213 * Create snapshot with optional pfs-type and optional 214 * label override. 215 */ 216 if (ac > 2) { 217 fprintf(stderr, "pfs-snapshot: too many arguments\n"); 218 usage(1); 219 } 220 if (ac != 2) 221 ecode = cmd_pfs_snapshot(sel_path, NULL); 222 else 223 ecode = cmd_pfs_snapshot(sel_path, av[1]); 224 } else if (strcmp(av[0], "service") == 0) { 225 /* 226 * Start the service daemon. This daemon accepts 227 * connections from local and remote clients, handles 228 * the security handshake, and manages the core messaging 229 * protocol. 230 */ 231 ecode = cmd_service(); 232 } else if (strcmp(av[0], "stat") == 0) { 233 ecode = cmd_stat(ac - 1, (const char **)(void *)&av[1]); 234 } else if (strcmp(av[0], "leaf") == 0) { 235 /* 236 * Start the management daemon for a specific PFS. 237 * 238 * This will typically connect to the local master node 239 * daemon, register the PFS, and then pass its side of 240 * the socket descriptor to the kernel HAMMER2 VFS via an 241 * ioctl(). The process and/or thread context remains in the 242 * kernel until the PFS is unmounted or the connection is 243 * lost, then returns from the ioctl. 244 * 245 * It is possible to connect directly to a remote master node 246 * instead of the local master node in situations where 247 * encryption is not desired or no local master node is 248 * desired. This is not recommended because it represents 249 * a single point of failure for the PFS's communications. 250 * 251 * Direct kernel<->kernel communication between HAMMER2 VFSs 252 * is theoretically possible for directly-connected 253 * registrations (i.e. where the spanning tree is degenerate), 254 * but not recommended. We specifically try to reduce the 255 * complexity of the HAMMER2 VFS kernel code. 256 */ 257 ecode = cmd_leaf(sel_path); 258 } else if (strcmp(av[0], "shell") == 0) { 259 /* 260 * Connect to the command line monitor in the hammer2 master 261 * node for the machine using HAMMER2_DBG_SHELL messages. 262 */ 263 ecode = cmd_shell((ac < 2) ? NULL : av[1]); 264 } else if (strcmp(av[0], "rsainit") == 0) { 265 /* 266 * Initialize a RSA keypair. If no target directory is 267 * specified we default to "/etc/hammer2". 268 */ 269 arg = (ac < 2) ? HAMMER2_DEFAULT_DIR : av[1]; 270 ecode = cmd_rsainit(arg); 271 } else if (strcmp(av[0], "rsaenc") == 0) { 272 /* 273 * Encrypt the input symmetrically by running it through 274 * the specified public and/or private key files. 275 * 276 * If no key files are specified data is encoded using 277 * "/etc/hammer2/rsa.pub". 278 * 279 * WARNING: no padding is added, data stream must contain 280 * random padding for this to be secure. 281 * 282 * Used for debugging only 283 */ 284 if (ac == 1) { 285 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.pub"; 286 ecode = cmd_rsaenc(&rsapath, 1); 287 } else { 288 ecode = cmd_rsaenc((const char **)(void *)&av[1], 289 ac - 1); 290 } 291 } else if (strcmp(av[0], "rsadec") == 0) { 292 /* 293 * Decrypt the input symmetrically by running it through 294 * the specified public and/or private key files. 295 * 296 * If no key files are specified data is decoded using 297 * "/etc/hammer2/rsa.prv". 298 * 299 * WARNING: no padding is added, data stream must contain 300 * random padding for this to be secure. 301 * 302 * Used for debugging only 303 */ 304 if (ac == 1) { 305 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.prv"; 306 ecode = cmd_rsadec(&rsapath, 1); 307 } else { 308 ecode = cmd_rsadec((const char **)(void *)&av[1], 309 ac - 1); 310 } 311 } else if (strcmp(av[0], "show") == 0) { 312 /* 313 * Raw dump of filesystem. Use -v to check all crc's, and 314 * -vv to dump bulk file data. 315 */ 316 if (ac != 2) { 317 fprintf(stderr, "show: requires device path\n"); 318 usage(1); 319 } else { 320 cmd_show(av[1], 0); 321 } 322 } else if (strcmp(av[0], "freemap") == 0) { 323 /* 324 * Raw dump of freemap. Use -v to check all crc's, and 325 * -vv to dump bulk file data. 326 */ 327 if (ac != 2) { 328 fprintf(stderr, "freemap: requires device path\n"); 329 usage(1); 330 } else { 331 cmd_show(av[1], 1); 332 } 333 } else { 334 fprintf(stderr, "Unrecognized command: %s\n", av[0]); 335 usage(1); 336 } 337 338 /* 339 * In DebugMode we may wind up starting several pthreads in the 340 * original process, in which case we have to let them run and 341 * not actually exit. 342 */ 343 if (NormalExit) { 344 return (ecode); 345 } else { 346 pthread_exit(NULL); 347 _exit(2); /* NOT REACHED */ 348 } 349 } 350 351 static 352 void 353 usage(int code) 354 { 355 fprintf(stderr, 356 "hammer2 [-s path] command...\n" 357 " -s path Select filesystem\n" 358 " -t type PFS type for pfs-create\n" 359 " -u uuid uuid for pfs-create\n" 360 "\n" 361 " connect <target> Add cluster link\n" 362 " disconnect <target> Del cluster link\n" 363 " hash filename* Print directory hash\n" 364 " status Report cluster status\n" 365 " pfs-list List PFSs\n" 366 " pfs-clid <label> Print cluster id for specific PFS\n" 367 " pfs-fsid <label> Print private id for specific PFS\n" 368 " pfs-create <label> Create a PFS\n" 369 " pfs-delete <label> Destroy a PFS\n" 370 " snapshot Snapshot a PFS\n" 371 " service Start service daemon\n" 372 " stat [<path>] Return inode quota & config\n" 373 " leaf Start pfs leaf daemon\n" 374 " shell [<host>] Connect to debug shell\n" 375 " debugspan <target> Connect to target, run CONN/SPAN\n" 376 " rsainit Initialize rsa fields\n" 377 " show devpath Raw hammer2 media dump\n" 378 " freemap devpath Raw hammer2 media dump\n" 379 ); 380 exit(code); 381 } 382