1 /* $NetBSD: autil.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997-2014 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/amd/autil.c
39 *
40 */
41
42 /*
43 * utilities specified to amd, taken out of the older amd/util.c.
44 */
45
46 #ifdef HAVE_CONFIG_H
47 # include <config.h>
48 #endif /* HAVE_CONFIG_H */
49 #include <am_defs.h>
50 #include <amd.h>
51
52 int NumChildren = 0; /* number of children of primary amd */
53 static char invalid_keys[] = "\"'!;@ \t\n";
54
55 /****************************************************************************
56 *** MACROS ***
57 ****************************************************************************/
58
59 #ifdef HAVE_TRANSPORT_TYPE_TLI
60 # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */
61 #endif /* HAVE_TRANSPORT_TYPE_TLI */
62
63
64 /****************************************************************************
65 *** FORWARD DEFINITIONS ***
66 ****************************************************************************/
67 static void domain_strip(char *otherdom, char *localdom);
68 static int dofork(void);
69
70
71 /****************************************************************************
72 *** FUNCTIONS ***
73 ****************************************************************************/
74
75 /*
76 * Copy s into p, reallocating p if necessary
77 */
78 char *
strealloc(char * p,char * s)79 strealloc(char *p, char *s)
80 {
81 size_t len = strlen(s) + 1;
82
83 p = (char *) xrealloc((voidp) p, len);
84
85 xstrlcpy(p, s, len);
86 #ifdef DEBUG_MEM
87 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
88 malloc_verify();
89 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
90 #endif /* DEBUG_MEM */
91 return p;
92 }
93
94
95 /*
96 * Strip off the trailing part of a domain
97 * to produce a short-form domain relative
98 * to the local host domain.
99 * Note that this has no effect if the domain
100 * names do not have the same number of
101 * components. If that restriction proves
102 * to be a problem then the loop needs recoding
103 * to skip from right to left and do partial
104 * matches along the way -- ie more expensive.
105 */
106 static void
domain_strip(char * otherdom,char * localdom)107 domain_strip(char *otherdom, char *localdom)
108 {
109 char *p1, *p2;
110
111 if ((p1 = strchr(otherdom, '.')) &&
112 (p2 = strchr(localdom, '.')) &&
113 STREQ(p1 + 1, p2 + 1))
114 *p1 = '\0';
115 }
116
117
118 /*
119 * Normalize a host name: replace cnames with real names, and decide if to
120 * strip domain name or not.
121 */
122 void
host_normalize(char ** chp)123 host_normalize(char **chp)
124 {
125 /*
126 * Normalize hosts is used to resolve host name aliases
127 * and replace them with the standard-form name.
128 * Invoked with "-n" command line option.
129 */
130 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
131 struct hostent *hp;
132 hp = gethostbyname(*chp);
133 if (hp && hp->h_addrtype == AF_INET) {
134 dlog("Hostname %s normalized to %s", *chp, hp->h_name);
135 *chp = strealloc(*chp, (char *) hp->h_name);
136 }
137 }
138 if (gopt.flags & CFM_DOMAIN_STRIP) {
139 domain_strip(*chp, hostd);
140 }
141 }
142
143
144 /*
145 * Keys are not allowed to contain " ' ! or ; to avoid
146 * problems with macro expansions.
147 */
148 int
valid_key(char * key)149 valid_key(char *key)
150 {
151 while (*key)
152 if (strchr(invalid_keys, *key++))
153 return FALSE;
154 return TRUE;
155 }
156
157
158 void
forcibly_timeout_mp(am_node * mp)159 forcibly_timeout_mp(am_node *mp)
160 {
161 mntfs *mf = mp->am_al->al_mnt;
162 /*
163 * Arrange to timeout this node
164 */
165 if (mf && ((mp->am_flags & AMF_ROOT) ||
166 (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
167 /*
168 * We aren't going to schedule a timeout, so we need to notify the
169 * child here unless we are already unmounting, in which case that
170 * process is responsible for notifying the child.
171 */
172 if (mf->mf_flags & MFF_UNMOUNTING)
173 plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
174 else {
175 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
176 notify_child(mp, AMQ_UMNT_FAILED, EBUSY, 0);
177 }
178 } else {
179 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
180 mp->am_flags &= ~AMF_NOTIMEOUT;
181 mp->am_ttl = clocktime(NULL);
182 /*
183 * Force mtime update of parent dir, to prevent DNLC/dcache from caching
184 * the old entry, which could result in ESTALE errors, bad symlinks, and
185 * more.
186 */
187 clocktime(&mp->am_parent->am_fattr.na_mtime);
188 reschedule_timeout_mp();
189 }
190 }
191
192
193 void
mf_mounted(mntfs * mf,bool_t call_free_opts)194 mf_mounted(mntfs *mf, bool_t call_free_opts)
195 {
196 int quoted;
197 int wasmounted = mf->mf_flags & MFF_MOUNTED;
198
199 if (!wasmounted) {
200 /*
201 * If this is a freshly mounted
202 * filesystem then update the
203 * mntfs structure...
204 */
205 mf->mf_flags |= MFF_MOUNTED;
206 mf->mf_error = 0;
207
208 /*
209 * Do mounted callback
210 */
211 if (mf->mf_ops->mounted)
212 mf->mf_ops->mounted(mf);
213
214 /*
215 * We used to free the mf_mo (options) here, however they're now stored
216 * and managed with the mntfs and do not need to be free'd here (this ensures
217 * that we use the same options to monitor/unmount the system as we used
218 * to mount it).
219 */
220 }
221
222 if (mf->mf_flags & MFF_RESTART) {
223 mf->mf_flags &= ~MFF_RESTART;
224 dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags);
225 }
226
227 /*
228 * Log message
229 */
230 quoted = strchr(mf->mf_info, ' ') != 0;
231 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
232 quoted ? "\"" : "",
233 mf->mf_info,
234 quoted ? "\"" : "",
235 wasmounted ? "referenced" : "mounted",
236 mf->mf_ops->fs_type, mf->mf_mount);
237 }
238
239
240 void
am_mounted(am_node * mp)241 am_mounted(am_node *mp)
242 {
243 int notimeout = 0; /* assume normal timeouts initially */
244 mntfs *mf = mp->am_al->al_mnt;
245
246 /*
247 * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
248 * we're passing TRUE here to tell mf_mounted to actually free the
249 * am_opts. See a related comment in mf_mounted().
250 */
251 mf_mounted(mf, TRUE);
252
253 #ifdef HAVE_FS_AUTOFS
254 if (mf->mf_flags & MFF_IS_AUTOFS)
255 autofs_mounted(mp);
256 #endif /* HAVE_FS_AUTOFS */
257
258 /*
259 * Patch up path for direct mounts
260 */
261 if (mp->am_parent && mp->am_parent->am_al->al_mnt->mf_fsflags & FS_DIRECT)
262 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
263
264 /*
265 * Check whether this mount should be cached permanently or not,
266 * and handle user-requested timeouts.
267 */
268 /* first check if file system was set to never timeout */
269 if (mf->mf_fsflags & FS_NOTIMEOUT)
270 notimeout = 1;
271 /* next, alter that decision by map flags */
272
273 if (mf->mf_mopts) {
274 mntent_t mnt;
275 mnt.mnt_opts = mf->mf_mopts;
276
277 /* umount option: user wants to unmount this entry */
278 if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount"))
279 notimeout = 0;
280 /* noumount option: user does NOT want to unmount this entry */
281 if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount"))
282 notimeout = 1;
283 /* utimeout=N option: user wants to unmount this option AND set timeout */
284 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
285 mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */
286 else
287 notimeout = 0;
288 /* special case: don't try to unmount "/" (it can never succeed) */
289 if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0')
290 notimeout = 1;
291 }
292 /* finally set actual flags */
293 if (notimeout) {
294 mp->am_flags |= AMF_NOTIMEOUT;
295 plog(XLOG_INFO, "%s set to never timeout", mp->am_path);
296 } else {
297 mp->am_flags &= ~AMF_NOTIMEOUT;
298 plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo);
299 }
300
301 /*
302 * If this node is a symlink then
303 * compute the length of the returned string.
304 */
305 if (mp->am_fattr.na_type == NFLNK)
306 mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount);
307
308 /*
309 * Record mount time, and update am_stats at the same time.
310 */
311 mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime);
312 new_ttl(mp);
313
314 /*
315 * Update mtime of parent node (copying "struct nfstime" in '=' below)
316 */
317 if (mp->am_parent && mp->am_parent->am_al->al_mnt)
318 mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime;
319
320 /*
321 * This is ugly, but essentially unavoidable
322 * Sublinks must be treated separately as type==link
323 * when the base type is different.
324 */
325 if (mp->am_link && mf->mf_ops != &amfs_link_ops)
326 amfs_link_ops.mount_fs(mp, mf);
327
328 /*
329 * Now, if we can, do a reply to our client here
330 * to speed things up.
331 */
332 #ifdef HAVE_FS_AUTOFS
333 if (mp->am_flags & AMF_AUTOFS)
334 autofs_mount_succeeded(mp);
335 else
336 #endif /* HAVE_FS_AUTOFS */
337 nfs_quick_reply(mp, 0);
338
339 /*
340 * Update stats
341 */
342 amd_stats.d_mok++;
343 }
344
345
346 /*
347 * Replace mount point with a reference to an error filesystem.
348 * The mount point (struct mntfs) is NOT discarded,
349 * the caller must do it if it wants to _before_ calling this function.
350 */
351 void
assign_error_mntfs(am_node * mp)352 assign_error_mntfs(am_node *mp)
353 {
354 int error;
355 dlog("assign_error_mntfs");
356
357 if (mp->am_al == NULL) {
358 plog(XLOG_ERROR, "%s: Can't assign error", __func__);
359 return;
360 }
361 /*
362 * Save the old error code
363 */
364 error = mp->am_error;
365 if (error <= 0)
366 error = mp->am_al->al_mnt->mf_error;
367 /*
368 * Allocate a new error reference
369 */
370 free_loc(mp->am_al);
371 mp->am_al = new_loc();
372 /*
373 * Put back the error code
374 */
375 mp->am_al->al_mnt->mf_error = error;
376 mp->am_al->al_mnt->mf_flags |= MFF_ERROR;
377 /*
378 * Zero the error in the mount point
379 */
380 mp->am_error = 0;
381 }
382
383
384 /*
385 * Build a new map cache for this node, or re-use
386 * an existing cache for the same map.
387 */
388 void
amfs_mkcacheref(mntfs * mf)389 amfs_mkcacheref(mntfs *mf)
390 {
391 char *cache;
392
393 if (mf->mf_fo && mf->mf_fo->opt_cache)
394 cache = mf->mf_fo->opt_cache;
395 else
396 cache = "none";
397 mf->mf_private = (opaque_t) mapc_find(mf->mf_info,
398 cache,
399 (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL),
400 mf->mf_mount);
401 mf->mf_prfree = mapc_free;
402 }
403
404
405 /*
406 * Locate next node in sibling list which is mounted
407 * and is not an error node.
408 */
409 am_node *
next_nonerror_node(am_node * xp)410 next_nonerror_node(am_node *xp)
411 {
412 mntfs *mf;
413
414 /*
415 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
416 * Fixes a race condition when mounting direct automounts.
417 * Also fixes a problem when doing a readdir on a directory
418 * containing hung automounts.
419 */
420 while (xp &&
421 (!(mf = xp->am_al->al_mnt) || /* No mounted filesystem */
422 mf->mf_error != 0 || /* There was a mntfs error */
423 xp->am_error != 0 || /* There was a mount error */
424 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
425 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
426 )
427 xp = xp->am_osib;
428
429 return xp;
430 }
431
432
433 /*
434 * Mount an automounter directory.
435 * The automounter is connected into the system
436 * as a user-level NFS server. amfs_mount constructs
437 * the necessary NFS parameters to be given to the
438 * kernel so that it will talk back to us.
439 *
440 * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP).
441 *
442 * NEW: on certain systems, mounting can be done using the
443 * kernel-level automount (autofs) support. In that case,
444 * we don't need NFS at all here.
445 */
446 int
amfs_mount(am_node * mp,mntfs * mf,char * opts)447 amfs_mount(am_node *mp, mntfs *mf, char *opts)
448 {
449 char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
450 int retry, error = 0, genflags;
451 int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
452 char *dir = mf->mf_mount;
453 mntent_t mnt;
454 MTYPE_TYPE type;
455 int forced_unmount = 0; /* are we using forced unmounts? */
456 u_long nfs_version = get_nfs_dispatcher_version(nfs_dispatcher);
457
458 memset(&mnt, 0, sizeof(mnt));
459 mnt.mnt_dir = dir;
460 mnt.mnt_fsname = pid_fsname;
461 mnt.mnt_opts = opts;
462
463 #ifdef HAVE_FS_AUTOFS
464 if (mf->mf_flags & MFF_IS_AUTOFS) {
465 type = MOUNT_TYPE_AUTOFS;
466 /*
467 * Make sure that amd's top-level autofs mounts are hidden by default
468 * from df.
469 * XXX: It works ok on Linux, might not work on other systems.
470 */
471 mnt.mnt_type = "autofs";
472 } else
473 #endif /* HAVE_FS_AUTOFS */
474 {
475 type = MOUNT_TYPE_NFS;
476 /*
477 * Make sure that amd's top-level NFS mounts are hidden by default
478 * from df.
479 * If they don't appear to support the either the "ignore" mnttab
480 * option entry, or the "auto" one, set the mount type to "nfs".
481 */
482 mnt.mnt_type = HIDE_MOUNT_TYPE;
483 }
484
485 retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
486 if (retry <= 0)
487 retry = 2; /* XXX: default to 2 retries */
488
489 /*
490 * SET MOUNT ARGS
491 */
492
493 /*
494 * Make a ``hostname'' string for the kernel
495 */
496 xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s",
497 get_server_pid(), am_get_hostname(), dir);
498 /*
499 * Most kernels have a name length restriction (64 bytes)...
500 */
501 if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
502 xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..",
503 sizeof(fs_hostname) - MAXHOSTNAMELEN + 3);
504 #ifdef HOSTNAMESZ
505 /*
506 * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
507 * If you need to get the definition for HOSTNAMESZ found, you may
508 * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
509 */
510 if (strlen(fs_hostname) >= HOSTNAMESZ)
511 xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..",
512 sizeof(fs_hostname) - HOSTNAMESZ + 3);
513 #endif /* HOSTNAMESZ */
514
515 /*
516 * Finally we can compute the mount genflags set above,
517 * and add any automounter specific flags.
518 */
519 genflags = compute_mount_flags(&mnt);
520 #ifdef HAVE_FS_AUTOFS
521 if (on_autofs)
522 genflags |= autofs_compute_mount_flags(&mnt);
523 #endif /* HAVE_FS_AUTOFS */
524 genflags |= compute_automounter_mount_flags(&mnt);
525
526 again:
527 if (!(mf->mf_flags & MFF_IS_AUTOFS)) {
528 nfs_args_t nfs_args;
529 am_nfs_handle_t *fhp, anh;
530 #ifndef HAVE_TRANSPORT_TYPE_TLI
531 u_short port;
532 struct sockaddr_in sin;
533 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
534
535 /*
536 * get fhandle of remote path for automount point
537 */
538 fhp = get_root_nfs_fh(dir, &anh);
539 if (!fhp) {
540 plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
541 return EINVAL;
542 }
543
544 #ifndef HAVE_TRANSPORT_TYPE_TLI
545 /*
546 * Create sockaddr to point to the local machine.
547 */
548 memset(&sin, 0, sizeof(sin));
549 /* as per POSIX, sin_len need not be set (used internally by kernel) */
550 sin.sin_family = AF_INET;
551 sin.sin_addr = myipaddr;
552 port = hasmntval(&mnt, MNTTAB_OPT_PORT);
553 if (port) {
554 sin.sin_port = htons(port);
555 } else {
556 plog(XLOG_ERROR, "no port number specified for %s", dir);
557 return EINVAL;
558 }
559 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
560
561 /* setup the many fields and flags within nfs_args */
562 #ifdef HAVE_TRANSPORT_TYPE_TLI
563 compute_nfs_args(&nfs_args,
564 &mnt,
565 genflags,
566 nfsncp,
567 NULL, /* remote host IP addr is set below */
568 nfs_version,
569 "udp",
570 fhp,
571 fs_hostname,
572 pid_fsname);
573 /*
574 * IMPORTANT: set the correct IP address AFTERWARDS. It cannot
575 * be done using the normal mechanism of compute_nfs_args(), because
576 * that one will allocate a new address and use NFS_SA_DREF() to copy
577 * parts to it, while assuming that the ip_addr passed is always
578 * a "struct sockaddr_in". That assumption is incorrect on TLI systems,
579 * because they define a special macro HOST_SELF which is DIFFERENT
580 * than localhost (127.0.0.1)!
581 */
582 nfs_args.addr = &nfsxprt->xp_ltaddr;
583 #else /* not HAVE_TRANSPORT_TYPE_TLI */
584 compute_nfs_args(&nfs_args,
585 &mnt,
586 genflags,
587 NULL,
588 &sin,
589 nfs_version,
590 "udp",
591 fhp,
592 fs_hostname,
593 pid_fsname);
594 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
595
596 /*************************************************************************
597 * NOTE: while compute_nfs_args() works ok for regular NFS mounts *
598 * the toplvl one is not quite regular, and so some options must be *
599 * corrected by hand more carefully, *after* compute_nfs_args() runs. *
600 *************************************************************************/
601 compute_automounter_nfs_args(&nfs_args, &mnt);
602
603 if (amuDebug(D_TRACE)) {
604 print_nfs_args(&nfs_args, 0);
605 plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
606 }
607
608 /* This is it! Here we try to mount amd on its mount points */
609 error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args,
610 retry, type, 0, NULL, mnttab_file_name, on_autofs);
611
612 #ifdef HAVE_TRANSPORT_TYPE_TLI
613 free_knetconfig(nfs_args.knconf);
614 /*
615 * local automounter mounts do not allocate a special address, so
616 * no need to XFREE(nfs_args.addr) under TLI.
617 */
618 #endif /* HAVE_TRANSPORT_TYPE_TLI */
619
620 #ifdef HAVE_FS_AUTOFS
621 } else {
622 /* This is it! Here we try to mount amd on its mount points */
623 error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh,
624 retry, type, 0, NULL, mnttab_file_name, on_autofs);
625 #endif /* HAVE_FS_AUTOFS */
626 }
627 if (error == 0 || forced_unmount)
628 return error;
629
630 /*
631 * If user wants forced/lazy unmount semantics, then try it iff the
632 * current mount failed with EIO or ESTALE.
633 */
634 if (gopt.flags & CFM_FORCED_UNMOUNTS) {
635 switch (errno) {
636 case ESTALE:
637 case EIO:
638 forced_unmount = errno;
639 plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path);
640 if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name,
641 AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) {
642 plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path);
643 errno = forced_unmount;
644 } else
645 goto again;
646 default:
647 break;
648 }
649 }
650
651 return error;
652 }
653
654
655 void
am_unmounted(am_node * mp)656 am_unmounted(am_node *mp)
657 {
658 mntfs *mf = mp->am_al->al_mnt;
659
660 if (!foreground) { /* firewall - should never happen */
661 /*
662 * This is a coding error. Make sure we hear about it!
663 */
664 plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)",
665 mp->am_name);
666 notify_child(mp, AMQ_UMNT_OK, 0, 0); /* XXX - be safe? */
667 return;
668 }
669
670 /*
671 * Do unmounted callback
672 */
673 if (mf->mf_ops->umounted)
674 mf->mf_ops->umounted(mf);
675
676 /*
677 * This is ugly, but essentially unavoidable.
678 * Sublinks must be treated separately as type==link
679 * when the base type is different.
680 */
681 if (mp->am_link && mf->mf_ops != &amfs_link_ops)
682 amfs_link_ops.umount_fs(mp, mf);
683
684 #ifdef HAVE_FS_AUTOFS
685 if (mf->mf_flags & MFF_IS_AUTOFS)
686 autofs_release_fh(mp);
687 if (mp->am_flags & AMF_AUTOFS)
688 autofs_umount_succeeded(mp);
689 #endif /* HAVE_FS_AUTOFS */
690
691 /*
692 * Clean up any directories that were made
693 *
694 * If we remove the mount point of a pending mount, any queued access
695 * to it will fail. So don't do it in that case.
696 * Also don't do it if the refcount is > 1.
697 */
698 if (mf->mf_flags & MFF_MKMNT &&
699 mf->mf_refc == 1 &&
700 !(mp->am_flags & AMF_REMOUNT)) {
701 plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
702 rmdirs(mf->mf_mount);
703 mf->mf_flags &= ~MFF_MKMNT;
704 }
705
706 /*
707 * If this is a pseudo-directory then adjust the link count
708 * in the parent
709 */
710 if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
711 --mp->am_parent->am_fattr.na_nlink;
712
713 /*
714 * Update mtime of parent node
715 */
716 if (mp->am_parent && mp->am_parent->am_al->al_mnt)
717 clocktime(&mp->am_parent->am_fattr.na_mtime);
718
719 if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
720 char *fname = xstrdup(mp->am_name);
721 am_node *mp_parent = mp->am_parent;
722 mntfs *mf_parent = mp_parent->am_al->al_mnt;
723 am_node fake_mp;
724 int error = 0;
725
726 /*
727 * We need to use notify_child() after free_map(), so save enough
728 * to do that in fake_mp.
729 */
730 fake_mp.am_fd[1] = mp->am_fd[1];
731 mp->am_fd[1] = -1;
732
733 free_map(mp);
734 plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
735 mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
736 if (mp && error < 0)
737 (void)mf_parent->mf_ops->mount_child(mp, &error);
738 if (error > 0) {
739 errno = error;
740 plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
741 notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0);
742 } else {
743 notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0);
744 }
745 XFREE(fname);
746 } else {
747 /*
748 * We have a race here.
749 * If this node has a pending mount and amd is going down (unmounting
750 * everything in the process), then we could potentially free it here
751 * while a struct continuation still has a reference to it. So when
752 * amfs_cont is called, it blows up.
753 * We avoid the race by refusing to free any nodes that have
754 * pending mounts (defined as having a non-NULL am_alarray).
755 */
756 notify_child(mp, AMQ_UMNT_OK, 0, 0); /* do this regardless */
757 if (!mp->am_alarray)
758 free_map(mp);
759 }
760 }
761
762
763 /*
764 * Fork the automounter
765 *
766 * TODO: Need a better strategy for handling errors
767 */
768 static int
dofork(void)769 dofork(void)
770 {
771 int pid;
772
773 top:
774 pid = fork();
775
776 if (pid < 0) { /* fork error, retry in 1 second */
777 sleep(1);
778 goto top;
779 }
780 if (pid == 0) { /* child process (foreground==false) */
781 am_set_mypid();
782 foreground = 0;
783 } else { /* parent process, has one more child */
784 NumChildren++;
785 }
786
787 return pid;
788 }
789
790
791 int
background(void)792 background(void)
793 {
794 int pid = dofork();
795
796 if (pid == 0) {
797 dlog("backgrounded");
798 foreground = 0;
799 } else
800 dlog("forked process %d", pid);
801 return pid;
802 }
803