xref: /illumos-gate/usr/src/cmd/fs.d/umount.c (revision 0463c800)
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