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