xref: /dragonfly/sbin/mountctl/mountctl.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * This code is derived from software contributed to The DragonFly Project
5*86d7f5d3SJohn Marino  * by Matthew Dillon <dillon@backplane.com>
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
8*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
9*86d7f5d3SJohn Marino  * are met:
10*86d7f5d3SJohn Marino  *
11*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
12*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
13*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
14*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in
15*86d7f5d3SJohn Marino  *    the documentation and/or other materials provided with the
16*86d7f5d3SJohn Marino  *    distribution.
17*86d7f5d3SJohn Marino  * 3. Neither the name of The DragonFly Project nor the names of its
18*86d7f5d3SJohn Marino  *    contributors may be used to endorse or promote products derived
19*86d7f5d3SJohn Marino  *    from this software without specific, prior written permission.
20*86d7f5d3SJohn Marino  *
21*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*86d7f5d3SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*86d7f5d3SJohn Marino  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24*86d7f5d3SJohn Marino  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25*86d7f5d3SJohn Marino  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26*86d7f5d3SJohn Marino  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27*86d7f5d3SJohn Marino  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28*86d7f5d3SJohn Marino  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29*86d7f5d3SJohn Marino  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30*86d7f5d3SJohn Marino  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31*86d7f5d3SJohn Marino  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*86d7f5d3SJohn Marino  * SUCH DAMAGE.
33*86d7f5d3SJohn Marino  *
34*86d7f5d3SJohn Marino  * $DragonFly: src/sbin/mountctl/mountctl.c,v 1.10 2008/02/05 20:49:50 dillon Exp $
35*86d7f5d3SJohn Marino  */
36*86d7f5d3SJohn Marino /*
37*86d7f5d3SJohn Marino  * This utility implements the userland mountctl command which is used to
38*86d7f5d3SJohn Marino  * manage high level journaling on mount points.
39*86d7f5d3SJohn Marino  */
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino #include <sys/types.h>
42*86d7f5d3SJohn Marino #include <sys/param.h>
43*86d7f5d3SJohn Marino #include <sys/ucred.h>
44*86d7f5d3SJohn Marino #include <sys/mount.h>
45*86d7f5d3SJohn Marino #include <sys/time.h>
46*86d7f5d3SJohn Marino #include <sys/mountctl.h>
47*86d7f5d3SJohn Marino #include <stdio.h>
48*86d7f5d3SJohn Marino #include <stdlib.h>
49*86d7f5d3SJohn Marino #include <fcntl.h>
50*86d7f5d3SJohn Marino #include <string.h>
51*86d7f5d3SJohn Marino #include <unistd.h>
52*86d7f5d3SJohn Marino #include <errno.h>
53*86d7f5d3SJohn Marino 
54*86d7f5d3SJohn Marino static void usage(void);
55*86d7f5d3SJohn Marino static void parse_option_keyword(const char *opt,
56*86d7f5d3SJohn Marino 		const char **wopt, const char **xopt);
57*86d7f5d3SJohn Marino static int64_t getsize(const char *str);
58*86d7f5d3SJohn Marino static const char *numtostr(int64_t num);
59*86d7f5d3SJohn Marino 
60*86d7f5d3SJohn Marino static int mountctl_scan(void (*func)(const char *, const char *, int, void *),
61*86d7f5d3SJohn Marino 		const char *keyword, const char *mountpt, int fd);
62*86d7f5d3SJohn Marino static void mountctl_list(const char *keyword, const char *mountpt,
63*86d7f5d3SJohn Marino 		int __unused fd, void *info);
64*86d7f5d3SJohn Marino static void mountctl_add(const char *keyword, const char *mountpt, int fd);
65*86d7f5d3SJohn Marino static void mountctl_restart(const char *keyword, const char *mountpt,
66*86d7f5d3SJohn Marino 		int fd, void __unused *);
67*86d7f5d3SJohn Marino static void mountctl_delete(const char *keyword, const char *mountpt,
68*86d7f5d3SJohn Marino 		int __unused fd, void __unused *);
69*86d7f5d3SJohn Marino static void mountctl_modify(const char *keyword, const char *mountpt, int fd, void __unused *);
70*86d7f5d3SJohn Marino 
71*86d7f5d3SJohn Marino /*
72*86d7f5d3SJohn Marino  * For all options 0 means unspecified, -1 means noOPT or nonOPT, and a
73*86d7f5d3SJohn Marino  * positive number indicates enabling or execution of the option.
74*86d7f5d3SJohn Marino  */
75*86d7f5d3SJohn Marino static int exitCode;
76*86d7f5d3SJohn Marino static int freeze_opt;
77*86d7f5d3SJohn Marino static int start_opt;
78*86d7f5d3SJohn Marino static int close_opt;
79*86d7f5d3SJohn Marino static int abort_opt;
80*86d7f5d3SJohn Marino static int flush_opt;
81*86d7f5d3SJohn Marino static int reversable_opt;
82*86d7f5d3SJohn Marino static int twoway_opt;
83*86d7f5d3SJohn Marino static int output_safety_override_opt;
84*86d7f5d3SJohn Marino static int64_t memfifo_opt;
85*86d7f5d3SJohn Marino static int64_t swapfifo_opt;
86*86d7f5d3SJohn Marino 
87*86d7f5d3SJohn Marino int
main(int ac,char ** av)88*86d7f5d3SJohn Marino main(int ac, char **av)
89*86d7f5d3SJohn Marino {
90*86d7f5d3SJohn Marino     int fd;
91*86d7f5d3SJohn Marino     int ch;
92*86d7f5d3SJohn Marino     int aopt = 0;
93*86d7f5d3SJohn Marino     int dopt = 0;
94*86d7f5d3SJohn Marino     int fopt = 0;
95*86d7f5d3SJohn Marino     int lopt = 0;
96*86d7f5d3SJohn Marino     int mopt = 0;
97*86d7f5d3SJohn Marino     int ropt = 0;
98*86d7f5d3SJohn Marino     int mimplied = 0;
99*86d7f5d3SJohn Marino     const char *wopt = NULL;
100*86d7f5d3SJohn Marino     const char *xopt = NULL;
101*86d7f5d3SJohn Marino     const char *keyword = NULL;
102*86d7f5d3SJohn Marino     const char *mountpt = NULL;
103*86d7f5d3SJohn Marino     char *tmp;
104*86d7f5d3SJohn Marino 
105*86d7f5d3SJohn Marino     while ((ch = getopt(ac, av, "2adflmo:rw:x:ACFSW:X:Z")) != -1) {
106*86d7f5d3SJohn Marino 	switch(ch) {
107*86d7f5d3SJohn Marino 	case '2':
108*86d7f5d3SJohn Marino 	    twoway_opt = 1;
109*86d7f5d3SJohn Marino 	    break;
110*86d7f5d3SJohn Marino 	case 'r':
111*86d7f5d3SJohn Marino 	    ropt = 1;
112*86d7f5d3SJohn Marino 	    if (aopt + dopt + lopt + mopt + ropt != 1) {
113*86d7f5d3SJohn Marino 		fprintf(stderr, "too many action options specified\n");
114*86d7f5d3SJohn Marino 		usage();
115*86d7f5d3SJohn Marino 	    }
116*86d7f5d3SJohn Marino 	    break;
117*86d7f5d3SJohn Marino 	case 'a':
118*86d7f5d3SJohn Marino 	    aopt = 1;
119*86d7f5d3SJohn Marino 	    if (aopt + dopt + lopt + mopt + ropt != 1) {
120*86d7f5d3SJohn Marino 		fprintf(stderr, "too many action options specified\n");
121*86d7f5d3SJohn Marino 		usage();
122*86d7f5d3SJohn Marino 	    }
123*86d7f5d3SJohn Marino 	    break;
124*86d7f5d3SJohn Marino 	case 'd':
125*86d7f5d3SJohn Marino 	    dopt = 1;
126*86d7f5d3SJohn Marino 	    if (aopt + dopt + lopt + mopt + ropt != 1) {
127*86d7f5d3SJohn Marino 		fprintf(stderr, "too many action options specified\n");
128*86d7f5d3SJohn Marino 		usage();
129*86d7f5d3SJohn Marino 	    }
130*86d7f5d3SJohn Marino 	    break;
131*86d7f5d3SJohn Marino 	case 'f':
132*86d7f5d3SJohn Marino 	    fopt = 1;
133*86d7f5d3SJohn Marino 	    break;
134*86d7f5d3SJohn Marino 	case 'l':
135*86d7f5d3SJohn Marino 	    lopt = 1;
136*86d7f5d3SJohn Marino 	    if (aopt + dopt + lopt + mopt + ropt != 1) {
137*86d7f5d3SJohn Marino 		fprintf(stderr, "too many action options specified\n");
138*86d7f5d3SJohn Marino 		usage();
139*86d7f5d3SJohn Marino 	    }
140*86d7f5d3SJohn Marino 	    break;
141*86d7f5d3SJohn Marino 	case 'o':
142*86d7f5d3SJohn Marino 	    parse_option_keyword(optarg, &wopt, &xopt);
143*86d7f5d3SJohn Marino 	    break;
144*86d7f5d3SJohn Marino 	case 'm':
145*86d7f5d3SJohn Marino 	    mopt = 1;
146*86d7f5d3SJohn Marino 	    if (aopt + dopt + lopt + mopt + ropt != 1) {
147*86d7f5d3SJohn Marino 		fprintf(stderr, "too many action options specified\n");
148*86d7f5d3SJohn Marino 		usage();
149*86d7f5d3SJohn Marino 	    }
150*86d7f5d3SJohn Marino 	    break;
151*86d7f5d3SJohn Marino 	case 'W':
152*86d7f5d3SJohn Marino 	    output_safety_override_opt = 1;
153*86d7f5d3SJohn Marino 	    /* fall through */
154*86d7f5d3SJohn Marino 	case 'w':
155*86d7f5d3SJohn Marino 	    wopt = optarg;
156*86d7f5d3SJohn Marino 	    mimplied = 1;
157*86d7f5d3SJohn Marino 	    break;
158*86d7f5d3SJohn Marino 	case 'X':
159*86d7f5d3SJohn Marino 	    output_safety_override_opt = 1;
160*86d7f5d3SJohn Marino 	    /* fall through */
161*86d7f5d3SJohn Marino 	case 'x':
162*86d7f5d3SJohn Marino 	    xopt = optarg;
163*86d7f5d3SJohn Marino 	    mimplied = 1;
164*86d7f5d3SJohn Marino 	    break;
165*86d7f5d3SJohn Marino 	case 'A':
166*86d7f5d3SJohn Marino 	    mimplied = 1;
167*86d7f5d3SJohn Marino 	    abort_opt = 1;
168*86d7f5d3SJohn Marino 	    break;
169*86d7f5d3SJohn Marino 	case 'C':
170*86d7f5d3SJohn Marino 	    mimplied = 1;
171*86d7f5d3SJohn Marino 	    close_opt = 1;
172*86d7f5d3SJohn Marino 	    break;
173*86d7f5d3SJohn Marino 	case 'F':
174*86d7f5d3SJohn Marino 	    mimplied = 1;
175*86d7f5d3SJohn Marino 	    flush_opt = 1;
176*86d7f5d3SJohn Marino 	    break;
177*86d7f5d3SJohn Marino 	case 'S':
178*86d7f5d3SJohn Marino 	    mimplied = 1;
179*86d7f5d3SJohn Marino 	    start_opt = 1;
180*86d7f5d3SJohn Marino 	    break;
181*86d7f5d3SJohn Marino 	case 'Z':
182*86d7f5d3SJohn Marino 	    mimplied = 1;
183*86d7f5d3SJohn Marino 	    freeze_opt = 1;
184*86d7f5d3SJohn Marino 	    break;
185*86d7f5d3SJohn Marino 	default:
186*86d7f5d3SJohn Marino 	    fprintf(stderr, "unknown option: -%c\n", optopt);
187*86d7f5d3SJohn Marino 	    usage();
188*86d7f5d3SJohn Marino 	}
189*86d7f5d3SJohn Marino     }
190*86d7f5d3SJohn Marino     ac -= optind;
191*86d7f5d3SJohn Marino     av += optind;
192*86d7f5d3SJohn Marino 
193*86d7f5d3SJohn Marino     /*
194*86d7f5d3SJohn Marino      * Parse the keyword and/or mount point.
195*86d7f5d3SJohn Marino      */
196*86d7f5d3SJohn Marino     switch(ac) {
197*86d7f5d3SJohn Marino     case 0:
198*86d7f5d3SJohn Marino 	if (aopt || ropt) {
199*86d7f5d3SJohn Marino 	    fprintf(stderr, "action requires a tag and/or mount "
200*86d7f5d3SJohn Marino 			    "point to be specified\n");
201*86d7f5d3SJohn Marino 	    usage();
202*86d7f5d3SJohn Marino 	}
203*86d7f5d3SJohn Marino 	break;
204*86d7f5d3SJohn Marino     case 1:
205*86d7f5d3SJohn Marino 	if (av[0][0] == '/') {
206*86d7f5d3SJohn Marino 	    mountpt = av[0];
207*86d7f5d3SJohn Marino 	    if ((keyword = strchr(mountpt, ':')) != NULL) {
208*86d7f5d3SJohn Marino 		++keyword;
209*86d7f5d3SJohn Marino 		tmp = strdup(mountpt);
210*86d7f5d3SJohn Marino 		*strchr(tmp, ':') = 0;
211*86d7f5d3SJohn Marino 		mountpt = tmp;
212*86d7f5d3SJohn Marino 	    }
213*86d7f5d3SJohn Marino 	} else {
214*86d7f5d3SJohn Marino 	    keyword = av[0];
215*86d7f5d3SJohn Marino 	}
216*86d7f5d3SJohn Marino 	break;
217*86d7f5d3SJohn Marino     default:
218*86d7f5d3SJohn Marino 	fprintf(stderr, "unexpected extra arguments to command\n");
219*86d7f5d3SJohn Marino 	usage();
220*86d7f5d3SJohn Marino     }
221*86d7f5d3SJohn Marino 
222*86d7f5d3SJohn Marino     /*
223*86d7f5d3SJohn Marino      * Additional sanity checks
224*86d7f5d3SJohn Marino      */
225*86d7f5d3SJohn Marino     if (aopt + dopt + lopt + mopt + ropt + mimplied == 0) {
226*86d7f5d3SJohn Marino 	fprintf(stderr, "no action or implied action options were specified\n");
227*86d7f5d3SJohn Marino 	usage();
228*86d7f5d3SJohn Marino     }
229*86d7f5d3SJohn Marino     if (mimplied && aopt + dopt + lopt + ropt == 0)
230*86d7f5d3SJohn Marino 	mopt = 1;
231*86d7f5d3SJohn Marino     if ((wopt || xopt) && !(aopt || ropt || mopt)) {
232*86d7f5d3SJohn Marino 	fprintf(stderr, "-w/-x/path/fd options may only be used with -m/-a/-r\n");
233*86d7f5d3SJohn Marino 	usage();
234*86d7f5d3SJohn Marino     }
235*86d7f5d3SJohn Marino     if (aopt && (keyword == NULL || mountpt == NULL)) {
236*86d7f5d3SJohn Marino 	fprintf(stderr, "a keyword AND a mountpt must be specified "
237*86d7f5d3SJohn Marino 			"when adding a journal\n");
238*86d7f5d3SJohn Marino 	usage();
239*86d7f5d3SJohn Marino     }
240*86d7f5d3SJohn Marino     if (fopt == 0 && mopt + dopt && keyword == NULL && mountpt == NULL) {
241*86d7f5d3SJohn Marino 	fprintf(stderr, "a keyword, a mountpt, or both must be specified "
242*86d7f5d3SJohn Marino 			"when modifying or deleting a journal, unless "
243*86d7f5d3SJohn Marino 			"-f is also specified for safety\n");
244*86d7f5d3SJohn Marino 	usage();
245*86d7f5d3SJohn Marino     }
246*86d7f5d3SJohn Marino 
247*86d7f5d3SJohn Marino     /*
248*86d7f5d3SJohn Marino      * Open the journaling file descriptor if required.
249*86d7f5d3SJohn Marino      */
250*86d7f5d3SJohn Marino     if (wopt && xopt) {
251*86d7f5d3SJohn Marino 	fprintf(stderr, "you must specify only one of -w/-x/path/fd\n");
252*86d7f5d3SJohn Marino 	exit(1);
253*86d7f5d3SJohn Marino     } else if (wopt) {
254*86d7f5d3SJohn Marino 	if ((fd = open(wopt, O_RDWR|O_CREAT|O_APPEND, 0666)) < 0) {
255*86d7f5d3SJohn Marino 	    fprintf(stderr, "unable to create %s: %s\n", wopt, strerror(errno));
256*86d7f5d3SJohn Marino 	    exit(1);
257*86d7f5d3SJohn Marino 	}
258*86d7f5d3SJohn Marino     } else if (xopt) {
259*86d7f5d3SJohn Marino 	fd = strtol(xopt, NULL, 0);
260*86d7f5d3SJohn Marino     } else if (aopt || ropt) {
261*86d7f5d3SJohn Marino 	fd = 1;		/* stdout default for -a */
262*86d7f5d3SJohn Marino     } else {
263*86d7f5d3SJohn Marino 	fd = -1;
264*86d7f5d3SJohn Marino     }
265*86d7f5d3SJohn Marino 
266*86d7f5d3SJohn Marino     /*
267*86d7f5d3SJohn Marino      * And finally execute the core command.
268*86d7f5d3SJohn Marino      */
269*86d7f5d3SJohn Marino     if (lopt)
270*86d7f5d3SJohn Marino 	mountctl_scan(mountctl_list, keyword, mountpt, fd);
271*86d7f5d3SJohn Marino     if (aopt)
272*86d7f5d3SJohn Marino 	mountctl_add(keyword, mountpt, fd);
273*86d7f5d3SJohn Marino     if (ropt) {
274*86d7f5d3SJohn Marino 	ch = mountctl_scan(mountctl_restart, keyword, mountpt, fd);
275*86d7f5d3SJohn Marino 	if (ch)
276*86d7f5d3SJohn Marino 	    fprintf(stderr, "%d journals restarted\n", ch);
277*86d7f5d3SJohn Marino 	else
278*86d7f5d3SJohn Marino 	    fprintf(stderr, "Unable to locate any matching journals\n");
279*86d7f5d3SJohn Marino     }
280*86d7f5d3SJohn Marino     if (dopt) {
281*86d7f5d3SJohn Marino 	ch = mountctl_scan(mountctl_delete, keyword, mountpt, -1);
282*86d7f5d3SJohn Marino 	if (ch)
283*86d7f5d3SJohn Marino 	    fprintf(stderr, "%d journals deleted\n", ch);
284*86d7f5d3SJohn Marino 	else
285*86d7f5d3SJohn Marino 	    fprintf(stderr, "Unable to locate any matching journals\n");
286*86d7f5d3SJohn Marino     }
287*86d7f5d3SJohn Marino     if (mopt) {
288*86d7f5d3SJohn Marino 	ch = mountctl_scan(mountctl_modify, keyword, mountpt, fd);
289*86d7f5d3SJohn Marino 	if (ch)
290*86d7f5d3SJohn Marino 	    fprintf(stderr, "%d journals modified\n", ch);
291*86d7f5d3SJohn Marino 	else
292*86d7f5d3SJohn Marino 	    fprintf(stderr, "Unable to locate any matching journals\n");
293*86d7f5d3SJohn Marino     }
294*86d7f5d3SJohn Marino 
295*86d7f5d3SJohn Marino     return(exitCode);
296*86d7f5d3SJohn Marino }
297*86d7f5d3SJohn Marino 
298*86d7f5d3SJohn Marino static void
parse_option_keyword(const char * opt,const char ** wopt,const char ** xopt)299*86d7f5d3SJohn Marino parse_option_keyword(const char *opt, const char **wopt, const char **xopt)
300*86d7f5d3SJohn Marino {
301*86d7f5d3SJohn Marino     char *str = strdup(opt);
302*86d7f5d3SJohn Marino     char *name;
303*86d7f5d3SJohn Marino     char *val;
304*86d7f5d3SJohn Marino     int negate;
305*86d7f5d3SJohn Marino     int hasval;
306*86d7f5d3SJohn Marino     int cannotnegate;
307*86d7f5d3SJohn Marino 
308*86d7f5d3SJohn Marino     /*
309*86d7f5d3SJohn Marino      * multiple comma delimited options may be specified.
310*86d7f5d3SJohn Marino      */
311*86d7f5d3SJohn Marino     while ((name = strsep(&str, ",")) != NULL) {
312*86d7f5d3SJohn Marino 	/*
313*86d7f5d3SJohn Marino 	 * some options have associated data.
314*86d7f5d3SJohn Marino 	 */
315*86d7f5d3SJohn Marino 	if ((val = strchr(name, '=')) != NULL)
316*86d7f5d3SJohn Marino 	    *val++ = 0;
317*86d7f5d3SJohn Marino 
318*86d7f5d3SJohn Marino 	/*
319*86d7f5d3SJohn Marino 	 * options beginning with 'no' or 'non' are negated.  A positive
320*86d7f5d3SJohn Marino 	 * number means not negated, a negative number means negated.
321*86d7f5d3SJohn Marino 	 */
322*86d7f5d3SJohn Marino 	negate = 1;
323*86d7f5d3SJohn Marino 	cannotnegate = 0;
324*86d7f5d3SJohn Marino 	hasval = 0;
325*86d7f5d3SJohn Marino 	if (strncmp(name, "non", 3) == 0) {
326*86d7f5d3SJohn Marino 	    name += 3;
327*86d7f5d3SJohn Marino 	    negate = -1;
328*86d7f5d3SJohn Marino 	} else if (strncmp(name, "no", 2) == 0) {
329*86d7f5d3SJohn Marino 	    name += 2;
330*86d7f5d3SJohn Marino 	    negate = -1;
331*86d7f5d3SJohn Marino 	}
332*86d7f5d3SJohn Marino 
333*86d7f5d3SJohn Marino 	/*
334*86d7f5d3SJohn Marino 	 * Parse supported options
335*86d7f5d3SJohn Marino 	 */
336*86d7f5d3SJohn Marino 	if (strcmp(name, "undo") == 0) {
337*86d7f5d3SJohn Marino 	    reversable_opt = negate;
338*86d7f5d3SJohn Marino 	} else if (strcmp(name, "reversable") == 0) {
339*86d7f5d3SJohn Marino 	    reversable_opt = negate;
340*86d7f5d3SJohn Marino 	} else if (strcmp(name, "twoway") == 0) {
341*86d7f5d3SJohn Marino 	    twoway_opt = negate;
342*86d7f5d3SJohn Marino 	} else if (strcmp(name, "memfifo") == 0) {
343*86d7f5d3SJohn Marino 	    cannotnegate = 1;
344*86d7f5d3SJohn Marino 	    hasval = 1;
345*86d7f5d3SJohn Marino 	    if (val) {
346*86d7f5d3SJohn Marino 		if ((memfifo_opt = getsize(val)) == 0)
347*86d7f5d3SJohn Marino 		    memfifo_opt = -1;
348*86d7f5d3SJohn Marino 	    }
349*86d7f5d3SJohn Marino 	} else if (strcmp(name, "swapfifo") == 0) {
350*86d7f5d3SJohn Marino 	    if (val) {
351*86d7f5d3SJohn Marino 		hasval = 1;
352*86d7f5d3SJohn Marino 		if ((swapfifo_opt = getsize(val)) == 0)
353*86d7f5d3SJohn Marino 		    swapfifo_opt = -1;
354*86d7f5d3SJohn Marino 	    } else if (negate < 0) {
355*86d7f5d3SJohn Marino 		swapfifo_opt = -1;
356*86d7f5d3SJohn Marino 	    } else {
357*86d7f5d3SJohn Marino 		hasval = 1;	/* force error */
358*86d7f5d3SJohn Marino 	    }
359*86d7f5d3SJohn Marino 	} else if (strcmp(name, "fd") == 0) {
360*86d7f5d3SJohn Marino 	    cannotnegate = 1;
361*86d7f5d3SJohn Marino 	    hasval = 1;
362*86d7f5d3SJohn Marino 	    if (val)
363*86d7f5d3SJohn Marino 		*xopt = val;
364*86d7f5d3SJohn Marino 	} else if (strcmp(name, "path") == 0) {
365*86d7f5d3SJohn Marino 	    cannotnegate = 1;
366*86d7f5d3SJohn Marino 	    hasval = 1;
367*86d7f5d3SJohn Marino 	    if (val)
368*86d7f5d3SJohn Marino 		*wopt = val;
369*86d7f5d3SJohn Marino 	} else if (strcmp(name, "freeze") == 0 || strcmp(name, "stop") == 0) {
370*86d7f5d3SJohn Marino 	    if (negate < 0)
371*86d7f5d3SJohn Marino 		start_opt = -negate;
372*86d7f5d3SJohn Marino 	    else
373*86d7f5d3SJohn Marino 		freeze_opt = negate;
374*86d7f5d3SJohn Marino 	} else if (strcmp(name, "start") == 0) {
375*86d7f5d3SJohn Marino 	    if (negate < 0)
376*86d7f5d3SJohn Marino 		freeze_opt = -negate;
377*86d7f5d3SJohn Marino 	    else
378*86d7f5d3SJohn Marino 		start_opt = negate;
379*86d7f5d3SJohn Marino 	} else if (strcmp(name, "close") == 0) {
380*86d7f5d3SJohn Marino 	    close_opt = negate;
381*86d7f5d3SJohn Marino 	} else if (strcmp(name, "abort") == 0) {
382*86d7f5d3SJohn Marino 	    abort_opt = negate;
383*86d7f5d3SJohn Marino 	} else if (strcmp(name, "flush") == 0) {
384*86d7f5d3SJohn Marino 	    flush_opt = negate;
385*86d7f5d3SJohn Marino 	} else {
386*86d7f5d3SJohn Marino 	    fprintf(stderr, "unknown option keyword: %s\n", name);
387*86d7f5d3SJohn Marino 	    exit(1);
388*86d7f5d3SJohn Marino 	}
389*86d7f5d3SJohn Marino 
390*86d7f5d3SJohn Marino 	/*
391*86d7f5d3SJohn Marino 	 * Sanity checks
392*86d7f5d3SJohn Marino 	 */
393*86d7f5d3SJohn Marino 	if (cannotnegate && negate < 0) {
394*86d7f5d3SJohn Marino 	    fprintf(stderr, "option %s may not be negated\n", name);
395*86d7f5d3SJohn Marino 	    exit(1);
396*86d7f5d3SJohn Marino 	}
397*86d7f5d3SJohn Marino 	if (hasval && val == NULL) {
398*86d7f5d3SJohn Marino 	    fprintf(stderr, "option %s requires assigned data\n", name);
399*86d7f5d3SJohn Marino 	    exit(1);
400*86d7f5d3SJohn Marino 	}
401*86d7f5d3SJohn Marino 	if (hasval == 0 && val) {
402*86d7f5d3SJohn Marino 	    fprintf(stderr, "option %s does not take an assignment\n", name);
403*86d7f5d3SJohn Marino 	    exit(1);
404*86d7f5d3SJohn Marino 	}
405*86d7f5d3SJohn Marino 
406*86d7f5d3SJohn Marino     }
407*86d7f5d3SJohn Marino }
408*86d7f5d3SJohn Marino 
409*86d7f5d3SJohn Marino static int
mountctl_scan(void (* func)(const char *,const char *,int,void *),const char * keyword,const char * mountpt,int fd)410*86d7f5d3SJohn Marino mountctl_scan(void (*func)(const char *, const char *, int, void *),
411*86d7f5d3SJohn Marino 	    const char *keyword, const char *mountpt, int fd)
412*86d7f5d3SJohn Marino {
413*86d7f5d3SJohn Marino     struct statfs *sfs;
414*86d7f5d3SJohn Marino     int count;
415*86d7f5d3SJohn Marino     int calls;
416*86d7f5d3SJohn Marino     int i;
417*86d7f5d3SJohn Marino     struct mountctl_status_journal statreq;
418*86d7f5d3SJohn Marino     struct mountctl_journal_ret_status rstat[4];	/* BIG */
419*86d7f5d3SJohn Marino 
420*86d7f5d3SJohn Marino     calls = 0;
421*86d7f5d3SJohn Marino     if (mountpt) {
422*86d7f5d3SJohn Marino 	bzero(&statreq, sizeof(statreq));
423*86d7f5d3SJohn Marino 	if (keyword) {
424*86d7f5d3SJohn Marino 	    statreq.index = MC_JOURNAL_INDEX_ID;
425*86d7f5d3SJohn Marino 	    count = strlen(keyword);
426*86d7f5d3SJohn Marino 	    if (count > JIDMAX)
427*86d7f5d3SJohn Marino 		count = JIDMAX;
428*86d7f5d3SJohn Marino 	    bcopy(keyword, statreq.id, count);
429*86d7f5d3SJohn Marino 	} else {
430*86d7f5d3SJohn Marino 	    statreq.index = MC_JOURNAL_INDEX_ALL;
431*86d7f5d3SJohn Marino 	}
432*86d7f5d3SJohn Marino 	count = mountctl(mountpt, MOUNTCTL_STATUS_VFS_JOURNAL, -1,
433*86d7f5d3SJohn Marino 			&statreq, sizeof(statreq), &rstat, sizeof(rstat));
434*86d7f5d3SJohn Marino 	if (count > 0 && rstat[0].recsize != sizeof(rstat[0])) {
435*86d7f5d3SJohn Marino 	    fprintf(stderr, "Unable to access status, "
436*86d7f5d3SJohn Marino 			    "structure size mismatch\n");
437*86d7f5d3SJohn Marino 	    exit(1);
438*86d7f5d3SJohn Marino 	}
439*86d7f5d3SJohn Marino 	if (count > 0) {
440*86d7f5d3SJohn Marino 	    count /= sizeof(rstat[0]);
441*86d7f5d3SJohn Marino 	    for (i = 0; i < count; ++i) {
442*86d7f5d3SJohn Marino 		func(rstat[i].id, mountpt, fd, &rstat[i]);
443*86d7f5d3SJohn Marino 		++calls;
444*86d7f5d3SJohn Marino 	    }
445*86d7f5d3SJohn Marino 	}
446*86d7f5d3SJohn Marino     } else {
447*86d7f5d3SJohn Marino 	if ((count = getmntinfo(&sfs, MNT_WAIT)) > 0) {
448*86d7f5d3SJohn Marino 	    for (i = 0; i < count; ++i) {
449*86d7f5d3SJohn Marino 		calls += mountctl_scan(func, keyword, sfs[i].f_mntonname, fd);
450*86d7f5d3SJohn Marino 	    }
451*86d7f5d3SJohn Marino 	} else if (count < 0) {
452*86d7f5d3SJohn Marino 	    /* XXX */
453*86d7f5d3SJohn Marino 	}
454*86d7f5d3SJohn Marino     }
455*86d7f5d3SJohn Marino     return(calls);
456*86d7f5d3SJohn Marino }
457*86d7f5d3SJohn Marino 
458*86d7f5d3SJohn Marino static void
mountctl_list(const char * keyword __unused,const char * mountpt,int fd __unused,void * info)459*86d7f5d3SJohn Marino mountctl_list(const char *keyword __unused, const char *mountpt,
460*86d7f5d3SJohn Marino 	      int fd __unused, void *info)
461*86d7f5d3SJohn Marino {
462*86d7f5d3SJohn Marino     struct mountctl_journal_ret_status *rstat = info;
463*86d7f5d3SJohn Marino 
464*86d7f5d3SJohn Marino     printf("%s:%s\n", mountpt, rstat->id[0] ? rstat->id : "<NOID>");
465*86d7f5d3SJohn Marino     printf("    membufsize=%s\n", numtostr(rstat->membufsize));
466*86d7f5d3SJohn Marino     printf("    membufused=%s\n", numtostr(rstat->membufused));
467*86d7f5d3SJohn Marino     printf("    membufunacked=%s\n", numtostr(rstat->membufunacked));
468*86d7f5d3SJohn Marino     printf("    total_bytes=%s\n", numtostr(rstat->bytessent));
469*86d7f5d3SJohn Marino     printf("    fifo_stalls=%jd\n", (intmax_t)rstat->fifostalls);
470*86d7f5d3SJohn Marino }
471*86d7f5d3SJohn Marino 
472*86d7f5d3SJohn Marino static void
mountctl_add(const char * keyword,const char * mountpt,int fd)473*86d7f5d3SJohn Marino mountctl_add(const char *keyword, const char *mountpt, int fd)
474*86d7f5d3SJohn Marino {
475*86d7f5d3SJohn Marino     struct mountctl_install_journal joinfo;
476*86d7f5d3SJohn Marino     struct stat st1;
477*86d7f5d3SJohn Marino     struct stat st2;
478*86d7f5d3SJohn Marino     int error;
479*86d7f5d3SJohn Marino 
480*86d7f5d3SJohn Marino     /*
481*86d7f5d3SJohn Marino      * Make sure the file descriptor is not on the same filesystem as the
482*86d7f5d3SJohn Marino      * mount point.  This isn't a perfect test, but it should catch most
483*86d7f5d3SJohn Marino      * foot shooting.
484*86d7f5d3SJohn Marino      */
485*86d7f5d3SJohn Marino     if (output_safety_override_opt == 0 &&
486*86d7f5d3SJohn Marino 	fstat(fd, &st1) == 0 && S_ISREG(st1.st_mode) &&
487*86d7f5d3SJohn Marino 	stat(mountpt, &st2) == 0 && st1.st_dev == st2.st_dev
488*86d7f5d3SJohn Marino     ) {
489*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s failed to add, the journal cannot be on the "
490*86d7f5d3SJohn Marino 			"same filesystem being journaled!\n",
491*86d7f5d3SJohn Marino 			mountpt, keyword);
492*86d7f5d3SJohn Marino 	exitCode = 1;
493*86d7f5d3SJohn Marino 	return;
494*86d7f5d3SJohn Marino     }
495*86d7f5d3SJohn Marino 
496*86d7f5d3SJohn Marino     /*
497*86d7f5d3SJohn Marino      * Setup joinfo and issue the add
498*86d7f5d3SJohn Marino      */
499*86d7f5d3SJohn Marino     bzero(&joinfo, sizeof(joinfo));
500*86d7f5d3SJohn Marino     snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
501*86d7f5d3SJohn Marino     if (memfifo_opt > 0)
502*86d7f5d3SJohn Marino 	joinfo.membufsize = memfifo_opt;
503*86d7f5d3SJohn Marino     if (twoway_opt > 0)
504*86d7f5d3SJohn Marino 	joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
505*86d7f5d3SJohn Marino     if (reversable_opt > 0)
506*86d7f5d3SJohn Marino 	joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
507*86d7f5d3SJohn Marino 
508*86d7f5d3SJohn Marino     error = mountctl(mountpt, MOUNTCTL_INSTALL_VFS_JOURNAL, fd,
509*86d7f5d3SJohn Marino 			&joinfo, sizeof(joinfo), NULL, 0);
510*86d7f5d3SJohn Marino     if (error == 0) {
511*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s added\n", mountpt, joinfo.id);
512*86d7f5d3SJohn Marino     } else {
513*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s failed to add, error %s\n", mountpt, joinfo.id, strerror(errno));
514*86d7f5d3SJohn Marino 	exitCode = 1;
515*86d7f5d3SJohn Marino     }
516*86d7f5d3SJohn Marino }
517*86d7f5d3SJohn Marino 
518*86d7f5d3SJohn Marino static void
mountctl_restart(const char * keyword,const char * mountpt,int fd,void __unused * info)519*86d7f5d3SJohn Marino mountctl_restart(const char *keyword, const char *mountpt,
520*86d7f5d3SJohn Marino 		 int fd, void __unused *info)
521*86d7f5d3SJohn Marino {
522*86d7f5d3SJohn Marino     struct mountctl_restart_journal joinfo;
523*86d7f5d3SJohn Marino     int error;
524*86d7f5d3SJohn Marino 
525*86d7f5d3SJohn Marino     /* XXX make sure descriptor is not on same filesystem as journal */
526*86d7f5d3SJohn Marino 
527*86d7f5d3SJohn Marino     bzero(&joinfo, sizeof(joinfo));
528*86d7f5d3SJohn Marino 
529*86d7f5d3SJohn Marino     snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
530*86d7f5d3SJohn Marino     if (twoway_opt > 0)
531*86d7f5d3SJohn Marino 	joinfo.flags |= MC_JOURNAL_WANT_FULLDUPLEX;
532*86d7f5d3SJohn Marino     if (reversable_opt > 0)
533*86d7f5d3SJohn Marino 	joinfo.flags |= MC_JOURNAL_WANT_REVERSABLE;
534*86d7f5d3SJohn Marino 
535*86d7f5d3SJohn Marino     error = mountctl(mountpt, MOUNTCTL_RESTART_VFS_JOURNAL, fd,
536*86d7f5d3SJohn Marino 			&joinfo, sizeof(joinfo), NULL, 0);
537*86d7f5d3SJohn Marino     if (error == 0) {
538*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s restarted\n", mountpt, joinfo.id);
539*86d7f5d3SJohn Marino     } else {
540*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s restart failed, error %s\n", mountpt, joinfo.id, strerror(errno));
541*86d7f5d3SJohn Marino     }
542*86d7f5d3SJohn Marino }
543*86d7f5d3SJohn Marino 
544*86d7f5d3SJohn Marino static void
mountctl_delete(const char * keyword,const char * mountpt,int __unused fd,void __unused * info)545*86d7f5d3SJohn Marino mountctl_delete(const char *keyword, const char *mountpt,
546*86d7f5d3SJohn Marino 		int __unused fd, void __unused *info)
547*86d7f5d3SJohn Marino {
548*86d7f5d3SJohn Marino     struct mountctl_remove_journal joinfo;
549*86d7f5d3SJohn Marino     int error;
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino     bzero(&joinfo, sizeof(joinfo));
552*86d7f5d3SJohn Marino     snprintf(joinfo.id, sizeof(joinfo.id), "%s", keyword);
553*86d7f5d3SJohn Marino     error = mountctl(mountpt, MOUNTCTL_REMOVE_VFS_JOURNAL, -1,
554*86d7f5d3SJohn Marino 			&joinfo, sizeof(joinfo), NULL, 0);
555*86d7f5d3SJohn Marino     if (error == 0) {
556*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s deleted\n", mountpt, joinfo.id);
557*86d7f5d3SJohn Marino     } else {
558*86d7f5d3SJohn Marino 	fprintf(stderr, "%s:%s deletion failed, error %s\n", mountpt, joinfo.id, strerror(errno));
559*86d7f5d3SJohn Marino     }
560*86d7f5d3SJohn Marino }
561*86d7f5d3SJohn Marino 
562*86d7f5d3SJohn Marino static void
mountctl_modify(const char * keyword __unused,const char * mountpt __unused,int fd __unused,void * info __unused)563*86d7f5d3SJohn Marino mountctl_modify(const char *keyword __unused, const char *mountpt __unused,
564*86d7f5d3SJohn Marino 		int fd __unused, void *info __unused)
565*86d7f5d3SJohn Marino {
566*86d7f5d3SJohn Marino     fprintf(stderr, "modify not yet implemented\n");
567*86d7f5d3SJohn Marino }
568*86d7f5d3SJohn Marino 
569*86d7f5d3SJohn Marino 
570*86d7f5d3SJohn Marino static void
usage(void)571*86d7f5d3SJohn Marino usage(void)
572*86d7f5d3SJohn Marino {
573*86d7f5d3SJohn Marino     printf(
574*86d7f5d3SJohn Marino 	"usage: mountctl -l {mountpt | tag | mountpt:tag}\n"
575*86d7f5d3SJohn Marino 	"       mountctl -a [-2] [-w/W output_path] [-x/X filedesc]\n"
576*86d7f5d3SJohn Marino 	"                   [-o options] mountpt:tag\n"
577*86d7f5d3SJohn Marino 	"       mountctl -r [-2] [-w/W output_path] [-x/X filedesc] mountpt:tag\n"
578*86d7f5d3SJohn Marino 	"       mountctl -d {mountpt | tag | mountpt:tag}\n"
579*86d7f5d3SJohn Marino 	"       mountctl -m [-o options] {mountpt | tag | mountpt:tag}\n"
580*86d7f5d3SJohn Marino 	"       mountctl -FZSCA {mountpt | tag | mountpt:tag}\n"
581*86d7f5d3SJohn Marino     );
582*86d7f5d3SJohn Marino     exit(1);
583*86d7f5d3SJohn Marino }
584*86d7f5d3SJohn Marino 
585*86d7f5d3SJohn Marino static int64_t
getsize(const char * str)586*86d7f5d3SJohn Marino getsize(const char *str)
587*86d7f5d3SJohn Marino {
588*86d7f5d3SJohn Marino     char *suffix;
589*86d7f5d3SJohn Marino     int64_t val;
590*86d7f5d3SJohn Marino 
591*86d7f5d3SJohn Marino     val = strtoll(str, &suffix, 0);
592*86d7f5d3SJohn Marino     if (suffix) {
593*86d7f5d3SJohn Marino 	switch(*suffix) {
594*86d7f5d3SJohn Marino 	case 'b':
595*86d7f5d3SJohn Marino 	    break;
596*86d7f5d3SJohn Marino 	case 't':
597*86d7f5d3SJohn Marino 	    val *= 1024;
598*86d7f5d3SJohn Marino 	    /* fall through */
599*86d7f5d3SJohn Marino 	case 'g':
600*86d7f5d3SJohn Marino 	    val *= 1024;
601*86d7f5d3SJohn Marino 	    /* fall through */
602*86d7f5d3SJohn Marino 	case 'm':
603*86d7f5d3SJohn Marino 	    val *= 1024;
604*86d7f5d3SJohn Marino 	    /* fall through */
605*86d7f5d3SJohn Marino 	case 'k':
606*86d7f5d3SJohn Marino 	    val *= 1024;
607*86d7f5d3SJohn Marino 	    /* fall through */
608*86d7f5d3SJohn Marino 	    break;
609*86d7f5d3SJohn Marino 	default:
610*86d7f5d3SJohn Marino 	    fprintf(stderr, "data value '%s' has unknown suffix\n", str);
611*86d7f5d3SJohn Marino 	    exit(1);
612*86d7f5d3SJohn Marino 	}
613*86d7f5d3SJohn Marino     }
614*86d7f5d3SJohn Marino     return(val);
615*86d7f5d3SJohn Marino }
616*86d7f5d3SJohn Marino 
617*86d7f5d3SJohn Marino static const char *
numtostr(int64_t num)618*86d7f5d3SJohn Marino numtostr(int64_t num)
619*86d7f5d3SJohn Marino {
620*86d7f5d3SJohn Marino     static char buf[64];
621*86d7f5d3SJohn Marino 
622*86d7f5d3SJohn Marino     if (num < 1024)
623*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%jd", (intmax_t)num);
624*86d7f5d3SJohn Marino     else if (num < 10 * 1024)
625*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%3.2fK", num / 1024.0);
626*86d7f5d3SJohn Marino     else if (num < 1024 * 1024)
627*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%3.0fK", num / 1024.0);
628*86d7f5d3SJohn Marino     else if (num < 10 * 1024 * 1024)
629*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%3.2fM", num / (1024.0 * 1024.0));
630*86d7f5d3SJohn Marino     else if (num < 1024 * 1024 * 1024)
631*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%3.0fM", num / (1024.0 * 1024.0));
632*86d7f5d3SJohn Marino     else if (num < 10LL * 1024 * 1024 * 1024)
633*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%3.2fG", num / (1024.0 * 1024.0 * 1024.0));
634*86d7f5d3SJohn Marino     else
635*86d7f5d3SJohn Marino 	snprintf(buf, sizeof(buf), "%3.0fG", num / (1024.0 * 1024.0 * 1024.0));
636*86d7f5d3SJohn Marino     return(buf);
637*86d7f5d3SJohn Marino }
638*86d7f5d3SJohn Marino 
639