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