Add a synchronous unmount amq rpc that will wait for the remote
authorChristos Zoulas <christos@zoulas.com>
Thu, 12 Feb 2009 21:10:21 +0000 (16:10 -0500)
committerChristos Zoulas <christos@zoulas.com>
Thu, 12 Feb 2009 21:10:21 +0000 (16:10 -0500)
filesystem to be unmounted, or return an error. Enabled by amq -uu

13 files changed:
ChangeLog
amd/amd.h
amd/amq_subr.c
amd/amq_svc.c
amd/autil.c
amd/map.c
amq/amq.8
amq/amq.c
amq/amq.h
amq/amq_clnt.c
doc/am-utils.texi
include/amq_defs.h
libamu/nfs_prot_xdr.c

index f9cd81bfccab13adaa210daafc28cbb2c802f4f2..583ccc6c75309487c1a604d41b83195ce2395733 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-02-12  Christos Zoulas <christos@zoulas.com>
+
+       * Add a synchronous unmount amq rpc that will wait for the remote
+         filesystem to be unmounted, or return an error. Enabled by amq -uu
+
 2009-01-11  Erez Zadok  <ezk@fsl.cs.sunysb.edu>
 
        * amd/ops_udf.c: don't define functions/variables which may not be
index ccdb7c312d6ce7af632034cae5d49a08c6d4394e..2af81ba241d3e487142319278295f3f5fdc62737 100644 (file)
--- a/amd/amd.h
+++ b/amd/amd.h
@@ -409,6 +409,7 @@ struct am_ops {
 #ifdef HAVE_FS_AUTOFS
   int          autofs_fs_flags;/* filesystem flags FS_* for autofs mounts */
 #endif /* HAVE_FS_AUTOFS */
+  int          am_fd[2];       /* parent child pipe fd's for sync umount */
 };
 
 /*
@@ -527,7 +528,10 @@ extern int *amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp);
 extern int *amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp);
 extern int *amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp);
 extern voidp amqproc_null_1_svc(voidp argp, struct svc_req *rqstp);
-extern voidp amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp);
+extern int *amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp);
+extern int *amqproc_sync_umnt_1_svc_parent(voidp argp, struct svc_req *rqstp);
+extern amq_sync_umnt *amqproc_sync_umnt_1_svc_child(voidp argp, struct svc_req *rqstp);
+extern amq_sync_umnt *amqproc_sync_umnt_1_svc_async(voidp argp, struct svc_req *rqstp);
 
 /* other external definitions */
 extern am_nfs_fh *get_root_nfs_fh(char *dir);
@@ -613,6 +617,7 @@ extern void mp_to_fh(am_node *, am_nfs_fh *);
 extern void new_ttl(am_node *);
 extern void nfs_quick_reply(am_node *mp, int error);
 extern void normalize_slash(char *);
+extern void notify_child(am_node *, au_etype, int, int);
 extern void ops_showamfstypes(char *buf, size_t l);
 extern void ops_showfstypes(char *outbuf, size_t l);
 extern void rem_que(qelem *);
index 28a3039d25679bcfcea69d65211247a990aad6c1..61a1c4be22195754030587473e125d907c463092 100644 (file)
@@ -80,16 +80,68 @@ amqproc_mnttree_1_svc(voidp argp, struct svc_req *rqstp)
 /*
  * Unmount a single node
  */
-voidp
+int *
 amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp)
 {
-  static char res;
+  static int res = AMQ_UMNT_OK;
   am_node *mp = find_ap(*(char **) argp);
 
   if (mp)
     forcibly_timeout_mp(mp);
 
-  return (voidp) &res;
+  return &res;
+}
+
+
+/*
+ * Synchronously unmount a single node - parent side.
+ */
+int *
+amqproc_sync_umnt_1_svc_parent(voidp argp, struct svc_req *rqstp)
+{
+  amqproc_umnt_1_svc(argp, rqstp);
+  return NULL;
+}
+
+
+/*
+ * Synchronously unmount a single node - child side.
+ */
+amq_sync_umnt *
+amqproc_sync_umnt_1_svc_child(voidp argp, struct svc_req *rqstp)
+{
+  static amq_sync_umnt rv;
+  amq_sync_umnt buf;
+  ssize_t n;
+
+  am_node *mp = find_ap(*(char **) argp);
+
+  memset(&rv, 0, sizeof(rv));
+  rv.au_etype = AMQ_UMNT_READ;
+  if (mp && mp->am_fd[0] >= 0) {
+    n = read(mp->am_fd[0], &buf, sizeof(buf));
+    if (n == sizeof(buf))
+      rv = buf;
+  }
+  return &rv;
+}
+
+
+/*
+ * Synchronously unmount a single node - use if we can't fork (asynchronous).
+ */
+amq_sync_umnt *
+amqproc_sync_umnt_1_svc_async(voidp argp, struct svc_req *rqstp)
+{
+  static amq_sync_umnt rv;
+
+  memset(&rv, 0, sizeof(rv));
+  rv.au_etype = AMQ_UMNT_FORK;
+  rv.au_errno = errno;
+
+  amqproc_umnt_1_svc(argp, rqstp);
+
+  return &rv;
 }
 
 
index 5d837c1243d3f842b2a8a007572a468daa62d818..ff261d09b06c281df0ab1675bd0d3c804dc7c698 100644 (file)
@@ -110,6 +110,75 @@ amqsvc_is_client_allowed(const struct sockaddr_in *addr, char *remote)
 #endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */
 
 
+/*
+ * Prepare the parent and child:
+ * 1) Setup IPC pipe.
+ * 2) Set signal masks.
+ * 3) Fork by calling background() so that NumChildren is updated.
+ */
+static int
+amq_fork(opaque_t argp)
+{
+#ifdef HAVE_SIGACTION
+  sigset_t new, mask;
+#else /* not HAVE_SIGACTION */
+  int mask;
+#endif /* not HAVE_SIGACTION */
+  am_node *mp;
+  pid_t pid;
+
+  mp = find_ap(*(char **) argp);
+  if (mp == NULL) {
+    errno = 0;
+    return -1;
+  }
+
+  if (pipe(mp->am_fd) == -1) {
+    mp->am_fd[0] = -1;
+    mp->am_fd[1] = -1;
+    return -1;
+  }
+
+#ifdef HAVE_SIGACTION
+  sigemptyset(&new);           /* initialize signal set we wish to block */
+  sigaddset(&new, SIGHUP);
+  sigaddset(&new, SIGINT);
+  sigaddset(&new, SIGQUIT);
+  sigaddset(&new, SIGCHLD);
+  sigprocmask(SIG_BLOCK, &new, &mask);
+#else /* not HAVE_SIGACTION */
+  mask =
+      sigmask(SIGHUP) |
+      sigmask(SIGINT) |
+      sigmask(SIGQUIT) |
+      sigmask(SIGCHLD);
+  mask = sigblock(mask);
+#endif /* not HAVE_SIGACTION */
+
+  switch ((pid = background())) {
+  case -1:     /* error */
+    dlog("amq_fork failed");
+    return -1;
+
+  case 0:      /* child */
+    close(mp->am_fd[1]);       /* close output end of pipe */
+    mp->am_fd[1] = -1;
+    return 0;
+
+  default:     /* parent */
+    close(mp->am_fd[0]);       /* close input end of pipe */
+    mp->am_fd[0] = -1;
+
+#ifdef HAVE_SIGACTION
+    sigprocmask(SIG_SETMASK, &mask, NULL);
+#else /* not HAVE_SIGACTION */
+    sigsetmask(mask);
+#endif /* not HAVE_SIGACTION */
+    return pid;
+  }
+}
+
+
 void
 amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
 {
@@ -121,6 +190,9 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
   char *result;
   xdrproc_t xdr_argument, xdr_result;
   amqsvcproc_t local;
+  amqsvcproc_t child;
+  amqsvcproc_t parent;
+  pid_t pid;
 
 #if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP)
   if (gopt.flags & CFM_USE_TCPWRAPPERS) {
@@ -137,6 +209,10 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
   }
 #endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */
 
+  local = NULL;
+  child = NULL;
+  parent = NULL;
+
   switch (rqstp->rq_proc) {
 
   case AMQPROC_NULL:
@@ -199,6 +275,15 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
     local = (amqsvcproc_t) amqproc_pawd_1_svc;
     break;
 
+  case AMQPROC_SYNC_UMNT:
+    xdr_argument = (xdrproc_t) xdr_amq_string;
+    xdr_result = (xdrproc_t) xdr_amq_sync_umnt;
+    parent = (amqsvcproc_t) amqproc_sync_umnt_1_svc_parent;
+    child = (amqsvcproc_t) amqproc_sync_umnt_1_svc_child;
+    /* used if fork fails */
+    local = (amqsvcproc_t) amqproc_sync_umnt_1_svc_async;
+    break;
+
   default:
     svcerr_noproc(transp);
     return;
@@ -212,7 +297,28 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
     return;
   }
 
-  result = (*local) (&argument, rqstp);
+  pid = -1;
+  result = NULL;
+
+  if (child) {
+    switch ((pid = amq_fork(&argument))) {
+    case -1:   /* error */
+      break;
+
+    case 0:    /* child */
+      result = (*child) (&argument, rqstp);
+      local = NULL;
+      break;
+
+    default:   /* parent */
+      result = (*parent) (&argument, rqstp);
+      local = NULL;
+      break;
+    }
+  }
+
+  if (local)
+    result = (*local) (&argument, rqstp);
 
   if (result != NULL && !svc_sendreply(transp,
                                       (XDRPROC_T_TYPE) xdr_result,
@@ -226,4 +332,7 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
     plog(XLOG_FATAL, "unable to free rpc arguments in amqprog_1");
     going_down(1);
   }
+
+  if (pid == 0)
+    exit(0);   /* the child is done! */
 }
index 1c14f832112d6c2530d98fe072632f4d4aa9226f..d97b165ec78d8d08a1f78be5bbffc8d4aaff30e0 100644 (file)
@@ -166,10 +166,17 @@ forcibly_timeout_mp(am_node *mp)
    */
   if (mf && ((mp->am_flags & AMF_ROOT) ||
             (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
+    /*
+     * We aren't going to schedule a timeout, so we need to notify the
+     * child here unless we are already unmounting, in which case that
+     * process is responsible for notifying the child.
+     */
     if (mf->mf_flags & MFF_UNMOUNTING)
       plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
-    else
+    else {
       plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
+      notify_child(mp, AMQ_UMNT_FAILED, EBUSY, 0);
+    }
   } else {
     plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
     mp->am_flags &= ~AMF_NOTIMEOUT;
@@ -659,8 +666,15 @@ am_unmounted(am_node *mp)
 {
   mntfs *mf = mp->am_mnt;
 
-  if (!foreground)             /* firewall - should never happen */
+  if (!foreground) {           /* firewall - should never happen */
+    /*
+     * This is a coding error.  Make sure we hear about it!
+     */
+    plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)",
+       mp->am_name);
+    notify_child(mp, AMQ_UMNT_OK, 0, 0);       /* XXX - be safe? */
     return;
+  }
 
   /*
    * Do unmounted callback
@@ -715,8 +729,16 @@ am_unmounted(am_node *mp)
     char *fname = strdup(mp->am_name);
     am_node *mp_parent = mp->am_parent;
     mntfs *mf_parent = mp_parent->am_mnt;
+    am_node fake_mp;
     int error = 0;
 
+    /*
+     * We need to use notify_child() after free_map(), so save enough
+     * to do that in fake_mp.
+     */
+    fake_mp.am_fd[1] = mp->am_fd[1];
+    mp->am_fd[1] = -1;
+
     free_map(mp);
     plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
     mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
@@ -725,9 +747,12 @@ am_unmounted(am_node *mp)
     if (error > 0) {
       errno = error;
       plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
+      notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0);
+    } else {
+      notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0);
     }
     XFREE(fname);
-  } else
+  } else {
     /*
      * We have a race here.
      * If this node has a pending mount and amd is going down (unmounting
@@ -737,8 +762,10 @@ am_unmounted(am_node *mp)
      * We avoid the race by refusing to free any nodes that have
      * pending mounts (defined as having a non-NULL am_mfarray).
      */
+    notify_child(mp, AMQ_UMNT_OK, 0, 0);       /* do this regardless */
     if (!mp->am_mfarray)
       free_map(mp);
+  }
 }
 
 
index 74c221215d4d744ead208d9b56406b946d15573e..acb30f5d5f9125b13bb5f9bfb04e35a756d713c5 100644 (file)
--- a/amd/map.c
+++ b/amd/map.c
@@ -430,6 +430,25 @@ init_map(am_node *mp, char *dir)
   mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds;
   mp->am_dev = -1;
   mp->am_rdev = -1;
+  mp->am_fd[0] = -1;
+  mp->am_fd[1] = -1;
+}
+
+
+void
+notify_child(am_node *mp, au_etype au_etype, int au_errno, int au_signal)
+{
+  amq_sync_umnt rv;
+
+  if (mp->am_fd[1] >= 0) {     /* we have a child process */
+    rv.au_etype = au_etype;
+    rv.au_signal = au_signal;
+    rv.au_errno = au_errno;
+
+    write(mp->am_fd[1], &rv, sizeof(rv));
+    close(mp->am_fd[1]);
+    mp->am_fd[1] = -1;
+  }
 }
 
 
@@ -442,6 +461,10 @@ free_map(am_node *mp)
 {
   remove_am(mp);
 
+  if (mp->am_fd[1] != -1)
+    plog(XLOG_FATAL, "free_map: called prior to notifying the child for %s.",
+       mp->am_path);
+
   if (mp->am_link)
     XFREE(mp->am_link);
   if (mp->am_name)
@@ -836,6 +859,7 @@ free_map_if_success(int rc, int term, opaque_t arg)
     reschedule_timeout_mp();
   }
   if (term) {
+    notify_child(mp, AMQ_UMNT_SIGNAL, 0, term);
     plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
 #if defined(DEBUG) && defined(SIGTRAP)
     /*
@@ -852,6 +876,7 @@ free_map_if_success(int rc, int term, opaque_t arg)
 #endif /* HAVE_FS_AUTOFS */
     amd_stats.d_uerr++;
   } else if (rc) {
+    notify_child(mp, AMQ_UMNT_FAILED, rc, 0);
     if (mf->mf_ops == &amfs_program_ops || rc == EBUSY)
       plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
     else
@@ -864,6 +889,9 @@ free_map_if_success(int rc, int term, opaque_t arg)
 #endif /* HAVE_FS_AUTOFS */
     amd_stats.d_uerr++;
   } else {
+    /*
+     * am_unmounted() will call notify_child() appropriately.
+     */
     am_unmounted(mp);
   }
 
@@ -914,6 +942,7 @@ unmount_mp(am_node *mp)
       plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
       mf->mf_flags |= MFF_LOGDOWN;
     }
+    notify_child(mp, AMQ_UMNT_SERVER, 0, 0);
     return 0;
   }
 
index dd5c5246a4387b24464d37f7422fca2079ce28a4..43e318c697385f50dfc6f0ed0036164edf27cf97 100644 (file)
--- a/amq/amq.8
+++ b/amq/amq.8
@@ -45,7 +45,7 @@ amq \- automounter query tool
 .SH SYNOPSIS
 .B amq
 [
-.BI \-fmpsvwHTU
+.BI \-fmpqsvwHTU
 ] [
 .BI \-h " hostname"
 ] [
@@ -115,6 +115,13 @@ search through the process table.  This option is used in the
 .I ctl-amd
 script.
 
+.TP
+.B \-q
+Suppress error messages produced when attempting synchronous unmounts
+with the
+.B \-u
+option.
+
 .TP
 .B \-s
 Ask the automounter to provide system-wide mount statistics.
@@ -128,6 +135,16 @@ information about them.  Unmounts are requested, not forced.  They merely
 cause the mounted filesystem to timeout, which will be picked up by
 .BR amd 's
 main scheduler thus causing the normal timeout action to be taken.
+If the
+.B \-u
+option is repeated,
+.B amq
+will attempt to unmount the file system synchronously by waiting until
+the timeout action is taken and returning an error if the unmount
+fails.
+Any error messages produced may be suppressed with the
+.B \-q
+option.
 
 .TP
 .B \-v
@@ -236,7 +253,7 @@ amd: ALL
 
 .SH "SEE ALSO"
 .BR amd (8),
-.BR ctl-amd (8),
+.\" .BR ctl-amd (8),
 .BR amd.conf (5),
 .BR hosts_access (5).
 .LP
index a0501c240ff9658cf4657738240f16d03e11ea30..433f5ed939708ce248885120be5f4840f515006c 100644 (file)
--- a/amq/amq.c
+++ b/amq/amq.c
 
 /* locals */
 static int flush_flag;
-static int minfo_flag;
 static int getpid_flag;
-static int unmount_flag;
-static int stats_flag;
+static int getpwd_flag;
 static int getvers_flag;
+static int minfo_flag;
+static int quiet_flag;
+static int stats_flag;
+static int unmount_flag;
+static int use_tcp_flag;
+static int use_udp_flag;
 static u_long amd_program_number = AMQ_PROGRAM;
-static int use_tcp_flag, use_udp_flag;
-static int getpwd_flag;
 static char *debug_opts;
 static char *amq_logfile;
 static char *xlog_optstr;
@@ -287,6 +289,71 @@ cluster_server(void)
 #endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
 
 
+static void
+print_umnt_error(amq_sync_umnt *rv, const char *fs)
+{
+
+  switch (rv->au_etype) {
+  case AMQ_UMNT_OK:
+    break;
+  case AMQ_UMNT_FAILED:
+    printf("unmount failed: %s\n", strerror(rv->au_errno));
+    break;
+  case AMQ_UMNT_FORK:
+    if (rv->au_errno == 0)
+      printf("%s is not mounted\n", fs);
+    else
+      printf("falling back to asynchronous unmount: %s\n",
+         strerror(rv->au_errno));
+    break;
+  case AMQ_UMNT_READ:
+    printf("pipe read error: %s\n", strerror(rv->au_errno));
+    break;
+  case AMQ_UMNT_SERVER:
+    printf("amd server down\n");
+    break;
+  case AMQ_UMNT_SIGNAL:
+    printf("got signal: %d\n", rv->au_signal);
+    break;
+  /*
+   * Omit default so the compiler can check for missing cases.
+   *
+  default:
+    break;
+   */
+  }
+}
+
+
+static int
+amu_sync_umnt_to_retval(amq_sync_umnt *rv)
+{
+  switch (rv->au_etype) {
+  case AMQ_UMNT_FORK:
+    if (rv->au_errno == 0) {
+      /*
+       * We allow this error so that things like:
+       *   amq -uu /l/cd0d && eject cd0
+       * will work when /l/cd0d is not mounted.
+       * XXX - We still print an error message.
+       */
+      return 0;
+    }
+  default:
+    return rv->au_etype;
+  }
+}
+
+
+static int
+clnt_failed(CLIENT *clnt, char *server)
+{
+  fprintf(stderr, "%s: ", am_get_progname());
+  clnt_perror(clnt, server);
+  return 1;
+}
+
+
 /*
  * MAIN
  */
@@ -320,7 +387,7 @@ main(int argc, char *argv[])
   /*
    * Parse arguments
    */
-  while ((opt_ch = getopt(argc, argv, "Hfh:l:msuvx:D:pP:TUw")) != -1)
+  while ((opt_ch = getopt(argc, argv, "Hfh:l:mqsuvx:D:pP:TUw")) != -1)
     switch (opt_ch) {
     case 'H':
       goto show_usage;
@@ -350,13 +417,18 @@ main(int argc, char *argv[])
       nodefault = 1;
       break;
 
+    case 'q':
+      quiet_flag = 1;
+      nodefault = 1;
+      break;
+
     case 's':
       stats_flag = 1;
       nodefault = 1;
       break;
 
     case 'u':
-      unmount_flag = 1;
+      unmount_flag++;
       nodefault = 1;
       break;
 
@@ -403,9 +475,9 @@ main(int argc, char *argv[])
   if (errs) {
   show_usage:
     fprintf(stderr, "\
-Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
+Usage: %s [-fmpqsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
 \t[-x log_options] [-D debug_options]\n\
-\t[-P program_number] [[-u] directory ...]\n",
+\t[-P program_number] [[-u[u]] directory ...]\n",
            am_get_progname()
     );
     exit(1);
@@ -616,7 +688,20 @@ Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
   if (optind < argc) {
     do {
       char *fs = argv[optind++];
-      if (unmount_flag) {
+      if (unmount_flag > 1) {
+       amq_sync_umnt *sup;
+       /*
+        * Synchronous unmount request
+        */
+        sup = amqproc_sync_umnt_1(&fs, clnt);
+       if (sup) {
+         if (quiet_flag == 0)
+           print_umnt_error(sup, fs);
+         errs = amu_sync_umnt_to_retval(sup);
+       } else {
+         errs = clnt_failed(clnt, server);
+       }
+      }        else if (unmount_flag) {
        /*
         * Unmount request
         */
@@ -641,9 +726,7 @@ Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
          }
          xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp);
        } else {
-         fprintf(stderr, "%s: ", am_get_progname());
-         clnt_perror(clnt, server);
-         errs = 1;
+         errs = clnt_failed(clnt, server);
        }
       }
     } while (optind < argc);
@@ -656,9 +739,7 @@ Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
     if (ms) {
       show_ms(ms);
     } else {
-      fprintf(stderr, "%s: ", am_get_progname());
-      clnt_perror(clnt, server);
-      errs = 1;
+      errs = clnt_failed(clnt, server);
     }
 
   } else if (!nodefault) {
@@ -682,9 +763,7 @@ Usage: %s [-fmpsvwHTU] [-h hostname] [-l log_file|\"syslog\"]\n\
       }
 
     } else {
-      fprintf(stderr, "%s: ", am_get_progname());
-      clnt_perror(clnt, server);
-      errs = 1;
+      errs = clnt_failed(clnt, server);
     }
   }
   exit(errs);
index bf21f2511cd61415268788e68efaf9e5c3af297d..579af43b962de201487b9fb9e743eceac6310643 100644 (file)
--- a/amq/amq.h
+++ b/amq/amq.h
@@ -51,6 +51,7 @@
 extern voidp amqproc_null_1(voidp argp, CLIENT *rqstp);
 extern amq_mount_tree_p *amqproc_mnttree_1(amq_string *argp, CLIENT *rqstp);
 extern voidp amqproc_umnt_1(amq_string *argp, CLIENT *rqstp);
+extern amq_sync_umnt *amqproc_sync_umnt_1(amq_string *argp, CLIENT *rqstp);
 extern amq_mount_stats *amqproc_stats_1(voidp argp, CLIENT *rqstp);
 extern amq_mount_tree_list *amqproc_export_1(voidp argp, CLIENT *rqstp);
 extern int *amqproc_setopt_1(amq_setopt *argp, CLIENT *rqstp);
index db0c12131c9219d979f832cc0fe67d8c5b60eca6..63b02a24cefe9396fa03612dc18d64583fd619a3 100644 (file)
@@ -99,6 +99,23 @@ amqproc_umnt_1(amq_string *argp, CLIENT *clnt)
 }
 
 
+amq_sync_umnt *
+amqproc_sync_umnt_1(amq_string *argp, CLIENT *clnt)
+{
+  static amq_sync_umnt res;
+  enum clnt_stat rv;
+
+  memset((char *) &res, 0, sizeof(res));
+  if ((rv = clnt_call(clnt, AMQPROC_SYNC_UMNT,
+               (XDRPROC_T_TYPE) xdr_amq_string, (SVC_IN_ARG_TYPE) argp,
+               (XDRPROC_T_TYPE) xdr_amq_sync_umnt, &res,
+               TIMEOUT)) != RPC_SUCCESS) {
+    return (NULL);
+  }
+  return &res;
+}
+
+
 amq_mount_stats *
 amqproc_stats_1(voidp argp, CLIENT *clnt)
 {
index 1db41c75a2000459987270b31faa63f71ae0612f..31593655166d928acdc4b74af7d6c87357765ba0 100644 (file)
@@ -5513,6 +5513,7 @@ mount point.
 * Amq -m option::     Obtaining mount statistics.
 * Amq -p option::     Getting Amd's process ID.
 * Amq -P option::     Contacting alternate Amd processes.
+* Amq -q option::     Suppress synchronous unmounting errors.
 * Amq -s option::     Obtaining global statistics.
 * Amq -T option::     Use TCP transport.
 * Amq -U option::     Use UDP transport.
@@ -5718,7 +5719,7 @@ rather not have to search through the process table.  This option is
 used in the @file{ctl-amd} script.
 
 @c ----------------------------------------------------------------
-@node Amq -P option, Amq -s option, Amq -p option, Controlling Amd
+@node Amq -P option, Amq -q option, Amq -p option, Controlling Amd
 @comment  node-name,  next,  previous,  up
 @subsection @i{Amq} @code{-P} option
 @cindex Multiple Amd processes
@@ -5741,7 +5742,16 @@ kill `amq -p -P 300020`
 @end example
 
 @c ----------------------------------------------------------------
-@node Amq -s option, Amq -T option, Amq -P option, Controlling Amd
+@node Amq -q option, Amq -s option, Amq -P option, Controlling Amd
+@comment  node-name,  next,  previous,  up
+@subsection @i{Amq} @code{-q} option
+@cindex Unmounting a filesystem
+
+Suppress any error messages produced when a synchronous unmount fails.
+See @ref{Amq -u option}.
+
+@c ----------------------------------------------------------------
+@node Amq -s option, Amq -T option, Amq -q option, Controlling Amd
 @comment  node-name,  next,  previous,  up
 @subsection @i{Amq} @code{-s} option
 @cindex Global statistics
@@ -5806,11 +5816,19 @@ and if that failed, will try UDP.
 @cindex Forcing filesystem to time out
 @cindex Unmounting a filesystem
 
-The @code{-u} option causes the time-to-live interval of the named mount
-points to be expired, thus causing an unmount attempt.  This is the only
-safe way to unmount an automounted filesystem.  It is not possible to
-unmount a filesystem which has been mounted with the @samp{nounmount}
-flag.
+The @code{-u} option causes the time-to-live interval of the named
+mount points to be expired, thus causing an unmount attempt.  This is
+the only safe way to unmount an automounted filesystem.  If @code{-u}
+is repeated, then @i{Amd} will attempt to unmount the filesystem
+synchronously.  This makes things like
+
+@example
+amq -uu /t/cd0d && eject cd0
+@end example
+
+@noindent
+work as expected.  Any error messages this might produce can be
+suppressed with the @code{-q} option.  See @ref{Amq -q option}.
 
 @c The @code{-H} option informs @i{Amd} that the specified mount point
 @c has hung - as if its keepalive timer had expired.
index cb4a110869e307d25eb5d3d510862795084b1330..a4a20046d8e4038b40e2d3c1315b517895a7ef61 100644 (file)
@@ -55,7 +55,7 @@
 #define AMQ_VERSION ((u_long)1)
 #define AMQPROC_NULL ((u_long)0)
 #define AMQPROC_MNTTREE ((u_long)1)
-#define AMQPROC_UMNT ((u_long)2)
+#define AMQPROC_UMNT ((u_long)2)       /* asynchronous unmount */
 #define AMQPROC_STATS ((u_long)3)
 #define AMQPROC_EXPORT ((u_long)4)
 #define AMQPROC_SETOPT ((u_long)5)
@@ -64,6 +64,7 @@
 #define AMQPROC_GETVERS ((u_long)8)
 #define AMQPROC_GETPID ((u_long)9)
 #define AMQPROC_PAWD ((u_long)10)
+#define AMQPROC_SYNC_UMNT ((u_long)11) /* synchronous unmount */
 
 /*
  * TYPEDEFS
@@ -73,6 +74,7 @@ typedef struct amq_mount_info amq_mount_info;
 typedef struct amq_mount_stats amq_mount_stats;
 typedef struct amq_mount_tree amq_mount_tree;
 typedef struct amq_setopt amq_setopt;
+typedef struct amq_sync_umnt amq_sync_umnt;
 typedef amq_mount_tree *amq_mount_tree_p;
 
 /*
@@ -122,6 +124,21 @@ struct amq_mount_stats {
   int as_uerr;
 };
 
+typedef enum {
+  AMQ_UMNT_OK          = 0,    /* must be zero! */
+  AMQ_UMNT_FAILED      = 1,    /* unmount failed */
+  AMQ_UMNT_FORK        = 2,    /* fork failed */
+  AMQ_UMNT_READ        = 3,    /* pipe read failed */
+  AMQ_UMNT_SERVER      = 4,    /* server down */
+  AMQ_UMNT_SIGNAL      = 5     /* received signal */
+} au_etype;
+
+struct amq_sync_umnt {
+       au_etype        au_etype;       /* error type */
+       int             au_errno;       /* error number */
+       int             au_signal;      /* signal received */
+};
+
 enum amq_opt {
   AMOPT_DEBUG = 0,
   AMOPT_LOGFILE = 1,
@@ -151,6 +168,7 @@ extern bool_t xdr_amq_mount_tree_list(XDR *xdrs, amq_mount_tree_list *objp);
 extern bool_t xdr_amq_mount_tree_p(XDR *xdrs, amq_mount_tree_p *objp);
 extern bool_t xdr_amq_opt(XDR *xdrs, amq_opt *objp);
 extern bool_t xdr_amq_setopt(XDR *xdrs, amq_setopt *objp);
+extern bool_t xdr_amq_sync_umnt(XDR *xdrs, amq_sync_umnt *objp);
 extern bool_t xdr_pri_free(XDRPROC_T_TYPE xdr_args, caddr_t args_ptr);
 extern bool_t xdr_time_type(XDR *xdrs, time_type *objp);
 
index 145d4691c9ea37a371dee1e23d6f5a99e3183ab0..e34c37d8a5ee9c6049059d2f81e4933099d03360 100644 (file)
@@ -56,3 +56,14 @@ xdr_amq_string(XDR *xdrs, amq_string *objp)
   }
   return (TRUE);
 }
+
+
+bool_t
+xdr_amq_sync_umnt(XDR *xdrs, amq_sync_umnt *objp)
+{
+
+  if (!xdr_opaque(xdrs, (char *) objp, sizeof(*objp))) {
+    return (FALSE);
+  }
+  return (TRUE);
+}