Merge from vendor branch GCC:
[dragonfly.git] / sbin / mountctl / mountctl.c
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:
10  * 
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.
20  * 
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.
33  * 
34  * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.2 2005/01/09 03:06:14 dillon Exp $
35  */
36 /*
37  * This utility implements the userland mountctl command which is used to
38  * manage high level journaling on mount points.
39  */
40
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>
52
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);
58
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 *);
67
68 /*
69  * For all options 0 means unspecified, -1 means noOPT or nonOPT, and a
70  * positive number indicates enabling or execution of the option.
71  */
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;
81
82 int
83 main(int ac, char **av)
84 {
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;
98
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();
106             }
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();
113             }
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();
123             }
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();
133             }
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();
166         }
167     }
168     ac -= optind;
169     av += optind;
170
171     /*
172      * Parse the keyword and/or mount point.
173      */
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();
180         }
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;
190             }
191         } else {
192             keyword = av[0];
193         }
194         break;
195     default:
196         fprintf(stderr, "unexpected extra arguments to command\n");
197         usage();
198     }
199
200     /*
201      * Additional sanity checks
202      */
203     if (aopt + dopt + lopt + mopt + mimplied == 0) {
204         fprintf(stderr, "no action or implied action options were specified\n");
205         usage();
206     }
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();
212     }
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();
217     }
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();
223     }
224
225     /*
226      * Open the journaling file descriptor if required.
227      */
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);
235         }
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;
242     }
243
244     /*
245      * And finally execute the core command.
246      */
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");
257     }
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");
264     }
265
266     return(0);
267 }
268
269 static void
270 parse_option_keyword(const char *opt, const char **wopt, const char **xopt)
271 {
272     char *str = strdup(opt);
273     char *name;
274     char *val;
275     int negate;
276     int hasval;
277     int cannotnegate;
278
279     /*
280      * multiple comma delimited options may be specified.
281      */
282     while ((name = strsep(&str, ",")) != NULL) {
283         /*
284          * some options have associated data.
285          */
286         if ((val = strchr(name, '=')) != NULL)
287             *val++ = 0;
288
289         /*
290          * options beginning with 'no' or 'non' are negated.  A positive
291          * number means not negated, a negative number means negated.
292          */
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;
302         }
303
304         /*
305          * Parse supported options
306          */
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;
317             }
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 */
327             }
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);
357         }
358
359         /*
360          * Sanity checks
361          */
362         if (cannotnegate && negate < 0) {
363             fprintf(stderr, "option %s may not be negated\n", name);
364             exit(1);
365         }
366         if (hasval && val == NULL) {
367             fprintf(stderr, "option %s requires assigned data\n", name);
368             exit(1);
369         }
370         if (hasval == 0 && val) {
371             fprintf(stderr, "option %s does not take an assignment\n", name);
372             exit(1);
373         }
374
375     }
376 }
377
378 static int
379 mountctl_scan(void (*func)(const char *, const char *, int, void *),
380             const char *keyword, const char *mountpt, int fd)
381 {
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 */
388
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;
400         }
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);
407         }
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;
413             }
414         }
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);
419             }
420         } else if (count < 0) {
421             /* XXX */
422         }
423     }
424     return(calls);
425 }
426
427 static void
428 mountctl_list(const char *keyword, const char *mountpt, int __unused fd, void *info)
429 {
430     struct mountctl_journal_ret_status *rstat = info;
431
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));
437 }
438
439 static void
440 mountctl_add(const char *keyword, const char *mountpt, int fd)
441 {
442     struct mountctl_install_journal joinfo;
443     int error;
444
445     bzero(&joinfo, sizeof(joinfo));
446     snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
447
448     error = mountctl(mountpt, MOUNTCTL_INSTALL_VFS_JOURNAL, fd,
449                         &joinfo, sizeof(joinfo), NULL, 0);
450     if (error == 0) {
451         fprintf(stderr, "%s:%s added\n", mountpt, joinfo.id);
452     } else {
453         fprintf(stderr, "%s:%s failed to add, error %s\n", mountpt, joinfo.id, strerror(errno));
454     }
455 }
456
457 static void
458 mountctl_delete(const char *keyword, const char *mountpt, int __unused fd, void __unused *info)
459 {
460     struct mountctl_remove_journal joinfo;
461     int error;
462
463     bzero(&joinfo, sizeof(joinfo));
464     snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
465     error = mountctl(mountpt, MOUNTCTL_REMOVE_VFS_JOURNAL, -1,
466                         &joinfo, sizeof(joinfo), NULL, 0);
467     if (error == 0) {
468         fprintf(stderr, "%s:%s deleted\n", mountpt, joinfo.id);
469     } else {
470         fprintf(stderr, "%s:%s deletion failed, error %s\n", mountpt, joinfo.id, strerror(errno));
471     }
472 }
473
474 static void
475 mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *info)
476 {
477     fprintf(stderr, "modify not yet implemented\n");
478 }
479
480
481 static volatile
482 void
483 usage(void)
484 {
485     printf(
486         " mountctl -l [tag/mountpt | mountpt:tag]\n"
487         " mountctl -a [-w output_path] [-x filedesc]\n"
488         "             [-o option] [-o option ...] mountpt:tag\n"
489         " mountctl -d [tag/mountpt | mountpt:tag]\n"
490         " mountctl -m [-o option] [-o option ...] [tag/mountpt | mountpt:tag]\n"
491         " mountctl -FZSCA [tag/mountpt | mountpt:tag]\n"
492     );
493     exit(1);
494 }
495
496 static
497 int64_t
498 getsize(const char *str)
499 {
500     const char *suffix;
501     int64_t val;
502
503     val = strtoll(str, &suffix, 0);
504     if (suffix) {
505         switch(*suffix) {
506         case 'b':
507             break;
508         case 't':
509             val *= 1024;
510             /* fall through */
511         case 'g':
512             val *= 1024;
513             /* fall through */
514         case 'm':
515             val *= 1024;
516             /* fall through */
517         case 'k':
518             val *= 1024;
519             /* fall through */
520             break;
521         default:
522             fprintf(stderr, "data value '%s' has unknown suffix\n", str);
523             exit(1);
524         }
525     }
526     return(val);
527 }
528
529 static
530 const char *
531 numtostr(int64_t num)
532 {
533     static char buf[64];
534     int n;
535     double v = num;
536
537     if (num < 1024)
538         snprintf(buf, sizeof(buf), "%lld", num);
539     else if (num < 10 * 1024)
540         snprintf(buf, sizeof(buf), "%3.2fK", num / 1024.0);
541     else if (num < 1024 * 1024)
542         snprintf(buf, sizeof(buf), "%3.0fK", num / 1024.0);
543     else if (num < 10 * 1024 * 1024)
544         snprintf(buf, sizeof(buf), "%3.2fM", num / (1024.0 * 1024.0));
545     else if (num < 1024 * 1024 * 1024)
546         snprintf(buf, sizeof(buf), "%3.0fM", num / (1024.0 * 1024.0));
547     else if (num < 10LL * 1024 * 1024 * 1024)
548         snprintf(buf, sizeof(buf), "%3.2fG", num / (1024.0 * 1024.0 * 1024.0));
549     else
550         snprintf(buf, sizeof(buf), "%3.0fG", num / (1024.0 * 1024.0 * 1024.0));
551     return(buf);
552 }
553