xref: /original-bsd/usr.sbin/amd/amd/host_ops.c (revision 92ab646d)
1 /*
2  * $Id: host_ops.c,v 5.2 90/06/23 22:19:26 jsp Rel $
3  *
4  * Copyright (c) 1990 Jan-Simon Pendry
5  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6  * Copyright (c) 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry at Imperial College, London.
11  *
12  * %sccs.include.redist.c%
13  *
14  *	@(#)host_ops.c	5.1 (Berkeley) 06/29/90
15  */
16 
17 #include "am.h"
18 
19 #ifdef HAS_HOST
20 
21 #include "mount.h"
22 #include <sys/stat.h>
23 
24 /*
25  * NFS host file system
26  */
27 
28 /*
29  * Define HOST_RPC_UDP to use dgram instead of stream RPC.
30  * Datagrams are generally much faster.
31  */
32 #define	HOST_RPC_UDP
33 
34 /*
35  * Define HOST_MKDIRS to make Amd automatically try
36  * to create the mount points.
37  */
38 #define HOST_MKDIRS
39 
40 /*
41  * Execute needs the same as NFS plus a helper command
42  */
43 static int host_match(fo)
44 am_opts *fo;
45 {
46 #ifdef HOST_EXEC
47 	if (!host_helper) {
48 		plog(XLOG_USER, "No host helper command given");
49 		return FALSE;
50 	}
51 #endif /* HOST_EXEC */
52 
53 	/*
54 	 * Make sure rfs is specified to keep nfs_match happy...
55 	 */
56 	if (!fo->opt_rfs)
57 		fo->opt_rfs = "/";
58 
59 	if (!(*nfs_ops.fs_match)(fo))
60 		return FALSE;
61 
62 	return TRUE;
63 }
64 
65 static int host_init(mf)
66 mntfs *mf;
67 {
68 	if (strchr(mf->mf_info, ':') == 0)
69 		return ENOENT;
70 	return 0;
71 }
72 
73 /*
74  * Two implementations:
75  * HOST_EXEC gets you the external version.  The program specified with
76  * the -h option is called.  The external program is not published...
77  * roll your own.
78  *
79  * Otherwise you get the native version.  Faster but makes the program
80  * bigger.
81  */
82 
83 #ifndef HOST_EXEC
84 
85 static bool_t
86 xdr_pri_free(xdr_args, args_ptr)
87 xdrproc_t xdr_args;
88 caddr_t args_ptr;
89 {
90 	XDR xdr;
91 	xdr.x_op = XDR_FREE;
92 	return ((*xdr_args)(&xdr, args_ptr));
93 }
94 
95 static int do_mount(fhp, dir, fs_name, opts, mf)
96 fhstatus *fhp;
97 char *dir;
98 char *fs_name;
99 char *opts;
100 mntfs *mf;
101 {
102 	struct stat stb;
103 #ifdef DEBUG
104 	dlog("host: mounting fs %s on %s\n", fs_name, dir);
105 #endif /* DEBUG */
106 #ifdef HOST_MKDIRS
107 	(void) mkdirs(dir, 0555);
108 #endif /* HOST_MKDIRS */
109 	if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
110 		plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
111 		return ENOENT;
112 	}
113 
114 	return mount_nfs_fh(fhp, dir, fs_name, opts, mf);
115 }
116 
117 static sortfun(a, b)
118 exports *a,*b;
119 {
120 	return strcmp((*a)->ex_dir, (*b)->ex_dir);
121 }
122 
123 /*
124  * Get filehandle
125  */
126 static int fetch_fhandle(client, dir, fhp)
127 CLIENT *client;
128 char *dir;
129 fhstatus *fhp;
130 {
131 	struct timeval tv;
132 	enum clnt_stat clnt_stat;
133 
134 	/*
135 	 * Pick a number, any number...
136 	 */
137 	tv.tv_sec = 10;
138 	tv.tv_usec = 0;
139 
140 #ifdef DEBUG
141 	dlog("Fetching fhandle for %s", dir);
142 #endif /* DEBUG */
143 	/*
144 	 * Call the mount daemon on the remote host to
145 	 * get the filehandle.
146 	 */
147 	clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_fhstatus, fhp, tv);
148 	if (clnt_stat != RPC_SUCCESS) {
149 		extern char *clnt_sperrno();
150 		char *msg = clnt_sperrno(clnt_stat);
151 		plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
152 		return EIO;
153 	}
154 	/*
155 	 * Check status of filehandle
156 	 */
157 	if (fhp->fhs_status) {
158 #ifdef DEBUG
159 		errno = fhp->fhs_status;
160 		dlog("fhandle fetch failed: %m");
161 #endif /* DEBUG */
162 		return fhp->fhs_status;
163 	}
164 	return 0;
165 }
166 
167 /*
168  * Mount the export tree from a host
169  */
170 static int host_mount(mp)
171 am_node *mp;
172 {
173 	struct timeval tv2;
174 	CLIENT *client;
175 	enum clnt_stat clnt_stat;
176 	int n_export;
177 	int j;
178 	exports exlist = 0, ex;
179 	exports *ep = 0;
180 	fhstatus *fp = 0;
181 	mntfs *mf = mp->am_mnt;
182 	char *host = mf->mf_server->fs_host;
183 	int error = 0;
184 	struct sockaddr_in sin;
185 	int sock = RPC_ANYSOCK;
186 	int ok = FALSE;
187 
188 #ifdef HOST_RPC_UDP
189 	struct timeval tv;
190 	tv.tv_sec = 10; tv.tv_usec = 0;
191 #endif /* HOST_RPC_UDP */
192 
193 	/*
194 	 * Take a copy of the server address
195 	 */
196 	sin = *mf->mf_server->fs_ip;
197 
198 	/*
199 	 * Zero out the port - make sure we recompute
200 	 */
201 	sin.sin_port = 0;
202 	/*
203 	 * Make a client end-point
204 	 */
205 #ifdef HOST_RPC_UDP
206 	if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL)
207 #else
208 	if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL)
209 #endif /* HOST_RPC_UDP */
210 	{
211 		plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
212 		error = EIO;
213 		goto out;
214 	}
215 
216 	if (!nfs_auth) {
217 		nfs_auth = authunix_create_default();
218 		if (!nfs_auth) {
219 			error = ENOBUFS;
220 			goto out;
221 		}
222 	}
223 
224 	client->cl_auth = nfs_auth;
225 
226 #ifdef DEBUG
227 	dlog("Fetching export list from %s", host);
228 #endif /* DEBUG */
229 
230 	/*
231 	 * Fetch the export list
232 	 */
233 	tv2.tv_sec = 10; tv2.tv_usec = 0;
234 	clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2);
235 	if (clnt_stat != RPC_SUCCESS) {
236 		/*clnt_perror(client, "rpc");*/
237 		error = EIO;
238 		goto out;
239 	}
240 
241 	/*
242 	 * Figure out how many exports were returned
243 	 */
244 	for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
245 		/*printf("export %s\n", ex->ex_dir);*/
246 		n_export++;
247 	}
248 #ifdef DEBUG
249 	/*dlog("%d exports returned\n", n_export);*/
250 #endif /* DEBUG */
251 
252 	/*
253 	 * Allocate an array of pointers into the list
254 	 * so that they can be sorted.
255 	 */
256 	ep = (exports *) xmalloc(n_export * sizeof(exports));
257 	for (j = 0, ex = exlist; ex; ex = ex->ex_next, j++)
258 		ep[j] = ex;
259 
260 	/*
261 	 * Sort into order.
262 	 * This way the mounts are done in order down the tree,
263 	 * instead of any random order returned by the mount
264 	 * daemon (the protocol doesn't specify...).
265 	 */
266 	qsort(ep, n_export, sizeof(exports), sortfun);
267 
268 	/*
269 	 * Allocate an array of filehandles
270 	 */
271 	fp = (fhstatus *) xmalloc(n_export * sizeof(fhstatus));
272 
273 	/*
274 	 * Try to obtain filehandles for each directory.
275 	 * If a fetch fails then just zero out the array
276 	 * reference but discard the error.
277 	 */
278 	for (j = 0; j < n_export; j++) {
279 		if (error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j]))
280 			ep[j] = 0;
281 	}
282 
283 	/*
284 	 * Mount each filesystem for which we have a filehandle.
285 	 * If any of the mounts succeed then mark "ok" and return
286 	 * error code 0 at the end.  If they all fail then return
287 	 * the last error code.
288 	 */
289 	for (j = 0; j < n_export; j++) {
290 		ex = ep[j];
291 		if (ex) {
292 			char fs_name[MAXPATHLEN];
293 			char mntpt[MAXPATHLEN];
294 			sprintf(fs_name, "%s:%s", host, ex->ex_dir);
295 			if (strcmp(ex->ex_dir, "/") == 0)
296 				strcpy(mntpt, mf->mf_mount);
297 			else
298 				sprintf(mntpt, "%s%s", mf->mf_mount, ex->ex_dir);
299 			error = do_mount(&fp[j], mntpt, fs_name, mf->mf_fo->opt_opts, mf);
300 			if (!error)
301 				ok = TRUE;
302 		}
303 	}
304 
305 	/*
306 	 * Clean up and exit
307 	 */
308 out:
309 	if (ep)
310 		free(ep);
311 	if (fp)
312 		free(fp);
313 	if (client)
314 		clnt_destroy(client);
315 	if (exlist)
316 		xdr_pri_free(xdr_exports, &exlist);
317 	if (ok)
318 		return 0;
319 	return error;
320 }
321 
322 /*
323  * Return true if pref is a directory prefix of dir.
324  *
325  * TODO:
326  * Does not work if pref is "/".
327  */
328 static int directory_prefix(pref, dir)
329 char *pref;
330 char *dir;
331 {
332 	int len = strlen(pref);
333 	if (strncmp(pref, dir, len) != 0)
334 		return FALSE;
335 	if (dir[len] == '/' || dir[len] == '\0')
336 		return TRUE;
337 	return FALSE;
338 }
339 
340 /*
341  * Unmount a mount tree
342  */
343 static int host_umount(mp)
344 am_node *mp;
345 {
346 	mntfs *mf = mp->am_mnt;
347 	mntlist *ml, *mprev;
348 	int xerror = 0;
349 
350 	/*
351 	 * Read the mount list
352 	 */
353 	mntlist *mlist = read_mtab(mf->mf_mount);
354 
355 	/*
356 	 * Unlock the mount list
357 	 */
358 	unlock_mntlist();
359 
360 	/*
361 	 * Reverse list...
362 	 */
363 	ml = mlist;
364 	mprev = 0;
365 	while (ml) {
366 		mntlist *ml2 = ml->mnext;
367 		ml->mnext = mprev;
368 		mprev = ml;
369 		ml = ml2;
370 	}
371 	mlist = mprev;
372 
373 	/*
374 	 * Unmount all filesystems...
375 	 */
376 	for (ml = mlist; ml; ml = ml->mnext) {
377 		char *dir = ml->mnt->mnt_dir;
378 		if (directory_prefix(mf->mf_mount, dir)) {
379 			int error;
380 #ifdef DEBUG
381 			dlog("host: unmounts %s", dir);
382 #endif /* DEBUG */
383 			/*
384 			 * Unmount "dir"
385 			 */
386 			error = UMOUNT_FS(dir);
387 			/*
388 			 * Keep track of errors
389 			 */
390 			if (error) {
391 				if (!xerror)
392 					xerror = error;
393 				if (error != EBUSY) {
394 					errno = error;
395 					plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
396 				}
397 			} else {
398 #ifdef HOST_MKDIRS
399 				(void) rmdirs(dir);
400 #endif /* HOST_MKDIRS */
401 			}
402 		}
403 	}
404 
405 	/*
406 	 * Throw away mount list
407 	 */
408 	discard_mntlist(mlist);
409 
410 	return xerror;
411 }
412 
413 #else /* HOST_EXEC */
414 
415 static int host_exec(op, host, fs, opts)
416 char *op;
417 char *host;
418 char *fs;
419 char *opts;
420 {
421 	int error;
422 	char *argv[7];
423 
424 	/*
425 	 * Build arg vector
426 	 */
427 	argv[0] = host_helper;
428 	argv[1] = host_helper;
429 	argv[2] = op;
430 	argv[3] = host;
431 	argv[4] = fs;
432 	argv[5] = opts && *opts ? opts : "rw,default";
433 	argv[6] = 0;
434 
435 	/*
436 	 * Put stdout to stderr
437 	 */
438 	(void) fclose(stdout);
439 	(void) dup(fileno(logfp));
440 	if (fileno(logfp) != fileno(stderr)) {
441 		(void) fclose(stderr);
442 		(void) dup(fileno(logfp));
443 	}
444 	/*
445 	 * Try the exec
446 	 */
447 #ifdef DEBUG
448 	Debug(D_FULL) {
449 		char **cp = argv;
450 		plog(XLOG_DEBUG, "executing (un)mount command...");
451 		while (*cp) {
452 	  		plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp);
453 			cp++;
454 		}
455 	}
456 #endif /* DEBUG */
457 	if (argv[0] == 0 || argv[1] == 0) {
458 		errno = EINVAL;
459 		plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
460 	} else {
461 		(void) execv(argv[0], argv+1);
462 	}
463 	/*
464 	 * Save error number
465 	 */
466 	error = errno;
467 	plog(XLOG_ERROR, "exec %s failed: %m", argv[0]);
468 
469 	/*
470 	 * Return error
471 	 */
472 	return error;
473 }
474 
475 static int host_mount(mp)
476 am_node *mp;
477 {
478 	mntfs *mf = mp->am_mnt;
479 
480 	return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_fo->opt_opts);
481 }
482 
483 static int host_umount(mp)
484 am_node *mp;
485 {
486 	mntfs *mf = mp->am_mnt;
487 
488 	return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
489 }
490 
491 #endif /* HOST_EXEC */
492 
493 /*
494  * Ops structure
495  */
496 am_ops host_ops = {
497 	"host",
498 	host_match,
499 	host_init,
500 	host_mount,
501 	host_umount,
502 	efs_lookuppn,
503 	efs_readdir,
504 	0, /* host_readlink */
505 	0, /* host_mounted */
506 	0, /* host_umounted */
507 	find_nfs_srvr,
508 	FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
509 };
510 
511 #endif /* HAS_HOST */
512