/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: auto_all.c,v 1.9.2.1 92/06/24 13:49:24 marker Exp $"
/*
 * @(#)auto_all.c	1.4 89/04/28 4.0NFSSRC Copyr 1988 Sun Micro
 *
 * Copyright (c) 1987 by Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <ctype.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#include <mntent.h>
#include <netdb.h>
#include <errno.h>
#include "nfs_prot.h"
#define NFSCLIENT
typedef nfs_fh fhandle_t;
#include <rpcsvc/mount.h>
#include <sys/mount.h>
#include "automount.h"
extern int errno;

#if defined(_RISCOS)
#include <syslog.h>
#define fprintf syslog
#ifdef stderr
#undef stderr
#endif
#define stderr LOG_DEBUG
#endif /* _RISCOS */

/* After a machine goes down, we won't unmount its filesystems right now
 * because we can't delete the mount entries from the local kernel.
 * Instead, on our next access, we will unmount the filesystems and remount
 * them.  This needs to be done because the mount device will change.
 */
do_unmount(fsys)
	struct filsys *fsys;
{
	struct queue tmpq;
	struct filsys *fs, *nextfs;
	nfsstat remount();
	extern int trace;
#ifdef _SVR4_MODS
	struct filsys *rootfs;
	dev_t olddev;
	int newdevs;
#endif

	tmpq.q_head = tmpq.q_tail = NULL;
	for (fs = HEAD(struct filsys, fs_q); fs; fs = nextfs) {
		nextfs = NEXT(struct filsys, fs);
		if (fs->fs_rootfs == fsys) {
			REMQUE(fs_q, fs);
			INSQUE(tmpq, fs);
		}
	}
	/* walk backwards trying to unmount */
	for (fs = TAIL(struct filsys, tmpq); fs; fs = nextfs) {
		nextfs = PREV(struct filsys, fs);
#ifdef _SVR4_MODS
		if (fs->fs_unmounted)
			continue;
#endif
		if (trace > 1) {
			fprintf(stderr, "unmount %s ", fs->fs_mntpnt);
#if !defined(_RISCOS)
			fflush(stderr);
#endif
		}
#ifdef _SVR4_MODS
		/* returns 1 for remote root filesystems.  We do this
		 * so the filesystems are put back into fs_q.
		 */
		if (pathok(tmpq, fs)) {
			if (unmount(fs->fs_mntpnt) < 0) {
				if (trace > 1)
					fprintf(stderr,
						"unmount %s: BUSY\n",
						fs->fs_mntpnt);
				goto inuse;
			}
#ifdef _RISCOS
			else
				fs->fs_unmounted = 1;
#endif
		}
#ifndef _RISCOS
		fs->fs_unmounted = 1;
#endif
		if (trace > 1)
			fprintf(stderr, "unmount: OK\n");
#else /* !_SVR4_MODS */
		if (unmount(fs->fs_mntpnt) < 0) {
			if (trace > 1)
				fprintf(stderr, "BUSY\n");
			/* remount previous unmounted ones; moved below */
			for (fs = NEXT(struct filsys, fs); fs; fs = nextfs) {
				nextfs = NEXT(struct filsys, fs);
				(void) remount(fs);
			}
			goto inuse;
		}
		if (trace > 1)
			fprintf(stderr, "OK\n");
#endif /* _SVR4_MODS */
	}

	/* all ok - walk backwards removing directories */
	clean_mtab(0, HEAD(struct filsys, tmpq));
	for (fs = TAIL(struct filsys, tmpq); fs; fs = nextfs) {
		nextfs = PREV(struct filsys, fs);
		nfsunmount(fs);
#if defined(_RISCOS)
		/* Only remove if original name */
		if (!strcmp(tmpdir, TMPDIR))
			safe_rmdir(fs->fs_mntpnt);
#else
		safe_rmdir(fs->fs_mntpnt);
#endif /* _RISCOS */
		REMQUE(tmpq, fs);
		INSQUE(fs_q, fs);
		free_filsys(fs);
	}
	/* success */
	return (1);

inuse:
#ifdef _SVR4_MODS
	/* remount previous unmounted ones */
	newdevs = 0;
	for (fs = HEAD(struct filsys, tmpq); fs; fs = nextfs) {
		nextfs = NEXT(struct filsys, fs);
		if (fs->fs_unmounted) {
			olddev = fs->fs_mountdev;
			if (remount(fs) == NFS_OK) {
				fs->fs_unmounted = 0;
				if (fs->fs_mountdev != olddev)
					newdevs++;
			}
		}
	}

	/* remove entries with old dev ids */
	if (newdevs) {
		clean_mtab(0, HEAD(struct filsys, tmpq));
		rootfs = HEAD(struct filsys, tmpq)->fs_rootfs;
	}
#endif /* _SVR4_MODS */

	/* put things back on the correct list */
	for (fs = HEAD(struct filsys, tmpq); fs; fs = nextfs) {
		nextfs = NEXT(struct filsys, fs);
		REMQUE(tmpq, fs);
		INSQUE(fs_q, fs);
	}

#ifdef _SVR4_MODS
	/* put updated entries back */
	if (newdevs)
		addtomtab(rootfs);
#endif /* _SVR4_MODS */

	return (0);
}


#ifdef _SVR4_MODS
/*
 * Check a path prior to using it.  This avoids hangups in
 * the unmount system call from dead mount points in the 
 * path to the mount point we're trying to unmount.
 * If all the mount points ping OK then return 1.
 *
 * NOTE: pathok returns 1 for the root filesystem of a remote machine.
 *	This is done intentionally, so we execute the umount which will
 *	fail and put the filesystems back into the fs_q.
 */
int
pathok(tmpq, tfs)
	struct queue tmpq;
	struct filsys *tfs;
{
	struct filsys *fs, *nextfs;
	extern dev_t tmpdev;
	enum clnt_stat pingmount();

	while (tfs->fs_mntpntdev != tmpdev && tfs->fs_rootfs != tfs) {
		for (fs = HEAD(struct filsys, tmpq); fs; fs = nextfs) {
			nextfs = NEXT(struct filsys, fs);
			if (tfs->fs_mntpntdev == fs->fs_mountdev)
				break;
		}
		if (fs == NULL) {
			syslog(LOG_ERR,
			       "pathok: couldn't find devid %04x(%04x) for %s",
			       tfs->fs_mntpntdev & 0xFFF,
			       tmpdev & 0xFFF,
			       tfs->fs_mntpnt);
			return (1);
		}
		if (trace > 1)
			fprintf(stderr, "pathok: %s\n", fs->fs_mntpnt);
		if (pingmount(fs->fs_host, fs->fs_addr.sin_addr) != RPC_SUCCESS) {
			if (trace > 1) {
				fprintf(stderr, "pathok: %s is dead\n",
					fs->fs_mntpnt);
			}
			return (0);
		}
		if (trace > 1)
			fprintf(stderr, "pathok: %s is OK\n",
				fs->fs_mntpnt);
		tfs = fs;
	}
	return (1);
}
#endif /* _SVR4_MODS */

freeex(ex)
	struct exports *ex;
{
	struct groups *groups, *tmpgroups;
	struct exports *tmpex;

	while (ex) {
		free(ex->ex_name);
		groups = ex->ex_groups;
		while (groups) {
			tmpgroups = groups->g_next;
			free((char *)groups);
			groups = tmpgroups;
		}
		tmpex = ex->ex_next;
		free((char *)ex);
		ex = tmpex;
	}
}

mkdir_r(dir)
	char *dir;
{
	int err;
	char *slash;
	char *rindex();

#ifdef _SVR4_MODS
	if (mkdir(dir, 0555) == 0 || errno == EEXIST)
		return (0);
#else
	if (mkdir(dir, 0555) == 0)
		return (0);
#endif /* _SVR4_MODS */
	if (errno != ENOENT)
		return (-1);
	slash = rindex(dir, '/');
	if (slash == NULL)
		return (-1);
	*slash = '\0';
	err = mkdir_r(dir);
	*slash++ = '/';
	if (err || !*slash)
		return (err);
	return mkdir(dir, 0555);
}

safe_rmdir(dir)
	char *dir;
{
#ifdef _SVR4_MODS
	extern dev_t tmpdev;
	struct stat stbuf;
#else
	dev_t tmpdev;
	struct stat stbuf;

	if (stat(tmpdir, &stbuf))
		return;
	tmpdev = stbuf.st_dev;
#endif /* _SVR4_MODS */

	if (stat(dir, &stbuf))
		return;
	if (tmpdev == stbuf.st_dev)
		(void) rmdir(dir);
}
