examples: Fix build
[dragonfly.git] / sbin / hammer2 / main.c
CommitLineData
2910a90c
MD
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
5ba65e34 38int DebugOpt;
bd878fc9 39int VerboseOpt;
9b8b748f 40int QuietOpt;
5ba65e34 41int NormalExit = 1; /* if set to 0 main() has to pthread_exit() */
f481450f
MD
42int RecurseOpt;
43int ForceOpt;
944ddad0 44size_t MemOpt;
f481450f
MD
45
46static void usage(int code);
5ba65e34 47
2910a90c
MD
48int
49main(int ac, char **av)
50{
5ba65e34 51 const char *sel_path = NULL;
2910a90c 52 const char *uuid_str = NULL;
62efe6ec 53 const char *arg;
944ddad0 54 char *opt;
7750fd72 55 int pfs_type = HAMMER2_PFSTYPE_NONE;
2910a90c
MD
56 int all_opt = 0;
57 int ecode = 0;
58 int ch;
59
62efe6ec 60 srandomdev();
7dc0f844 61 signal(SIGPIPE, SIG_IGN);
0c3a8cd0 62 dmsg_crypto_setup();
62efe6ec 63
2910a90c
MD
64 /*
65 * Core options
66 */
944ddad0 67 while ((ch = getopt(ac, av, "adfm:rqs:t:u:v")) != -1) {
2910a90c
MD
68 switch(ch) {
69 case 'a':
70 all_opt = 1;
71 break;
bd878fc9 72 case 'd':
78b00acd 73 if (DebugOpt)
d157d4dc 74 ++DMsgDebugOpt;
bd878fc9
MD
75 DebugOpt = 1;
76 break;
f481450f
MD
77 case 'f':
78 ForceOpt = 1;
79 break;
944ddad0
MD
80 case 'm':
81 MemOpt = strtoul(optarg, &opt, 0);
82 switch(*opt) {
83 case 'g':
84 case 'G':
85 MemOpt *= 1024;
86 /* FALLTHROUGH */
87 case 'm':
88 case 'M':
89 MemOpt *= 1024;
90 /* FALLTHROUGH */
91 case 'k':
92 case 'K':
93 MemOpt *= 1024;
94 break;
95 case 0:
96 break;
97 default:
98 fprintf(stderr, "-m: unrecognized suffix\n");
99 usage(1);
100 break;
101 }
102 break;
f481450f
MD
103 case 'r':
104 RecurseOpt = 1;
105 break;
2910a90c
MD
106 case 's':
107 sel_path = optarg;
108 break;
109 case 't':
110 /*
111 * set node type for mkpfs
112 */
afa78c43 113 if (strcasecmp(optarg, "CACHE") == 0) {
2910a90c 114 pfs_type = HAMMER2_PFSTYPE_CACHE;
b93cc2e0
MD
115 } else if (strcasecmp(optarg, "DUMMY") == 0) {
116 pfs_type = HAMMER2_PFSTYPE_DUMMY;
2910a90c
MD
117 } else if (strcasecmp(optarg, "SLAVE") == 0) {
118 pfs_type = HAMMER2_PFSTYPE_SLAVE;
119 } else if (strcasecmp(optarg, "SOFT_SLAVE") == 0) {
120 pfs_type = HAMMER2_PFSTYPE_SOFT_SLAVE;
121 } else if (strcasecmp(optarg, "SOFT_MASTER") == 0) {
122 pfs_type = HAMMER2_PFSTYPE_SOFT_MASTER;
123 } else if (strcasecmp(optarg, "MASTER") == 0) {
124 pfs_type = HAMMER2_PFSTYPE_MASTER;
125 } else {
126 fprintf(stderr, "-t: Unrecognized node type\n");
127 usage(1);
128 }
129 break;
130 case 'u':
131 /*
132 * set uuid for mkpfs, else one will be generated
133 * (required for all except the MASTER node_type)
134 */
135 uuid_str = optarg;
136 break;
bd878fc9 137 case 'v':
9b8b748f
MD
138 if (QuietOpt)
139 --QuietOpt;
140 else
141 ++VerboseOpt;
142 break;
143 case 'q':
144 if (VerboseOpt)
145 --VerboseOpt;
146 else
147 ++QuietOpt;
5ba65e34 148 break;
2910a90c
MD
149 default:
150 fprintf(stderr, "Unknown option: %c\n", ch);
151 usage(1);
152 /* not reached */
153 break;
154 }
155 }
156
157 /*
158 * Adjust, then process the command
159 */
160 ac -= optind;
161 av += optind;
162 if (ac < 1) {
163 fprintf(stderr, "Missing command\n");
164 usage(1);
165 /* not reached */
166 }
167
168 if (strcmp(av[0], "connect") == 0) {
169 /*
170 * Add cluster connection
171 */
172 if (ac < 2) {
173 fprintf(stderr, "connect: missing argument\n");
174 usage(1);
175 }
176 ecode = cmd_remote_connect(sel_path, av[1]);
40498d1c 177 } else if (strcmp(av[0], "dumpchain") == 0) {
8f579e46 178 if (ac < 2)
40498d1c
MD
179 ecode = cmd_dumpchain(".", (u_int)-1);
180 else if (ac < 3)
181 ecode = cmd_dumpchain(av[1], (u_int)-1);
8f579e46 182 else
40498d1c
MD
183 ecode = cmd_dumpchain(av[1],
184 (u_int)strtoul(av[2], NULL, 0));
02454b3e
MD
185 } else if (strcmp(av[0], "debugspan") == 0) {
186 /*
187 * Debug connection to the target hammer2 service and run
188 * the CONN/SPAN protocol.
189 */
190 if (ac < 2) {
191 fprintf(stderr, "debugspan: requires hostname\n");
192 usage(1);
193 }
194 ecode = cmd_debugspan(av[1]);
2910a90c
MD
195 } else if (strcmp(av[0], "disconnect") == 0) {
196 /*
197 * Remove cluster connection
198 */
199 if (ac < 2) {
200 fprintf(stderr, "disconnect: missing argument\n");
201 usage(1);
202 }
203 ecode = cmd_remote_disconnect(sel_path, av[1]);
f6aebb44
MD
204 } else if (strcmp(av[0], "destroy") == 0) {
205 if (ac < 2) {
206 fprintf(stderr,
207 "Specify one or more paths to destroy\n");
208 }
209 ecode = cmd_destroy_path(ac - 1, (const char **)(void *)&av[1]);
ead47bfa
MD
210 } else if (strcmp(av[0], "destroy-inum") == 0) {
211 if (ac < 2) {
212 fprintf(stderr,
213 "Specify one or more inode numbers to destroy\n");
214 }
215 ecode = cmd_destroy_inum(sel_path, ac - 1, (const char **)(void *)&av[1]);
775153c2
MD
216 } else if (strcmp(av[0], "hash") == 0) {
217 ecode = cmd_hash(ac - 1, (const char **)(void *)&av[1]);
b92bfd39
MD
218 } else if (strcmp(av[0], "info") == 0) {
219 ecode = cmd_info(ac - 1, (const char **)(void *)&av[1]);
220 } else if (strcmp(av[0], "mountall") == 0) {
221 ecode = cmd_mountall(ac - 1, (const char **)(void *)&av[1]);
2910a90c
MD
222 } else if (strcmp(av[0], "status") == 0) {
223 /*
224 * Get status of PFS and its connections (-a for all PFSs)
225 */
3c198419
MD
226 if (ac < 2) {
227 ecode = cmd_remote_status(sel_path, all_opt);
228 } else {
229 int i;
230
231 for (i = 1; i < ac; ++i)
232 ecode = cmd_remote_status(av[i], all_opt);
233 }
7dc0f844
MD
234 } else if (strcmp(av[0], "pfs-clid") == 0) {
235 /*
236 * Print cluster id (uuid) for specific PFS
237 */
238 if (ac < 2) {
239 fprintf(stderr, "pfs-clid: requires name\n");
240 usage(1);
241 }
242 ecode = cmd_pfs_getid(sel_path, av[1], 0);
243 } else if (strcmp(av[0], "pfs-fsid") == 0) {
244 /*
245 * Print private id (uuid) for specific PFS
246 */
247 if (ac < 2) {
248 fprintf(stderr, "pfs-fsid: requires name\n");
249 usage(1);
250 }
251 ecode = cmd_pfs_getid(sel_path, av[1], 1);
bd878fc9 252 } else if (strcmp(av[0], "pfs-list") == 0) {
ae183399
MD
253 /*
254 * List all PFSs
255 */
b92bfd39
MD
256 if (ac >= 2) {
257 ecode = cmd_pfs_list(ac - 1,
258 (const char **)(void *)&av[1]);
259 } else {
260 ecode = cmd_pfs_list(1, &sel_path);
d0ceb671 261 }
bd878fc9 262 } else if (strcmp(av[0], "pfs-create") == 0) {
2910a90c
MD
263 /*
264 * Create new PFS using pfs_type
265 */
ae183399 266 if (ac < 2) {
bd878fc9 267 fprintf(stderr, "pfs-create: requires name\n");
ae183399
MD
268 usage(1);
269 }
270 ecode = cmd_pfs_create(sel_path, av[1], pfs_type, uuid_str);
bd878fc9 271 } else if (strcmp(av[0], "pfs-delete") == 0) {
ae183399
MD
272 /*
273 * Delete a PFS by name
274 */
275 if (ac < 2) {
bd878fc9 276 fprintf(stderr, "pfs-delete: requires name\n");
ae183399
MD
277 usage(1);
278 }
279 ecode = cmd_pfs_delete(sel_path, av[1]);
2910a90c
MD
280 } else if (strcmp(av[0], "snapshot") == 0) {
281 /*
bd878fc9 282 * Create snapshot with optional pfs-type and optional
2910a90c
MD
283 * label override.
284 */
d0ceb671 285 if (ac > 3) {
87b1094e
MD
286 fprintf(stderr, "pfs-snapshot: too many arguments\n");
287 usage(1);
288 }
d0ceb671
MD
289 switch(ac) {
290 case 1:
291 ecode = cmd_pfs_snapshot(sel_path, NULL, NULL);
292 break;
293 case 2:
294 ecode = cmd_pfs_snapshot(sel_path, av[1], NULL);
295 break;
296 case 3:
297 ecode = cmd_pfs_snapshot(sel_path, av[1], av[2]);
298 break;
299 }
62efe6ec 300 } else if (strcmp(av[0], "service") == 0) {
2910a90c 301 /*
62efe6ec
MD
302 * Start the service daemon. This daemon accepts
303 * connections from local and remote clients, handles
304 * the security handshake, and manages the core messaging
305 * protocol.
2910a90c 306 */
62efe6ec 307 ecode = cmd_service();
ad7cf8ea 308 } else if (strcmp(av[0], "stat") == 0) {
a1b07447 309 ecode = cmd_stat(ac - 1, (const char **)(void *)&av[1]);
9ab15106
MD
310 } else if (strcmp(av[0], "leaf") == 0) {
311 /*
312 * Start the management daemon for a specific PFS.
313 *
314 * This will typically connect to the local master node
315 * daemon, register the PFS, and then pass its side of
316 * the socket descriptor to the kernel HAMMER2 VFS via an
317 * ioctl(). The process and/or thread context remains in the
318 * kernel until the PFS is unmounted or the connection is
319 * lost, then returns from the ioctl.
320 *
321 * It is possible to connect directly to a remote master node
322 * instead of the local master node in situations where
323 * encryption is not desired or no local master node is
324 * desired. This is not recommended because it represents
325 * a single point of failure for the PFS's communications.
326 *
327 * Direct kernel<->kernel communication between HAMMER2 VFSs
328 * is theoretically possible for directly-connected
329 * registrations (i.e. where the spanning tree is degenerate),
330 * but not recommended. We specifically try to reduce the
331 * complexity of the HAMMER2 VFS kernel code.
332 */
333 ecode = cmd_leaf(sel_path);
bd878fc9 334 } else if (strcmp(av[0], "shell") == 0) {
9ab15106
MD
335 /*
336 * Connect to the command line monitor in the hammer2 master
337 * node for the machine using HAMMER2_DBG_SHELL messages.
338 */
bd878fc9 339 ecode = cmd_shell((ac < 2) ? NULL : av[1]);
62efe6ec
MD
340 } else if (strcmp(av[0], "rsainit") == 0) {
341 /*
342 * Initialize a RSA keypair. If no target directory is
343 * specified we default to "/etc/hammer2".
344 */
345 arg = (ac < 2) ? HAMMER2_DEFAULT_DIR : av[1];
346 ecode = cmd_rsainit(arg);
347 } else if (strcmp(av[0], "rsaenc") == 0) {
348 /*
349 * Encrypt the input symmetrically by running it through
350 * the specified public and/or private key files.
351 *
352 * If no key files are specified data is encoded using
353 * "/etc/hammer2/rsa.pub".
354 *
355 * WARNING: no padding is added, data stream must contain
356 * random padding for this to be secure.
357 *
358 * Used for debugging only
359 */
360 if (ac == 1) {
361 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.pub";
362 ecode = cmd_rsaenc(&rsapath, 1);
363 } else {
a1b07447
MD
364 ecode = cmd_rsaenc((const char **)(void *)&av[1],
365 ac - 1);
62efe6ec
MD
366 }
367 } else if (strcmp(av[0], "rsadec") == 0) {
368 /*
369 * Decrypt the input symmetrically by running it through
370 * the specified public and/or private key files.
371 *
372 * If no key files are specified data is decoded using
373 * "/etc/hammer2/rsa.prv".
374 *
375 * WARNING: no padding is added, data stream must contain
376 * random padding for this to be secure.
377 *
378 * Used for debugging only
379 */
380 if (ac == 1) {
381 const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.prv";
382 ecode = cmd_rsadec(&rsapath, 1);
383 } else {
a1b07447
MD
384 ecode = cmd_rsadec((const char **)(void *)&av[1],
385 ac - 1);
62efe6ec 386 }
bd878fc9
MD
387 } else if (strcmp(av[0], "show") == 0) {
388 /*
389 * Raw dump of filesystem. Use -v to check all crc's, and
390 * -vv to dump bulk file data.
391 */
392 if (ac != 2) {
393 fprintf(stderr, "show: requires device path\n");
394 usage(1);
395 } else {
78ac5385
MD
396 cmd_show(av[1], 0);
397 }
398 } else if (strcmp(av[0], "freemap") == 0) {
399 /*
400 * Raw dump of freemap. Use -v to check all crc's, and
401 * -vv to dump bulk file data.
402 */
403 if (ac != 2) {
404 fprintf(stderr, "freemap: requires device path\n");
405 usage(1);
406 } else {
407 cmd_show(av[1], 1);
bd878fc9 408 }
355d67fc 409 } else if (strcmp(av[0], "setcomp") == 0) {
f481450f
MD
410 if (ac < 3) {
411 /*
412 * Missing compression method and at least one
413 * path.
414 */
415 fprintf(stderr,
416 "setcomp: requires compression method and"
355d67fc
MD
417 "directory/file path\n");
418 usage(1);
419 } else {
f481450f
MD
420 /*
421 * Multiple paths may be specified
422 */
423 ecode = cmd_setcomp(av[1], &av[2]);
355d67fc 424 }
8adee7de
MD
425 } else if (strcmp(av[0], "setcheck") == 0) {
426 if (ac < 3) {
427 /*
428 * Missing compression method and at least one
429 * path.
430 */
431 fprintf(stderr,
432 "setcheck: requires check code method and"
433 "directory/file path\n");
434 usage(1);
435 } else {
436 /*
437 * Multiple paths may be specified
438 */
439 ecode = cmd_setcheck(av[1], &av[2]);
440 }
441 } else if (strcmp(av[0], "clrcheck") == 0) {
442 ecode = cmd_setcheck("none", &av[1]);
443 } else if (strcmp(av[0], "setcrc32") == 0) {
444 ecode = cmd_setcheck("crc32", &av[1]);
b83c55fc
MD
445 } else if (strcmp(av[0], "setxxhash64") == 0) {
446 ecode = cmd_setcheck("xxhash64", &av[1]);
8adee7de
MD
447 } else if (strcmp(av[0], "setsha192") == 0) {
448 ecode = cmd_setcheck("sha192", &av[1]);
355d67fc
MD
449 } else if (strcmp(av[0], "printinode") == 0) {
450 if (ac != 2) {
f481450f
MD
451 fprintf(stderr,
452 "printinode: requires directory/file path\n");
355d67fc 453 usage(1);
00261fe0 454 } else {
355d67fc 455 print_inode(av[1]);
00261fe0
MD
456 }
457 } else if (strcmp(av[0], "bulkfree") == 0) {
458 if (ac != 2) {
459 fprintf(stderr,
460 "bulkfree: requires path to mount\n");
461 usage(1);
462 } else {
463 ecode = cmd_bulkfree(av[1]);
464 }
b83c55fc 465#if 0
9dca9515
MD
466 } else if (strcmp(av[0], "bulkfree-async") == 0) {
467 if (ac != 2) {
468 fprintf(stderr,
469 "bulkfree-async: requires path to mount\n");
470 usage(1);
471 } else {
472 ecode = cmd_bulkfree_async(av[1]);
473 }
b83c55fc 474#endif
30ce27d4
MD
475 } else if (strcmp(av[0], "cleanup") == 0) {
476 ecode = cmd_cleanup(av[1]); /* can be NULL */
2910a90c
MD
477 } else {
478 fprintf(stderr, "Unrecognized command: %s\n", av[0]);
479 usage(1);
480 }
5ba65e34
MD
481
482 /*
483 * In DebugMode we may wind up starting several pthreads in the
484 * original process, in which case we have to let them run and
485 * not actually exit.
486 */
487 if (NormalExit) {
488 return (ecode);
489 } else {
490 pthread_exit(NULL);
491 _exit(2); /* NOT REACHED */
492 }
2910a90c
MD
493}
494
495static
496void
497usage(int code)
498{
499 fprintf(stderr,
d0ceb671 500 "hammer2 [options] command...\n"
2910a90c 501 " -s path Select filesystem\n"
bd878fc9
MD
502 " -t type PFS type for pfs-create\n"
503 " -u uuid uuid for pfs-create\n"
944ddad0 504 " -m mem[k,m,g] buffer memory (bulkfree)\n"
bd878fc9 505 "\n"
4aedc17b 506 " cleanup [<path>...] "
507 "Run cleanup passes\n"
f481450f
MD
508 " connect <target> "
509 "Add cluster link\n"
f8a108fb
MD
510 " debugspan <target> "
511 "Debug spanning tree\n"
40498d1c
MD
512 " dumpchain <path> [chnflags] "
513 "Dump in-memory chain topo from\n"
514 "NOTE: ONFLUSH flag is 0x200\n"
4aedc17b 515 " destroy <path>* "
ead47bfa
MD
516 "Destroy directory entries (only use if inode bad)\n"
517 " destroy-inum <inum>* "
518 "Destroy inodes (only use if inode bad)\n"
f481450f
MD
519 " disconnect <target> "
520 "Del cluster link\n"
521 " hash filename* "
522 "Print directory hash\n"
b92bfd39
MD
523 " info [devpath...] "
524 "Info on all offline or online H2 partitions\n"
525 " mountall [devpath...] "
526 "Mount @LOCAL for all H2 partitions\n"
3c198419 527 " status [<path>...] "
b92bfd39
MD
528 "Report active cluster status\n"
529 " pfs-list [<path>...] "
f481450f
MD
530 "List PFSs\n"
531 " pfs-clid <label> "
532 "Print cluster id for specific PFS\n"
533 " pfs-fsid <label> "
534 "Print private id for specific PFS\n"
535 " pfs-create <label> "
536 "Create a PFS\n"
537 " pfs-delete <label> "
538 "Destroy a PFS\n"
4aedc17b 539 " snapshot <path> [<label>] "
d0ceb671 540 "Snapshot a PFS or directory\n"
f481450f
MD
541 " service "
542 "Start service daemon\n"
4aedc17b 543 " stat [<path>] "
f481450f
MD
544 "Return inode quota & config\n"
545 " leaf "
546 "Start pfs leaf daemon\n"
547 " shell [<host>] "
548 "Connect to debug shell\n"
549 " debugspan <target> "
550 "Connect to target, run CONN/SPAN\n"
551 " rsainit "
552 "Initialize rsa fields\n"
553 " show devpath "
554 "Raw hammer2 media dump\n"
555 " freemap devpath "
556 "Raw hammer2 media dump\n"
8adee7de
MD
557 " setcomp comp[:level] path... "
558 "Set comp algo {none, autozero, lz4, zlib} & level\n"
559 " setcheck check path... "
b83c55fc 560 "Set check algo {none, crc32, xxhash64, sha192}\n"
3c198419
MD
561 " clrcheck path... "
562 "Clear check code override\n"
8adee7de
MD
563 " setcrc32 path... "
564 "Set check algo to crc32\n"
b83c55fc
MD
565 " setxxhash64 path... "
566 "Set check algo to xxhash64\n"
8adee7de
MD
567 " setsha192 path... "
568 "Set check algo to sha192\n"
00261fe0
MD
569 " bulkfree path... "
570 "Run bulkfree pass\n"
b83c55fc 571#if 0
4aedc17b 572 " bulkfree-async path... "
9dca9515 573 "Run bulkfree pass asynchronously\n"
b83c55fc 574#endif
2910a90c
MD
575 );
576 exit(code);
577}