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