xref: /original-bsd/usr.sbin/amd/amd/host_ops.c (revision 4ddaeef2)
1 /*
2  * Copyright (c) 1990 Jan-Simon Pendry
3  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1990 The Regents of the University of California.
5  * 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	5.3 (Berkeley) 05/12/91
13  *
14  * $Id: host_ops.c,v 5.2.1.6 91/05/07 22:17:53 jsp Alpha $
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));
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 
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
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));
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));
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));
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));
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));
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 
224 #ifdef HOST_RPC_UDP
225 	struct timeval tv;
226 	tv.tv_sec = 10; tv.tv_usec = 0;
227 #endif /* HOST_RPC_UDP */
228 
229 	/*
230 	 * Read the mount list
231 	 */
232 	mlist = read_mtab(mf->mf_mount);
233 
234 	/*
235 	 * Unlock the mount list
236 	 */
237 	unlock_mntlist();
238 
239 	/*
240 	 * Take a copy of the server address
241 	 */
242 	sin = *mf->mf_server->fs_ip;
243 
244 	/*
245 	 * Zero out the port - make sure we recompute
246 	 */
247 	sin.sin_port = 0;
248 	/*
249 	 * Make a client end-point
250 	 */
251 #ifdef HOST_RPC_UDP
252 	if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL)
253 #else
254 	if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL)
255 #endif /* HOST_RPC_UDP */
256 	{
257 		plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
258 		error = EIO;
259 		goto out;
260 	}
261 
262 	if (!nfs_auth) {
263 		error = make_nfs_auth();
264 		if (error)
265 			goto out;
266 	}
267 
268 	client->cl_auth = nfs_auth;
269 
270 #ifdef DEBUG
271 	dlog("Fetching export list from %s", host);
272 #endif /* DEBUG */
273 
274 	/*
275 	 * Fetch the export list
276 	 */
277 	tv2.tv_sec = 10; tv2.tv_usec = 0;
278 	clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2);
279 	if (clnt_stat != RPC_SUCCESS) {
280 		/*clnt_perror(client, "rpc");*/
281 		error = EIO;
282 		goto out;
283 	}
284 
285 	/*
286 	 * Figure out how many exports were returned
287 	 */
288 	for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
289 		/*printf("export %s\n", ex->ex_dir);*/
290 		n_export++;
291 	}
292 #ifdef DEBUG
293 	/*dlog("%d exports returned\n", n_export);*/
294 #endif /* DEBUG */
295 
296 	/*
297 	 * Allocate an array of pointers into the list
298 	 * so that they can be sorted.  If the filesystem
299 	 * is already mounted then ignore it.
300 	 */
301 	ep = (exports *) xmalloc(n_export * sizeof(exports));
302 	for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
303 		MAKE_MNTPT(mntpt, ex, mf);
304 		if (!already_mounted(mlist, mntpt))
305 			ep[j++] = ex;
306 	}
307 	n_export = j;
308 
309 	/*
310 	 * Sort into order.
311 	 * This way the mounts are done in order down the tree,
312 	 * instead of any random order returned by the mount
313 	 * daemon (the protocol doesn't specify...).
314 	 */
315 	qsort(ep, n_export, sizeof(exports), sortfun);
316 
317 	/*
318 	 * Allocate an array of filehandles
319 	 */
320 	fp = (fhstatus *) xmalloc(n_export * sizeof(fhstatus));
321 
322 	/*
323 	 * Try to obtain filehandles for each directory.
324 	 * If a fetch fails then just zero out the array
325 	 * reference but discard the error.
326 	 */
327 	for (j = k = 0; j < n_export; j++) {
328 		/* Check and avoid a duplicated export entry */
329 		if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) {
330 #ifdef DEBUG
331 			dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
332 #endif
333 			ep[j] = 0;
334 		} else {
335 			k = j;
336 			if (error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j]))
337 				ep[j] = 0;
338 		}
339 	}
340 
341 	/*
342 	 * Mount each filesystem for which we have a filehandle.
343 	 * If any of the mounts succeed then mark "ok" and return
344 	 * error code 0 at the end.  If they all fail then return
345 	 * the last error code.
346 	 */
347 	strncpy(fs_name, mf->mf_info, sizeof(fs_name));
348 	if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
349 		plog(XLOG_FATAL, "host_fmount: mf_info has no colon");
350 		error = EINVAL;
351 		goto out;
352 	}
353 	++rfs_dir;
354 	for (j = 0; j < n_export; j++) {
355 		ex = ep[j];
356 		if (ex) {
357 			strcpy(rfs_dir, ex->ex_dir);
358 			MAKE_MNTPT(mntpt, ex, mf);
359 			if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0)
360 				ok = TRUE;
361 		}
362 	}
363 
364 	/*
365 	 * Clean up and exit
366 	 */
367 out:
368 	discard_mntlist(mlist);
369 	if (ep)
370 		free(ep);
371 	if (fp)
372 		free(fp);
373 	if (client)
374 		clnt_destroy(client);
375 	if (exlist)
376 		xdr_pri_free(xdr_exports, &exlist);
377 	if (ok)
378 		return 0;
379 	return error;
380 }
381 
382 /*
383  * Return true if pref is a directory prefix of dir.
384  *
385  * TODO:
386  * Does not work if pref is "/".
387  */
388 static int directory_prefix P((char *pref, char *dir));
389 static int directory_prefix(pref, dir)
390 char *pref;
391 char *dir;
392 {
393 	int len = strlen(pref);
394 	if (strncmp(pref, dir, len) != 0)
395 		return FALSE;
396 	if (dir[len] == '/' || dir[len] == '\0')
397 		return TRUE;
398 	return FALSE;
399 }
400 
401 /*
402  * Unmount a mount tree
403  */
404 static int host_fumount P((mntfs *mf));
405 static int host_fumount(mf)
406 mntfs *mf;
407 {
408 	mntlist *ml, *mprev;
409 	int xerror = 0;
410 
411 	/*
412 	 * Read the mount list
413 	 */
414 	mntlist *mlist = read_mtab(mf->mf_mount);
415 
416 	/*
417 	 * Unlock the mount list
418 	 */
419 	unlock_mntlist();
420 
421 	/*
422 	 * Reverse list...
423 	 */
424 	ml = mlist;
425 	mprev = 0;
426 	while (ml) {
427 		mntlist *ml2 = ml->mnext;
428 		ml->mnext = mprev;
429 		mprev = ml;
430 		ml = ml2;
431 	}
432 	mlist = mprev;
433 
434 	/*
435 	 * Unmount all filesystems...
436 	 */
437 	for (ml = mlist; ml && !xerror; ml = ml->mnext) {
438 		char *dir = ml->mnt->mnt_dir;
439 		if (directory_prefix(mf->mf_mount, dir)) {
440 			int error;
441 #ifdef DEBUG
442 			dlog("host: unmounts %s", dir);
443 #endif /* DEBUG */
444 			/*
445 			 * Unmount "dir"
446 			 */
447 			error = UMOUNT_FS(dir);
448 			/*
449 			 * Keep track of errors
450 			 */
451 			if (error) {
452 				if (!xerror)
453 					xerror = error;
454 				if (error != EBUSY) {
455 					errno = error;
456 					plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
457 				}
458 			} else {
459 #ifdef HOST_MKDIRS
460 				(void) rmdirs(dir);
461 #endif /* HOST_MKDIRS */
462 			}
463 		}
464 	}
465 
466 	/*
467 	 * Throw away mount list
468 	 */
469 	discard_mntlist(mlist);
470 
471 	/*
472 	 * Try to remount, except when we are shutting down.
473 	 */
474 	if (xerror && amd_state != Finishing) {
475 		xerror = host_fmount(mf);
476 		if (!xerror) {
477 			/*
478 			 * Don't log this - it's usually too verbose
479 			plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
480 			 */
481 			xerror = EBUSY;
482 		}
483 	}
484 	return xerror;
485 }
486 
487 #else /* HOST_EXEC */
488 
489 static int host_exec P((char*op, char*host, char*fs, char*opts));
490 static int host_exec(op, host, fs, opts)
491 char *op;
492 char *host;
493 char *fs;
494 char *opts;
495 {
496 	int error;
497 	char *argv[7];
498 
499 	/*
500 	 * Build arg vector
501 	 */
502 	argv[0] = host_helper;
503 	argv[1] = host_helper;
504 	argv[2] = op;
505 	argv[3] = host;
506 	argv[4] = fs;
507 	argv[5] = opts && *opts ? opts : "rw,default";
508 	argv[6] = 0;
509 
510 	/*
511 	 * Put stdout to stderr
512 	 */
513 	(void) fclose(stdout);
514 	(void) dup(fileno(logfp));
515 	if (fileno(logfp) != fileno(stderr)) {
516 		(void) fclose(stderr);
517 		(void) dup(fileno(logfp));
518 	}
519 	/*
520 	 * Try the exec
521 	 */
522 #ifdef DEBUG
523 	Debug(D_FULL) {
524 		char **cp = argv;
525 		plog(XLOG_DEBUG, "executing (un)mount command...");
526 		while (*cp) {
527 	  		plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp);
528 			cp++;
529 		}
530 	}
531 #endif /* DEBUG */
532 	if (argv[0] == 0 || argv[1] == 0) {
533 		errno = EINVAL;
534 		plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
535 	} else {
536 		(void) execv(argv[0], argv+1);
537 	}
538 	/*
539 	 * Save error number
540 	 */
541 	error = errno;
542 	plog(XLOG_ERROR, "exec %s failed: %m", argv[0]);
543 
544 	/*
545 	 * Return error
546 	 */
547 	return error;
548 }
549 
550 static int host_mount P((am_node *mp));
551 static int host_mount(mp)
552 am_node *mp;
553 {
554 	mntfs *mf = mp->am_mnt;
555 
556 	return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts);
557 }
558 
559 static int host_umount P((am_node *mp));
560 static int host_umount(mp)
561 am_node *mp;
562 {
563 	mntfs *mf = mp->am_mnt;
564 
565 	return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
566 }
567 
568 #endif /* HOST_EXEC */
569 
570 /*
571  * Ops structure
572  */
573 am_ops host_ops = {
574 	"host",
575 	host_match,
576 	host_init,
577 	auto_fmount,
578 	host_fmount,
579 	auto_fumount,
580 	host_fumount,
581 	efs_lookuppn,
582 	efs_readdir,
583 	0, /* host_readlink */
584 	0, /* host_mounted */
585 	0, /* host_umounted */
586 	find_nfs_srvr,
587 	FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
588 };
589 
590 #endif /* HAS_HOST */
591