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], "status") == 0) { 164 /* 165 * Get status of PFS and its connections (-a for all PFSs) 166 */ 167 ecode = cmd_remote_status(sel_path, all_opt); 168 } else if (strcmp(av[0], "pfs-clid") == 0) { 169 /* 170 * Print cluster id (uuid) for specific PFS 171 */ 172 if (ac < 2) { 173 fprintf(stderr, "pfs-clid: requires name\n"); 174 usage(1); 175 } 176 ecode = cmd_pfs_getid(sel_path, av[1], 0); 177 } else if (strcmp(av[0], "pfs-fsid") == 0) { 178 /* 179 * Print private id (uuid) for specific PFS 180 */ 181 if (ac < 2) { 182 fprintf(stderr, "pfs-fsid: requires name\n"); 183 usage(1); 184 } 185 ecode = cmd_pfs_getid(sel_path, av[1], 1); 186 } else if (strcmp(av[0], "pfs-list") == 0) { 187 /* 188 * List all PFSs 189 */ 190 ecode = cmd_pfs_list(sel_path); 191 } else if (strcmp(av[0], "pfs-create") == 0) { 192 /* 193 * Create new PFS using pfs_type 194 */ 195 if (ac < 2) { 196 fprintf(stderr, "pfs-create: requires name\n"); 197 usage(1); 198 } 199 ecode = cmd_pfs_create(sel_path, av[1], pfs_type, uuid_str); 200 } else if (strcmp(av[0], "pfs-delete") == 0) { 201 /* 202 * Delete a PFS by name 203 */ 204 if (ac < 2) { 205 fprintf(stderr, "pfs-delete: requires name\n"); 206 usage(1); 207 } 208 ecode = cmd_pfs_delete(sel_path, av[1]); 209 } else if (strcmp(av[0], "snapshot") == 0) { 210 /* 211 * Create snapshot with optional pfs-type and optional 212 * label override. 213 */ 214 } else if (strcmp(av[0], "service") == 0) { 215 /* 216 * Start the service daemon. This daemon accepts 217 * connections from local and remote clients, handles 218 * the security handshake, and manages the core messaging 219 * protocol. 220 */ 221 ecode = cmd_service(); 222 } else if (strcmp(av[0], "stat") == 0) { 223 ecode = cmd_stat(ac - 1, (const char **)&av[1]); 224 } else if (strcmp(av[0], "leaf") == 0) { 225 /* 226 * Start the management daemon for a specific PFS. 227 * 228 * This will typically connect to the local master node 229 * daemon, register the PFS, and then pass its side of 230 * the socket descriptor to the kernel HAMMER2 VFS via an 231 * ioctl(). The process and/or thread context remains in the 232 * kernel until the PFS is unmounted or the connection is 233 * lost, then returns from the ioctl. 234 * 235 * It is possible to connect directly to a remote master node 236 * instead of the local master node in situations where 237 * encryption is not desired or no local master node is 238 * desired. This is not recommended because it represents 239 * a single point of failure for the PFS's communications. 240 * 241 * Direct kernel<->kernel communication between HAMMER2 VFSs 242 * is theoretically possible for directly-connected 243 * registrations (i.e. where the spanning tree is degenerate), 244 * but not recommended. We specifically try to reduce the 245 * complexity of the HAMMER2 VFS kernel code. 246 */ 247 ecode = cmd_leaf(sel_path); 248 } else if (strcmp(av[0], "shell") == 0) { 249 /* 250 * Connect to the command line monitor in the hammer2 master 251 * node for the machine using HAMMER2_DBG_SHELL messages. 252 */ 253 ecode = cmd_shell((ac < 2) ? NULL : av[1]); 254 } else if (strcmp(av[0], "rsainit") == 0) { 255 /* 256 * Initialize a RSA keypair. If no target directory is 257 * specified we default to "/etc/hammer2". 258 */ 259 arg = (ac < 2) ? HAMMER2_DEFAULT_DIR : av[1]; 260 ecode = cmd_rsainit(arg); 261 } else if (strcmp(av[0], "rsaenc") == 0) { 262 /* 263 * Encrypt the input symmetrically by running it through 264 * the specified public and/or private key files. 265 * 266 * If no key files are specified data is encoded using 267 * "/etc/hammer2/rsa.pub". 268 * 269 * WARNING: no padding is added, data stream must contain 270 * random padding for this to be secure. 271 * 272 * Used for debugging only 273 */ 274 if (ac == 1) { 275 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.pub"; 276 ecode = cmd_rsaenc(&rsapath, 1); 277 } else { 278 ecode = cmd_rsaenc((const char **)&av[1], ac - 1); 279 } 280 } else if (strcmp(av[0], "rsadec") == 0) { 281 /* 282 * Decrypt the input symmetrically by running it through 283 * the specified public and/or private key files. 284 * 285 * If no key files are specified data is decoded using 286 * "/etc/hammer2/rsa.prv". 287 * 288 * WARNING: no padding is added, data stream must contain 289 * random padding for this to be secure. 290 * 291 * Used for debugging only 292 */ 293 if (ac == 1) { 294 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.prv"; 295 ecode = cmd_rsadec(&rsapath, 1); 296 } else { 297 ecode = cmd_rsadec((const char **)&av[1], ac - 1); 298 } 299 } else if (strcmp(av[0], "show") == 0) { 300 /* 301 * Raw dump of filesystem. Use -v to check all crc's, and 302 * -vv to dump bulk file data. 303 */ 304 if (ac != 2) { 305 fprintf(stderr, "show: requires device path\n"); 306 usage(1); 307 } else { 308 cmd_show(av[1]); 309 } 310 } else { 311 fprintf(stderr, "Unrecognized command: %s\n", av[0]); 312 usage(1); 313 } 314 315 /* 316 * In DebugMode we may wind up starting several pthreads in the 317 * original process, in which case we have to let them run and 318 * not actually exit. 319 */ 320 if (NormalExit) { 321 return (ecode); 322 } else { 323 pthread_exit(NULL); 324 _exit(2); /* NOT REACHED */ 325 } 326 } 327 328 static 329 void 330 usage(int code) 331 { 332 fprintf(stderr, 333 "hammer2 [-s path] command...\n" 334 " -s path Select filesystem\n" 335 " -t type PFS type for pfs-create\n" 336 " -u uuid uuid for pfs-create\n" 337 "\n" 338 " connect <target> Add cluster link\n" 339 " disconnect <target> Del cluster link\n" 340 " status Report cluster status\n" 341 " pfs-list List PFSs\n" 342 " pfs-clid <label> Print cluster id for specific PFS\n" 343 " pfs-fsid <label> Print private id for specific PFS\n" 344 " pfs-create <label> Create a PFS\n" 345 " pfs-delete <label> Destroy a PFS\n" 346 " snapshot Snapshot a PFS\n" 347 " service Start service daemon\n" 348 " stat [<path>] Return inode quota & config\n" 349 " leaf Start pfs leaf daemon\n" 350 " shell [<host>] Connect to debug shell\n" 351 " debugspan <target> Connect to target, run CONN/SPAN\n" 352 " rsainit Initialize rsa fields\n" 353 " show devpath Raw hammer2 media dump\n" 354 ); 355 exit(code); 356 } 357