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
50int NumChildren = 0;		/* number of children of primary amd */
51static 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 ****************************************************************************/
65static void domain_strip(char *otherdom, char *localdom);
66static int dofork(void);
67
68
69/****************************************************************************
70 *** FUNCTIONS                                                             ***
71 ****************************************************************************/
72
73/*
74 * Copy s into p, reallocating p if necessary
75 */
76char *
77strealloc(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 */
104static void
105domain_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 */
120void
121host_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 */
146int
147valid_key(char *key)
148{
149  while (*key)
150    if (strchr(invalid_keys, *key++))
151      return FALSE;
152  return TRUE;
153}
154
155
156void
157forcibly_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
191void
192mf_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
238void
239am_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 */
349void
350assign_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 */
386void
387amfs_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 */
407am_node *
408next_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 */
444int
445amfs_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
524again:
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
653void
654am_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 */
766static int
767dofork(void)
768{
769  int pid;
770
771top:
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
789int
790background(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}
801