1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29
30 #include <stdio.h>
31 #include <stdio_ext.h>
32 #include <limits.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/signal.h>
37 #include <sys/mnttab.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/param.h>
42 #include <sys/wait.h>
43 #include <sys/vfstab.h>
44 #include <sys/fcntl.h>
45 #include <sys/resource.h>
46 #include <sys/mntent.h>
47 #include <sys/ctfs.h>
48 #include <locale.h>
49 #include <stdarg.h>
50 #include <sys/mount.h>
51 #include <sys/objfs.h>
52 #include "fslib.h"
53 #include <sharefs/share.h>
54
55 #define FS_PATH "/usr/lib/fs"
56 #define ALT_PATH "/etc/fs"
57 #define FULLPATH_MAX 32
58 #define FSTYPE_MAX 8
59 #define ARGV_MAX 16
60
61 int aflg, oflg, Vflg, dashflg, dflg, fflg;
62
63 extern void rpterr(), usage(), mnterror();
64
65 extern char *optarg; /* used by getopt */
66 extern int optind, opterr;
67
68 static char *myname;
69 char fs_path[] = FS_PATH;
70 char alt_path[] = ALT_PATH;
71 char mnttab[MAXPATHLEN + 1];
72 char *oarg, *farg;
73 int maxrun, nrun;
74 int no_mnttab;
75 int lofscnt; /* presence of lofs prohibits parallel */
76 /* umounting */
77 int exitcode;
78 char resolve[MAXPATHLEN];
79 static char ibuf[BUFSIZ];
80
81 /*
82 * Currently, mounting cachefs's simultaneous uncovers various problems.
83 * For the short term, we serialize cachefs activity while we fix
84 * these cachefs bugs.
85 */
86 #define CACHEFS_BUG
87 #ifdef CACHEFS_BUG
88 #include <sys/fs/cachefs_fs.h> /* for BACKMNT_NAME */
89 int cachefs_running; /* parallel cachefs not supported yet */
90 #endif
91
92 /*
93 * The basic mount struct that describes an mnttab entry.
94 * It is used both in an array and as a linked list elem.
95 */
96
97 typedef struct mountent {
98 struct mnttab ment; /* the mnttab data */
99 int mlevel; /* mount level of the mount pt */
100 pid_t pid; /* the pid of this mount process */
101 #define RDPIPE 0
102 #define WRPIPE 1
103 int sopipe[2]; /* pipe attached to child's stdout */
104 int sepipe[2]; /* pipe attached to child's stderr */
105 struct mountent *link; /* used when in linked list */
106 } mountent_t;
107
108 static mountent_t *mntll; /* head of global linked list of */
109 /* mountents */
110 int listlength; /* # of elems in this list */
111
112 /*
113 * If the automatic flag (-a) is given and mount points are not specified
114 * on the command line, then do not attempt to umount these. These
115 * generally need to be kept mounted until system shutdown.
116 */
117 static const char *keeplist[] = {
118 "/",
119 "/dev",
120 "/dev/fd",
121 "/devices",
122 "/etc/mnttab",
123 "/etc/svc/volatile",
124 "/lib",
125 "/proc",
126 "/sbin",
127 CTFS_ROOT,
128 OBJFS_ROOT,
129 "/tmp",
130 "/usr",
131 "/var",
132 "/var/adm",
133 "/var/run",
134 SHARETAB,
135 NULL
136 };
137
138 static void nomem();
139 static void doexec(struct mnttab *);
140 static int setup_iopipe(mountent_t *);
141 static void setup_output(mountent_t *);
142 static void doio(mountent_t *);
143 static void do_umounts(mountent_t **);
144 static int dowait();
145 static int parumount();
146 static int mcompar(const void *, const void *);
147 static void cleanup(int);
148
149 static mountent_t **make_mntarray(char **, int);
150 static mountent_t *getmntall();
151 static mountent_t *new_mountent(struct mnttab *);
152 static mountent_t *getmntlast(mountent_t *, char *, char *);
153
154 int
main(int argc,char ** argv)155 main(int argc, char **argv)
156 {
157 int cc;
158 struct mnttab mget;
159 char *mname, *is_special;
160 int fscnt;
161 mountent_t *mp;
162
163 (void) setlocale(LC_ALL, "");
164
165 #if !defined(TEXT_DOMAIN)
166 #define TEXT_DOMAIN "SYS_TEST"
167 #endif
168 (void) textdomain(TEXT_DOMAIN);
169
170 myname = strrchr(argv[0], '/');
171 if (myname)
172 myname++;
173 else
174 myname = argv[0];
175
176 /*
177 * Process the args.
178 * "-d" for compatibility
179 */
180 while ((cc = getopt(argc, argv, "ado:Vf?")) != -1)
181 switch (cc) {
182 case 'a':
183 aflg++;
184 break;
185 #ifdef DEBUG
186 case 'd':
187 dflg++;
188 break;
189 #endif
190
191 case '?':
192 usage();
193 break;
194 case 'o':
195 if (oflg)
196 usage();
197 else {
198 oflg++;
199 oarg = optarg;
200 }
201 break;
202 case 'f':
203 fflg++;
204 break;
205 case 'V':
206 if (Vflg)
207 usage();
208 else
209 Vflg++;
210 break;
211 default:
212 usage();
213 break;
214 }
215
216 fscnt = argc - optind;
217 if (!aflg && fscnt != 1)
218 usage();
219
220 /* copy '--' to specific */
221 if (strcmp(argv[optind-1], "--") == 0)
222 dashflg++;
223
224 /*
225 * mnttab may be a symlink to a file in another file system.
226 * This happens during install when / is mounted read-only
227 * and /etc/mnttab is symlinked to a file in /tmp.
228 * If this is the case, we need to follow the symlink to the
229 * read-write file itself so that the subsequent mnttab.temp
230 * open and rename will work.
231 */
232 if (realpath(MNTTAB, mnttab) == NULL) {
233 strcpy(mnttab, MNTTAB);
234 }
235
236 /*
237 * bugid 1205242
238 * call the realpath() here, so that if the user is
239 * trying to umount an autofs directory, the directory
240 * is forced to mount.
241 */
242
243 mname = argv[optind];
244 is_special = realpath(mname, resolve);
245
246 /*
247 * Read the whole mnttab into memory.
248 */
249 mntll = getmntall();
250
251 if (aflg && fscnt != 1)
252 exit(parumount(argv + optind, fscnt));
253
254 aflg = 0;
255
256 mntnull(&mget);
257 if (listlength == 0) {
258 fprintf(stderr, gettext(
259 "%s: warning: no entries found in %s\n"),
260 myname, mnttab);
261 mget.mnt_mountp = mname; /* assume mount point */
262 no_mnttab++;
263 doexec(&mget);
264 exit(0);
265 }
266
267 mp = NULL;
268
269 /*
270 * if realpath fails, it can't be a mount point, so we'll
271 * go straight to the code that treats the arg as a special.
272 * if realpath succeeds, it could be a special or a mount point;
273 * we'll start by assuming it's a mount point, and if it's not,
274 * try to treat it as a special.
275 */
276 if (is_special != NULL) {
277 /*
278 * if this succeeds,
279 * we'll have the appropriate record; if it fails
280 * we'll assume the arg is a special of some sort
281 */
282 mp = getmntlast(mntll, NULL, resolve);
283 }
284 /*
285 * Since stackable mount is allowed (RFE 2001535),
286 * we will un-mount the last entry in the MNTTAB that matches.
287 */
288 if (mp == NULL) {
289 /*
290 * Perhaps there is a bogus mnttab entry that
291 * can't be resolved:
292 */
293 if ((mp = getmntlast(mntll, NULL, mname)) == NULL)
294 /*
295 * assume it's a device (special) now
296 */
297 mp = getmntlast(mntll, mname, NULL);
298 if (mp) {
299 /*
300 * Found it.
301 * This is a device. Now we want to know if
302 * it stackmounted on by something else.
303 * The original fix for bug 1103850 has a
304 * problem with lockfs (bug 1119731). This
305 * is a revised method.
306 */
307 mountent_t *lmp;
308 lmp = getmntlast(mntll, NULL, mp->ment.mnt_mountp);
309
310 if (lmp && strcmp(lmp->ment.mnt_special,
311 mp->ment.mnt_special)) {
312 errno = EBUSY;
313 rpterr(mname);
314 exit(1);
315 }
316 } else {
317 fprintf(stderr, gettext(
318 "%s: warning: %s not in mnttab\n"),
319 myname, mname);
320 if (Vflg)
321 exit(1);
322 /*
323 * same error as mount -V
324 * would give for unknown
325 * mount point
326 */
327 mget.mnt_special = mget.mnt_mountp = mname;
328 }
329 }
330
331 if (mp)
332 doexec(&mp->ment);
333 else
334 doexec(&mget);
335
336 return (0);
337 }
338
339 void
doexec(struct mnttab * ment)340 doexec(struct mnttab *ment)
341 {
342 int ret;
343
344 #ifdef DEBUG
345 if (dflg)
346 fprintf(stderr, "%d: umounting %s\n",
347 getpid(), ment->mnt_mountp);
348 #endif
349
350 /* try to exec the dependent portion */
351 if ((ment->mnt_fstype != NULL) || Vflg) {
352 char full_path[FULLPATH_MAX];
353 char alter_path[FULLPATH_MAX];
354 char *newargv[ARGV_MAX];
355 int ii;
356
357 if (strlen(ment->mnt_fstype) > (size_t)FSTYPE_MAX) {
358 fprintf(stderr, gettext(
359 "%s: FSType %s exceeds %d characters\n"),
360 myname, ment->mnt_fstype, FSTYPE_MAX);
361 exit(1);
362 }
363
364 /* build the full pathname of the fstype dependent command. */
365 sprintf(full_path, "%s/%s/%s", fs_path, ment->mnt_fstype,
366 myname);
367 sprintf(alter_path, "%s/%s/%s", alt_path, ment->mnt_fstype,
368 myname);
369
370 /*
371 * create the new arg list, and end the list with a
372 * null pointer
373 */
374 ii = 2;
375 if (oflg) {
376 newargv[ii++] = "-o";
377 newargv[ii++] = oarg;
378 }
379 if (dashflg) {
380 newargv[ii++] = "--";
381 }
382 if (fflg) {
383 newargv[ii++] = "-f";
384 }
385 newargv[ii++] = (ment->mnt_mountp)
386 ? ment->mnt_mountp : ment->mnt_special;
387 newargv[ii] = NULL;
388
389 /* set the new argv[0] to the filename */
390 newargv[1] = myname;
391
392 if (Vflg) {
393 printf("%s", myname);
394 for (ii = 2; newargv[ii]; ii++)
395 printf(" %s", newargv[ii]);
396 printf("\n");
397 fflush(stdout);
398 exit(0);
399 }
400
401 /* Try to exec the fstype dependent umount. */
402 execv(full_path, &newargv[1]);
403 if (errno == ENOEXEC) {
404 newargv[0] = "sh";
405 newargv[1] = full_path;
406 execv("/sbin/sh", &newargv[0]);
407 }
408 newargv[1] = myname;
409 execv(alter_path, &newargv[1]);
410 if (errno == ENOEXEC) {
411 newargv[0] = "sh";
412 newargv[1] = alter_path;
413 execv("/sbin/sh", &newargv[0]);
414 }
415 /* exec failed */
416 if (errno != ENOENT) {
417 fprintf(stderr, gettext("umount: cannot execute %s\n"),
418 full_path);
419 exit(1);
420 }
421 }
422 /*
423 * No fstype independent executable then. We'll go generic
424 * from here.
425 */
426
427 /* don't use -o with generic */
428 if (oflg) {
429 fprintf(stderr, gettext(
430 "%s: %s specific umount does not exist;"
431 " -o suboption ignored\n"),
432 myname, ment->mnt_fstype ? ment->mnt_fstype : "<null>");
433 }
434
435 signal(SIGHUP, SIG_IGN);
436 signal(SIGQUIT, SIG_IGN);
437 signal(SIGINT, SIG_IGN);
438 /*
439 * Try to umount the mountpoint.
440 * If that fails, try the corresponding special.
441 * (This ordering is necessary for nfs umounts.)
442 * (for remote resources: if the first umount returns EBUSY
443 * don't call umount again - umount() with a resource name
444 * will return a misleading error to the user
445 */
446 if (fflg) {
447 if (((ret = umount2(ment->mnt_mountp, MS_FORCE)) < 0) &&
448 (errno != EBUSY && errno != ENOTSUP &&
449 errno != EPERM))
450 ret = umount2(ment->mnt_special, MS_FORCE);
451 } else {
452 if (((ret = umount2(ment->mnt_mountp, 0)) < 0) &&
453 (errno != EBUSY) && (errno != EPERM))
454 ret = umount2(ment->mnt_special, 0);
455 }
456
457 if (ret < 0) {
458 rpterr(ment->mnt_mountp);
459 if (errno != EINVAL && errno != EFAULT)
460 exit(1);
461
462 exitcode = 1;
463 }
464
465 exit(exitcode);
466 }
467
468 void
rpterr(char * sp)469 rpterr(char *sp)
470 {
471 switch (errno) {
472 case EPERM:
473 fprintf(stderr, gettext("%s: permission denied\n"), myname);
474 break;
475 case ENXIO:
476 fprintf(stderr, gettext("%s: %s no device\n"), myname, sp);
477 break;
478 case ENOENT:
479 fprintf(stderr,
480 gettext("%s: %s no such file or directory\n"),
481 myname, sp);
482 break;
483 case EINVAL:
484 fprintf(stderr, gettext("%s: %s not mounted\n"), myname, sp);
485 break;
486 case EBUSY:
487 fprintf(stderr, gettext("%s: %s busy\n"), myname, sp);
488 break;
489 case ENOTBLK:
490 fprintf(stderr,
491 gettext("%s: %s block device required\n"), myname, sp);
492 break;
493 case ECOMM:
494 fprintf(stderr,
495 gettext("%s: warning: broken link detected\n"), myname);
496 break;
497 default:
498 perror(myname);
499 fprintf(stderr, gettext("%s: cannot unmount %s\n"), myname, sp);
500 }
501 }
502
503 void
usage(void)504 usage(void)
505 {
506 fprintf(stderr, gettext(
507 "Usage:\n%s [-f] [-V] [-o specific_options] {special | mount-point}\n"),
508 myname);
509 fprintf(stderr, gettext(
510 "%s -a [-f] [-V] [-o specific_options] [mount_point ...]\n"), myname);
511 exit(1);
512 }
513
514 void
mnterror(int flag)515 mnterror(int flag)
516 {
517 switch (flag) {
518 case MNT_TOOLONG:
519 fprintf(stderr,
520 gettext("%s: line in mnttab exceeds %d characters\n"),
521 myname, MNT_LINE_MAX-2);
522 break;
523 case MNT_TOOFEW:
524 fprintf(stderr,
525 gettext("%s: line in mnttab has too few entries\n"),
526 myname);
527 break;
528 default:
529 break;
530 }
531 }
532
533 /*
534 * Search the mlist linked list for the
535 * first match of specp or mntp. The list is expected to be in reverse
536 * order of /etc/mnttab.
537 * If both are specified, then both have to match.
538 * Returns the (mountent_t *) of the match, otherwise returns NULL.
539 */
540 mountent_t *
getmntlast(mountent_t * mlist,char * specp,char * mntp)541 getmntlast(mountent_t *mlist, char *specp, char *mntp)
542 {
543 int mfound, sfound;
544
545 for (/* */; mlist; mlist = mlist->link) {
546 mfound = sfound = 0;
547 if (mntp && (strcmp(mlist->ment.mnt_mountp, mntp) == 0)) {
548 if (specp == NULL)
549 return (mlist);
550 mfound++;
551 }
552 if (specp && (strcmp(mlist->ment.mnt_special, specp) == 0)) {
553 if (mntp == NULL)
554 return (mlist);
555 sfound++;
556 }
557 if (mfound && sfound)
558 return (mlist);
559 }
560 return (NULL);
561 }
562
563
564
565 /*
566 * Perform the parallel version of umount. Returns 0 if no errors occurred,
567 * non zero otherwise.
568 */
569 int
parumount(char ** mntlist,int count)570 parumount(char **mntlist, int count)
571 {
572 int maxfd = OPEN_MAX;
573 struct rlimit rl;
574 mountent_t **mntarray, **ml, *mp;
575
576 /*
577 * If no mount points are specified and none were found in mnttab,
578 * then end it all here.
579 */
580 if (count == 0 && mntll == NULL)
581 return (0);
582
583 /*
584 * This is the process scaling section. After running a series
585 * of tests based on the number of simultaneous processes and
586 * processors available, optimum performance was achieved near or
587 * at (PROCN * 2).
588 */
589 if ((maxrun = sysconf(_SC_NPROCESSORS_ONLN)) == -1)
590 maxrun = 4;
591 else
592 maxrun = maxrun * 2 + 1;
593
594 if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
595 rl.rlim_cur = rl.rlim_max;
596 if (setrlimit(RLIMIT_NOFILE, &rl) == 0)
597 maxfd = (int)rl.rlim_cur;
598 (void) enable_extended_FILE_stdio(-1, -1);
599 }
600
601 /*
602 * The parent needs to maintain 3 of its own fd's, plus 2 for
603 * each child (the stdout and stderr pipes).
604 */
605 maxfd = (maxfd / 2) - 6; /* 6 takes care of temporary */
606 /* periods of open fds */
607 if (maxfd < maxrun)
608 maxrun = maxfd;
609 if (maxrun < 4)
610 maxrun = 4; /* sanity check */
611
612 mntarray = make_mntarray(mntlist, count);
613
614 if (listlength == 0) {
615 if (count == 0) /* not an error, just none found */
616 return (0);
617 fprintf(stderr, gettext("%s: no valid entries found in %s\n"),
618 myname, mnttab);
619 return (1);
620 }
621
622 /*
623 * Sort the entries based on their mount level only if lofs's are
624 * not present.
625 */
626 if (lofscnt == 0) {
627 qsort((void *)mntarray, listlength, sizeof (mountent_t *),
628 mcompar);
629 /*
630 * If we do not detect a lofs by now, we never will.
631 */
632 lofscnt = -1;
633 }
634 /*
635 * Now link them up so that a given pid is easier to find when
636 * we go to clean up after they are done.
637 */
638 mntll = mntarray[0];
639 for (ml = mntarray; mp = *ml; /* */)
640 mp->link = *++ml;
641
642 /*
643 * Try to handle interrupts in a reasonable way.
644 */
645 sigset(SIGHUP, cleanup);
646 sigset(SIGQUIT, cleanup);
647 sigset(SIGINT, cleanup);
648
649 do_umounts(mntarray); /* do the umounts */
650 return (exitcode);
651 }
652
653 /*
654 * Returns a mountent_t array based on mntlist. If mntlist is NULL, then
655 * it returns all mnttab entries with a few exceptions. Sets the global
656 * variable listlength to the number of entries in the array.
657 */
658 mountent_t **
make_mntarray(char ** mntlist,int count)659 make_mntarray(char **mntlist, int count)
660 {
661 mountent_t *mp, **mpp;
662 int ndx;
663 char *cp;
664
665 if (count > 0)
666 listlength = count;
667
668 mpp = (mountent_t **)malloc(sizeof (*mp) * (listlength + 1));
669 if (mpp == NULL)
670 nomem();
671
672 if (count == 0) {
673 if (mntll == NULL) { /* no entries? */
674 listlength = 0;
675 return (NULL);
676 }
677 /*
678 * No mount list specified: take all mnttab mount points
679 * except for a few cases.
680 */
681 for (ndx = 0, mp = mntll; mp; mp = mp->link) {
682 if (fsstrinlist(mp->ment.mnt_mountp, keeplist))
683 continue;
684 mp->mlevel = fsgetmlevel(mp->ment.mnt_mountp);
685 if (mp->ment.mnt_fstype &&
686 (strcmp(mp->ment.mnt_fstype, MNTTYPE_LOFS) == 0))
687 lofscnt++;
688
689 mpp[ndx++] = mp;
690 }
691 mpp[ndx] = NULL;
692 listlength = ndx;
693 return (mpp);
694 }
695
696 /*
697 * A list of mount points was specified on the command line.
698 * Build an array out of these.
699 */
700 for (ndx = 0; count--; ) {
701 cp = *mntlist++;
702 if (realpath(cp, resolve) == NULL) {
703 fprintf(stderr,
704 gettext("%s: warning: can't resolve %s\n"),
705 myname, cp);
706 exitcode = 1;
707 mp = getmntlast(mntll, NULL, cp); /* try anyways */
708 } else
709 mp = getmntlast(mntll, NULL, resolve);
710 if (mp == NULL) {
711 struct mnttab mnew;
712 /*
713 * Then we've reached the end without finding
714 * what we are looking for, but we still have to
715 * try to umount it: append it to mntarray.
716 */
717 fprintf(stderr, gettext(
718 "%s: warning: %s not found in %s\n"),
719 myname, resolve, mnttab);
720 exitcode = 1;
721 mntnull(&mnew);
722 mnew.mnt_special = mnew.mnt_mountp = strdup(resolve);
723 if (mnew.mnt_special == NULL)
724 nomem();
725 mp = new_mountent(&mnew);
726 }
727 if (mp->ment.mnt_fstype &&
728 (strcmp(mp->ment.mnt_fstype, MNTTYPE_LOFS) == 0))
729 lofscnt++;
730
731 mp->mlevel = fsgetmlevel(mp->ment.mnt_mountp);
732 mpp[ndx++] = mp;
733 }
734 mpp[ndx] = NULL;
735 listlength = ndx;
736 return (mpp);
737 }
738
739 /*
740 * Returns the tail of a linked list of all mnttab entries. I.e, it's faster
741 * to return the mnttab in reverse order.
742 * Sets listlength to the number of entries in the list.
743 * Returns NULL if none are found.
744 */
745 mountent_t *
getmntall(void)746 getmntall(void)
747 {
748 FILE *fp;
749 mountent_t *mtail;
750 int cnt = 0, ret;
751 struct mnttab mget;
752
753 if ((fp = fopen(mnttab, "r")) == NULL) {
754 fprintf(stderr, gettext("%s: warning cannot open %s\n"),
755 myname, mnttab);
756 return (0);
757 }
758 mtail = NULL;
759
760 while ((ret = getmntent(fp, &mget)) != -1) {
761 mountent_t *mp;
762
763 if (ret > 0) {
764 mnterror(ret);
765 continue;
766 }
767
768 mp = new_mountent(&mget);
769 mp->link = mtail;
770 mtail = mp;
771 cnt++;
772 }
773 fclose(fp);
774 if (mtail == NULL) {
775 listlength = 0;
776 return (NULL);
777 }
778 listlength = cnt;
779 return (mtail);
780 }
781
782 void
do_umounts(mountent_t ** mntarray)783 do_umounts(mountent_t **mntarray)
784 {
785 mountent_t *mp, *mpprev, **ml = mntarray;
786 int cnt = listlength;
787
788 /*
789 * Main loop for the forked children:
790 */
791 for (mpprev = *ml; mp = *ml; mpprev = mp, ml++, cnt--) {
792 pid_t pid;
793
794 /*
795 * Check to see if we cross a mount level: e.g.,
796 * /a/b/c -> /a/b. If so, we need to wait for all current
797 * umounts to finish before umounting the rest.
798 *
799 * Also, we unmount serially as long as there are lofs's
800 * to mount to avoid improper umount ordering.
801 */
802 if (mp->mlevel < mpprev->mlevel || lofscnt > 0)
803 while (nrun > 0 && (dowait() != -1))
804 ;
805
806 if (lofscnt == 0) {
807 /*
808 * We can now go to parallel umounting.
809 */
810 qsort((void *)ml, cnt, sizeof (mountent_t *), mcompar);
811 mp = *ml; /* possible first entry */
812 lofscnt--; /* so we don't do this again */
813 }
814
815 while (setup_iopipe(mp) == -1 && (dowait() != -1))
816 ;
817
818 while (nrun >= maxrun && (dowait() != -1)) /* throttle */
819 ;
820
821 #ifdef CACHEFS_BUG
822 /*
823 * If this is the back file system, then let cachefs/umount
824 * unmount it.
825 */
826 if (strstr(mp->ment.mnt_mountp, BACKMNT_NAME))
827 continue;
828
829
830 if (mp->ment.mnt_fstype &&
831 (strcmp(mp->ment.mnt_fstype, "cachefs") == 0)) {
832 while (cachefs_running && (dowait() != -1))
833 ;
834 cachefs_running = 1;
835 }
836 #endif
837
838 if ((pid = fork()) == -1) {
839 perror("fork");
840 cleanup(-1);
841 /* not reached */
842 }
843 #ifdef DEBUG
844 if (dflg && pid > 0) {
845 fprintf(stderr, "parent %d: umounting %d %s\n",
846 getpid(), pid, mp->ment.mnt_mountp);
847 }
848 #endif
849 if (pid == 0) { /* child */
850 signal(SIGHUP, SIG_IGN);
851 signal(SIGQUIT, SIG_IGN);
852 signal(SIGINT, SIG_IGN);
853 setup_output(mp);
854 doexec(&mp->ment);
855 perror("exec");
856 exit(1);
857 }
858
859 /* parent */
860 (void) close(mp->sopipe[WRPIPE]);
861 (void) close(mp->sepipe[WRPIPE]);
862 mp->pid = pid;
863 nrun++;
864 }
865 cleanup(0);
866 }
867
868 /*
869 * cleanup the existing children and exit with an error
870 * if asig != 0.
871 */
872 void
cleanup(int asig)873 cleanup(int asig)
874 {
875 /*
876 * Let the stragglers finish.
877 */
878 while (nrun > 0 && (dowait() != -1))
879 ;
880 if (asig != 0)
881 exit(1);
882 }
883
884
885 /*
886 * Waits for 1 child to die.
887 *
888 * Returns -1 if no children are left to wait for.
889 * Returns 0 if a child died without an error.
890 * Returns 1 if a child died with an error.
891 * Sets the global exitcode if an error occurred.
892 */
893 int
dowait(void)894 dowait(void)
895 {
896 int wstat, child, ret;
897 mountent_t *mp, *prevp;
898
899 if ((child = wait(&wstat)) == -1)
900 return (-1);
901
902 if (WIFEXITED(wstat)) /* this should always be true */
903 ret = WEXITSTATUS(wstat);
904 else
905 ret = 1; /* assume some kind of error */
906 nrun--;
907 if (ret)
908 exitcode = 1;
909
910 /*
911 * Find our child so we can process its std output, if any.
912 * This search gets smaller and smaller as children are cleaned
913 * up.
914 */
915 for (prevp = NULL, mp = mntll; mp; mp = mp->link) {
916 if (mp->pid != child) {
917 prevp = mp;
918 continue;
919 }
920 /*
921 * Found: let's remove it from this list.
922 */
923 if (prevp) {
924 prevp->link = mp->link;
925 mp->link = NULL;
926 }
927 break;
928 }
929
930 if (mp == NULL) {
931 /*
932 * This should never happen.
933 */
934 #ifdef DEBUG
935 fprintf(stderr, gettext(
936 "%s: unknown child %d\n"), myname, child);
937 #endif
938 exitcode = 1;
939 return (1);
940 }
941 doio(mp); /* Any output? */
942
943 if (mp->ment.mnt_fstype &&
944 (strcmp(mp->ment.mnt_fstype, MNTTYPE_LOFS) == 0))
945 lofscnt--;
946
947 #ifdef CACHEFS_BUG
948 if (mp->ment.mnt_fstype &&
949 (strcmp(mp->ment.mnt_fstype, "cachefs") == 0))
950 cachefs_running = 0;
951 #endif
952
953 return (ret);
954 }
955
956 static const mountent_t zmount = { 0 };
957
958 mountent_t *
new_mountent(struct mnttab * ment)959 new_mountent(struct mnttab *ment)
960 {
961 mountent_t *new;
962
963 new = (mountent_t *)malloc(sizeof (*new));
964 if (new == NULL)
965 nomem();
966
967 *new = zmount;
968 if (ment->mnt_special &&
969 (new->ment.mnt_special = strdup(ment->mnt_special)) == NULL)
970 nomem();
971 if (ment->mnt_mountp &&
972 (new->ment.mnt_mountp = strdup(ment->mnt_mountp)) == NULL)
973 nomem();
974 if (ment->mnt_fstype &&
975 (new->ment.mnt_fstype = strdup(ment->mnt_fstype)) == NULL)
976 nomem();
977 return (new);
978 }
979
980
981 /*
982 * Sort in descending order of "mount level". For example, /a/b/c is
983 * placed before /a/b .
984 */
985 int
mcompar(const void * a,const void * b)986 mcompar(const void *a, const void *b)
987 {
988 mountent_t *a1, *b1;
989
990 a1 = *(mountent_t **)a;
991 b1 = *(mountent_t **)b;
992 return (b1->mlevel - a1->mlevel);
993 }
994
995 /*
996 * The purpose of this routine is to form stdout and stderr
997 * pipes for the children's output. The parent then reads and writes it
998 * out it serially in order to ensure that the output is
999 * not garbled.
1000 */
1001
1002 int
setup_iopipe(mountent_t * mp)1003 setup_iopipe(mountent_t *mp)
1004 {
1005 /*
1006 * Make a stdout and stderr pipe. This should never fail.
1007 */
1008 if (pipe(mp->sopipe) == -1)
1009 return (-1);
1010 if (pipe(mp->sepipe) == -1) {
1011 (void) close(mp->sopipe[RDPIPE]);
1012 (void) close(mp->sopipe[WRPIPE]);
1013 return (-1);
1014 }
1015 /*
1016 * Don't block on an empty pipe.
1017 */
1018 (void) fcntl(mp->sopipe[RDPIPE], F_SETFL, O_NDELAY|O_NONBLOCK);
1019 (void) fcntl(mp->sepipe[RDPIPE], F_SETFL, O_NDELAY|O_NONBLOCK);
1020 return (0);
1021 }
1022
1023 /*
1024 * Called by a child to attach its stdout and stderr to the write side of
1025 * the pipes.
1026 */
1027 void
setup_output(mountent_t * mp)1028 setup_output(mountent_t *mp)
1029 {
1030 (void) close(fileno(stdout));
1031 (void) dup(mp->sopipe[WRPIPE]);
1032 (void) close(mp->sopipe[WRPIPE]);
1033
1034 (void) close(fileno(stderr));
1035 (void) dup(mp->sepipe[WRPIPE]);
1036 (void) close(mp->sepipe[WRPIPE]);
1037 }
1038
1039 /*
1040 * Parent uses this to print any stdout or stderr output issued by
1041 * the child.
1042 */
1043 static void
doio(mountent_t * mp)1044 doio(mountent_t *mp)
1045 {
1046 int bytes;
1047
1048 while ((bytes = read(mp->sepipe[RDPIPE], ibuf, sizeof (ibuf))) > 0)
1049 write(fileno(stderr), ibuf, bytes);
1050 while ((bytes = read(mp->sopipe[RDPIPE], ibuf, sizeof (ibuf))) > 0)
1051 write(fileno(stdout), ibuf, bytes);
1052
1053 (void) close(mp->sopipe[RDPIPE]);
1054 (void) close(mp->sepipe[RDPIPE]);
1055 }
1056
1057 void
nomem(void)1058 nomem(void)
1059 {
1060 fprintf(stderr, gettext("%s: out of memory\n"), myname);
1061 /*
1062 * Let the stragglers finish.
1063 */
1064 while (nrun > 0 && (dowait() != -1))
1065 ;
1066 exit(1);
1067 }
1068