2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.10 2008/02/05 20:49:50 dillon Exp $
37 * This utility implements the userland mountctl command which is used to
38 * manage high level journaling on mount points.
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/ucred.h>
44 #include <sys/mount.h>
46 #include <sys/mountctl.h>
54 static void usage(void);
55 static void parse_option_keyword(const char *opt
,
56 const char **wopt
, const char **xopt
);
57 static int64_t getsize(const char *str
);
58 static const char *numtostr(int64_t num
);
60 static int mountctl_scan(void (*func
)(const char *, const char *, int, void *),
61 const char *keyword
, const char *mountpt
, int fd
);
62 static void mountctl_list(const char *keyword
, const char *mountpt
,
63 int __unused fd
, void *info
);
64 static void mountctl_add(const char *keyword
, const char *mountpt
, int fd
);
65 static void mountctl_restart(const char *keyword
, const char *mountpt
,
66 int fd
, void __unused
*);
67 static void mountctl_delete(const char *keyword
, const char *mountpt
,
68 int __unused fd
, void __unused
*);
69 static void mountctl_modify(const char *keyword
, const char *mountpt
, int fd
, void __unused
*);
72 * For all options 0 means unspecified, -1 means noOPT or nonOPT, and a
73 * positive number indicates enabling or execution of the option.
76 static int freeze_opt
;
81 static int reversable_opt
;
82 static int twoway_opt
;
83 static int output_safety_override_opt
;
84 static int64_t memfifo_opt
;
85 static int64_t swapfifo_opt
;
88 main(int ac
, char **av
)
99 const char *wopt
= NULL
;
100 const char *xopt
= NULL
;
101 const char *keyword
= NULL
;
102 const char *mountpt
= NULL
;
105 while ((ch
= getopt(ac
, av
, "2adflmo:rw:x:ACFSW:X:Z")) != -1) {
112 if (aopt
+ dopt
+ lopt
+ mopt
+ ropt
!= 1) {
113 fprintf(stderr
, "too many action options specified\n");
119 if (aopt
+ dopt
+ lopt
+ mopt
+ ropt
!= 1) {
120 fprintf(stderr
, "too many action options specified\n");
126 if (aopt
+ dopt
+ lopt
+ mopt
+ ropt
!= 1) {
127 fprintf(stderr
, "too many action options specified\n");
136 if (aopt
+ dopt
+ lopt
+ mopt
+ ropt
!= 1) {
137 fprintf(stderr
, "too many action options specified\n");
142 parse_option_keyword(optarg
, &wopt
, &xopt
);
146 if (aopt
+ dopt
+ lopt
+ mopt
+ ropt
!= 1) {
147 fprintf(stderr
, "too many action options specified\n");
152 output_safety_override_opt
= 1;
159 output_safety_override_opt
= 1;
186 fprintf(stderr
, "unknown option: -%c\n", optopt
);
194 * Parse the keyword and/or mount point.
199 fprintf(stderr
, "action requires a tag and/or mount "
200 "point to be specified\n");
205 if (av
[0][0] == '/') {
207 if ((keyword
= strchr(mountpt
, ':')) != NULL
) {
209 tmp
= strdup(mountpt
);
210 *strchr(tmp
, ':') = 0;
218 fprintf(stderr
, "unexpected extra arguments to command\n");
223 * Additional sanity checks
225 if (aopt
+ dopt
+ lopt
+ mopt
+ ropt
+ mimplied
== 0) {
226 fprintf(stderr
, "no action or implied action options were specified\n");
229 if (mimplied
&& aopt
+ dopt
+ lopt
+ ropt
== 0)
231 if ((wopt
|| xopt
) && !(aopt
|| ropt
|| mopt
)) {
232 fprintf(stderr
, "-w/-x/path/fd options may only be used with -m/-a/-r\n");
235 if (aopt
&& (keyword
== NULL
|| mountpt
== NULL
)) {
236 fprintf(stderr
, "a keyword AND a mountpt must be specified "
237 "when adding a journal\n");
240 if (fopt
== 0 && mopt
+ dopt
&& keyword
== NULL
&& mountpt
== NULL
) {
241 fprintf(stderr
, "a keyword, a mountpt, or both must be specified "
242 "when modifying or deleting a journal, unless "
243 "-f is also specified for safety\n");
248 * Open the journaling file descriptor if required.
251 fprintf(stderr
, "you must specify only one of -w/-x/path/fd\n");
254 if ((fd
= open(wopt
, O_RDWR
|O_CREAT
|O_APPEND
, 0666)) < 0) {
255 fprintf(stderr
, "unable to create %s: %s\n", wopt
, strerror(errno
));
259 fd
= strtol(xopt
, NULL
, 0);
260 } else if (aopt
|| ropt
) {
261 fd
= 1; /* stdout default for -a */
267 * And finally execute the core command.
270 mountctl_scan(mountctl_list
, keyword
, mountpt
, fd
);
272 mountctl_add(keyword
, mountpt
, fd
);
274 ch
= mountctl_scan(mountctl_restart
, keyword
, mountpt
, fd
);
276 fprintf(stderr
, "%d journals restarted\n", ch
);
278 fprintf(stderr
, "Unable to locate any matching journals\n");
281 ch
= mountctl_scan(mountctl_delete
, keyword
, mountpt
, -1);
283 fprintf(stderr
, "%d journals deleted\n", ch
);
285 fprintf(stderr
, "Unable to locate any matching journals\n");
288 ch
= mountctl_scan(mountctl_modify
, keyword
, mountpt
, fd
);
290 fprintf(stderr
, "%d journals modified\n", ch
);
292 fprintf(stderr
, "Unable to locate any matching journals\n");
299 parse_option_keyword(const char *opt
, const char **wopt
, const char **xopt
)
301 char *str
= strdup(opt
);
309 * multiple comma delimited options may be specified.
311 while ((name
= strsep(&str
, ",")) != NULL
) {
313 * some options have associated data.
315 if ((val
= strchr(name
, '=')) != NULL
)
319 * options beginning with 'no' or 'non' are negated. A positive
320 * number means not negated, a negative number means negated.
325 if (strncmp(name
, "non", 3) == 0) {
328 } else if (strncmp(name
, "no", 2) == 0) {
334 * Parse supported options
336 if (strcmp(name
, "undo") == 0) {
337 reversable_opt
= negate
;
338 } else if (strcmp(name
, "reversable") == 0) {
339 reversable_opt
= negate
;
340 } else if (strcmp(name
, "twoway") == 0) {
342 } else if (strcmp(name
, "memfifo") == 0) {
346 if ((memfifo_opt
= getsize(val
)) == 0)
349 } else if (strcmp(name
, "swapfifo") == 0) {
352 if ((swapfifo_opt
= getsize(val
)) == 0)
354 } else if (negate
< 0) {
357 hasval
= 1; /* force error */
359 } else if (strcmp(name
, "fd") == 0) {
364 } else if (strcmp(name
, "path") == 0) {
369 } else if (strcmp(name
, "freeze") == 0 || strcmp(name
, "stop") == 0) {
374 } else if (strcmp(name
, "start") == 0) {
376 freeze_opt
= -negate
;
379 } else if (strcmp(name
, "close") == 0) {
381 } else if (strcmp(name
, "abort") == 0) {
383 } else if (strcmp(name
, "flush") == 0) {
386 fprintf(stderr
, "unknown option keyword: %s\n", name
);
393 if (cannotnegate
&& negate
< 0) {
394 fprintf(stderr
, "option %s may not be negated\n", name
);
397 if (hasval
&& val
== NULL
) {
398 fprintf(stderr
, "option %s requires assigned data\n", name
);
401 if (hasval
== 0 && val
) {
402 fprintf(stderr
, "option %s does not take an assignment\n", name
);
410 mountctl_scan(void (*func
)(const char *, const char *, int, void *),
411 const char *keyword
, const char *mountpt
, int fd
)
417 struct mountctl_status_journal statreq
;
418 struct mountctl_journal_ret_status rstat
[4]; /* BIG */
422 bzero(&statreq
, sizeof(statreq
));
424 statreq
.index
= MC_JOURNAL_INDEX_ID
;
425 count
= strlen(keyword
);
428 bcopy(keyword
, statreq
.id
, count
);
430 statreq
.index
= MC_JOURNAL_INDEX_ALL
;
432 count
= mountctl(mountpt
, MOUNTCTL_STATUS_VFS_JOURNAL
, -1,
433 &statreq
, sizeof(statreq
), &rstat
, sizeof(rstat
));
434 if (count
> 0 && rstat
[0].recsize
!= sizeof(rstat
[0])) {
435 fprintf(stderr
, "Unable to access status, "
436 "structure size mismatch\n");
440 count
/= sizeof(rstat
[0]);
441 for (i
= 0; i
< count
; ++i
) {
442 func(rstat
[i
].id
, mountpt
, fd
, &rstat
[i
]);
447 if ((count
= getmntinfo(&sfs
, MNT_WAIT
)) > 0) {
448 for (i
= 0; i
< count
; ++i
) {
449 calls
+= mountctl_scan(func
, keyword
, sfs
[i
].f_mntonname
, fd
);
451 } else if (count
< 0) {
459 mountctl_list(const char *keyword __unused
, const char *mountpt
,
460 int fd __unused
, void *info
)
462 struct mountctl_journal_ret_status
*rstat
= info
;
464 printf("%s:%s\n", mountpt
, rstat
->id
[0] ? rstat
->id
: "<NOID>");
465 printf(" membufsize=%s\n", numtostr(rstat
->membufsize
));
466 printf(" membufused=%s\n", numtostr(rstat
->membufused
));
467 printf(" membufunacked=%s\n", numtostr(rstat
->membufunacked
));
468 printf(" total_bytes=%s\n", numtostr(rstat
->bytessent
));
469 printf(" fifo_stalls=%jd\n", (intmax_t)rstat
->fifostalls
);
473 mountctl_add(const char *keyword
, const char *mountpt
, int fd
)
475 struct mountctl_install_journal joinfo
;
481 * Make sure the file descriptor is not on the same filesystem as the
482 * mount point. This isn't a perfect test, but it should catch most
485 if (output_safety_override_opt
== 0 &&
486 fstat(fd
, &st1
) == 0 && S_ISREG(st1
.st_mode
) &&
487 stat(mountpt
, &st2
) == 0 && st1
.st_dev
== st2
.st_dev
489 fprintf(stderr
, "%s:%s failed to add, the journal cannot be on the "
490 "same filesystem being journaled!\n",
497 * Setup joinfo and issue the add
499 bzero(&joinfo
, sizeof(joinfo
));
500 snprintf(joinfo
.id
, sizeof(joinfo
.id
), "%s", keyword
);
502 joinfo
.membufsize
= memfifo_opt
;
504 joinfo
.flags
|= MC_JOURNAL_WANT_FULLDUPLEX
;
505 if (reversable_opt
> 0)
506 joinfo
.flags
|= MC_JOURNAL_WANT_REVERSABLE
;
508 error
= mountctl(mountpt
, MOUNTCTL_INSTALL_VFS_JOURNAL
, fd
,
509 &joinfo
, sizeof(joinfo
), NULL
, 0);
511 fprintf(stderr
, "%s:%s added\n", mountpt
, joinfo
.id
);
513 fprintf(stderr
, "%s:%s failed to add, error %s\n", mountpt
, joinfo
.id
, strerror(errno
));
519 mountctl_restart(const char *keyword
, const char *mountpt
,
520 int fd
, void __unused
*info
)
522 struct mountctl_restart_journal joinfo
;
525 /* XXX make sure descriptor is not on same filesystem as journal */
527 bzero(&joinfo
, sizeof(joinfo
));
529 snprintf(joinfo
.id
, sizeof(joinfo
.id
), "%s", keyword
);
531 joinfo
.flags
|= MC_JOURNAL_WANT_FULLDUPLEX
;
532 if (reversable_opt
> 0)
533 joinfo
.flags
|= MC_JOURNAL_WANT_REVERSABLE
;
535 error
= mountctl(mountpt
, MOUNTCTL_RESTART_VFS_JOURNAL
, fd
,
536 &joinfo
, sizeof(joinfo
), NULL
, 0);
538 fprintf(stderr
, "%s:%s restarted\n", mountpt
, joinfo
.id
);
540 fprintf(stderr
, "%s:%s restart failed, error %s\n", mountpt
, joinfo
.id
, strerror(errno
));
545 mountctl_delete(const char *keyword
, const char *mountpt
,
546 int __unused fd
, void __unused
*info
)
548 struct mountctl_remove_journal joinfo
;
551 bzero(&joinfo
, sizeof(joinfo
));
552 snprintf(joinfo
.id
, sizeof(joinfo
.id
), "%s", keyword
);
553 error
= mountctl(mountpt
, MOUNTCTL_REMOVE_VFS_JOURNAL
, -1,
554 &joinfo
, sizeof(joinfo
), NULL
, 0);
556 fprintf(stderr
, "%s:%s deleted\n", mountpt
, joinfo
.id
);
558 fprintf(stderr
, "%s:%s deletion failed, error %s\n", mountpt
, joinfo
.id
, strerror(errno
));
563 mountctl_modify(const char *keyword __unused
, const char *mountpt __unused
,
564 int fd __unused
, void *info __unused
)
566 fprintf(stderr
, "modify not yet implemented\n");
574 " mountctl -l [tag/mountpt | mountpt:tag]\n"
575 " mountctl -a [-w output_path] [-x filedesc]\n"
576 " [-o option] [-o option ...] mountpt:tag\n"
577 " mountctl -d [tag/mountpt | mountpt:tag]\n"
578 " mountctl -m [-o option] [-o option ...] [tag/mountpt | mountpt:tag]\n"
579 " mountctl -FZSCA [tag/mountpt | mountpt:tag]\n"
585 getsize(const char *str
)
590 val
= strtoll(str
, &suffix
, 0);
609 fprintf(stderr
, "data value '%s' has unknown suffix\n", str
);
617 numtostr(int64_t num
)
622 snprintf(buf
, sizeof(buf
), "%jd", (intmax_t)num
);
623 else if (num
< 10 * 1024)
624 snprintf(buf
, sizeof(buf
), "%3.2fK", num
/ 1024.0);
625 else if (num
< 1024 * 1024)
626 snprintf(buf
, sizeof(buf
), "%3.0fK", num
/ 1024.0);
627 else if (num
< 10 * 1024 * 1024)
628 snprintf(buf
, sizeof(buf
), "%3.2fM", num
/ (1024.0 * 1024.0));
629 else if (num
< 1024 * 1024 * 1024)
630 snprintf(buf
, sizeof(buf
), "%3.0fM", num
/ (1024.0 * 1024.0));
631 else if (num
< 10LL * 1024 * 1024 * 1024)
632 snprintf(buf
, sizeof(buf
), "%3.2fG", num
/ (1024.0 * 1024.0 * 1024.0));
634 snprintf(buf
, sizeof(buf
), "%3.0fG", num
/ (1024.0 * 1024.0 * 1024.0));