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