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
54int NumChildren = 0;		/* number of children of primary amd */
55static 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 ****************************************************************************/
69static void domain_strip(char *otherdom, char *localdom);
70static int dofork(void);
71
72
73/****************************************************************************
74 *** FUNCTIONS                                                             ***
75 ****************************************************************************/
76
77/*
78 * Copy s into p, reallocating p if necessary
79 */
80char *
81strealloc(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 */
108static void
109domain_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 */
124void
125host_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 */
150int
151valid_key(char *key)
152{
153  while (*key)
154    if (strchr(invalid_keys, *key++))
155      return FALSE;
156  return TRUE;
157}
158
159
160void
161forcibly_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
188void
189mf_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
248void
249am_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 */
358void
359assign_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 */
389void
390amfs_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 */
409am_node *
410next_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 */
446int
447amfs_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
525again:
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
656void
657am_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 */
749static int
750dofork(void)
751{
752  int pid;
753
754top:
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
772int
773background(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}
784