138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174294Sobrien * File: am-utils/amd/autil.c
3738494Sobrien *
3838494Sobrien */
3938494Sobrien
4038494Sobrien/*
4138494Sobrien * utilities specified to amd, taken out of the older amd/util.c.
4238494Sobrien */
4338494Sobrien
4438494Sobrien#ifdef HAVE_CONFIG_H
4538494Sobrien# include <config.h>
4638494Sobrien#endif /* HAVE_CONFIG_H */
4738494Sobrien#include <am_defs.h>
4838494Sobrien#include <amd.h>
4938494Sobrien
50174294Sobrienint NumChildren = 0;		/* number of children of primary amd */
5138494Sobrienstatic char invalid_keys[] = "\"'!;@ \t\n";
5238494Sobrien
53174294Sobrien/****************************************************************************
54174294Sobrien *** MACROS                                                               ***
55174294Sobrien ****************************************************************************/
56174294Sobrien
5738494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
5838494Sobrien# define PARENT_USLEEP_TIME	100000 /* 0.1 seconds */
5938494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */
6038494Sobrien
6138494Sobrien
62174294Sobrien/****************************************************************************
63174294Sobrien *** FORWARD DEFINITIONS                                                  ***
64174294Sobrien ****************************************************************************/
65174294Sobrienstatic void domain_strip(char *otherdom, char *localdom);
66174294Sobrienstatic int dofork(void);
67174294Sobrien
68174294Sobrien
69174294Sobrien/****************************************************************************
70174294Sobrien *** FUNCTIONS                                                             ***
71174294Sobrien ****************************************************************************/
72174294Sobrien
73174294Sobrien/*
74174294Sobrien * Copy s into p, reallocating p if necessary
75174294Sobrien */
7638494Sobrienchar *
7738494Sobrienstrealloc(char *p, char *s)
7838494Sobrien{
79174294Sobrien  size_t len = strlen(s) + 1;
8038494Sobrien
8138494Sobrien  p = (char *) xrealloc((voidp) p, len);
8238494Sobrien
83174294Sobrien  xstrlcpy(p, s, len);
8438494Sobrien#ifdef DEBUG_MEM
85174294Sobrien# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
8638494Sobrien  malloc_verify();
87174294Sobrien# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
8838494Sobrien#endif /* DEBUG_MEM */
8938494Sobrien  return p;
9038494Sobrien}
9138494Sobrien
9238494Sobrien
9338494Sobrien/*
9438494Sobrien * Strip off the trailing part of a domain
9538494Sobrien * to produce a short-form domain relative
9638494Sobrien * to the local host domain.
9738494Sobrien * Note that this has no effect if the domain
9838494Sobrien * names do not have the same number of
9938494Sobrien * components.  If that restriction proves
10038494Sobrien * to be a problem then the loop needs recoding
10138494Sobrien * to skip from right to left and do partial
10238494Sobrien * matches along the way -- ie more expensive.
10338494Sobrien */
10438494Sobrienstatic void
10538494Sobriendomain_strip(char *otherdom, char *localdom)
10638494Sobrien{
10738494Sobrien  char *p1, *p2;
10838494Sobrien
10938494Sobrien  if ((p1 = strchr(otherdom, '.')) &&
11038494Sobrien      (p2 = strchr(localdom, '.')) &&
11138494Sobrien      STREQ(p1 + 1, p2 + 1))
11238494Sobrien    *p1 = '\0';
11338494Sobrien}
11438494Sobrien
11538494Sobrien
11638494Sobrien/*
117174294Sobrien * Normalize a host name: replace cnames with real names, and decide if to
118174294Sobrien * strip domain name or not.
11938494Sobrien */
12038494Sobrienvoid
12138494Sobrienhost_normalize(char **chp)
12238494Sobrien{
12338494Sobrien  /*
12438494Sobrien   * Normalize hosts is used to resolve host name aliases
12538494Sobrien   * and replace them with the standard-form name.
12638494Sobrien   * Invoked with "-n" command line option.
12738494Sobrien   */
12838494Sobrien  if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
12938494Sobrien    struct hostent *hp;
13038494Sobrien    hp = gethostbyname(*chp);
13138494Sobrien    if (hp && hp->h_addrtype == AF_INET) {
13238494Sobrien      dlog("Hostname %s normalized to %s", *chp, hp->h_name);
13338494Sobrien      *chp = strealloc(*chp, (char *) hp->h_name);
13438494Sobrien    }
13538494Sobrien  }
136174294Sobrien  if (gopt.flags & CFM_DOMAIN_STRIP) {
137174294Sobrien    domain_strip(*chp, hostd);
138174294Sobrien  }
13938494Sobrien}
14038494Sobrien
14138494Sobrien
14238494Sobrien/*
14338494Sobrien * Keys are not allowed to contain " ' ! or ; to avoid
14438494Sobrien * problems with macro expansions.
14538494Sobrien */
14638494Sobrienint
14738494Sobrienvalid_key(char *key)
14838494Sobrien{
14938494Sobrien  while (*key)
15038494Sobrien    if (strchr(invalid_keys, *key++))
15138494Sobrien      return FALSE;
15238494Sobrien  return TRUE;
15338494Sobrien}
15438494Sobrien
15538494Sobrien
15638494Sobrienvoid
15738494Sobrienforcibly_timeout_mp(am_node *mp)
15838494Sobrien{
159310490Scy  mntfs *mf = mp->am_al->al_mnt;
16038494Sobrien  /*
16138494Sobrien   * Arrange to timeout this node
16238494Sobrien   */
16338494Sobrien  if (mf && ((mp->am_flags & AMF_ROOT) ||
16438494Sobrien	     (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
165310490Scy    /*
166310490Scy     * We aren't going to schedule a timeout, so we need to notify the
167310490Scy     * child here unless we are already unmounting, in which case that
168310490Scy     * process is responsible for notifying the child.
169310490Scy     */
17082794Sobrien    if (mf->mf_flags & MFF_UNMOUNTING)
17182794Sobrien      plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
172310490Scy    else {
17338494Sobrien      plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
174310490Scy      notify_child(mp, AMQ_UMNT_FAILED, EBUSY, 0);
175310490Scy    }
17638494Sobrien  } else {
17738494Sobrien    plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
17838494Sobrien    mp->am_flags &= ~AMF_NOTIMEOUT;
179174294Sobrien    mp->am_ttl = clocktime(NULL);
180174294Sobrien    /*
181174294Sobrien     * Force mtime update of parent dir, to prevent DNLC/dcache from caching
182174294Sobrien     * the old entry, which could result in ESTALE errors, bad symlinks, and
183174294Sobrien     * more.
184174294Sobrien     */
185174294Sobrien    clocktime(&mp->am_parent->am_fattr.na_mtime);
18638494Sobrien    reschedule_timeout_mp();
18738494Sobrien  }
18838494Sobrien}
18938494Sobrien
19038494Sobrien
19138494Sobrienvoid
192174294Sobrienmf_mounted(mntfs *mf, bool_t call_free_opts)
19338494Sobrien{
19438494Sobrien  int quoted;
19538494Sobrien  int wasmounted = mf->mf_flags & MFF_MOUNTED;
19638494Sobrien
19738494Sobrien  if (!wasmounted) {
19838494Sobrien    /*
19938494Sobrien     * If this is a freshly mounted
20038494Sobrien     * filesystem then update the
20138494Sobrien     * mntfs structure...
20238494Sobrien     */
20338494Sobrien    mf->mf_flags |= MFF_MOUNTED;
20438494Sobrien    mf->mf_error = 0;
20538494Sobrien
20638494Sobrien    /*
20738494Sobrien     * Do mounted callback
20838494Sobrien     */
209174294Sobrien    if (mf->mf_ops->mounted)
210174294Sobrien      mf->mf_ops->mounted(mf);
211174294Sobrien
212174294Sobrien    /*
213310490Scy     * We used to free the mf_mo (options) here, however they're now stored
214310490Scy     * and managed with the mntfs and do not need to be free'd here (this ensures
215310490Scy     * that we use the same options to monitor/unmount the system as we used
216310490Scy     * to mount it).
217174294Sobrien     */
21838494Sobrien  }
21938494Sobrien
220174294Sobrien  if (mf->mf_flags & MFF_RESTART) {
221174294Sobrien    mf->mf_flags &= ~MFF_RESTART;
222174294Sobrien    dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags);
223174294Sobrien  }
224174294Sobrien
22538494Sobrien  /*
22638494Sobrien   * Log message
22738494Sobrien   */
22838494Sobrien  quoted = strchr(mf->mf_info, ' ') != 0;
22938494Sobrien  plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
23038494Sobrien       quoted ? "\"" : "",
23138494Sobrien       mf->mf_info,
23238494Sobrien       quoted ? "\"" : "",
23338494Sobrien       wasmounted ? "referenced" : "mounted",
23438494Sobrien       mf->mf_ops->fs_type, mf->mf_mount);
23538494Sobrien}
23638494Sobrien
23738494Sobrien
23838494Sobrienvoid
23938494Sobrienam_mounted(am_node *mp)
24038494Sobrien{
241174294Sobrien  int notimeout = 0;		/* assume normal timeouts initially */
242310490Scy  mntfs *mf = mp->am_al->al_mnt;
24338494Sobrien
244174294Sobrien  /*
245174294Sobrien   * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
246174294Sobrien   * we're passing TRUE here to tell mf_mounted to actually free the
247174294Sobrien   * am_opts.  See a related comment in mf_mounted().
248174294Sobrien   */
249174294Sobrien  mf_mounted(mf, TRUE);
25038494Sobrien
251174294Sobrien#ifdef HAVE_FS_AUTOFS
252174294Sobrien  if (mf->mf_flags & MFF_IS_AUTOFS)
253174294Sobrien    autofs_mounted(mp);
254174294Sobrien#endif /* HAVE_FS_AUTOFS */
255174294Sobrien
25638494Sobrien  /*
25738494Sobrien   * Patch up path for direct mounts
25838494Sobrien   */
259310490Scy  if (mp->am_parent && mp->am_parent->am_al->al_mnt->mf_fsflags & FS_DIRECT)
26038494Sobrien    mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
26138494Sobrien
26238494Sobrien  /*
263174294Sobrien   * Check whether this mount should be cached permanently or not,
264174294Sobrien   * and handle user-requested timeouts.
26538494Sobrien   */
266174294Sobrien  /* first check if file system was set to never timeout */
267174294Sobrien  if (mf->mf_fsflags & FS_NOTIMEOUT)
268174294Sobrien    notimeout = 1;
269174294Sobrien  /* next, alter that decision by map flags */
270310490Scy
271174294Sobrien  if (mf->mf_mopts) {
272119679Smbr    mntent_t mnt;
273119679Smbr    mnt.mnt_opts = mf->mf_mopts;
274119679Smbr
275174294Sobrien    /* umount option: user wants to unmount this entry */
276174294Sobrien    if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount"))
277174294Sobrien      notimeout = 0;
278174294Sobrien    /* noumount option: user does NOT want to unmount this entry */
279174294Sobrien    if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount"))
280174294Sobrien      notimeout = 1;
281174294Sobrien    /* utimeout=N option: user wants to unmount this option AND set timeout */
282174294Sobrien    if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
283174294Sobrien      mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */
284119679Smbr    else
285174294Sobrien      notimeout = 0;
286174294Sobrien    /* special case: don't try to unmount "/" (it can never succeed) */
287174294Sobrien    if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0')
288174294Sobrien      notimeout = 1;
289174294Sobrien  }
290174294Sobrien  /* finally set actual flags */
291174294Sobrien  if (notimeout) {
29238494Sobrien    mp->am_flags |= AMF_NOTIMEOUT;
293174294Sobrien    plog(XLOG_INFO, "%s set to never timeout", mp->am_path);
29438494Sobrien  } else {
295174294Sobrien    mp->am_flags &= ~AMF_NOTIMEOUT;
296174294Sobrien    plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo);
29738494Sobrien  }
29838494Sobrien
29938494Sobrien  /*
30038494Sobrien   * If this node is a symlink then
30138494Sobrien   * compute the length of the returned string.
30238494Sobrien   */
30338494Sobrien  if (mp->am_fattr.na_type == NFLNK)
304174294Sobrien    mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount);
30538494Sobrien
30638494Sobrien  /*
307174294Sobrien   * Record mount time, and update am_stats at the same time.
30838494Sobrien   */
309174294Sobrien  mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime);
31038494Sobrien  new_ttl(mp);
31138494Sobrien
31238494Sobrien  /*
313174294Sobrien   * Update mtime of parent node (copying "struct nfstime" in '=' below)
31438494Sobrien   */
315310490Scy  if (mp->am_parent && mp->am_parent->am_al->al_mnt)
316174294Sobrien    mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime;
31738494Sobrien
31838494Sobrien  /*
319174294Sobrien   * This is ugly, but essentially unavoidable
320174294Sobrien   * Sublinks must be treated separately as type==link
321174294Sobrien   * when the base type is different.
322174294Sobrien   */
323174294Sobrien  if (mp->am_link && mf->mf_ops != &amfs_link_ops)
324174294Sobrien    amfs_link_ops.mount_fs(mp, mf);
325174294Sobrien
326174294Sobrien  /*
327174294Sobrien   * Now, if we can, do a reply to our client here
32838494Sobrien   * to speed things up.
32938494Sobrien   */
330174294Sobrien#ifdef HAVE_FS_AUTOFS
331174294Sobrien  if (mp->am_flags & AMF_AUTOFS)
332174294Sobrien    autofs_mount_succeeded(mp);
333174294Sobrien  else
334174294Sobrien#endif /* HAVE_FS_AUTOFS */
335174294Sobrien    nfs_quick_reply(mp, 0);
33638494Sobrien
33738494Sobrien  /*
33838494Sobrien   * Update stats
33938494Sobrien   */
34038494Sobrien  amd_stats.d_mok++;
34138494Sobrien}
34238494Sobrien
34338494Sobrien
344174294Sobrien/*
345174294Sobrien * Replace mount point with a reference to an error filesystem.
346174294Sobrien * The mount point (struct mntfs) is NOT discarded,
347174294Sobrien * the caller must do it if it wants to _before_ calling this function.
348174294Sobrien */
349174294Sobrienvoid
350174294Sobrienassign_error_mntfs(am_node *mp)
351174294Sobrien{
352174294Sobrien  int error;
353174294Sobrien  dlog("assign_error_mntfs");
354310490Scy
355310490Scy  if (mp->am_al == NULL) {
356310490Scy    plog(XLOG_ERROR, "%s: Can't assign error", __func__);
357310490Scy    return;
358310490Scy  }
359174294Sobrien  /*
360174294Sobrien   * Save the old error code
361174294Sobrien   */
362174294Sobrien  error = mp->am_error;
363174294Sobrien  if (error <= 0)
364310490Scy    error = mp->am_al->al_mnt->mf_error;
365174294Sobrien  /*
366174294Sobrien   * Allocate a new error reference
367174294Sobrien   */
368310490Scy  free_loc(mp->am_al);
369310490Scy  mp->am_al = new_loc();
370174294Sobrien  /*
371174294Sobrien   * Put back the error code
372174294Sobrien   */
373310490Scy  mp->am_al->al_mnt->mf_error = error;
374310490Scy  mp->am_al->al_mnt->mf_flags |= MFF_ERROR;
375174294Sobrien  /*
376174294Sobrien   * Zero the error in the mount point
377174294Sobrien   */
378174294Sobrien  mp->am_error = 0;
379174294Sobrien}
380174294Sobrien
381174294Sobrien
382174294Sobrien/*
383174294Sobrien * Build a new map cache for this node, or re-use
384174294Sobrien * an existing cache for the same map.
385174294Sobrien */
386174294Sobrienvoid
387174294Sobrienamfs_mkcacheref(mntfs *mf)
388174294Sobrien{
389174294Sobrien  char *cache;
390174294Sobrien
391174294Sobrien  if (mf->mf_fo && mf->mf_fo->opt_cache)
392174294Sobrien    cache = mf->mf_fo->opt_cache;
393174294Sobrien  else
394174294Sobrien    cache = "none";
395174294Sobrien  mf->mf_private = (opaque_t) mapc_find(mf->mf_info,
396174294Sobrien					cache,
397310490Scy					(mf->mf_fo ? mf->mf_fo->opt_maptype : NULL),
398310490Scy					mf->mf_mount);
399174294Sobrien  mf->mf_prfree = mapc_free;
400174294Sobrien}
401174294Sobrien
402174294Sobrien
403174294Sobrien/*
404174294Sobrien * Locate next node in sibling list which is mounted
405174294Sobrien * and is not an error node.
406174294Sobrien */
407174294Sobrienam_node *
408174294Sobriennext_nonerror_node(am_node *xp)
409174294Sobrien{
410174294Sobrien  mntfs *mf;
411174294Sobrien
412174294Sobrien  /*
413174294Sobrien   * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
414174294Sobrien   * Fixes a race condition when mounting direct automounts.
415174294Sobrien   * Also fixes a problem when doing a readdir on a directory
416174294Sobrien   * containing hung automounts.
417174294Sobrien   */
418174294Sobrien  while (xp &&
419310490Scy	 (!(mf = xp->am_al->al_mnt) ||	/* No mounted filesystem */
420174294Sobrien	  mf->mf_error != 0 ||	/* There was a mntfs error */
421174294Sobrien	  xp->am_error != 0 ||	/* There was a mount error */
422174294Sobrien	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
423174294Sobrien	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
424174294Sobrien	 )
425174294Sobrien    xp = xp->am_osib;
426174294Sobrien
427174294Sobrien  return xp;
428174294Sobrien}
429174294Sobrien
430174294Sobrien
431174294Sobrien/*
432174294Sobrien * Mount an automounter directory.
433174294Sobrien * The automounter is connected into the system
434174294Sobrien * as a user-level NFS server.  amfs_mount constructs
435174294Sobrien * the necessary NFS parameters to be given to the
436174294Sobrien * kernel so that it will talk back to us.
437174294Sobrien *
438174294Sobrien * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP).
439174294Sobrien *
440174294Sobrien * NEW: on certain systems, mounting can be done using the
441174294Sobrien * kernel-level automount (autofs) support. In that case,
442174294Sobrien * we don't need NFS at all here.
443174294Sobrien */
44438494Sobrienint
445174294Sobrienamfs_mount(am_node *mp, mntfs *mf, char *opts)
44638494Sobrien{
447174294Sobrien  char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
448174294Sobrien  int retry, error = 0, genflags;
449174294Sobrien  int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
450174294Sobrien  char *dir = mf->mf_mount;
451174294Sobrien  mntent_t mnt;
452174294Sobrien  MTYPE_TYPE type;
453174294Sobrien  int forced_unmount = 0;	/* are we using forced unmounts? */
454310490Scy  u_long nfs_version = get_nfs_dispatcher_version(nfs_dispatcher);
45538494Sobrien
456310490Scy  memset(&mnt, 0, sizeof(mnt));
457174294Sobrien  mnt.mnt_dir = dir;
458174294Sobrien  mnt.mnt_fsname = pid_fsname;
459174294Sobrien  mnt.mnt_opts = opts;
46038494Sobrien
461174294Sobrien#ifdef HAVE_FS_AUTOFS
462174294Sobrien  if (mf->mf_flags & MFF_IS_AUTOFS) {
463174294Sobrien    type = MOUNT_TYPE_AUTOFS;
464174294Sobrien    /*
465174294Sobrien     * Make sure that amd's top-level autofs mounts are hidden by default
466174294Sobrien     * from df.
467174294Sobrien     * XXX: It works ok on Linux, might not work on other systems.
468174294Sobrien     */
469174294Sobrien    mnt.mnt_type = "autofs";
470174294Sobrien  } else
471174294Sobrien#endif /* HAVE_FS_AUTOFS */
472174294Sobrien  {
473174294Sobrien    type = MOUNT_TYPE_NFS;
474174294Sobrien    /*
475174294Sobrien     * Make sure that amd's top-level NFS mounts are hidden by default
476174294Sobrien     * from df.
477174294Sobrien     * If they don't appear to support the either the "ignore" mnttab
478174294Sobrien     * option entry, or the "auto" one, set the mount type to "nfs".
479174294Sobrien     */
480174294Sobrien    mnt.mnt_type = HIDE_MOUNT_TYPE;
48138494Sobrien  }
48238494Sobrien
483174294Sobrien  retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
484174294Sobrien  if (retry <= 0)
485174294Sobrien    retry = 2;			/* XXX: default to 2 retries */
486174294Sobrien
487174294Sobrien  /*
488174294Sobrien   * SET MOUNT ARGS
489174294Sobrien   */
490174294Sobrien
491174294Sobrien  /*
492174294Sobrien   * Make a ``hostname'' string for the kernel
493174294Sobrien   */
494174294Sobrien  xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s",
495174294Sobrien	    get_server_pid(), am_get_hostname(), dir);
496174294Sobrien  /*
497174294Sobrien   * Most kernels have a name length restriction (64 bytes)...
498174294Sobrien   */
499174294Sobrien  if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
500174294Sobrien    xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..",
501174294Sobrien	     sizeof(fs_hostname) - MAXHOSTNAMELEN + 3);
502174294Sobrien#ifdef HOSTNAMESZ
503174294Sobrien  /*
504174294Sobrien   * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
505174294Sobrien   * If you need to get the definition for HOSTNAMESZ found, you may
506174294Sobrien   * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
507174294Sobrien   */
508174294Sobrien  if (strlen(fs_hostname) >= HOSTNAMESZ)
509174294Sobrien    xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..",
510174294Sobrien	     sizeof(fs_hostname) - HOSTNAMESZ + 3);
511174294Sobrien#endif /* HOSTNAMESZ */
512174294Sobrien
513174294Sobrien  /*
514174294Sobrien   * Finally we can compute the mount genflags set above,
515174294Sobrien   * and add any automounter specific flags.
516174294Sobrien   */
517174294Sobrien  genflags = compute_mount_flags(&mnt);
518174294Sobrien#ifdef HAVE_FS_AUTOFS
519174294Sobrien  if (on_autofs)
520174294Sobrien    genflags |= autofs_compute_mount_flags(&mnt);
521174294Sobrien#endif /* HAVE_FS_AUTOFS */
522174294Sobrien  genflags |= compute_automounter_mount_flags(&mnt);
523174294Sobrien
524174294Sobrienagain:
525174294Sobrien  if (!(mf->mf_flags & MFF_IS_AUTOFS)) {
526174294Sobrien    nfs_args_t nfs_args;
527310490Scy    am_nfs_handle_t *fhp, anh;
528174294Sobrien#ifndef HAVE_TRANSPORT_TYPE_TLI
529174294Sobrien    u_short port;
530174294Sobrien    struct sockaddr_in sin;
531174294Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
532174294Sobrien
533174294Sobrien    /*
534174294Sobrien     * get fhandle of remote path for automount point
535174294Sobrien     */
536310490Scy    fhp = get_root_nfs_fh(dir, &anh);
537174294Sobrien    if (!fhp) {
538174294Sobrien      plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
539174294Sobrien      return EINVAL;
540174294Sobrien    }
541174294Sobrien
542174294Sobrien#ifndef HAVE_TRANSPORT_TYPE_TLI
543174294Sobrien    /*
544174294Sobrien     * Create sockaddr to point to the local machine.
545174294Sobrien     */
546310490Scy    memset(&sin, 0, sizeof(sin));
547174294Sobrien    /* as per POSIX, sin_len need not be set (used internally by kernel) */
548174294Sobrien    sin.sin_family = AF_INET;
549174294Sobrien    sin.sin_addr = myipaddr;
550174294Sobrien    port = hasmntval(&mnt, MNTTAB_OPT_PORT);
551174294Sobrien    if (port) {
552174294Sobrien      sin.sin_port = htons(port);
553174294Sobrien    } else {
554174294Sobrien      plog(XLOG_ERROR, "no port number specified for %s", dir);
555174294Sobrien      return EINVAL;
556174294Sobrien    }
557174294Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
558174294Sobrien
559174294Sobrien    /* setup the many fields and flags within nfs_args */
560174294Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
561174294Sobrien    compute_nfs_args(&nfs_args,
562174294Sobrien		     &mnt,
563174294Sobrien		     genflags,
564174294Sobrien		     nfsncp,
565174294Sobrien		     NULL,	/* remote host IP addr is set below */
566310490Scy		     nfs_version,
567174294Sobrien		     "udp",
568310490Scy		     fhp,
569174294Sobrien		     fs_hostname,
570174294Sobrien		     pid_fsname);
571174294Sobrien    /*
572174294Sobrien     * IMPORTANT: set the correct IP address AFTERWARDS.  It cannot
573174294Sobrien     * be done using the normal mechanism of compute_nfs_args(), because
574174294Sobrien     * that one will allocate a new address and use NFS_SA_DREF() to copy
575174294Sobrien     * parts to it, while assuming that the ip_addr passed is always
576174294Sobrien     * a "struct sockaddr_in".  That assumption is incorrect on TLI systems,
577174294Sobrien     * because they define a special macro HOST_SELF which is DIFFERENT
578174294Sobrien     * than localhost (127.0.0.1)!
579174294Sobrien     */
580174294Sobrien    nfs_args.addr = &nfsxprt->xp_ltaddr;
581174294Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */
582174294Sobrien    compute_nfs_args(&nfs_args,
583174294Sobrien		     &mnt,
584174294Sobrien		     genflags,
585174294Sobrien		     NULL,
586174294Sobrien		     &sin,
587310490Scy		     nfs_version,
588174294Sobrien		     "udp",
589310490Scy		     fhp,
590174294Sobrien		     fs_hostname,
591174294Sobrien		     pid_fsname);
592174294Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
593174294Sobrien
594174294Sobrien    /*************************************************************************
595174294Sobrien     * NOTE: while compute_nfs_args() works ok for regular NFS mounts	     *
596174294Sobrien     * the toplvl one is not quite regular, and so some options must be      *
597174294Sobrien     * corrected by hand more carefully, *after* compute_nfs_args() runs.    *
598174294Sobrien     *************************************************************************/
599174294Sobrien    compute_automounter_nfs_args(&nfs_args, &mnt);
600174294Sobrien
601174294Sobrien    if (amuDebug(D_TRACE)) {
602174294Sobrien      print_nfs_args(&nfs_args, 0);
603174294Sobrien      plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
604174294Sobrien    }
605174294Sobrien
606174294Sobrien    /* This is it!  Here we try to mount amd on its mount points */
607174294Sobrien    error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args,
608174294Sobrien		     retry, type, 0, NULL, mnttab_file_name, on_autofs);
609174294Sobrien
610174294Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
611174294Sobrien    free_knetconfig(nfs_args.knconf);
612174294Sobrien    /*
613174294Sobrien     * local automounter mounts do not allocate a special address, so
614174294Sobrien     * no need to XFREE(nfs_args.addr) under TLI.
615174294Sobrien     */
616174294Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */
617174294Sobrien
618174294Sobrien#ifdef HAVE_FS_AUTOFS
619174294Sobrien  } else {
620174294Sobrien    /* This is it!  Here we try to mount amd on its mount points */
621174294Sobrien    error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh,
622174294Sobrien		     retry, type, 0, NULL, mnttab_file_name, on_autofs);
623174294Sobrien#endif /* HAVE_FS_AUTOFS */
624174294Sobrien  }
625174294Sobrien  if (error == 0 || forced_unmount)
626174294Sobrien     return error;
627174294Sobrien
628174294Sobrien  /*
629174294Sobrien   * If user wants forced/lazy unmount semantics, then try it iff the
630174294Sobrien   * current mount failed with EIO or ESTALE.
631174294Sobrien   */
632174294Sobrien  if (gopt.flags & CFM_FORCED_UNMOUNTS) {
633174294Sobrien    switch (errno) {
634174294Sobrien    case ESTALE:
635174294Sobrien    case EIO:
636174294Sobrien      forced_unmount = errno;
637174294Sobrien      plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path);
638174294Sobrien      if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name,
639174294Sobrien			     AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) {
640174294Sobrien	plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path);
641174294Sobrien	errno = forced_unmount;
642174294Sobrien      } else
643174294Sobrien	goto again;
644174294Sobrien    default:
645174294Sobrien      break;
646174294Sobrien    }
647174294Sobrien  }
648174294Sobrien
64938494Sobrien  return error;
65038494Sobrien}
65138494Sobrien
65238494Sobrien
65338494Sobrienvoid
65438494Sobrienam_unmounted(am_node *mp)
65538494Sobrien{
656310490Scy  mntfs *mf = mp->am_al->al_mnt;
65738494Sobrien
658310490Scy  if (!foreground) {		/* firewall - should never happen */
659310490Scy    /*
660310490Scy     * This is a coding error.  Make sure we hear about it!
661310490Scy     */
662310490Scy    plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)",
663310490Scy	mp->am_name);
664310490Scy    notify_child(mp, AMQ_UMNT_OK, 0, 0);	/* XXX - be safe? */
66538494Sobrien    return;
666310490Scy  }
66738494Sobrien
66838494Sobrien  /*
66938494Sobrien   * Do unmounted callback
67038494Sobrien   */
67138494Sobrien  if (mf->mf_ops->umounted)
672174294Sobrien    mf->mf_ops->umounted(mf);
67338494Sobrien
67438494Sobrien  /*
675174294Sobrien   * This is ugly, but essentially unavoidable.
676174294Sobrien   * Sublinks must be treated separately as type==link
677174294Sobrien   * when the base type is different.
678174294Sobrien   */
679174294Sobrien  if (mp->am_link && mf->mf_ops != &amfs_link_ops)
680174294Sobrien    amfs_link_ops.umount_fs(mp, mf);
681174294Sobrien
682174294Sobrien#ifdef HAVE_FS_AUTOFS
683174294Sobrien  if (mf->mf_flags & MFF_IS_AUTOFS)
684174294Sobrien    autofs_release_fh(mp);
685174294Sobrien  if (mp->am_flags & AMF_AUTOFS)
686174294Sobrien    autofs_umount_succeeded(mp);
687174294Sobrien#endif /* HAVE_FS_AUTOFS */
688174294Sobrien
689174294Sobrien  /*
690174294Sobrien   * Clean up any directories that were made
691174294Sobrien   *
692174294Sobrien   * If we remove the mount point of a pending mount, any queued access
693174294Sobrien   * to it will fail. So don't do it in that case.
694174294Sobrien   * Also don't do it if the refcount is > 1.
695174294Sobrien   */
696174294Sobrien  if (mf->mf_flags & MFF_MKMNT &&
697174294Sobrien      mf->mf_refc == 1 &&
698174294Sobrien      !(mp->am_flags & AMF_REMOUNT)) {
699174294Sobrien    plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
700174294Sobrien    rmdirs(mf->mf_mount);
701174294Sobrien    mf->mf_flags &= ~MFF_MKMNT;
702174294Sobrien  }
703174294Sobrien
704174294Sobrien  /*
705174294Sobrien   * If this is a pseudo-directory then adjust the link count
706174294Sobrien   * in the parent
707174294Sobrien   */
708174294Sobrien  if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
709174294Sobrien    --mp->am_parent->am_fattr.na_nlink;
710174294Sobrien
711174294Sobrien  /*
71238494Sobrien   * Update mtime of parent node
71338494Sobrien   */
714310490Scy  if (mp->am_parent && mp->am_parent->am_al->al_mnt)
715174294Sobrien    clocktime(&mp->am_parent->am_fattr.na_mtime);
71638494Sobrien
717174294Sobrien  if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
718310490Scy    char *fname = xstrdup(mp->am_name);
719174294Sobrien    am_node *mp_parent = mp->am_parent;
720310490Scy    mntfs *mf_parent = mp_parent->am_al->al_mnt;
721310490Scy    am_node fake_mp;
722174294Sobrien    int error = 0;
723174294Sobrien
724310490Scy    /*
725310490Scy     * We need to use notify_child() after free_map(), so save enough
726310490Scy     * to do that in fake_mp.
727310490Scy     */
728310490Scy    fake_mp.am_fd[1] = mp->am_fd[1];
729310490Scy    mp->am_fd[1] = -1;
730310490Scy
731174294Sobrien    free_map(mp);
732174294Sobrien    plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
733174294Sobrien    mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
734174294Sobrien    if (mp && error < 0)
735310490Scy      (void)mf_parent->mf_ops->mount_child(mp, &error);
736174294Sobrien    if (error > 0) {
737174294Sobrien      errno = error;
738174294Sobrien      plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
739310490Scy      notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0);
740310490Scy    } else {
741310490Scy      notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0);
742174294Sobrien    }
743174294Sobrien    XFREE(fname);
744310490Scy  } else {
745174294Sobrien    /*
746174294Sobrien     * We have a race here.
747174294Sobrien     * If this node has a pending mount and amd is going down (unmounting
748174294Sobrien     * everything in the process), then we could potentially free it here
749174294Sobrien     * while a struct continuation still has a reference to it. So when
750174294Sobrien     * amfs_cont is called, it blows up.
751174294Sobrien     * We avoid the race by refusing to free any nodes that have
752310490Scy     * pending mounts (defined as having a non-NULL am_alarray).
753174294Sobrien     */
754310490Scy    notify_child(mp, AMQ_UMNT_OK, 0, 0);	/* do this regardless */
755310490Scy    if (!mp->am_alarray)
756174294Sobrien      free_map(mp);
757310490Scy  }
75838494Sobrien}
75938494Sobrien
76038494Sobrien
76138494Sobrien/*
76238494Sobrien * Fork the automounter
76338494Sobrien *
76438494Sobrien * TODO: Need a better strategy for handling errors
76538494Sobrien */
76638494Sobrienstatic int
76738494Sobriendofork(void)
76838494Sobrien{
76938494Sobrien  int pid;
77038494Sobrien
77138494Sobrientop:
77238494Sobrien  pid = fork();
77338494Sobrien
77438494Sobrien  if (pid < 0) {		/* fork error, retry in 1 second */
77538494Sobrien    sleep(1);
77638494Sobrien    goto top;
77738494Sobrien  }
77838494Sobrien  if (pid == 0) {		/* child process (foreground==false) */
77942629Sobrien    am_set_mypid();
78038494Sobrien    foreground = 0;
78138494Sobrien  } else {			/* parent process, has one more child */
782174294Sobrien    NumChildren++;
78338494Sobrien  }
78438494Sobrien
78538494Sobrien  return pid;
78638494Sobrien}
78738494Sobrien
78838494Sobrien
78938494Sobrienint
79038494Sobrienbackground(void)
79138494Sobrien{
79238494Sobrien  int pid = dofork();
79338494Sobrien
79438494Sobrien  if (pid == 0) {
79538494Sobrien    dlog("backgrounded");
79638494Sobrien    foreground = 0;
797174294Sobrien  } else
798174294Sobrien    dlog("forked process %d", pid);
79938494Sobrien  return pid;
80038494Sobrien}
801