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.6 2005/07/13 02:00:19 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>
53 static volatile void usage(void);
54 static void parse_option_keyword(const char *opt,
55 const char **wopt, const char **xopt);
56 static int64_t getsize(const char *str);
57 static const char *numtostr(int64_t num);
59 static int mountctl_scan(void (*func)(const char *, const char *, int, void *),
60 const char *keyword, const char *mountpt, int fd);
61 static void mountctl_list(const char *keyword, const char *mountpt,
62 int __unused fd, void *info);
63 static void mountctl_add(const char *keyword, const char *mountpt, int fd);
64 static void mountctl_restart(const char *keyword, const char *mountpt,
65 int fd, void __unused *);
66 static void mountctl_delete(const char *keyword, const char *mountpt,
67 int __unused fd, void __unused *);
68 static void mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *);
71 * For all options 0 means unspecified, -1 means noOPT or nonOPT, and a
72 * positive number indicates enabling or execution of the option.
75 static int freeze_opt;
80 static int reversable_opt;
81 static int twoway_opt;
82 static int64_t memfifo_opt;
83 static int64_t swapfifo_opt;
86 main(int ac, char **av)
97 const char *wopt = NULL;
98 const char *xopt = NULL;
99 const char *keyword = NULL;
100 const char *mountpt = NULL;
103 while ((ch = getopt(ac, av, "2adflmo:rw:x:ACFSZ")) != -1) {
110 if (aopt + dopt + lopt + mopt + ropt != 1) {
111 fprintf(stderr, "too many action options specified\n");
117 if (aopt + dopt + lopt + mopt + ropt != 1) {
118 fprintf(stderr, "too many action options specified\n");
124 if (aopt + dopt + lopt + mopt + ropt != 1) {
125 fprintf(stderr, "too many action options specified\n");
134 if (aopt + dopt + lopt + mopt + ropt != 1) {
135 fprintf(stderr, "too many action options specified\n");
140 parse_option_keyword(optarg, &wopt, &xopt);
144 if (aopt + dopt + lopt + mopt + ropt != 1) {
145 fprintf(stderr, "too many action options specified\n");
178 fprintf(stderr, "unknown option: -%c\n", optopt);
186 * Parse the keyword and/or mount point.
191 fprintf(stderr, "action requires a tag and/or mount "
192 "point to be specified\n");
197 if (av[0][0] == '/') {
199 if ((keyword = strchr(mountpt, ':')) != NULL) {
201 tmp = strdup(mountpt);
202 *strchr(tmp, ':') = 0;
210 fprintf(stderr, "unexpected extra arguments to command\n");
215 * Additional sanity checks
217 if (aopt + dopt + lopt + mopt + ropt + mimplied == 0) {
218 fprintf(stderr, "no action or implied action options were specified\n");
221 if (mimplied && aopt + dopt + lopt + ropt == 0)
223 if ((wopt || xopt) && !(aopt || ropt || mopt)) {
224 fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a/-r\n");
227 if (aopt && (keyword == NULL || mountpt == NULL)) {
228 fprintf(stderr, "a keyword AND a mountpt must be specified "
229 "when adding a journal\n");
232 if (fopt == 0 && mopt + dopt && keyword == NULL && mountpt == NULL) {
233 fprintf(stderr, "a keyword, a mountpt, or both must be specified "
234 "when modifying or deleting a journal, unless "
235 "-f is also specified for safety\n");
240 * Open the journaling file descriptor if required.
243 fprintf(stderr, "you must specify only one of -w/-x/path/fd\n");
246 if ((fd = open(wopt, O_RDWR|O_CREAT|O_APPEND, 0666)) < 0) {
247 fprintf(stderr, "unable to create %s: %s\n", wopt, strerror(errno));
251 fd = strtol(xopt, NULL, 0);
252 } else if (aopt || ropt) {
253 fd = 1; /* stdout default for -a */
259 * And finally execute the core command.
262 mountctl_scan(mountctl_list, keyword, mountpt, fd);
264 mountctl_add(keyword, mountpt, fd);
266 ch = mountctl_scan(mountctl_restart, keyword, mountpt, fd);
268 fprintf(stderr, "%d journals restarted\n", ch);
270 fprintf(stderr, "Unable to locate any matching journals\n");
273 ch = mountctl_scan(mountctl_delete, keyword, mountpt, -1);
275 fprintf(stderr, "%d journals deleted\n", ch);
277 fprintf(stderr, "Unable to locate any matching journals\n");
280 ch = mountctl_scan(mountctl_modify, keyword, mountpt, fd);
282 fprintf(stderr, "%d journals modified\n", ch);
284 fprintf(stderr, "Unable to locate any matching journals\n");
291 parse_option_keyword(const char *opt, const char **wopt, const char **xopt)
293 char *str = strdup(opt);
301 * multiple comma delimited options may be specified.
303 while ((name = strsep(&str, ",")) != NULL) {
305 * some options have associated data.
307 if ((val = strchr(name, '=')) != NULL)
311 * options beginning with 'no' or 'non' are negated. A positive
312 * number means not negated, a negative number means negated.
317 if (strncmp(name, "non", 3) == 0) {
320 } else if (strncmp(name, "no", 2) == 0) {
326 * Parse supported options
328 if (strcmp(name, "reversable") == 0) {
329 reversable_opt = negate;
330 } else if (strcmp(name, "twoway") == 0) {
332 } else if (strcmp(name, "memfifo") == 0) {
336 if ((memfifo_opt = getsize(val)) == 0)
339 } else if (strcmp(name, "swapfifo") == 0) {
342 if ((swapfifo_opt = getsize(val)) == 0)
344 } else if (negate < 0) {
347 hasval = 1; /* force error */
349 } else if (strcmp(name, "fd") == 0) {
354 } else if (strcmp(name, "path") == 0) {
359 } else if (strcmp(name, "freeze") == 0 || strcmp(name, "stop") == 0) {
364 } else if (strcmp(name, "start") == 0) {
366 freeze_opt = -negate;
369 } else if (strcmp(name, "close") == 0) {
371 } else if (strcmp(name, "abort") == 0) {
373 } else if (strcmp(name, "flush") == 0) {
376 fprintf(stderr, "unknown option keyword: %s\n", name);
383 if (cannotnegate && negate < 0) {
384 fprintf(stderr, "option %s may not be negated\n", name);
387 if (hasval && val == NULL) {
388 fprintf(stderr, "option %s requires assigned data\n", name);
391 if (hasval == 0 && val) {
392 fprintf(stderr, "option %s does not take an assignment\n", name);
400 mountctl_scan(void (*func)(const char *, const char *, int, void *),
401 const char *keyword, const char *mountpt, int fd)
407 struct mountctl_status_journal statreq;
408 struct mountctl_journal_ret_status rstat[4]; /* BIG */
412 bzero(&statreq, sizeof(statreq));
414 statreq.index = MC_JOURNAL_INDEX_ID;
415 count = strlen(keyword);
418 bcopy(keyword, statreq.id, count);
420 statreq.index = MC_JOURNAL_INDEX_ALL;
422 count = mountctl(mountpt, MOUNTCTL_STATUS_VFS_JOURNAL, -1,
423 &statreq, sizeof(statreq), &rstat, sizeof(rstat));
424 if (count > 0 && rstat[0].recsize != sizeof(rstat[0])) {
425 fprintf(stderr, "Unable to access status, "
426 "structure size mismatch\n");
430 count /= sizeof(rstat[0]);
431 for (i = 0; i < count; ++i) {
432 func(rstat[i].id, mountpt, fd, &rstat[i]);
437 if ((count = getmntinfo(&sfs, MNT_WAIT)) > 0) {
438 for (i = 0; i < count; ++i) {
439 calls += mountctl_scan(func, keyword, sfs[i].f_mntonname, fd);
441 } else if (count < 0) {
449 mountctl_list(const char *keyword, const char *mountpt, int __unused fd, void *info)
451 struct mountctl_journal_ret_status *rstat = info;
453 printf("%s:%s\n", mountpt, rstat->id[0] ? rstat->id : "<NOID>");
454 printf(" membufsize=%s\n", numtostr(rstat->membufsize));
455 printf(" membufused=%s\n", numtostr(rstat->membufused));
456 printf(" membufunacked=%s\n", numtostr(rstat->membufunacked));
457 printf(" total_bytes=%s\n", numtostr(rstat->bytessent));
458 printf(" fifo_stalls=%lld\n", rstat->fifostalls);
462 mountctl_add(const char *keyword, const char *mountpt, int fd)
464 struct mountctl_install_journal joinfo;
470 * Make sure the file descriptor is not on the same filesystem as the
471 * mount point. This isn't a perfect test, but it should catch most
474 if (fstat(fd, &st1) == 0 && S_ISREG(st1.st_mode) &&
475 stat(mountpt, &st2) == 0 && st1.st_dev == st2.st_dev
477 fprintf(stderr, "%s:%s failed to add, the journal cannot be on the "
478 "same filesystem being journaled!\n",
485 * Setup joinfo and issue the add
487 bzero(&joinfo, sizeof(joinfo));
488 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
490 joinfo.membufsize = memfifo_opt;
492 joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
493 if (reversable_opt > 0)
494 joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
496 error = mountctl(mountpt, MOUNTCTL_INSTALL_VFS_JOURNAL, fd,
497 &joinfo, sizeof(joinfo), NULL, 0);
499 fprintf(stderr, "%s:%s added\n", mountpt, joinfo.id);
501 fprintf(stderr, "%s:%s failed to add, error %s\n", mountpt, joinfo.id, strerror(errno));
507 mountctl_restart(const char *keyword, const char *mountpt,
508 int fd, void __unused *info)
510 struct mountctl_restart_journal joinfo;
513 /* XXX make sure descriptor is not on same filesystem as journal */
515 bzero(&joinfo, sizeof(joinfo));
517 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
519 joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
520 if (reversable_opt > 0)
521 joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
523 error = mountctl(mountpt, MOUNTCTL_RESTART_VFS_JOURNAL, fd,
524 &joinfo, sizeof(joinfo), NULL, 0);
526 fprintf(stderr, "%s:%s restarted\n", mountpt, joinfo.id);
528 fprintf(stderr, "%s:%s restart failed, error %s\n", mountpt, joinfo.id, strerror(errno));
533 mountctl_delete(const char *keyword, const char *mountpt,
534 int __unused fd, void __unused *info)
536 struct mountctl_remove_journal joinfo;
539 bzero(&joinfo, sizeof(joinfo));
540 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
541 error = mountctl(mountpt, MOUNTCTL_REMOVE_VFS_JOURNAL, -1,
542 &joinfo, sizeof(joinfo), NULL, 0);
544 fprintf(stderr, "%s:%s deleted\n", mountpt, joinfo.id);
546 fprintf(stderr, "%s:%s deletion failed, error %s\n", mountpt, joinfo.id, strerror(errno));
551 mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *info)
553 fprintf(stderr, "modify not yet implemented\n");
562 " mountctl -l [tag/mountpt | mountpt:tag]\n"
563 " mountctl -a [-w output_path] [-x filedesc]\n"
564 " [-o option] [-o option ...] mountpt:tag\n"
565 " mountctl -d [tag/mountpt | mountpt:tag]\n"
566 " mountctl -m [-o option] [-o option ...] [tag/mountpt | mountpt:tag]\n"
567 " mountctl -FZSCA [tag/mountpt | mountpt:tag]\n"
574 getsize(const char *str)
579 val = strtoll(str, &suffix, 0);
598 fprintf(stderr, "data value '%s' has unknown suffix\n", str);
607 numtostr(int64_t num)
614 snprintf(buf, sizeof(buf), "%lld", num);
615 else if (num < 10 * 1024)
616 snprintf(buf, sizeof(buf), "%3.2fK", num / 1024.0);
617 else if (num < 1024 * 1024)
618 snprintf(buf, sizeof(buf), "%3.0fK", num / 1024.0);
619 else if (num < 10 * 1024 * 1024)
620 snprintf(buf, sizeof(buf), "%3.2fM", num / (1024.0 * 1024.0));
621 else if (num < 1024 * 1024 * 1024)
622 snprintf(buf, sizeof(buf), "%3.0fM", num / (1024.0 * 1024.0));
623 else if (num < 10LL * 1024 * 1024 * 1024)
624 snprintf(buf, sizeof(buf), "%3.2fG", num / (1024.0 * 1024.0 * 1024.0));
626 snprintf(buf, sizeof(buf), "%3.0fG", num / (1024.0 * 1024.0 * 1024.0));