#include "am.h"
#ifdef HAS_HOST
#include "mount.h"
#include <sys/stat.h>
#define HOST_MKDIRS
#define MAKE_MNTPT(mntpt, ex, mf) { \
if (strcmp((ex)->ex_dir, "/") == 0) \
strlcpy((mntpt), (mf)->mf_mount, sizeof((mntpt))); \
else \
snprintf((mntpt), sizeof(mntpt), "%s%s", (mf)->mf_mount, (ex)->ex_dir); \
}
static char *
host_match(am_opts *fo)
{
#ifdef HOST_EXEC
if (!host_helper) {
plog(XLOG_USER, "No host helper command given");
return FALSE;
}
#endif
if (!fo->opt_rfs)
fo->opt_rfs = "/";
return (*nfs_ops.fs_match)(fo);
}
static int
host_init(mntfs *mf)
{
if (strchr(mf->mf_info, ':') == 0)
return ENOENT;
return 0;
}
#ifndef HOST_EXEC
static bool_t
xdr_pri_free(xdrproc_t xdr_args, void *args_ptr)
{
XDR xdr;
xdr.x_op = XDR_FREE;
return ((*xdr_args)(&xdr, args_ptr));
}
static int
do_mount(fhstatus *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
{
struct stat stb;
#ifdef DEBUG
dlog("host: mounting fs %s on %s", fs_name, dir);
#endif
#ifdef HOST_MKDIRS
(void) mkdirs(dir, 0555);
#endif
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 int
sortfun(const void *arg1, const void *arg2)
{
const exports *a = arg1, *b = arg2;
return strcmp((*a)->ex_dir, (*b)->ex_dir);
}
static int
fetch_fhandle(CLIENT *client, char *dir, fhstatus *fhp)
{
struct timeval tv;
enum clnt_stat clnt_stat;
tv.tv_sec = 20;
tv.tv_usec = 0;
#ifdef DEBUG
dlog("Fetching fhandle for %s", dir);
#endif
fhp->fhs_vers = MOUNTVERS;
clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_fhstatus, fhp, tv);
if (clnt_stat != RPC_SUCCESS) {
char *msg = clnt_sperrno(clnt_stat);
plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
return EIO;
}
if (fhp->fhs_stat) {
#ifdef DEBUG
errno = fhp->fhs_stat;
dlog("fhandle fetch failed: %m");
#endif
return fhp->fhs_stat;
}
return 0;
}
static int
already_mounted(mntlist *mlist, char *dir)
{
mntlist *ml;
for (ml = mlist; ml; ml = ml->mnext)
if (strcmp(ml->mnt->mnt_dir, dir) == 0)
return 1;
return 0;
}
static int
host_fmount(mntfs *mf)
{
struct timeval tv2;
CLIENT *client;
enum clnt_stat clnt_stat;
int n_export;
int j, k;
exports exlist = 0, ex;
exports *ep = 0;
fhstatus *fp = 0;
char *host = mf->mf_server->fs_host;
int error = 0;
struct sockaddr_in sin;
int sock = RPC_ANYSOCK;
int ok = FALSE;
mntlist *mlist;
char fs_name[PATH_MAX], *rfs_dir;
char mntpt[PATH_MAX];
struct timeval tv;
tv.tv_sec = 10; tv.tv_usec = 0;
mlist = read_mtab(mf->mf_mount);
sin = *mf->mf_server->fs_ip;
sin.sin_port = 0;
if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL &&
(client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) {
plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
error = EIO;
goto out;
}
if (!nfs_auth) {
error = make_nfs_auth();
if (error)
goto out;
}
client->cl_auth = nfs_auth;
#ifdef DEBUG
dlog("Fetching export list from %s", host);
#endif
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) {
error = EIO;
goto out;
}
for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
n_export++;
}
#ifdef DEBUG
#endif
ep = xreallocarray(NULL, n_export, sizeof *ep);
for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
MAKE_MNTPT(mntpt, ex, mf);
if (!already_mounted(mlist, mntpt))
ep[j++] = ex;
}
n_export = j;
qsort(ep, n_export, sizeof(exports), sortfun);
fp = xreallocarray(NULL, n_export, sizeof *fp);
for (j = k = 0; j < n_export; j++) {
if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) {
#ifdef DEBUG
dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
#endif
ep[j] = 0;
} else {
k = j;
if ((error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j])))
ep[j] = 0;
}
}
strlcpy(fs_name, mf->mf_info, sizeof(fs_name));
if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
plog(XLOG_FATAL, "host_fmount: mf_info has no colon");
error = EINVAL;
goto out;
}
++rfs_dir;
for (j = 0; j < n_export; j++) {
ex = ep[j];
if (ex) {
strlcpy(rfs_dir, ex->ex_dir, fs_name + sizeof fs_name - rfs_dir);
MAKE_MNTPT(mntpt, ex, mf);
if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0)
ok = TRUE;
}
}
out:
discard_mntlist(mlist);
free(ep);
free(fp);
if (client)
clnt_destroy(client);
if (exlist)
xdr_pri_free(xdr_exports, &exlist);
if (ok)
return 0;
return error;
}
static int
directory_prefix(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;
}
static int
host_fumount(mntfs *mf)
{
mntlist *ml, *mprev;
int xerror = 0;
mntlist *mlist = read_mtab(mf->mf_mount);
ml = mlist;
mprev = 0;
while (ml) {
mntlist *ml2 = ml->mnext;
ml->mnext = mprev;
mprev = ml;
ml = ml2;
}
mlist = mprev;
for (ml = mlist; ml && !xerror; 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
error = umount_fs(dir);
if (error) {
if (!xerror)
xerror = error;
if (error != EBUSY) {
errno = error;
plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
}
} else {
#ifdef HOST_MKDIRS
(void) rmdirs(dir);
#endif
}
}
}
discard_mntlist(mlist);
if (xerror && amd_state != Finishing) {
xerror = host_fmount(mf);
if (!xerror) {
xerror = EBUSY;
}
}
return xerror;
}
static void host_umounted(am_node *mp)
{
}
#else
static int
host_exec(char *op, char *host, char *fs, char *opts)
{
int error;
char *argv[7];
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;
(void) fclose(stdout);
(void) dup(fileno(logfp));
if (fileno(logfp) != fileno(stderr)) {
(void) fclose(stderr);
(void) dup(fileno(logfp));
}
#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
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);
}
error = errno;
plog(XLOG_ERROR, "exec %s failed: %m", argv[0]);
return error;
}
static int
host_mount(am_node *mp)
{
mntfs *mf = mp->am_mnt;
return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts);
}
static int
host_umount(am_node *mp)
{
mntfs *mf = mp->am_mnt;
return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
}
#endif
am_ops host_ops = {
"host",
host_match,
host_init,
auto_fmount,
host_fmount,
auto_fumount,
host_fumount,
efs_lookuppn,
efs_readdir,
0,
0,
#ifdef HOST_EXEC
0,
#else
host_umounted,
#endif
find_nfs_srvr,
FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
};
#endif