2 * Copyright (c) 2011-2019 The DragonFly Project. All rights reserved.
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>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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
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.
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
43 int NormalExit
= 1; /* if set to 0 main() has to pthread_exit() */
46 static void usage(int code
);
49 main(int ac
, char **av
)
51 char *sel_path
= NULL
;
52 const char *uuid_str
= NULL
;
55 int pfs_type
= HAMMER2_PFSTYPE_NONE
;
61 signal(SIGPIPE
, SIG_IGN
);
67 while ((ch
= getopt(ac
, av
, "adfm:rs:t:u:vq")) != -1) {
81 MemOpt
= strtoul(optarg
, &opt
, 0);
98 fprintf(stderr
, "-m: Unrecognized suffix\n");
107 sel_path
= strdup(optarg
);
111 * set node type for pfs-create
113 if (strcasecmp(optarg
, "CACHE") == 0) {
114 pfs_type
= HAMMER2_PFSTYPE_CACHE
;
115 } else if (strcasecmp(optarg
, "DUMMY") == 0) {
116 pfs_type
= HAMMER2_PFSTYPE_DUMMY
;
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
;
126 fprintf(stderr
, "-t: Unrecognized node type\n");
132 * set uuid for pfs-create, else one will be generated
133 * (required for all except the MASTER node_type)
150 fprintf(stderr
, "Unknown option: %c\n", ch
);
158 * Adjust, then process the command
163 fprintf(stderr
, "Missing command\n");
168 if (strcmp(av
[0], "connect") == 0) {
170 * Add cluster connection
173 fprintf(stderr
, "connect: missing argument\n");
176 ecode
= cmd_remote_connect(sel_path
, av
[1]);
177 } else if (strcmp(av
[0], "dumpchain") == 0) {
179 ecode
= cmd_dumpchain(".", (u_int
)-1);
181 ecode
= cmd_dumpchain(av
[1], (u_int
)-1);
183 ecode
= cmd_dumpchain(av
[1],
184 (u_int
)strtoul(av
[2], NULL
, 0));
185 } else if (strcmp(av
[0], "debugspan") == 0) {
187 * Debug connection to the target hammer2 service and run
188 * the CONN/SPAN protocol.
191 fprintf(stderr
, "debugspan: requires hostname\n");
194 ecode
= cmd_debugspan(av
[1]);
195 } else if (strcmp(av
[0], "disconnect") == 0) {
197 * Remove cluster connection
200 fprintf(stderr
, "disconnect: missing argument\n");
203 ecode
= cmd_remote_disconnect(sel_path
, av
[1]);
204 } else if (strcmp(av
[0], "destroy") == 0) {
207 "destroy: specify one or more paths to "
211 ecode
= cmd_destroy_path(ac
- 1, (const char **)(void *)&av
[1]);
212 } else if (strcmp(av
[0], "destroy-inum") == 0) {
215 "destroy-inum: specify one or more inode "
216 "numbers to destroy\n");
219 ecode
= cmd_destroy_inum(sel_path
, ac
- 1,
220 (const char **)(void *)&av
[1]);
221 } else if (strcmp(av
[0], "emergency-mode-enable") == 0) {
222 ecode
= cmd_emergency_mode(sel_path
, 1, ac
- 1,
223 (const char **)(void *)&av
[1]);
224 } else if (strcmp(av
[0], "emergency-mode-disable") == 0) {
225 ecode
= cmd_emergency_mode(sel_path
, 0, ac
- 1,
226 (const char **)(void *)&av
[1]);
227 } else if (strcmp(av
[0], "growfs") == 0) {
228 ecode
= cmd_growfs(sel_path
, ac
- 1,
229 (const char **)(void *)&av
[1]);
230 } else if (strcmp(av
[0], "hash") == 0) {
231 ecode
= cmd_hash(ac
- 1, (const char **)(void *)&av
[1]);
232 } else if (strcmp(av
[0], "dhash") == 0) {
233 ecode
= cmd_dhash(ac
- 1, (const char **)(void *)&av
[1]);
234 } else if (strcmp(av
[0], "info") == 0) {
235 ecode
= cmd_info(ac
- 1, (const char **)(void *)&av
[1]);
236 } else if (strcmp(av
[0], "mountall") == 0) {
237 ecode
= cmd_mountall(ac
- 1, (const char **)(void *)&av
[1]);
238 } else if (strcmp(av
[0], "status") == 0) {
240 * Get status of PFS and its connections (-a for all PFSs)
243 ecode
= cmd_remote_status(sel_path
, all_opt
);
246 for (i
= 1; i
< ac
; ++i
)
247 ecode
= cmd_remote_status(av
[i
], all_opt
);
249 } else if (strcmp(av
[0], "pfs-clid") == 0) {
251 * Print cluster id (uuid) for specific PFS
254 fprintf(stderr
, "pfs-clid: requires name\n");
257 ecode
= cmd_pfs_getid(sel_path
, av
[1], 0);
258 } else if (strcmp(av
[0], "pfs-fsid") == 0) {
260 * Print private id (uuid) for specific PFS
263 fprintf(stderr
, "pfs-fsid: requires name\n");
266 ecode
= cmd_pfs_getid(sel_path
, av
[1], 1);
267 } else if (strcmp(av
[0], "pfs-list") == 0) {
272 ecode
= cmd_pfs_list(ac
- 1,
273 (char **)(void *)&av
[1]);
275 ecode
= cmd_pfs_list(1, &sel_path
);
277 } else if (strcmp(av
[0], "pfs-create") == 0) {
279 * Create new PFS using pfs_type
282 fprintf(stderr
, "pfs-create: requires name\n");
285 ecode
= cmd_pfs_create(sel_path
, av
[1], pfs_type
, uuid_str
);
286 } else if (strcmp(av
[0], "pfs-delete") == 0) {
288 * Delete a PFS by name
291 fprintf(stderr
, "pfs-delete: requires name\n");
294 ecode
= cmd_pfs_delete(sel_path
, av
, ac
);
295 } else if (strcmp(av
[0], "recover") == 0 ||
296 strcmp(av
[0], "recover-relaxed") == 0 ||
297 strcmp(av
[0], "recover-file") == 0)
300 * Recover a relative path (unanchored match), absolute path,
301 * specific file, or directory sub-tree. File restorals are
305 fprintf(stderr
, "recover device [/]path destdir\n");
308 int strict
= (strcmp(av
[0], "recover-relaxed") != 0);
309 int isafile
= (strcmp(av
[0], "recover-file") == 0);
310 cmd_recover(av
[1], av
[2], av
[3], strict
, isafile
);
312 } else if (strcmp(av
[0], "snapshot") == 0 ||
313 strcmp(av
[0], "snapshot-debug") == 0) {
315 * Create snapshot with optional pfs-type and optional
320 if (strcmp(av
[0], "snapshot-debug") == 0)
321 flags
= HAMMER2_PFSFLAGS_NOSYNC
;
324 fprintf(stderr
, "%s: too many arguments\n", av
[0]);
329 ecode
= cmd_pfs_snapshot(sel_path
, NULL
, NULL
, flags
);
332 ecode
= cmd_pfs_snapshot(sel_path
, av
[1], NULL
, flags
);
335 ecode
= cmd_pfs_snapshot(sel_path
, av
[1], av
[2], flags
);
338 } else if (strcmp(av
[0], "service") == 0) {
340 * Start the service daemon. This daemon accepts
341 * connections from local and remote clients, handles
342 * the security handshake, and manages the core messaging
345 ecode
= cmd_service();
346 } else if (strcmp(av
[0], "stat") == 0) {
347 ecode
= cmd_stat(ac
- 1, (const char **)(void *)&av
[1]);
348 } else if (strcmp(av
[0], "leaf") == 0) {
350 * Start the management daemon for a specific PFS.
352 * This will typically connect to the local master node
353 * daemon, register the PFS, and then pass its side of
354 * the socket descriptor to the kernel HAMMER2 VFS via an
355 * ioctl(). The process and/or thread context remains in the
356 * kernel until the PFS is unmounted or the connection is
357 * lost, then returns from the ioctl.
359 * It is possible to connect directly to a remote master node
360 * instead of the local master node in situations where
361 * encryption is not desired or no local master node is
362 * desired. This is not recommended because it represents
363 * a single point of failure for the PFS's communications.
365 * Direct kernel<->kernel communication between HAMMER2 VFSs
366 * is theoretically possible for directly-connected
367 * registrations (i.e. where the spanning tree is degenerate),
368 * but not recommended. We specifically try to reduce the
369 * complexity of the HAMMER2 VFS kernel code.
371 ecode
= cmd_leaf(sel_path
);
372 } else if (strcmp(av
[0], "shell") == 0) {
374 * Connect to the command line monitor in the hammer2 master
375 * node for the machine using HAMMER2_DBG_SHELL messages.
377 ecode
= cmd_shell((ac
< 2) ? NULL
: av
[1]);
378 } else if (strcmp(av
[0], "rsainit") == 0) {
380 * Initialize a RSA keypair. If no target directory is
381 * specified we default to "/etc/hammer2".
383 arg
= (ac
< 2) ? HAMMER2_DEFAULT_DIR
: av
[1];
384 ecode
= cmd_rsainit(arg
);
385 } else if (strcmp(av
[0], "rsaenc") == 0) {
387 * Encrypt the input symmetrically by running it through
388 * the specified public and/or private key files.
390 * If no key files are specified data is encoded using
391 * "/etc/hammer2/rsa.pub".
393 * WARNING: no padding is added, data stream must contain
394 * random padding for this to be secure.
396 * Used for debugging only
399 const char *rsapath
= HAMMER2_DEFAULT_DIR
"/rsa.pub";
400 ecode
= cmd_rsaenc(&rsapath
, 1);
402 ecode
= cmd_rsaenc((const char **)(void *)&av
[1],
405 } else if (strcmp(av
[0], "rsadec") == 0) {
407 * Decrypt the input symmetrically by running it through
408 * the specified public and/or private key files.
410 * If no key files are specified data is decoded using
411 * "/etc/hammer2/rsa.prv".
413 * WARNING: no padding is added, data stream must contain
414 * random padding for this to be secure.
416 * Used for debugging only
419 const char *rsapath
= HAMMER2_DEFAULT_DIR
"/rsa.prv";
420 ecode
= cmd_rsadec(&rsapath
, 1);
422 ecode
= cmd_rsadec((const char **)(void *)&av
[1],
425 } else if (strcmp(av
[0], "show") == 0) {
427 * Raw dump of filesystem. Use -v to check all crc's, and
428 * -vv to dump bulk file data.
431 fprintf(stderr
, "show: requires device path\n");
436 } else if (strcmp(av
[0], "freemap") == 0) {
438 * Raw dump of freemap. Use -v to check all crc's, and
439 * -vv to dump bulk file data.
442 fprintf(stderr
, "freemap: requires device path\n");
447 } else if (strcmp(av
[0], "volhdr") == 0) {
449 * Dump the volume header.
452 fprintf(stderr
, "volhdr: requires device path\n");
457 } else if (strcmp(av
[0], "volume-list") == 0) {
462 ecode
= cmd_volume_list(ac
- 1,
463 (char **)(void *)&av
[1]);
465 ecode
= cmd_volume_list(1, &sel_path
);
467 } else if (strcmp(av
[0], "setcomp") == 0) {
470 * Missing compression method and at least one
474 "setcomp: requires compression method and "
475 "directory/file path\n");
479 * Multiple paths may be specified
481 ecode
= cmd_setcomp(av
[1], &av
[2]);
483 } else if (strcmp(av
[0], "setcheck") == 0) {
486 * Missing compression method and at least one
490 "setcheck: requires check code method and "
491 "directory/file path\n");
495 * Multiple paths may be specified
497 ecode
= cmd_setcheck(av
[1], &av
[2]);
499 } else if (strcmp(av
[0], "clrcheck") == 0) {
500 ecode
= cmd_setcheck("none", &av
[1]);
501 } else if (strcmp(av
[0], "setcrc32") == 0) {
502 ecode
= cmd_setcheck("crc32", &av
[1]);
503 } else if (strcmp(av
[0], "setxxhash64") == 0) {
504 ecode
= cmd_setcheck("xxhash64", &av
[1]);
505 } else if (strcmp(av
[0], "setsha192") == 0) {
506 ecode
= cmd_setcheck("sha192", &av
[1]);
507 } else if (strcmp(av
[0], "printinode") == 0) {
510 "printinode: requires directory/file path\n");
515 } else if (strcmp(av
[0], "bulkfree") == 0) {
517 fprintf(stderr
, "bulkfree: requires path to mount\n");
520 ecode
= cmd_bulkfree(av
[1]);
523 } else if (strcmp(av
[0], "bulkfree-async") == 0) {
526 "bulkfree-async: requires path to mount\n");
529 ecode
= cmd_bulkfree_async(av
[1]);
532 } else if (strcmp(av
[0], "cleanup") == 0) {
533 ecode
= cmd_cleanup(av
[1]); /* can be NULL */
535 fprintf(stderr
, "Unrecognized command: %s\n", av
[0]);
540 * In DebugMode we may wind up starting several pthreads in the
541 * original process, in which case we have to let them run and
548 _exit(2); /* NOT REACHED */
557 "hammer2 [options] command [argument ...]\n"
558 " -s path Select filesystem\n"
559 " -t type PFS type for pfs-create\n"
560 " -u uuid uuid for pfs-create\n"
561 " -m mem[k,m,g] buffer memory (bulkfree)\n"
564 "Run cleanup passes\n"
567 " destroy <path>... "
568 "Destroy directory entries (only use if inode bad)\n"
569 " destroy-inum <inum>... "
570 "Destroy inodes (only use if inode bad)\n"
571 " disconnect <target> "
573 " emergency-mode-enable <target> "
574 "Enable emergency operations mode on filesystem\n"
576 "THIS IS A VERY DANGEROUS MODE\n"
577 " emergency-mode-disable <target> "
578 "Disable emergency operations mode on filesystem\n"
579 " info [<devpath>...] "
580 "Info on all offline or online H2 partitions\n"
581 " mountall [<devpath>...] "
582 "Mount @LOCAL for all H2 partitions\n"
583 " status [<path>...] "
584 "Report active cluster status\n"
585 " hash [<filename>...] "
586 "Print directory hash (key) for name\n"
587 " dhash [<filename>...] "
588 "Print data hash for long directory entry\n"
589 " pfs-list [<path>...] "
592 "Print cluster id for specific PFS\n"
594 "Print private id for specific PFS\n"
595 " pfs-create <label> "
597 " pfs-delete <label> "
599 " recover <devpath> <path> <destdir> "
600 "Recover deleted or corrupt files or trees\n"
601 " recover-relaxed <devpath> <path> <destdir> "
602 "Recover deleted or corrupt files or trees\n"
603 " recover-file <devpath> <path> <destdir> "
604 "Recover, target is explicitly a regular file\n"
605 " snapshot <path> [<label>] "
606 "Snapshot a PFS or directory\n"
607 " snapshot-debug <path> [<label>] "
608 "Snapshot without filesystem sync\n"
610 "Start service daemon\n"
612 "Return inode quota & config\n"
614 "Start pfs leaf daemon\n"
616 "Connect to debug shell\n"
617 " debugspan <target> "
618 "Connect to target, run CONN/SPAN\n"
619 " growfs [<path>...] "
620 "Grow a filesystem into resized partition\n"
622 "Initialize rsa fields\n"
624 "Raw hammer2 media dump for topology\n"
625 " freemap <devpath> "
626 "Raw hammer2 media dump for freemap\n"
628 "Raw hammer2 media dump for the volume header(s)\n"
629 " volume-list [<path>...] "
631 " setcomp <comp[:level]> <path>... "
632 "Set comp algo {none, autozero, lz4, zlib} & level\n"
633 " setcheck <check> <path>... "
634 "Set check algo {none, crc32, xxhash64, sha192}\n"
635 " clrcheck [<path>...] "
636 "Clear check code override\n"
637 " setcrc32 [<path>...] "
638 "Set check algo to crc32\n"
639 " setxxhash64 [<path>...] "
640 "Set check algo to xxhash64\n"
641 " setsha192 [<path>...] "
642 "Set check algo to sha192\n"
644 "Run bulkfree pass\n"
645 " printinode <path> "
647 " dumpchain [<path> [<chnflags>]] "
648 "Dump in-memory chain topology (ONFLUSH flag is 0x200)\n"
650 " bulkfree-async path "
651 "Run bulkfree pass asynchronously\n"