1 |
/* |
2 |
* Copyright (c) 2001 Dima Dorfman. |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* 1. Redistributions of source code must retain the above copyright |
9 |
* notice, this list of conditions and the following disclaimer. |
10 |
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
* notice, this list of conditions and the following disclaimer in the |
12 |
* documentation and/or other materials provided with the distribution. |
13 |
* |
14 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
15 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
18 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 |
* SUCH DAMAGE. |
25 |
*/ |
26 |
|
27 |
/* |
28 |
* mdmfs (md/MFS) is a wrapper around mdconfig(8), |
29 |
* newfs(8), and mount(8) that mimics the command line option set of |
30 |
* the deprecated mount_mfs(8). |
31 |
*/ |
32 |
|
33 |
#include <sys/cdefs.h> |
34 |
__FBSDID("$FreeBSD: src/sbin/mdmfs/mdmfs.c,v 1.33 2007/05/14 19:23:13 remko Exp $"); |
35 |
|
36 |
#include <sys/param.h> |
37 |
#include <sys/mdioctl.h> |
38 |
#include <sys/stat.h> |
39 |
#include <sys/wait.h> |
40 |
|
41 |
#include <assert.h> |
42 |
#include <err.h> |
43 |
#include <fcntl.h> |
44 |
#include <grp.h> |
45 |
#include <paths.h> |
46 |
#include <pwd.h> |
47 |
#include <stdarg.h> |
48 |
#include <stdio.h> |
49 |
#include <stdlib.h> |
50 |
#include <string.h> |
51 |
#include <ctype.h> |
52 |
#include <unistd.h> |
53 |
|
54 |
typedef enum { false, true } bool; |
55 |
|
56 |
struct mtpt_info { |
57 |
uid_t mi_uid; |
58 |
bool mi_have_uid; |
59 |
gid_t mi_gid; |
60 |
bool mi_have_gid; |
61 |
mode_t mi_mode; |
62 |
bool mi_have_mode; |
63 |
}; |
64 |
|
65 |
static bool debug; /* Emit debugging information? */ |
66 |
static bool loudsubs; /* Suppress output from helper programs? */ |
67 |
static bool norun; /* Actually run the helper programs? */ |
68 |
static int unit; /* The unit we're working with. */ |
69 |
static const char *mdname; /* Name of memory disk device (e.g., "md"). */ |
70 |
static const char *mdsuffix; /* Suffix of memory disk device (e.g., ".uzip"). */ |
71 |
static size_t mdnamelen; /* Length of mdname. */ |
72 |
static const char *path_mdconfig =_PATH_MDCONFIG; |
73 |
|
74 |
static void argappend(char **, const char *, ...) __printflike(2, 3); |
75 |
static void debugprintf(const char *, ...) __printflike(1, 2); |
76 |
static void do_mdconfig_attach(const char *, const enum md_types); |
77 |
static void do_mdconfig_attach_au(const char *, const enum md_types); |
78 |
static void do_mdconfig_detach(void); |
79 |
static void do_mount(const char *, const char *); |
80 |
static void do_mtptsetup(const char *, struct mtpt_info *); |
81 |
static void do_newfs(const char *); |
82 |
static void extract_ugid(const char *, struct mtpt_info *); |
83 |
static int run(int *, const char *, ...) __printflike(2, 3); |
84 |
static void usage(void); |
85 |
|
86 |
int |
87 |
main(int argc, char **argv) |
88 |
{ |
89 |
struct mtpt_info mi; /* Mountpoint info. */ |
90 |
char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */ |
91 |
*mount_arg; |
92 |
enum md_types mdtype; /* The type of our memory disk. */ |
93 |
bool have_mdtype; |
94 |
bool detach, softdep, autounit, newfs; |
95 |
char *mtpoint, *unitstr; |
96 |
char *p; |
97 |
int ch; |
98 |
void *set; |
99 |
unsigned long ul; |
100 |
|
101 |
/* Misc. initialization. */ |
102 |
(void)memset(&mi, '\0', sizeof(mi)); |
103 |
detach = true; |
104 |
softdep = true; |
105 |
autounit = false; |
106 |
newfs = true; |
107 |
have_mdtype = false; |
108 |
mdtype = MD_SWAP; |
109 |
mdname = MD_NAME; |
110 |
mdnamelen = strlen(mdname); |
111 |
/* |
112 |
* Can't set these to NULL. They may be passed to the |
113 |
* respective programs without modification. I.e., we may not |
114 |
* receive any command-line options which will caused them to |
115 |
* be modified. |
116 |
*/ |
117 |
mdconfig_arg = strdup(""); |
118 |
newfs_arg = strdup(""); |
119 |
mount_arg = strdup(""); |
120 |
|
121 |
/* If we were started as mount_mfs or mfs, imply -C. */ |
122 |
if (strcmp(getprogname(), "mount_mfs") == 0 || |
123 |
strcmp(getprogname(), "mfs") == 0) { |
124 |
/* Make compatibility assumptions. */ |
125 |
mi.mi_mode = 01777; |
126 |
mi.mi_have_mode = true; |
127 |
} |
128 |
|
129 |
while ((ch = getopt(argc, argv, |
130 |
"a:b:Cc:Dd:E:e:F:f:hi:LlMm:NnO:o:Pp:Ss:t:Uv:w:X")) != -1) |
131 |
switch (ch) { |
132 |
case 'a': |
133 |
argappend(&newfs_arg, "-a %s", optarg); |
134 |
break; |
135 |
case 'b': |
136 |
argappend(&newfs_arg, "-b %s", optarg); |
137 |
break; |
138 |
case 'C': |
139 |
/* Ignored for compatibility. */ |
140 |
break; |
141 |
case 'c': |
142 |
argappend(&newfs_arg, "-c %s", optarg); |
143 |
break; |
144 |
case 'D': |
145 |
detach = false; |
146 |
break; |
147 |
case 'd': |
148 |
argappend(&newfs_arg, "-d %s", optarg); |
149 |
break; |
150 |
case 'E': |
151 |
path_mdconfig = optarg; |
152 |
break; |
153 |
case 'e': |
154 |
argappend(&newfs_arg, "-e %s", optarg); |
155 |
break; |
156 |
case 'F': |
157 |
if (have_mdtype) |
158 |
usage(); |
159 |
mdtype = MD_VNODE; |
160 |
have_mdtype = true; |
161 |
argappend(&mdconfig_arg, "-f %s", optarg); |
162 |
break; |
163 |
case 'f': |
164 |
argappend(&newfs_arg, "-f %s", optarg); |
165 |
break; |
166 |
case 'h': |
167 |
usage(); |
168 |
break; |
169 |
case 'i': |
170 |
argappend(&newfs_arg, "-i %s", optarg); |
171 |
break; |
172 |
case 'L': |
173 |
loudsubs = true; |
174 |
break; |
175 |
case 'l': |
176 |
argappend(&newfs_arg, "-l"); |
177 |
break; |
178 |
case 'M': |
179 |
if (have_mdtype) |
180 |
usage(); |
181 |
mdtype = MD_MALLOC; |
182 |
have_mdtype = true; |
183 |
break; |
184 |
case 'm': |
185 |
argappend(&newfs_arg, "-m %s", optarg); |
186 |
break; |
187 |
case 'N': |
188 |
norun = true; |
189 |
break; |
190 |
case 'n': |
191 |
argappend(&newfs_arg, "-n"); |
192 |
break; |
193 |
case 'O': |
194 |
argappend(&newfs_arg, "-o %s", optarg); |
195 |
break; |
196 |
case 'o': |
197 |
argappend(&mount_arg, "-o %s", optarg); |
198 |
break; |
199 |
case 'P': |
200 |
newfs = false; |
201 |
break; |
202 |
case 'p': |
203 |
if ((set = setmode(optarg)) == NULL) |
204 |
usage(); |
205 |
mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); |
206 |
mi.mi_have_mode = true; |
207 |
free(set); |
208 |
break; |
209 |
case 'S': |
210 |
softdep = false; |
211 |
break; |
212 |
case 's': |
213 |
argappend(&mdconfig_arg, "-s %s", optarg); |
214 |
break; |
215 |
case 'U': |
216 |
softdep = true; |
217 |
break; |
218 |
case 'v': |
219 |
argappend(&newfs_arg, "-O %s", optarg); |
220 |
break; |
221 |
case 'w': |
222 |
extract_ugid(optarg, &mi); |
223 |
break; |
224 |
case 'X': |
225 |
debug = true; |
226 |
break; |
227 |
default: |
228 |
usage(); |
229 |
} |
230 |
argc -= optind; |
231 |
argv += optind; |
232 |
if (argc < 2) |
233 |
usage(); |
234 |
|
235 |
/* Derive 'unit' (global). */ |
236 |
unitstr = argv[0]; |
237 |
if (strncmp(unitstr, "/dev/", 5) == 0) |
238 |
unitstr += 5; |
239 |
if (strncmp(unitstr, mdname, mdnamelen) == 0) |
240 |
unitstr += mdnamelen; |
241 |
if (!isdigit(*unitstr)) { |
242 |
autounit = true; |
243 |
unit = -1; |
244 |
mdsuffix = unitstr; |
245 |
} else { |
246 |
ul = strtoul(unitstr, &p, 10); |
247 |
if (ul == ULONG_MAX) |
248 |
errx(1, "bad device unit: %s", unitstr); |
249 |
unit = ul; |
250 |
mdsuffix = p; /* can be empty */ |
251 |
} |
252 |
|
253 |
mtpoint = argv[1]; |
254 |
if (!have_mdtype) |
255 |
mdtype = MD_SWAP; |
256 |
if (softdep) |
257 |
argappend(&newfs_arg, "-U"); |
258 |
if (mdtype != MD_VNODE && !newfs) |
259 |
errx(1, "-P requires a vnode-backed disk"); |
260 |
|
261 |
/* Do the work. */ |
262 |
if (detach && !autounit) |
263 |
do_mdconfig_detach(); |
264 |
if (autounit) |
265 |
do_mdconfig_attach_au(mdconfig_arg, mdtype); |
266 |
else |
267 |
do_mdconfig_attach(mdconfig_arg, mdtype); |
268 |
if (newfs) |
269 |
do_newfs(newfs_arg); |
270 |
do_mount(mount_arg, mtpoint); |
271 |
do_mtptsetup(mtpoint, &mi); |
272 |
|
273 |
return (0); |
274 |
} |
275 |
|
276 |
/* |
277 |
* Append the expansion of 'fmt' to the buffer pointed to by '*dstp'; |
278 |
* reallocate as required. |
279 |
*/ |
280 |
static void |
281 |
argappend(char **dstp, const char *fmt, ...) |
282 |
{ |
283 |
char *old, *new; |
284 |
va_list ap; |
285 |
|
286 |
old = *dstp; |
287 |
assert(old != NULL); |
288 |
|
289 |
va_start(ap, fmt); |
290 |
if (vasprintf(&new, fmt,ap) == -1) |
291 |
errx(1, "vasprintf"); |
292 |
va_end(ap); |
293 |
|
294 |
*dstp = new; |
295 |
if (asprintf(&new, "%s %s", old, new) == -1) |
296 |
errx(1, "asprintf"); |
297 |
free(*dstp); |
298 |
free(old); |
299 |
|
300 |
*dstp = new; |
301 |
} |
302 |
|
303 |
/* |
304 |
* If run-time debugging is enabled, print the expansion of 'fmt'. |
305 |
* Otherwise, do nothing. |
306 |
*/ |
307 |
static void |
308 |
debugprintf(const char *fmt, ...) |
309 |
{ |
310 |
va_list ap; |
311 |
|
312 |
if (!debug) |
313 |
return; |
314 |
fprintf(stderr, "DEBUG: "); |
315 |
va_start(ap, fmt); |
316 |
vfprintf(stderr, fmt, ap); |
317 |
va_end(ap); |
318 |
fprintf(stderr, "\n"); |
319 |
fflush(stderr); |
320 |
} |
321 |
|
322 |
/* |
323 |
* Attach a memory disk with a known unit. |
324 |
*/ |
325 |
static void |
326 |
do_mdconfig_attach(const char *args, const enum md_types mdtype) |
327 |
{ |
328 |
int rv; |
329 |
const char *ta; /* Type arg. */ |
330 |
|
331 |
switch (mdtype) { |
332 |
case MD_SWAP: |
333 |
ta = "-t swap"; |
334 |
break; |
335 |
case MD_VNODE: |
336 |
ta = "-t vnode"; |
337 |
break; |
338 |
case MD_MALLOC: |
339 |
ta = "-t malloc"; |
340 |
break; |
341 |
default: |
342 |
abort(); |
343 |
} |
344 |
rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args, |
345 |
mdname, unit); |
346 |
if (rv) |
347 |
errx(1, "mdconfig (attach) exited with error code %d", rv); |
348 |
} |
349 |
|
350 |
/* |
351 |
* Attach a memory disk with an unknown unit; use autounit. |
352 |
*/ |
353 |
static void |
354 |
do_mdconfig_attach_au(const char *args, const enum md_types mdtype) |
355 |
{ |
356 |
const char *ta; /* Type arg. */ |
357 |
char *linep, *linebuf; /* Line pointer, line buffer. */ |
358 |
int fd; /* Standard output of mdconfig invocation. */ |
359 |
FILE *sfd; |
360 |
int rv; |
361 |
char *p; |
362 |
size_t linelen; |
363 |
unsigned long ul; |
364 |
|
365 |
switch (mdtype) { |
366 |
case MD_SWAP: |
367 |
ta = "-t swap"; |
368 |
break; |
369 |
case MD_VNODE: |
370 |
ta = "-t vnode"; |
371 |
break; |
372 |
case MD_MALLOC: |
373 |
ta = "-t malloc"; |
374 |
break; |
375 |
default: |
376 |
abort(); |
377 |
} |
378 |
rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args); |
379 |
if (rv) |
380 |
errx(1, "mdconfig (attach) exited with error code %d", rv); |
381 |
|
382 |
/* Receive the unit number. */ |
383 |
if (norun) { /* Since we didn't run, we can't read. Fake it. */ |
384 |
unit = 0; |
385 |
return; |
386 |
} |
387 |
sfd = fdopen(fd, "r"); |
388 |
if (sfd == NULL) |
389 |
err(1, "fdopen"); |
390 |
linep = fgetln(sfd, &linelen); |
391 |
if (linep == NULL && linelen < mdnamelen + 1) |
392 |
errx(1, "unexpected output from mdconfig (attach)"); |
393 |
/* If the output format changes, we want to know about it. */ |
394 |
assert(strncmp(linep, mdname, mdnamelen) == 0); |
395 |
linebuf = malloc(linelen - mdnamelen + 1); |
396 |
assert(linebuf != NULL); |
397 |
/* Can't use strlcpy because linep is not NULL-terminated. */ |
398 |
strncpy(linebuf, linep + mdnamelen, linelen); |
399 |
linebuf[linelen] = '\0'; |
400 |
ul = strtoul(linebuf, &p, 10); |
401 |
if (ul == ULONG_MAX || *p != '\n') |
402 |
errx(1, "unexpected output from mdconfig (attach)"); |
403 |
unit = ul; |
404 |
|
405 |
fclose(sfd); |
406 |
close(fd); |
407 |
} |
408 |
|
409 |
/* |
410 |
* Detach a memory disk. |
411 |
*/ |
412 |
static void |
413 |
do_mdconfig_detach(void) |
414 |
{ |
415 |
int rv; |
416 |
|
417 |
rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit); |
418 |
if (rv && debug) /* This is allowed to fail. */ |
419 |
warnx("mdconfig (detach) exited with error code %d (ignored)", |
420 |
rv); |
421 |
} |
422 |
|
423 |
/* |
424 |
* Mount the configured memory disk. |
425 |
*/ |
426 |
static void |
427 |
do_mount(const char *args, const char *mtpoint) |
428 |
{ |
429 |
int rv; |
430 |
|
431 |
rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args, |
432 |
mdname, unit, mdsuffix, mtpoint); |
433 |
if (rv) |
434 |
errx(1, "mount exited with error code %d", rv); |
435 |
} |
436 |
|
437 |
/* |
438 |
* Various configuration of the mountpoint. Mostly, enact 'mip'. |
439 |
*/ |
440 |
static void |
441 |
do_mtptsetup(const char *mtpoint, struct mtpt_info *mip) |
442 |
{ |
443 |
|
444 |
if (mip->mi_have_mode) { |
445 |
debugprintf("changing mode of %s to %o.", mtpoint, |
446 |
mip->mi_mode); |
447 |
if (!norun) |
448 |
if (chmod(mtpoint, mip->mi_mode) == -1) |
449 |
err(1, "chmod: %s", mtpoint); |
450 |
} |
451 |
/* |
452 |
* We have to do these separately because the user may have |
453 |
* only specified one of them. |
454 |
*/ |
455 |
if (mip->mi_have_uid) { |
456 |
debugprintf("changing owner (user) or %s to %u.", mtpoint, |
457 |
mip->mi_uid); |
458 |
if (!norun) |
459 |
if (chown(mtpoint, mip->mi_uid, -1) == -1) |
460 |
err(1, "chown %s to %u (user)", mtpoint, |
461 |
mip->mi_uid); |
462 |
} |
463 |
if (mip->mi_have_gid) { |
464 |
debugprintf("changing owner (group) or %s to %u.", mtpoint, |
465 |
mip->mi_gid); |
466 |
if (!norun) |
467 |
if (chown(mtpoint, -1, mip->mi_gid) == -1) |
468 |
err(1, "chown %s to %u (group)", mtpoint, |
469 |
mip->mi_gid); |
470 |
} |
471 |
} |
472 |
|
473 |
/* |
474 |
* Put a file system on the memory disk. |
475 |
*/ |
476 |
static void |
477 |
do_newfs(const char *args) |
478 |
{ |
479 |
int rv; |
480 |
|
481 |
rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit); |
482 |
if (rv) |
483 |
errx(1, "newfs exited with error code %d", rv); |
484 |
} |
485 |
|
486 |
/* |
487 |
* 'str' should be a user and group name similar to the last argument |
488 |
* to chown(1); i.e., a user, followed by a colon, followed by a |
489 |
* group. The user and group in 'str' may be either a [ug]id or a |
490 |
* name. Upon return, the uid and gid fields in 'mip' will contain |
491 |
* the uid and gid of the user and group name in 'str', respectively. |
492 |
* |
493 |
* In other words, this derives a user and group id from a string |
494 |
* formatted like the last argument to chown(1). |
495 |
* |
496 |
* Notice: At this point we don't support only a username or only a |
497 |
* group name. do_mtptsetup already does, so when this feature is |
498 |
* desired, this is the only routine that needs to be changed. |
499 |
*/ |
500 |
static void |
501 |
extract_ugid(const char *str, struct mtpt_info *mip) |
502 |
{ |
503 |
char *ug; /* Writable 'str'. */ |
504 |
char *user, *group; /* Result of extracton. */ |
505 |
struct passwd *pw; |
506 |
struct group *gr; |
507 |
char *p; |
508 |
uid_t *uid; |
509 |
gid_t *gid; |
510 |
|
511 |
uid = &mip->mi_uid; |
512 |
gid = &mip->mi_gid; |
513 |
mip->mi_have_uid = mip->mi_have_gid = false; |
514 |
|
515 |
/* Extract the user and group from 'str'. Format above. */ |
516 |
ug = strdup(str); |
517 |
assert(ug != NULL); |
518 |
group = ug; |
519 |
user = strsep(&group, ":"); |
520 |
if (user == NULL || group == NULL || *user == '\0' || *group == '\0') |
521 |
usage(); |
522 |
|
523 |
/* Derive uid. */ |
524 |
*uid = strtoul(user, &p, 10); |
525 |
if (*uid == (uid_t)ULONG_MAX) |
526 |
usage(); |
527 |
if (*p != '\0') { |
528 |
pw = getpwnam(user); |
529 |
if (pw == NULL) |
530 |
errx(1, "invalid user: %s", user); |
531 |
*uid = pw->pw_uid; |
532 |
} |
533 |
mip->mi_have_uid = true; |
534 |
|
535 |
/* Derive gid. */ |
536 |
*gid = strtoul(group, &p, 10); |
537 |
if (*gid == (gid_t)ULONG_MAX) |
538 |
usage(); |
539 |
if (*p != '\0') { |
540 |
gr = getgrnam(group); |
541 |
if (gr == NULL) |
542 |
errx(1, "invalid group: %s", group); |
543 |
*gid = gr->gr_gid; |
544 |
} |
545 |
mip->mi_have_gid = true; |
546 |
|
547 |
free(ug); |
548 |
} |
549 |
|
550 |
/* |
551 |
* Run a process with command name and arguments pointed to by the |
552 |
* formatted string 'cmdline'. Since system(3) is not used, the first |
553 |
* space-delimited token of 'cmdline' must be the full pathname of the |
554 |
* program to run. The return value is the return code of the process |
555 |
* spawned. If 'ofd' is non-NULL, it is set to the standard output of |
556 |
* the program spawned (i.e., you can read from ofd and get the output |
557 |
* of the program). |
558 |
*/ |
559 |
static int |
560 |
run(int *ofd, const char *cmdline, ...) |
561 |
{ |
562 |
char **argv, **argvp; /* Result of splitting 'cmd'. */ |
563 |
int argc; |
564 |
char *cmd; /* Expansion of 'cmdline'. */ |
565 |
int pid, status; /* Child info. */ |
566 |
int pfd[2]; /* Pipe to the child. */ |
567 |
int nfd; /* Null (/dev/null) file descriptor. */ |
568 |
bool dup2dn; /* Dup /dev/null to stdout? */ |
569 |
va_list ap; |
570 |
char *p; |
571 |
int rv, i; |
572 |
|
573 |
dup2dn = true; |
574 |
va_start(ap, cmdline); |
575 |
rv = vasprintf(&cmd, cmdline, ap); |
576 |
if (rv == -1) |
577 |
err(1, "vasprintf"); |
578 |
va_end(ap); |
579 |
|
580 |
/* Split up 'cmd' into 'argv' for use with execve. */ |
581 |
for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) |
582 |
argc++; /* 'argc' generation loop. */ |
583 |
argv = (char **)malloc(sizeof(*argv) * (argc + 1)); |
584 |
assert(argv != NULL); |
585 |
for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) |
586 |
if (**argvp != '\0') |
587 |
if (++argvp >= &argv[argc]) { |
588 |
*argvp = NULL; |
589 |
break; |
590 |
} |
591 |
assert(*argv); |
592 |
/* The argv array ends up NULL-terminated here. */ |
593 |
|
594 |
/* Make sure the above loop works as expected. */ |
595 |
if (debug) { |
596 |
/* |
597 |
* We can't, but should, use debugprintf here. First, |
598 |
* it appends a trailing newline to the output, and |
599 |
* second it prepends "DEBUG: " to the output. The |
600 |
* former is a problem for this would-be first call, |
601 |
* and the latter for the would-be call inside the |
602 |
* loop. |
603 |
*/ |
604 |
(void)fprintf(stderr, "DEBUG: running:"); |
605 |
/* Should be equivilent to 'cmd' (before strsep, of course). */ |
606 |
for (i = 0; argv[i] != NULL; i++) |
607 |
(void)fprintf(stderr, " %s", argv[i]); |
608 |
(void)fprintf(stderr, "\n"); |
609 |
} |
610 |
|
611 |
/* Create a pipe if necessary and fork the helper program. */ |
612 |
if (ofd != NULL) { |
613 |
if (pipe(&pfd[0]) == -1) |
614 |
err(1, "pipe"); |
615 |
*ofd = pfd[0]; |
616 |
dup2dn = false; |
617 |
} |
618 |
pid = fork(); |
619 |
switch (pid) { |
620 |
case 0: |
621 |
/* XXX can we call err() in here? */ |
622 |
if (norun) |
623 |
_exit(0); |
624 |
if (ofd != NULL) |
625 |
if (dup2(pfd[1], STDOUT_FILENO) < 0) |
626 |
err(1, "dup2"); |
627 |
if (!loudsubs) { |
628 |
nfd = open(_PATH_DEVNULL, O_RDWR); |
629 |
if (nfd == -1) |
630 |
err(1, "open: %s", _PATH_DEVNULL); |
631 |
if (dup2(nfd, STDIN_FILENO) < 0) |
632 |
err(1, "dup2"); |
633 |
if (dup2dn) |
634 |
if (dup2(nfd, STDOUT_FILENO) < 0) |
635 |
err(1, "dup2"); |
636 |
if (dup2(nfd, STDERR_FILENO) < 0) |
637 |
err(1, "dup2"); |
638 |
} |
639 |
|
640 |
(void)execv(argv[0], argv); |
641 |
warn("exec: %s", argv[0]); |
642 |
_exit(-1); |
643 |
case -1: |
644 |
err(1, "fork"); |
645 |
} |
646 |
|
647 |
free(cmd); |
648 |
free(argv); |
649 |
while (waitpid(pid, &status, 0) != pid) |
650 |
; |
651 |
return (WEXITSTATUS(status)); |
652 |
} |
653 |
|
654 |
static void |
655 |
usage(void) |
656 |
{ |
657 |
|
658 |
fprintf(stderr, |
659 |
"usage: %s [-DLlMNnPSUX] [-a maxcontig] [-b block-size]\n" |
660 |
"\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n" |
661 |
"\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-m percent-free]\n" |
662 |
"\t[-O optimization] [-o mount-options]\n" |
663 |
"\t[-p permissions] [-s size] [-v version] [-w user:group]\n" |
664 |
"\tmd-device mount-point\n", getprogname()); |
665 |
exit(1); |
666 |
} |