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