4 * Copyright (c) 1997-2009 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.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/autil.c
47 * utilities specified to amd, taken out of the older amd/util.c.
52 #endif /* HAVE_CONFIG_H */
56 int NumChildren
= 0; /* number of children of primary amd */
57 static char invalid_keys
[] = "\"'!;@ \t\n";
59 /****************************************************************************
61 ****************************************************************************/
63 #ifdef HAVE_TRANSPORT_TYPE_TLI
64 # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */
65 #endif /* HAVE_TRANSPORT_TYPE_TLI */
68 /****************************************************************************
69 *** FORWARD DEFINITIONS ***
70 ****************************************************************************/
71 static void domain_strip(char *otherdom
, char *localdom
);
72 static int dofork(void);
75 /****************************************************************************
77 ****************************************************************************/
80 * Copy s into p, reallocating p if necessary
83 strealloc(char *p
, char *s
)
85 size_t len
= strlen(s
) + 1;
87 p
= (char *) xrealloc((voidp
) p
, len
);
91 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
93 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
94 #endif /* DEBUG_MEM */
100 * Strip off the trailing part of a domain
101 * to produce a short-form domain relative
102 * to the local host domain.
103 * Note that this has no effect if the domain
104 * names do not have the same number of
105 * components. If that restriction proves
106 * to be a problem then the loop needs recoding
107 * to skip from right to left and do partial
108 * matches along the way -- ie more expensive.
111 domain_strip(char *otherdom
, char *localdom
)
115 if ((p1
= strchr(otherdom
, '.')) &&
116 (p2
= strchr(localdom
, '.')) &&
117 STREQ(p1
+ 1, p2
+ 1))
123 * Normalize a host name: replace cnames with real names, and decide if to
124 * strip domain name or not.
127 host_normalize(char **chp
)
130 * Normalize hosts is used to resolve host name aliases
131 * and replace them with the standard-form name.
132 * Invoked with "-n" command line option.
134 if (gopt
.flags
& CFM_NORMALIZE_HOSTNAMES
) {
136 hp
= gethostbyname(*chp
);
137 if (hp
&& hp
->h_addrtype
== AF_INET
) {
138 dlog("Hostname %s normalized to %s", *chp
, hp
->h_name
);
139 *chp
= strealloc(*chp
, (char *) hp
->h_name
);
142 if (gopt
.flags
& CFM_DOMAIN_STRIP
) {
143 domain_strip(*chp
, hostd
);
149 * Keys are not allowed to contain " ' ! or ; to avoid
150 * problems with macro expansions.
156 if (strchr(invalid_keys
, *key
++))
163 forcibly_timeout_mp(am_node
*mp
)
165 mntfs
*mf
= mp
->am_mnt
;
167 * Arrange to timeout this node
169 if (mf
&& ((mp
->am_flags
& AMF_ROOT
) ||
170 (mf
->mf_flags
& (MFF_MOUNTING
| MFF_UNMOUNTING
)))) {
172 * We aren't going to schedule a timeout, so we need to notify the
173 * child here unless we are already unmounting, in which case that
174 * process is responsible for notifying the child.
176 if (mf
->mf_flags
& MFF_UNMOUNTING
)
177 plog(XLOG_WARNING
, "node %s is currently being unmounted, ignoring timeout request", mp
->am_path
);
179 plog(XLOG_WARNING
, "ignoring timeout request for active node %s", mp
->am_path
);
180 notify_child(mp
, AMQ_UMNT_FAILED
, EBUSY
, 0);
183 plog(XLOG_INFO
, "\"%s\" forcibly timed out", mp
->am_path
);
184 mp
->am_flags
&= ~AMF_NOTIMEOUT
;
185 mp
->am_ttl
= clocktime(NULL
);
187 * Force mtime update of parent dir, to prevent DNLC/dcache from caching
188 * the old entry, which could result in ESTALE errors, bad symlinks, and
191 clocktime(&mp
->am_parent
->am_fattr
.na_mtime
);
192 reschedule_timeout_mp();
198 mf_mounted(mntfs
*mf
, bool_t call_free_opts
)
201 int wasmounted
= mf
->mf_flags
& MFF_MOUNTED
;
205 * If this is a freshly mounted
206 * filesystem then update the
209 mf
->mf_flags
|= MFF_MOUNTED
;
213 * Do mounted callback
215 if (mf
->mf_ops
->mounted
)
216 mf
->mf_ops
->mounted(mf
);
219 * Be careful when calling free_ops and XFREE here. Some pseudo file
220 * systems like nfsx call this function (mf_mounted), even though it
221 * would be called by the lower-level amd file system functions. nfsx
222 * needs to call this function because of the other actions it takes.
223 * So we pass a boolean from the caller (yes, not so clean workaround)
224 * to determine if we should free or not. If we're not freeing (often
225 * because we're called from a callback function), then just to be sure,
226 * we'll zero out the am_opts structure and set the pointer to NULL.
227 * The parent mntfs node owns this memory and is going to free it with a
228 * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code).
230 if (call_free_opts
) {
231 free_opts(mf
->mf_fo
); /* this free is needed to prevent leaks */
232 XFREE(mf
->mf_fo
); /* (also this one) */
234 memset(mf
->mf_fo
, 0, sizeof(am_opts
));
239 if (mf
->mf_flags
& MFF_RESTART
) {
240 mf
->mf_flags
&= ~MFF_RESTART
;
241 dlog("Restarted filesystem %s, flags 0x%x", mf
->mf_mount
, mf
->mf_flags
);
247 quoted
= strchr(mf
->mf_info
, ' ') != 0;
248 plog(XLOG_INFO
, "%s%s%s %s fstype %s on %s",
252 wasmounted
? "referenced" : "mounted",
253 mf
->mf_ops
->fs_type
, mf
->mf_mount
);
258 am_mounted(am_node
*mp
)
260 int notimeout
= 0; /* assume normal timeouts initially */
261 mntfs
*mf
= mp
->am_mnt
;
264 * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
265 * we're passing TRUE here to tell mf_mounted to actually free the
266 * am_opts. See a related comment in mf_mounted().
268 mf_mounted(mf
, TRUE
);
270 #ifdef HAVE_FS_AUTOFS
271 if (mf
->mf_flags
& MFF_IS_AUTOFS
)
273 #endif /* HAVE_FS_AUTOFS */
276 * Patch up path for direct mounts
278 if (mp
->am_parent
&& mp
->am_parent
->am_mnt
->mf_fsflags
& FS_DIRECT
)
279 mp
->am_path
= str3cat(mp
->am_path
, mp
->am_parent
->am_path
, "/", ".");
282 * Check whether this mount should be cached permanently or not,
283 * and handle user-requested timeouts.
285 /* first check if file system was set to never timeout */
286 if (mf
->mf_fsflags
& FS_NOTIMEOUT
)
288 /* next, alter that decision by map flags */
291 mnt
.mnt_opts
= mf
->mf_mopts
;
293 /* umount option: user wants to unmount this entry */
294 if (amu_hasmntopt(&mnt
, "unmount") || amu_hasmntopt(&mnt
, "umount"))
296 /* noumount option: user does NOT want to unmount this entry */
297 if (amu_hasmntopt(&mnt
, "nounmount") || amu_hasmntopt(&mnt
, "noumount"))
299 /* utimeout=N option: user wants to unmount this option AND set timeout */
300 if ((mp
->am_timeo
= hasmntval(&mnt
, "utimeout")) == 0)
301 mp
->am_timeo
= gopt
.am_timeo
; /* otherwise use default timeout */
304 /* special case: don't try to unmount "/" (it can never succeed) */
305 if (mf
->mf_mount
[0] == '/' && mf
->mf_mount
[1] == '\0')
308 /* finally set actual flags */
310 mp
->am_flags
|= AMF_NOTIMEOUT
;
311 plog(XLOG_INFO
, "%s set to never timeout", mp
->am_path
);
313 mp
->am_flags
&= ~AMF_NOTIMEOUT
;
314 plog(XLOG_INFO
, "%s set to timeout in %d seconds", mp
->am_path
, mp
->am_timeo
);
318 * If this node is a symlink then
319 * compute the length of the returned string.
321 if (mp
->am_fattr
.na_type
== NFLNK
)
322 mp
->am_fattr
.na_size
= strlen(mp
->am_link
? mp
->am_link
: mf
->mf_mount
);
325 * Record mount time, and update am_stats at the same time.
327 mp
->am_stats
.s_mtime
= clocktime(&mp
->am_fattr
.na_mtime
);
331 * Update mtime of parent node (copying "struct nfstime" in '=' below)
333 if (mp
->am_parent
&& mp
->am_parent
->am_mnt
)
334 mp
->am_parent
->am_fattr
.na_mtime
= mp
->am_fattr
.na_mtime
;
337 * This is ugly, but essentially unavoidable
338 * Sublinks must be treated separately as type==link
339 * when the base type is different.
341 if (mp
->am_link
&& mf
->mf_ops
!= &amfs_link_ops
)
342 amfs_link_ops
.mount_fs(mp
, mf
);
345 * Now, if we can, do a reply to our client here
346 * to speed things up.
348 #ifdef HAVE_FS_AUTOFS
349 if (mp
->am_flags
& AMF_AUTOFS
)
350 autofs_mount_succeeded(mp
);
352 #endif /* HAVE_FS_AUTOFS */
353 nfs_quick_reply(mp
, 0);
363 * Replace mount point with a reference to an error filesystem.
364 * The mount point (struct mntfs) is NOT discarded,
365 * the caller must do it if it wants to _before_ calling this function.
368 assign_error_mntfs(am_node
*mp
)
371 dlog("assign_error_mntfs");
373 * Save the old error code
375 error
= mp
->am_error
;
377 error
= mp
->am_mnt
->mf_error
;
379 * Allocate a new error reference
381 mp
->am_mnt
= new_mntfs();
383 * Put back the error code
385 mp
->am_mnt
->mf_error
= error
;
386 mp
->am_mnt
->mf_flags
|= MFF_ERROR
;
388 * Zero the error in the mount point
395 * Build a new map cache for this node, or re-use
396 * an existing cache for the same map.
399 amfs_mkcacheref(mntfs
*mf
)
403 if (mf
->mf_fo
&& mf
->mf_fo
->opt_cache
)
404 cache
= mf
->mf_fo
->opt_cache
;
407 mf
->mf_private
= (opaque_t
) mapc_find(mf
->mf_info
,
409 (mf
->mf_fo
? mf
->mf_fo
->opt_maptype
: NULL
),
411 mf
->mf_prfree
= mapc_free
;
416 * Locate next node in sibling list which is mounted
417 * and is not an error node.
420 next_nonerror_node(am_node
*xp
)
425 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
426 * Fixes a race condition when mounting direct automounts.
427 * Also fixes a problem when doing a readdir on a directory
428 * containing hung automounts.
431 (!(mf
= xp
->am_mnt
) || /* No mounted filesystem */
432 mf
->mf_error
!= 0 || /* There was a mntfs error */
433 xp
->am_error
!= 0 || /* There was a mount error */
434 !(mf
->mf_flags
& MFF_MOUNTED
) || /* The fs is not mounted */
435 (mf
->mf_server
->fs_flags
& FSF_DOWN
)) /* The fs may be down */
444 * Mount an automounter directory.
445 * The automounter is connected into the system
446 * as a user-level NFS server. amfs_mount constructs
447 * the necessary NFS parameters to be given to the
448 * kernel so that it will talk back to us.
450 * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP).
452 * NEW: on certain systems, mounting can be done using the
453 * kernel-level automount (autofs) support. In that case,
454 * we don't need NFS at all here.
457 amfs_mount(am_node
*mp
, mntfs
*mf
, char *opts
)
459 char fs_hostname
[MAXHOSTNAMELEN
+ MAXPATHLEN
+ 1];
460 int retry
, error
= 0, genflags
;
461 int on_autofs
= mf
->mf_flags
& MFF_ON_AUTOFS
;
462 char *dir
= mf
->mf_mount
;
465 int forced_unmount
= 0; /* are we using forced unmounts? */
467 memset((voidp
) &mnt
, 0, sizeof(mnt
));
469 mnt
.mnt_fsname
= pid_fsname
;
472 #ifdef HAVE_FS_AUTOFS
473 if (mf
->mf_flags
& MFF_IS_AUTOFS
) {
474 type
= MOUNT_TYPE_AUTOFS
;
476 * Make sure that amd's top-level autofs mounts are hidden by default
478 * XXX: It works ok on Linux, might not work on other systems.
480 mnt
.mnt_type
= "autofs";
482 #endif /* HAVE_FS_AUTOFS */
484 type
= MOUNT_TYPE_NFS
;
486 * Make sure that amd's top-level NFS mounts are hidden by default
488 * If they don't appear to support the either the "ignore" mnttab
489 * option entry, or the "auto" one, set the mount type to "nfs".
491 mnt
.mnt_type
= HIDE_MOUNT_TYPE
;
494 retry
= hasmntval(&mnt
, MNTTAB_OPT_RETRY
);
496 retry
= 2; /* XXX: default to 2 retries */
503 * Make a ``hostname'' string for the kernel
505 xsnprintf(fs_hostname
, sizeof(fs_hostname
), "pid%ld@%s:%s",
506 get_server_pid(), am_get_hostname(), dir
);
508 * Most kernels have a name length restriction (64 bytes)...
510 if (strlen(fs_hostname
) >= MAXHOSTNAMELEN
)
511 xstrlcpy(fs_hostname
+ MAXHOSTNAMELEN
- 3, "..",
512 sizeof(fs_hostname
) - MAXHOSTNAMELEN
+ 3);
515 * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
516 * If you need to get the definition for HOSTNAMESZ found, you may
517 * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
519 if (strlen(fs_hostname
) >= HOSTNAMESZ
)
520 xstrlcpy(fs_hostname
+ HOSTNAMESZ
- 3, "..",
521 sizeof(fs_hostname
) - HOSTNAMESZ
+ 3);
522 #endif /* HOSTNAMESZ */
525 * Finally we can compute the mount genflags set above,
526 * and add any automounter specific flags.
528 genflags
= compute_mount_flags(&mnt
);
529 #ifdef HAVE_FS_AUTOFS
531 genflags
|= autofs_compute_mount_flags(&mnt
);
532 #endif /* HAVE_FS_AUTOFS */
533 genflags
|= compute_automounter_mount_flags(&mnt
);
536 if (!(mf
->mf_flags
& MFF_IS_AUTOFS
)) {
540 #ifndef HAVE_TRANSPORT_TYPE_TLI
542 struct sockaddr_in sin
;
543 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
546 * get fhandle of remote path for automount point
548 fhp
= get_root_nfs_fh(dir
);
550 plog(XLOG_FATAL
, "Can't find root file handle for %s", dir
);
554 #ifndef HAVE_TRANSPORT_TYPE_TLI
556 * Create sockaddr to point to the local machine.
558 memset((voidp
) &sin
, 0, sizeof(sin
));
559 /* as per POSIX, sin_len need not be set (used internally by kernel) */
560 sin
.sin_family
= AF_INET
;
561 sin
.sin_addr
= myipaddr
;
562 port
= hasmntval(&mnt
, MNTTAB_OPT_PORT
);
564 sin
.sin_port
= htons(port
);
566 plog(XLOG_ERROR
, "no port number specified for %s", dir
);
569 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
571 /* setup the many fields and flags within nfs_args */
572 memmove(&anh
.v2
, fhp
, sizeof(*fhp
));
573 #ifdef HAVE_TRANSPORT_TYPE_TLI
574 compute_nfs_args(&nfs_args
,
578 NULL
, /* remote host IP addr is set below */
579 NFS_VERSION
, /* version 2 */
585 * IMPORTANT: set the correct IP address AFTERWARDS. It cannot
586 * be done using the normal mechanism of compute_nfs_args(), because
587 * that one will allocate a new address and use NFS_SA_DREF() to copy
588 * parts to it, while assuming that the ip_addr passed is always
589 * a "struct sockaddr_in". That assumption is incorrect on TLI systems,
590 * because they define a special macro HOST_SELF which is DIFFERENT
591 * than localhost (127.0.0.1)!
593 nfs_args
.addr
= &nfsxprt
->xp_ltaddr
;
594 #else /* not HAVE_TRANSPORT_TYPE_TLI */
595 compute_nfs_args(&nfs_args
,
600 NFS_VERSION
, /* version 2 */
605 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
607 /*************************************************************************
608 * NOTE: while compute_nfs_args() works ok for regular NFS mounts *
609 * the toplvl one is not quite regular, and so some options must be *
610 * corrected by hand more carefully, *after* compute_nfs_args() runs. *
611 *************************************************************************/
612 compute_automounter_nfs_args(&nfs_args
, &mnt
);
614 if (amuDebug(D_TRACE
)) {
615 print_nfs_args(&nfs_args
, 0);
616 plog(XLOG_DEBUG
, "Generic mount flags 0x%x", genflags
);
619 /* This is it! Here we try to mount amd on its mount points */
620 error
= mount_fs(&mnt
, genflags
, (caddr_t
) &nfs_args
,
621 retry
, type
, 0, NULL
, mnttab_file_name
, on_autofs
);
623 #ifdef HAVE_TRANSPORT_TYPE_TLI
624 free_knetconfig(nfs_args
.knconf
);
626 * local automounter mounts do not allocate a special address, so
627 * no need to XFREE(nfs_args.addr) under TLI.
629 #endif /* HAVE_TRANSPORT_TYPE_TLI */
631 #ifdef HAVE_FS_AUTOFS
633 /* This is it! Here we try to mount amd on its mount points */
634 error
= mount_fs(&mnt
, genflags
, (caddr_t
) mp
->am_autofs_fh
,
635 retry
, type
, 0, NULL
, mnttab_file_name
, on_autofs
);
636 #endif /* HAVE_FS_AUTOFS */
638 if (error
== 0 || forced_unmount
)
642 * If user wants forced/lazy unmount semantics, then try it iff the
643 * current mount failed with EIO or ESTALE.
645 if (gopt
.flags
& CFM_FORCED_UNMOUNTS
) {
649 forced_unmount
= errno
;
650 plog(XLOG_WARNING
, "Mount %s failed (%m); force unmount.", mp
->am_path
);
651 if ((error
= UMOUNT_FS(mp
->am_path
, mnttab_file_name
,
652 AMU_UMOUNT_FORCE
| AMU_UMOUNT_DETACH
)) < 0) {
653 plog(XLOG_WARNING
, "Forced umount %s failed: %m.", mp
->am_path
);
654 errno
= forced_unmount
;
667 am_unmounted(am_node
*mp
)
669 mntfs
*mf
= mp
->am_mnt
;
671 if (!foreground
) { /* firewall - should never happen */
673 * This is a coding error. Make sure we hear about it!
675 plog(XLOG_FATAL
, "am_unmounted: illegal use in background (%s)",
677 notify_child(mp
, AMQ_UMNT_OK
, 0, 0); /* XXX - be safe? */
682 * Do unmounted callback
684 if (mf
->mf_ops
->umounted
)
685 mf
->mf_ops
->umounted(mf
);
688 * This is ugly, but essentially unavoidable.
689 * Sublinks must be treated separately as type==link
690 * when the base type is different.
692 if (mp
->am_link
&& mf
->mf_ops
!= &amfs_link_ops
)
693 amfs_link_ops
.umount_fs(mp
, mf
);
695 #ifdef HAVE_FS_AUTOFS
696 if (mf
->mf_flags
& MFF_IS_AUTOFS
)
697 autofs_release_fh(mp
);
698 if (mp
->am_flags
& AMF_AUTOFS
)
699 autofs_umount_succeeded(mp
);
700 #endif /* HAVE_FS_AUTOFS */
703 * Clean up any directories that were made
705 * If we remove the mount point of a pending mount, any queued access
706 * to it will fail. So don't do it in that case.
707 * Also don't do it if the refcount is > 1.
709 if (mf
->mf_flags
& MFF_MKMNT
&&
711 !(mp
->am_flags
& AMF_REMOUNT
)) {
712 plog(XLOG_INFO
, "removing mountpoint directory '%s'", mf
->mf_mount
);
713 rmdirs(mf
->mf_mount
);
714 mf
->mf_flags
&= ~MFF_MKMNT
;
718 * If this is a pseudo-directory then adjust the link count
721 if (mp
->am_parent
&& mp
->am_fattr
.na_type
== NFDIR
)
722 --mp
->am_parent
->am_fattr
.na_nlink
;
725 * Update mtime of parent node
727 if (mp
->am_parent
&& mp
->am_parent
->am_mnt
)
728 clocktime(&mp
->am_parent
->am_fattr
.na_mtime
);
730 if (mp
->am_parent
&& (mp
->am_flags
& AMF_REMOUNT
)) {
731 char *fname
= strdup(mp
->am_name
);
732 am_node
*mp_parent
= mp
->am_parent
;
733 mntfs
*mf_parent
= mp_parent
->am_mnt
;
738 * We need to use notify_child() after free_map(), so save enough
739 * to do that in fake_mp.
741 fake_mp
.am_fd
[1] = mp
->am_fd
[1];
745 plog(XLOG_INFO
, "am_unmounted: remounting %s", fname
);
746 mp
= mf_parent
->mf_ops
->lookup_child(mp_parent
, fname
, &error
, VLOOK_CREATE
);
748 mp
= mf_parent
->mf_ops
->mount_child(mp
, &error
);
751 plog(XLOG_ERROR
, "am_unmounted: could not remount %s: %m", fname
);
752 notify_child(&fake_mp
, AMQ_UMNT_OK
, 0, 0);
754 notify_child(&fake_mp
, AMQ_UMNT_FAILED
, EBUSY
, 0);
759 * We have a race here.
760 * If this node has a pending mount and amd is going down (unmounting
761 * everything in the process), then we could potentially free it here
762 * while a struct continuation still has a reference to it. So when
763 * amfs_cont is called, it blows up.
764 * We avoid the race by refusing to free any nodes that have
765 * pending mounts (defined as having a non-NULL am_mfarray).
767 notify_child(mp
, AMQ_UMNT_OK
, 0, 0); /* do this regardless */
775 * Fork the automounter
777 * TODO: Need a better strategy for handling errors
787 if (pid
< 0) { /* fork error, retry in 1 second */
791 if (pid
== 0) { /* child process (foreground==false) */
794 } else { /* parent process, has one more child */
808 dlog("backgrounded");
811 dlog("forked process %d", pid
);