4.3BSD-Reno/src/usr.sbin/amd/amd/host_ops.c
/*
* $Id: host_ops.c,v 5.2 90/06/23 22:19:26 jsp Rel $
*
* Copyright (c) 1990 Jan-Simon Pendry
* Copyright (c) 1990 Imperial College of Science, Technology & Medicine
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry at Imperial College, London.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* Neither the name of the University nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#)host_ops.c 5.1 (Berkeley) 6/29/90
*/
#include "am.h"
#ifdef HAS_HOST
#include "mount.h"
#include <sys/stat.h>
/*
* NFS host file system
*/
/*
* Define HOST_RPC_UDP to use dgram instead of stream RPC.
* Datagrams are generally much faster.
*/
#define HOST_RPC_UDP
/*
* Define HOST_MKDIRS to make Amd automatically try
* to create the mount points.
*/
#define HOST_MKDIRS
/*
* Execute needs the same as NFS plus a helper command
*/
static int host_match(fo)
am_opts *fo;
{
#ifdef HOST_EXEC
if (!host_helper) {
plog(XLOG_USER, "No host helper command given");
return FALSE;
}
#endif /* HOST_EXEC */
/*
* Make sure rfs is specified to keep nfs_match happy...
*/
if (!fo->opt_rfs)
fo->opt_rfs = "/";
if (!(*nfs_ops.fs_match)(fo))
return FALSE;
return TRUE;
}
static int host_init(mf)
mntfs *mf;
{
if (strchr(mf->mf_info, ':') == 0)
return ENOENT;
return 0;
}
/*
* Two implementations:
* HOST_EXEC gets you the external version. The program specified with
* the -h option is called. The external program is not published...
* roll your own.
*
* Otherwise you get the native version. Faster but makes the program
* bigger.
*/
#ifndef HOST_EXEC
static bool_t
xdr_pri_free(xdr_args, args_ptr)
xdrproc_t xdr_args;
caddr_t args_ptr;
{
XDR xdr;
xdr.x_op = XDR_FREE;
return ((*xdr_args)(&xdr, args_ptr));
}
static int do_mount(fhp, dir, fs_name, opts, mf)
fhstatus *fhp;
char *dir;
char *fs_name;
char *opts;
mntfs *mf;
{
struct stat stb;
#ifdef DEBUG
dlog("host: mounting fs %s on %s\n", fs_name, dir);
#endif /* DEBUG */
#ifdef HOST_MKDIRS
(void) mkdirs(dir, 0555);
#endif /* HOST_MKDIRS */
if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
return ENOENT;
}
return mount_nfs_fh(fhp, dir, fs_name, opts, mf);
}
static sortfun(a, b)
exports *a,*b;
{
return strcmp((*a)->ex_dir, (*b)->ex_dir);
}
/*
* Get filehandle
*/
static int fetch_fhandle(client, dir, fhp)
CLIENT *client;
char *dir;
fhstatus *fhp;
{
struct timeval tv;
enum clnt_stat clnt_stat;
/*
* Pick a number, any number...
*/
tv.tv_sec = 10;
tv.tv_usec = 0;
#ifdef DEBUG
dlog("Fetching fhandle for %s", dir);
#endif /* DEBUG */
/*
* Call the mount daemon on the remote host to
* get the filehandle.
*/
clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_fhstatus, fhp, tv);
if (clnt_stat != RPC_SUCCESS) {
extern char *clnt_sperrno();
char *msg = clnt_sperrno(clnt_stat);
plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
return EIO;
}
/*
* Check status of filehandle
*/
if (fhp->fhs_status) {
#ifdef DEBUG
errno = fhp->fhs_status;
dlog("fhandle fetch failed: %m");
#endif /* DEBUG */
return fhp->fhs_status;
}
return 0;
}
/*
* Mount the export tree from a host
*/
static int host_mount(mp)
am_node *mp;
{
struct timeval tv2;
CLIENT *client;
enum clnt_stat clnt_stat;
int n_export;
int j;
exports exlist = 0, ex;
exports *ep = 0;
fhstatus *fp = 0;
mntfs *mf = mp->am_mnt;
char *host = mf->mf_server->fs_host;
int error = 0;
struct sockaddr_in sin;
int sock = RPC_ANYSOCK;
int ok = FALSE;
#ifdef HOST_RPC_UDP
struct timeval tv;
tv.tv_sec = 10; tv.tv_usec = 0;
#endif /* HOST_RPC_UDP */
/*
* Take a copy of the server address
*/
sin = *mf->mf_server->fs_ip;
/*
* Zero out the port - make sure we recompute
*/
sin.sin_port = 0;
/*
* Make a client end-point
*/
#ifdef HOST_RPC_UDP
if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL)
#else
if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL)
#endif /* HOST_RPC_UDP */
{
plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
error = EIO;
goto out;
}
if (!nfs_auth) {
nfs_auth = authunix_create_default();
if (!nfs_auth) {
error = ENOBUFS;
goto out;
}
}
client->cl_auth = nfs_auth;
#ifdef DEBUG
dlog("Fetching export list from %s", host);
#endif /* DEBUG */
/*
* Fetch the export list
*/
tv2.tv_sec = 10; tv2.tv_usec = 0;
clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2);
if (clnt_stat != RPC_SUCCESS) {
/*clnt_perror(client, "rpc");*/
error = EIO;
goto out;
}
/*
* Figure out how many exports were returned
*/
for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
/*printf("export %s\n", ex->ex_dir);*/
n_export++;
}
#ifdef DEBUG
/*dlog("%d exports returned\n", n_export);*/
#endif /* DEBUG */
/*
* Allocate an array of pointers into the list
* so that they can be sorted.
*/
ep = (exports *) xmalloc(n_export * sizeof(exports));
for (j = 0, ex = exlist; ex; ex = ex->ex_next, j++)
ep[j] = ex;
/*
* Sort into order.
* This way the mounts are done in order down the tree,
* instead of any random order returned by the mount
* daemon (the protocol doesn't specify...).
*/
qsort(ep, n_export, sizeof(exports), sortfun);
/*
* Allocate an array of filehandles
*/
fp = (fhstatus *) xmalloc(n_export * sizeof(fhstatus));
/*
* Try to obtain filehandles for each directory.
* If a fetch fails then just zero out the array
* reference but discard the error.
*/
for (j = 0; j < n_export; j++) {
if (error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j]))
ep[j] = 0;
}
/*
* Mount each filesystem for which we have a filehandle.
* If any of the mounts succeed then mark "ok" and return
* error code 0 at the end. If they all fail then return
* the last error code.
*/
for (j = 0; j < n_export; j++) {
ex = ep[j];
if (ex) {
char fs_name[MAXPATHLEN];
char mntpt[MAXPATHLEN];
sprintf(fs_name, "%s:%s", host, ex->ex_dir);
if (strcmp(ex->ex_dir, "/") == 0)
strcpy(mntpt, mf->mf_mount);
else
sprintf(mntpt, "%s%s", mf->mf_mount, ex->ex_dir);
error = do_mount(&fp[j], mntpt, fs_name, mf->mf_fo->opt_opts, mf);
if (!error)
ok = TRUE;
}
}
/*
* Clean up and exit
*/
out:
if (ep)
free(ep);
if (fp)
free(fp);
if (client)
clnt_destroy(client);
if (exlist)
xdr_pri_free(xdr_exports, &exlist);
if (ok)
return 0;
return error;
}
/*
* Return true if pref is a directory prefix of dir.
*
* TODO:
* Does not work if pref is "/".
*/
static int directory_prefix(pref, dir)
char *pref;
char *dir;
{
int len = strlen(pref);
if (strncmp(pref, dir, len) != 0)
return FALSE;
if (dir[len] == '/' || dir[len] == '\0')
return TRUE;
return FALSE;
}
/*
* Unmount a mount tree
*/
static int host_umount(mp)
am_node *mp;
{
mntfs *mf = mp->am_mnt;
mntlist *ml, *mprev;
int xerror = 0;
/*
* Read the mount list
*/
mntlist *mlist = read_mtab(mf->mf_mount);
/*
* Unlock the mount list
*/
unlock_mntlist();
/*
* Reverse list...
*/
ml = mlist;
mprev = 0;
while (ml) {
mntlist *ml2 = ml->mnext;
ml->mnext = mprev;
mprev = ml;
ml = ml2;
}
mlist = mprev;
/*
* Unmount all filesystems...
*/
for (ml = mlist; ml; ml = ml->mnext) {
char *dir = ml->mnt->mnt_dir;
if (directory_prefix(mf->mf_mount, dir)) {
int error;
#ifdef DEBUG
dlog("host: unmounts %s", dir);
#endif /* DEBUG */
/*
* Unmount "dir"
*/
error = UMOUNT_FS(dir);
/*
* Keep track of errors
*/
if (error) {
if (!xerror)
xerror = error;
if (error != EBUSY) {
errno = error;
plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
}
} else {
#ifdef HOST_MKDIRS
(void) rmdirs(dir);
#endif /* HOST_MKDIRS */
}
}
}
/*
* Throw away mount list
*/
discard_mntlist(mlist);
return xerror;
}
#else /* HOST_EXEC */
static int host_exec(op, host, fs, opts)
char *op;
char *host;
char *fs;
char *opts;
{
int error;
char *argv[7];
/*
* Build arg vector
*/
argv[0] = host_helper;
argv[1] = host_helper;
argv[2] = op;
argv[3] = host;
argv[4] = fs;
argv[5] = opts && *opts ? opts : "rw,default";
argv[6] = 0;
/*
* Put stdout to stderr
*/
(void) fclose(stdout);
(void) dup(fileno(logfp));
if (fileno(logfp) != fileno(stderr)) {
(void) fclose(stderr);
(void) dup(fileno(logfp));
}
/*
* Try the exec
*/
#ifdef DEBUG
Debug(D_FULL) {
char **cp = argv;
plog(XLOG_DEBUG, "executing (un)mount command...");
while (*cp) {
plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp);
cp++;
}
}
#endif /* DEBUG */
if (argv[0] == 0 || argv[1] == 0) {
errno = EINVAL;
plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
} else {
(void) execv(argv[0], argv+1);
}
/*
* Save error number
*/
error = errno;
plog(XLOG_ERROR, "exec %s failed: %m", argv[0]);
/*
* Return error
*/
return error;
}
static int host_mount(mp)
am_node *mp;
{
mntfs *mf = mp->am_mnt;
return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_fo->opt_opts);
}
static int host_umount(mp)
am_node *mp;
{
mntfs *mf = mp->am_mnt;
return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
}
#endif /* HOST_EXEC */
/*
* Ops structure
*/
am_ops host_ops = {
"host",
host_match,
host_init,
host_mount,
host_umount,
efs_lookuppn,
efs_readdir,
0, /* host_readlink */
0, /* host_mounted */
0, /* host_umounted */
find_nfs_srvr,
FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
};
#endif /* HAS_HOST */