More stuff for Citrus.
[dragonfly/netmp.git] / sbin / mountctl / mountctl.c
blobdb04f5d8dbfa7ab002ddead06820f1dbe9135197
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.3 2005/03/04 05:17:37 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>
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_delete(const char *keyword, const char *mountpt,
65 int __unused fd, void __unused *);
66 static void mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *);
69 * For all options 0 means unspecified, -1 means noOPT or nonOPT, and a
70 * positive number indicates enabling or execution of the option.
72 static int freeze_opt;
73 static int start_opt;
74 static int close_opt;
75 static int abort_opt;
76 static int flush_opt;
77 static int reversable_opt;
78 static int twoway_opt;
79 static int64_t memfifo_opt;
80 static int64_t swapfifo_opt;
82 int
83 main(int ac, char **av)
85 int fd;
86 int ch;
87 int aopt = 0;
88 int dopt = 0;
89 int fopt = 0;
90 int lopt = 0;
91 int mopt = 0;
92 int mimplied = 0;
93 const char *wopt = NULL;
94 const char *xopt = NULL;
95 const char *keyword = NULL;
96 const char *mountpt = NULL;
97 char *tmp;
99 while ((ch = getopt(ac, av, "adflo:mw:x:ACFSZ")) != -1) {
100 switch(ch) {
101 case 'a':
102 aopt = 1;
103 if (aopt + dopt + lopt + mopt != 1) {
104 fprintf(stderr, "too many action options specified\n");
105 usage();
107 break;
108 case 'd':
109 dopt = 1;
110 if (aopt + dopt + lopt + mopt != 1) {
111 fprintf(stderr, "too many action options specified\n");
112 usage();
114 break;
115 case 'f':
116 fopt = 1;
117 break;
118 case 'l':
119 lopt = 1;
120 if (aopt + dopt + lopt + mopt != 1) {
121 fprintf(stderr, "too many action options specified\n");
122 usage();
124 break;
125 case 'o':
126 parse_option_keyword(optarg, &wopt, &xopt);
127 break;
128 case 'm':
129 mopt = 1;
130 if (aopt + dopt + lopt + mopt != 1) {
131 fprintf(stderr, "too many action options specified\n");
132 usage();
134 break;
135 case 'w':
136 wopt = optarg;
137 mimplied = 1;
138 break;
139 case 'x':
140 xopt = optarg;
141 mimplied = 1;
142 break;
143 case 'A':
144 mimplied = 1;
145 abort_opt = 1;
146 break;
147 case 'C':
148 mimplied = 1;
149 close_opt = 1;
150 break;
151 case 'F':
152 mimplied = 1;
153 flush_opt = 1;
154 break;
155 case 'S':
156 mimplied = 1;
157 start_opt = 1;
158 break;
159 case 'Z':
160 mimplied = 1;
161 freeze_opt = 1;
162 break;
163 default:
164 fprintf(stderr, "unknown option: -%c\n", optopt);
165 usage();
168 ac -= optind;
169 av += optind;
172 * Parse the keyword and/or mount point.
174 switch(ac) {
175 case 0:
176 if (aopt) {
177 fprintf(stderr, "action requires a tag and/or mount "
178 "point to be specified\n");
179 usage();
181 break;
182 case 1:
183 if (av[0][0] == '/') {
184 mountpt = av[0];
185 if ((keyword = strchr(mountpt, ':')) != NULL) {
186 ++keyword;
187 tmp = strdup(mountpt);
188 *strchr(tmp, ':') = 0;
189 mountpt = tmp;
191 } else {
192 keyword = av[0];
194 break;
195 default:
196 fprintf(stderr, "unexpected extra arguments to command\n");
197 usage();
201 * Additional sanity checks
203 if (aopt + dopt + lopt + mopt + mimplied == 0) {
204 fprintf(stderr, "no action or implied action options were specified\n");
205 usage();
207 if (mimplied && aopt + dopt + lopt == 0)
208 mopt = 1;
209 if ((wopt || xopt) && !(aopt || mopt)) {
210 fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a\n");
211 usage();
213 if (aopt && (keyword == NULL || mountpt == NULL)) {
214 fprintf(stderr, "a keyword AND a mountpt must be specified "
215 "when adding a journal\n");
216 usage();
218 if (fopt == 0 && mopt + dopt && keyword == NULL && mountpt == NULL) {
219 fprintf(stderr, "a keyword, a mountpt, or both must be specified "
220 "when modifying or deleting a journal, unless "
221 "-f is also specified for safety\n");
222 usage();
226 * Open the journaling file descriptor if required.
228 if (wopt && xopt) {
229 fprintf(stderr, "you must specify only one of -w/-x/path/fd\n");
230 exit(1);
231 } else if (wopt) {
232 if ((fd = open(wopt, O_RDWR|O_CREAT|O_APPEND, 0666)) < 0) {
233 fprintf(stderr, "unable to create %s: %s\n", wopt, strerror(errno));
234 exit(1);
236 } else if (xopt) {
237 fd = strtol(xopt, NULL, 0);
238 } else if (aopt) {
239 fd = 1; /* stdout default for -a */
240 } else {
241 fd = -1;
245 * And finally execute the core command.
247 if (lopt)
248 mountctl_scan(mountctl_list, keyword, mountpt, fd);
249 if (aopt)
250 mountctl_add(keyword, mountpt, fd);
251 if (dopt) {
252 ch = mountctl_scan(mountctl_delete, keyword, mountpt, -1);
253 if (ch)
254 printf("%d journals deleted\n", ch);
255 else
256 printf("Unable to locate any matching journals\n");
258 if (mopt) {
259 ch = mountctl_scan(mountctl_modify, keyword, mountpt, fd);
260 if (ch)
261 printf("%d journals modified\n", ch);
262 else
263 printf("Unable to locate any matching journals\n");
266 return(0);
269 static void
270 parse_option_keyword(const char *opt, const char **wopt, const char **xopt)
272 char *str = strdup(opt);
273 char *name;
274 char *val;
275 int negate;
276 int hasval;
277 int cannotnegate;
280 * multiple comma delimited options may be specified.
282 while ((name = strsep(&str, ",")) != NULL) {
284 * some options have associated data.
286 if ((val = strchr(name, '=')) != NULL)
287 *val++ = 0;
290 * options beginning with 'no' or 'non' are negated. A positive
291 * number means not negated, a negative number means negated.
293 negate = 1;
294 cannotnegate = 0;
295 hasval = 0;
296 if (strncmp(name, "non", 3) == 0) {
297 name += 3;
298 negate = -1;
299 } else if (strncmp(name, "no", 2) == 0) {
300 name += 2;
301 negate = -1;
305 * Parse supported options
307 if (strcmp(name, "reversable") == 0) {
308 reversable_opt = negate;
309 } else if (strcmp(name, "twoway") == 0) {
310 twoway_opt = negate;
311 } else if (strcmp(name, "memfifo") == 0) {
312 cannotnegate = 1;
313 hasval = 1;
314 if (val) {
315 if ((memfifo_opt = getsize(val)) == 0)
316 memfifo_opt = -1;
318 } else if (strcmp(name, "swapfifo") == 0) {
319 if (val) {
320 hasval = 1;
321 if ((swapfifo_opt = getsize(val)) == 0)
322 swapfifo_opt = -1;
323 } else if (negate < 0) {
324 swapfifo_opt = -1;
325 } else {
326 hasval = 1; /* force error */
328 } else if (strcmp(name, "fd") == 0) {
329 cannotnegate = 1;
330 hasval = 1;
331 if (val)
332 *xopt = val;
333 } else if (strcmp(name, "path") == 0) {
334 cannotnegate = 1;
335 hasval = 1;
336 if (val)
337 *wopt = val;
338 } else if (strcmp(name, "freeze") == 0 || strcmp(name, "stop") == 0) {
339 if (negate < 0)
340 start_opt = -negate;
341 else
342 freeze_opt = negate;
343 } else if (strcmp(name, "start") == 0) {
344 if (negate < 0)
345 freeze_opt = -negate;
346 else
347 start_opt = negate;
348 } else if (strcmp(name, "close") == 0) {
349 close_opt = negate;
350 } else if (strcmp(name, "abort") == 0) {
351 abort_opt = negate;
352 } else if (strcmp(name, "flush") == 0) {
353 flush_opt = negate;
354 } else {
355 fprintf(stderr, "unknown option keyword: %s\n", name);
356 exit(1);
360 * Sanity checks
362 if (cannotnegate && negate < 0) {
363 fprintf(stderr, "option %s may not be negated\n", name);
364 exit(1);
366 if (hasval && val == NULL) {
367 fprintf(stderr, "option %s requires assigned data\n", name);
368 exit(1);
370 if (hasval == 0 && val) {
371 fprintf(stderr, "option %s does not take an assignment\n", name);
372 exit(1);
378 static int
379 mountctl_scan(void (*func)(const char *, const char *, int, void *),
380 const char *keyword, const char *mountpt, int fd)
382 struct statfs *sfs;
383 int count;
384 int calls;
385 int i;
386 struct mountctl_status_journal statreq;
387 struct mountctl_journal_ret_status rstat[4]; /* BIG */
389 calls = 0;
390 if (mountpt) {
391 bzero(&statreq, sizeof(statreq));
392 if (keyword) {
393 statreq.index = MC_JOURNAL_INDEX_ID;
394 count = strlen(keyword);
395 if (count > JIDMAX)
396 count = JIDMAX;
397 bcopy(keyword, statreq.id, count);
398 } else {
399 statreq.index = MC_JOURNAL_INDEX_ALL;
401 count = mountctl(mountpt, MOUNTCTL_STATUS_VFS_JOURNAL, -1,
402 &statreq, sizeof(statreq), &rstat, sizeof(rstat));
403 if (count > 0 && rstat[0].recsize != sizeof(rstat[0])) {
404 fprintf(stderr, "Unable to access status, "
405 "structure size mismatch\n");
406 exit(1);
408 if (count > 0) {
409 count /= sizeof(rstat[0]);
410 for (i = 0; i < count; ++i) {
411 func(rstat[i].id, mountpt, fd, &rstat[i]);
412 ++calls;
415 } else {
416 if ((count = getmntinfo(&sfs, MNT_WAIT)) > 0) {
417 for (i = 0; i < count; ++i) {
418 calls += mountctl_scan(func, keyword, sfs[i].f_mntonname, fd);
420 } else if (count < 0) {
421 /* XXX */
424 return(calls);
427 static void
428 mountctl_list(const char *keyword, const char *mountpt, int __unused fd, void *info)
430 struct mountctl_journal_ret_status *rstat = info;
432 printf("%s:%s\n", mountpt, rstat->id[0] ? rstat->id : "<NOID>");
433 printf(" membufsize=%s\n", numtostr(rstat->membufsize));
434 printf(" membufused=%s\n", numtostr(rstat->membufused));
435 printf(" membufiopend=%s\n", numtostr(rstat->membufiopend));
436 printf(" total_bytes=%s\n", numtostr(rstat->bytessent));
439 static void
440 mountctl_add(const char *keyword, const char *mountpt, int fd)
442 struct mountctl_install_journal joinfo;
443 int error;
445 bzero(&joinfo, sizeof(joinfo));
446 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
447 if (memfifo_opt > 0)
448 joinfo.membufsize = memfifo_opt;
450 error = mountctl(mountpt, MOUNTCTL_INSTALL_VFS_JOURNAL, fd,
451 &joinfo, sizeof(joinfo), NULL, 0);
452 if (error == 0) {
453 fprintf(stderr, "%s:%s added\n", mountpt, joinfo.id);
454 } else {
455 fprintf(stderr, "%s:%s failed to add, error %s\n", mountpt, joinfo.id, strerror(errno));
459 static void
460 mountctl_delete(const char *keyword, const char *mountpt, int __unused fd, void __unused *info)
462 struct mountctl_remove_journal joinfo;
463 int error;
465 bzero(&joinfo, sizeof(joinfo));
466 snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
467 error = mountctl(mountpt, MOUNTCTL_REMOVE_VFS_JOURNAL, -1,
468 &joinfo, sizeof(joinfo), NULL, 0);
469 if (error == 0) {
470 fprintf(stderr, "%s:%s deleted\n", mountpt, joinfo.id);
471 } else {
472 fprintf(stderr, "%s:%s deletion failed, error %s\n", mountpt, joinfo.id, strerror(errno));
476 static void
477 mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *info)
479 fprintf(stderr, "modify not yet implemented\n");
483 static volatile
484 void
485 usage(void)
487 printf(
488 " mountctl -l [tag/mountpt | mountpt:tag]\n"
489 " mountctl -a [-w output_path] [-x filedesc]\n"
490 " [-o option] [-o option ...] mountpt:tag\n"
491 " mountctl -d [tag/mountpt | mountpt:tag]\n"
492 " mountctl -m [-o option] [-o option ...] [tag/mountpt | mountpt:tag]\n"
493 " mountctl -FZSCA [tag/mountpt | mountpt:tag]\n"
495 exit(1);
498 static
499 int64_t
500 getsize(const char *str)
502 const char *suffix;
503 int64_t val;
505 val = strtoll(str, &suffix, 0);
506 if (suffix) {
507 switch(*suffix) {
508 case 'b':
509 break;
510 case 't':
511 val *= 1024;
512 /* fall through */
513 case 'g':
514 val *= 1024;
515 /* fall through */
516 case 'm':
517 val *= 1024;
518 /* fall through */
519 case 'k':
520 val *= 1024;
521 /* fall through */
522 break;
523 default:
524 fprintf(stderr, "data value '%s' has unknown suffix\n", str);
525 exit(1);
528 return(val);
531 static
532 const char *
533 numtostr(int64_t num)
535 static char buf[64];
536 int n;
537 double v = num;
539 if (num < 1024)
540 snprintf(buf, sizeof(buf), "%lld", num);
541 else if (num < 10 * 1024)
542 snprintf(buf, sizeof(buf), "%3.2fK", num / 1024.0);
543 else if (num < 1024 * 1024)
544 snprintf(buf, sizeof(buf), "%3.0fK", num / 1024.0);
545 else if (num < 10 * 1024 * 1024)
546 snprintf(buf, sizeof(buf), "%3.2fM", num / (1024.0 * 1024.0));
547 else if (num < 1024 * 1024 * 1024)
548 snprintf(buf, sizeof(buf), "%3.0fM", num / (1024.0 * 1024.0));
549 else if (num < 10LL * 1024 * 1024 * 1024)
550 snprintf(buf, sizeof(buf), "%3.2fG", num / (1024.0 * 1024.0 * 1024.0));
551 else
552 snprintf(buf, sizeof(buf), "%3.0fG", num / (1024.0 * 1024.0 * 1024.0));
553 return(buf);