More minor IPI work.
[dragonfly/vkernel-mp.git] / sbin / mountctl / mountctl.c
blobb10914fc1f35c828dd27832a2327a5f3dc217132
1 /*
2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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
16 * distribution.
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
32 * SUCH DAMAGE.
34 * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.8 2005/11/06 13:42:26 swildner 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>
45 #include <sys/time.h>
46 #include <sys/mountctl.h>
47 #include <stdio.h>
48 #include <fcntl.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <errno.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.
74 static int exitCode;
75 static int freeze_opt;
76 static int start_opt;
77 static int close_opt;
78 static int abort_opt;
79 static int flush_opt;
80 static int reversable_opt;
81 static int twoway_opt;
82 static int output_safety_override_opt;
83 static int64_t memfifo_opt;
84 static int64_t swapfifo_opt;
86 int
87 main(int ac, char **av)
89 int fd;
90 int ch;
91 int aopt = 0;
92 int dopt = 0;
93 int fopt = 0;
94 int lopt = 0;
95 int mopt = 0;
96 int ropt = 0;
97 int mimplied = 0;
98 const char *wopt = NULL;
99 const char *xopt = NULL;
100 const char *keyword = NULL;
101 const char *mountpt = NULL;
102 char *tmp;
104 while ((ch = getopt(ac, av, "2adflmo:rw:x:ACFSW:X:Z")) != -1) {
105 switch(ch) {
106 case '2':
107 twoway_opt = 1;
108 break;
109 case 'r':
110 ropt = 1;
111 if (aopt + dopt + lopt + mopt + ropt != 1) {
112 fprintf(stderr, "too many action options specified\n");
113 usage();
115 break;
116 case 'a':
117 aopt = 1;
118 if (aopt + dopt + lopt + mopt + ropt != 1) {
119 fprintf(stderr, "too many action options specified\n");
120 usage();
122 break;
123 case 'd':
124 dopt = 1;
125 if (aopt + dopt + lopt + mopt + ropt != 1) {
126 fprintf(stderr, "too many action options specified\n");
127 usage();
129 break;
130 case 'f':
131 fopt = 1;
132 break;
133 case 'l':
134 lopt = 1;
135 if (aopt + dopt + lopt + mopt + ropt != 1) {
136 fprintf(stderr, "too many action options specified\n");
137 usage();
139 break;
140 case 'o':
141 parse_option_keyword(optarg, &wopt, &xopt);
142 break;
143 case 'm':
144 mopt = 1;
145 if (aopt + dopt + lopt + mopt + ropt != 1) {
146 fprintf(stderr, "too many action options specified\n");
147 usage();
149 break;
150 case 'W':
151 output_safety_override_opt = 1;
152 /* fall through */
153 case 'w':
154 wopt = optarg;
155 mimplied = 1;
156 break;
157 case 'X':
158 output_safety_override_opt = 1;
159 /* fall through */
160 case 'x':
161 xopt = optarg;
162 mimplied = 1;
163 break;
164 case 'A':
165 mimplied = 1;
166 abort_opt = 1;
167 break;
168 case 'C':
169 mimplied = 1;
170 close_opt = 1;
171 break;
172 case 'F':
173 mimplied = 1;
174 flush_opt = 1;
175 break;
176 case 'S':
177 mimplied = 1;
178 start_opt = 1;
179 break;
180 case 'Z':
181 mimplied = 1;
182 freeze_opt = 1;
183 break;
184 default:
185 fprintf(stderr, "unknown option: -%c\n", optopt);
186 usage();
189 ac -= optind;
190 av += optind;
193 * Parse the keyword and/or mount point.
195 switch(ac) {
196 case 0:
197 if (aopt || ropt) {
198 fprintf(stderr, "action requires a tag and/or mount "
199 "point to be specified\n");
200 usage();
202 break;
203 case 1:
204 if (av[0][0] == '/') {
205 mountpt = av[0];
206 if ((keyword = strchr(mountpt, ':')) != NULL) {
207 ++keyword;
208 tmp = strdup(mountpt);
209 *strchr(tmp, ':') = 0;
210 mountpt = tmp;
212 } else {
213 keyword = av[0];
215 break;
216 default:
217 fprintf(stderr, "unexpected extra arguments to command\n");
218 usage();
222 * Additional sanity checks
224 if (aopt + dopt + lopt + mopt + ropt + mimplied == 0) {
225 fprintf(stderr, "no action or implied action options were specified\n");
226 usage();
228 if (mimplied && aopt + dopt + lopt + ropt == 0)
229 mopt = 1;
230 if ((wopt || xopt) && !(aopt || ropt || mopt)) {
231 fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a/-r\n");
232 usage();
234 if (aopt && (keyword == NULL || mountpt == NULL)) {
235 fprintf(stderr, "a keyword AND a mountpt must be specified "
236 "when adding a journal\n");
237 usage();
239 if (fopt == 0 && mopt + dopt && keyword == NULL && mountpt == NULL) {
240 fprintf(stderr, "a keyword, a mountpt, or both must be specified "
241 "when modifying or deleting a journal, unless "
242 "-f is also specified for safety\n");
243 usage();
247 * Open the journaling file descriptor if required.
249 if (wopt && xopt) {
250 fprintf(stderr, "you must specify only one of -w/-x/path/fd\n");
251 exit(1);
252 } else if (wopt) {
253 if ((fd = open(wopt, O_RDWR|O_CREAT|O_APPEND, 0666)) < 0) {
254 fprintf(stderr, "unable to create %s: %s\n", wopt, strerror(errno));
255 exit(1);
257 } else if (xopt) {
258 fd = strtol(xopt, NULL, 0);
259 } else if (aopt || ropt) {
260 fd = 1; /* stdout default for -a */
261 } else {
262 fd = -1;
266 * And finally execute the core command.
268 if (lopt)
269 mountctl_scan(mountctl_list, keyword, mountpt, fd);
270 if (aopt)
271 mountctl_add(keyword, mountpt, fd);
272 if (ropt) {
273 ch = mountctl_scan(mountctl_restart, keyword, mountpt, fd);
274 if (ch)
275 fprintf(stderr, "%d journals restarted\n", ch);
276 else
277 fprintf(stderr, "Unable to locate any matching journals\n");
279 if (dopt) {
280 ch = mountctl_scan(mountctl_delete, keyword, mountpt, -1);
281 if (ch)
282 fprintf(stderr, "%d journals deleted\n", ch);
283 else
284 fprintf(stderr, "Unable to locate any matching journals\n");
286 if (mopt) {
287 ch = mountctl_scan(mountctl_modify, keyword, mountpt, fd);
288 if (ch)
289 fprintf(stderr, "%d journals modified\n", ch);
290 else
291 fprintf(stderr, "Unable to locate any matching journals\n");
294 return(exitCode);
297 static void
298 parse_option_keyword(const char *opt, const char **wopt, const char **xopt)
300 char *str = strdup(opt);
301 char *name;
302 char *val;
303 int negate;
304 int hasval;
305 int cannotnegate;
308 * multiple comma delimited options may be specified.
310 while ((name = strsep(&str, ",")) != NULL) {
312 * some options have associated data.
314 if ((val = strchr(name, '=')) != NULL)
315 *val++ = 0;
318 * options beginning with 'no' or 'non' are negated. A positive
319 * number means not negated, a negative number means negated.
321 negate = 1;
322 cannotnegate = 0;
323 hasval = 0;
324 if (strncmp(name, "non", 3) == 0) {
325 name += 3;
326 negate = -1;
327 } else if (strncmp(name, "no", 2) == 0) {
328 name += 2;
329 negate = -1;
333 * Parse supported options
335 if (strcmp(name, "undo") == 0) {
336 reversable_opt = negate;
337 } else if (strcmp(name, "reversable") == 0) {
338 reversable_opt = negate;
339 } else if (strcmp(name, "twoway") == 0) {
340 twoway_opt = negate;
341 } else if (strcmp(name, "memfifo") == 0) {
342 cannotnegate = 1;
343 hasval = 1;
344 if (val) {
345 if ((memfifo_opt = getsize(val)) == 0)
346 memfifo_opt = -1;
348 } else if (strcmp(name, "swapfifo") == 0) {
349 if (val) {
350 hasval = 1;
351 if ((swapfifo_opt = getsize(val)) == 0)
352 swapfifo_opt = -1;
353 } else if (negate < 0) {
354 swapfifo_opt = -1;
355 } else {
356 hasval = 1; /* force error */
358 } else if (strcmp(name, "fd") == 0) {
359 cannotnegate = 1;
360 hasval = 1;
361 if (val)
362 *xopt = val;
363 } else if (strcmp(name, "path") == 0) {
364 cannotnegate = 1;
365 hasval = 1;
366 if (val)
367 *wopt = val;
368 } else if (strcmp(name, "freeze") == 0 || strcmp(name, "stop") == 0) {
369 if (negate < 0)
370 start_opt = -negate;
371 else
372 freeze_opt = negate;
373 } else if (strcmp(name, "start") == 0) {
374 if (negate < 0)
375 freeze_opt = -negate;
376 else
377 start_opt = negate;
378 } else if (strcmp(name, "close") == 0) {
379 close_opt = negate;
380 } else if (strcmp(name, "abort") == 0) {
381 abort_opt = negate;
382 } else if (strcmp(name, "flush") == 0) {
383 flush_opt = negate;
384 } else {
385 fprintf(stderr, "unknown option keyword: %s\n", name);
386 exit(1);
390 * Sanity checks
392 if (cannotnegate && negate < 0) {
393 fprintf(stderr, "option %s may not be negated\n", name);
394 exit(1);
396 if (hasval && val == NULL) {
397 fprintf(stderr, "option %s requires assigned data\n", name);
398 exit(1);
400 if (hasval == 0 && val) {
401 fprintf(stderr, "option %s does not take an assignment\n", name);
402 exit(1);
408 static int
409 mountctl_scan(void (*func)(const char *, const char *, int, void *),
410 const char *keyword, const char *mountpt, int fd)
412 struct statfs *sfs;
413 int count;
414 int calls;
415 int i;
416 struct mountctl_status_journal statreq;
417 struct mountctl_journal_ret_status rstat[4]; /* BIG */
419 calls = 0;
420 if (mountpt) {
421 bzero(&statreq, sizeof(statreq));
422 if (keyword) {
423 statreq.index = MC_JOURNAL_INDEX_ID;
424 count = strlen(keyword);
425 if (count > JIDMAX)
426 count = JIDMAX;
427 bcopy(keyword, statreq.id, count);
428 } else {
429 statreq.index = MC_JOURNAL_INDEX_ALL;
431 count = mountctl(mountpt, MOUNTCTL_STATUS_VFS_JOURNAL, -1,
432 &statreq, sizeof(statreq), &rstat, sizeof(rstat));
433 if (count > 0 && rstat[0].recsize != sizeof(rstat[0])) {
434 fprintf(stderr, "Unable to access status, "
435 "structure size mismatch\n");
436 exit(1);
438 if (count > 0) {
439 count /= sizeof(rstat[0]);
440 for (i = 0; i < count; ++i) {
441 func(rstat[i].id, mountpt, fd, &rstat[i]);
442 ++calls;
445 } else {
446 if ((count = getmntinfo(&sfs, MNT_WAIT)) > 0) {
447 for (i = 0; i < count; ++i) {
448 calls += mountctl_scan(func, keyword, sfs[i].f_mntonname, fd);
450 } else if (count < 0) {
451 /* XXX */
454 return(calls);
457 static void
458 mountctl_list(const char *keyword, const char *mountpt, int __unused fd, void *info)
460 struct mountctl_journal_ret_status *rstat = info;
462 printf("%s:%s\n", mountpt, rstat->id[0] ? rstat->id : "<NOID>");
463 printf(" membufsize=%s\n", numtostr(rstat->membufsize));
464 printf(" membufused=%s\n", numtostr(rstat->membufused));
465 printf(" membufunacked=%s\n", numtostr(rstat->membufunacked));
466 printf(" total_bytes=%s\n", numtostr(rstat->bytessent));
467 printf(" fifo_stalls=%lld\n", rstat->fifostalls);
470 static void
471 mountctl_add(const char *keyword, const char *mountpt, int fd)
473 struct mountctl_install_journal joinfo;
474 struct stat st1;
475 struct stat st2;
476 int error;
479 * Make sure the file descriptor is not on the same filesystem as the
480 * mount point. This isn't a perfect test, but it should catch most
481 * foot shooting.
483 if (output_safety_override_opt == 0 &&
484 fstat(fd, &st1) == 0 && S_ISREG(st1.st_mode) &&
485 stat(mountpt, &st2) == 0 && st1.st_dev == st2.st_dev
487 fprintf(stderr, "%s:%s failed to add, the journal cannot be on the "
488 "same filesystem being journaled!\n",
489 mountpt, keyword);
490 exitCode = 1;
491 return;
495 * Setup joinfo and issue the add
497 bzero(&joinfo, sizeof(joinfo));
498 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
499 if (memfifo_opt > 0)
500 joinfo.membufsize = memfifo_opt;
501 if (twoway_opt > 0)
502 joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
503 if (reversable_opt > 0)
504 joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
506 error = mountctl(mountpt, MOUNTCTL_INSTALL_VFS_JOURNAL, fd,
507 &joinfo, sizeof(joinfo), NULL, 0);
508 if (error == 0) {
509 fprintf(stderr, "%s:%s added\n", mountpt, joinfo.id);
510 } else {
511 fprintf(stderr, "%s:%s failed to add, error %s\n", mountpt, joinfo.id, strerror(errno));
512 exitCode = 1;
516 static void
517 mountctl_restart(const char *keyword, const char *mountpt,
518 int fd, void __unused *info)
520 struct mountctl_restart_journal joinfo;
521 int error;
523 /* XXX make sure descriptor is not on same filesystem as journal */
525 bzero(&joinfo, sizeof(joinfo));
527 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
528 if (twoway_opt > 0)
529 joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
530 if (reversable_opt > 0)
531 joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
533 error = mountctl(mountpt, MOUNTCTL_RESTART_VFS_JOURNAL, fd,
534 &joinfo, sizeof(joinfo), NULL, 0);
535 if (error == 0) {
536 fprintf(stderr, "%s:%s restarted\n", mountpt, joinfo.id);
537 } else {
538 fprintf(stderr, "%s:%s restart failed, error %s\n", mountpt, joinfo.id, strerror(errno));
542 static void
543 mountctl_delete(const char *keyword, const char *mountpt,
544 int __unused fd, void __unused *info)
546 struct mountctl_remove_journal joinfo;
547 int error;
549 bzero(&joinfo, sizeof(joinfo));
550 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
551 error = mountctl(mountpt, MOUNTCTL_REMOVE_VFS_JOURNAL, -1,
552 &joinfo, sizeof(joinfo), NULL, 0);
553 if (error == 0) {
554 fprintf(stderr, "%s:%s deleted\n", mountpt, joinfo.id);
555 } else {
556 fprintf(stderr, "%s:%s deletion failed, error %s\n", mountpt, joinfo.id, strerror(errno));
560 static void
561 mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *info)
563 fprintf(stderr, "modify not yet implemented\n");
567 static volatile void
568 usage(void)
570 printf(
571 " mountctl -l [tag/mountpt | mountpt:tag]\n"
572 " mountctl -a [-w output_path] [-x filedesc]\n"
573 " [-o option] [-o option ...] mountpt:tag\n"
574 " mountctl -d [tag/mountpt | mountpt:tag]\n"
575 " mountctl -m [-o option] [-o option ...] [tag/mountpt | mountpt:tag]\n"
576 " mountctl -FZSCA [tag/mountpt | mountpt:tag]\n"
578 exit(1);
581 static int64_t
582 getsize(const char *str)
584 const char *suffix;
585 int64_t val;
587 val = strtoll(str, &suffix, 0);
588 if (suffix) {
589 switch(*suffix) {
590 case 'b':
591 break;
592 case 't':
593 val *= 1024;
594 /* fall through */
595 case 'g':
596 val *= 1024;
597 /* fall through */
598 case 'm':
599 val *= 1024;
600 /* fall through */
601 case 'k':
602 val *= 1024;
603 /* fall through */
604 break;
605 default:
606 fprintf(stderr, "data value '%s' has unknown suffix\n", str);
607 exit(1);
610 return(val);
613 static const char *
614 numtostr(int64_t num)
616 static char buf[64];
617 int n;
618 double v = num;
620 if (num < 1024)
621 snprintf(buf, sizeof(buf), "%lld", num);
622 else if (num < 10 * 1024)
623 snprintf(buf, sizeof(buf), "%3.2fK", num / 1024.0);
624 else if (num < 1024 * 1024)
625 snprintf(buf, sizeof(buf), "%3.0fK", num / 1024.0);
626 else if (num < 10 * 1024 * 1024)
627 snprintf(buf, sizeof(buf), "%3.2fM", num / (1024.0 * 1024.0));
628 else if (num < 1024 * 1024 * 1024)
629 snprintf(buf, sizeof(buf), "%3.0fM", num / (1024.0 * 1024.0));
630 else if (num < 10LL * 1024 * 1024 * 1024)
631 snprintf(buf, sizeof(buf), "%3.2fG", num / (1024.0 * 1024.0 * 1024.0));
632 else
633 snprintf(buf, sizeof(buf), "%3.0fG", num / (1024.0 * 1024.0 * 1024.0));
634 return(buf);