Hello everyone,

This is the library Justus suggested some time ago and it contains the
implementation of settrans. This patch actually does three things.

1) It creates libhurdutil/hurdutil.h which exposes the interface and a
struct called settrans_flags_t which contains all the flags to be used
by settrans.
2) It creates libhurdutil/settrans.c which includes the c implementation
of utils/settrans.c without the arguments parsing.
3) It modifies utils/settrans.c to use the new library. The arguments
parsing of the settrans binary happens here.

Whoever uses the library directly will modify the flags accordingly and
pass them to settrans() from the library.

In order to use it you first have to declare a variable settrans_flags_t
flags, initialize it with settrans_flags_create(&flags), change the
flags inside settrans_flags_t, pass it to settrans() and when you are
done call settrans_flags_cleanup(flags).

I have tested it and it works. I am currently using it in my vm. But I
have some questions.

Implementation questions:
1) How should I handle errors of settrans inside the library? Normally
utils/settrans.c used error() to report any problems. But settrans()
should return error_t codes. Should I define a list of errors for
settrans() and use them accordingly? (e.g TRANS_NOT_SET etc.)
2) Do you agree with the way I handle arguments? Should I do it in a
different way?

Code indentations questions:
1) I am using emacs and I have configured it to automatically indent
code according to the gnu style. But I noticed that this causes changes
from the original. For example in the original, some '{'  are not being
tabbed 4 spaces to the right. Why? Is this a special case?

This is my first serious c library so I am sure there will be many
things that need to be changed. settrans_flags_cleanup() needs some work
and I need to add more comments. It's still work in progress so please
feel free to correct/suggest anything. :-)

Thank you,
Manolis

From 4fe21e255e483a711f898227b90b130afd969b33 Mon Sep 17 00:00:00 2001
From: Manolis Ragkousis <manolis...@gmail.com>
Date: Thu, 12 May 2016 15:08:36 +0300
Subject: [PATCH] libhurdutil: New library containing utils to be used by Guix.

* libhurdutil/hurdutil.h: New file.
* libhurdutil/settrans.c: New file.
* libhurdutil/Makefile: New file.
* utils/Makefile (HURDLIBS, settrans): Use the new library.
* utils/settrans.c: Update to use the new library.
* Makefile: (lib-subdirs): Add library.
---
 Makefile               |   2 +-
 libhurdutil/Makefile   |  28 +++
 libhurdutil/hurdutil.h |  78 +++++++
 libhurdutil/settrans.c | 377 ++++++++++++++++++++++++++++++++++
 utils/Makefile         |   4 +-
 utils/settrans.c       | 536 ++++++++++++++-----------------------------------
 6 files changed, 633 insertions(+), 392 deletions(-)
 create mode 100644 libhurdutil/Makefile
 create mode 100644 libhurdutil/hurdutil.h
 create mode 100644 libhurdutil/settrans.c

diff --git a/Makefile b/Makefile
index d48baaa..e712767 100644
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ include ./Makeconf
 lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
 	      libpager libfshelp libdiskfs libtrivfs libps \
 	      libnetfs libpipe libstore libhurdbugaddr libftpconn libcons \
-	      libhurd-slab
+	      libhurd-slab libhurdutil
 
 # Hurd programs
 prog-subdirs = auth proc exec term \
diff --git a/libhurdutil/Makefile b/libhurdutil/Makefile
new file mode 100644
index 0000000..2e0e642
--- /dev/null
+++ b/libhurdutil/Makefile
@@ -0,0 +1,28 @@
+#   Copyright (C) 2016 Free Software Foundation, Inc.
+#
+#   This file is part of the GNU Hurd.
+#
+#   The GNU Hurd is free software; you can redistribute it and/or
+#   modify it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation; either version 2, or (at
+#   your option) any later version.
+#
+#   The GNU Hurd is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libhurdutil
+makemode := library
+
+libname := libhurdutil
+SRCS = settrans.c
+installhdrs = hurdutil.h
+
+OBJS = $(SRCS:.c=.o)
+
+include ../Makeconf
diff --git a/libhurdutil/hurdutil.h b/libhurdutil/hurdutil.h
new file mode 100644
index 0000000..5786cff
--- /dev/null
+++ b/libhurdutil/hurdutil.h
@@ -0,0 +1,78 @@
+/* hurdutil.h - Hurd utils interface.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Written by Manolis Fragkiskos Ragkousis <manolis...@gmail.com>.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef _HURD_UTIL_H
+#define _HURD_UTIL_H
+
+#include <errno.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include <hurd/fsys.h>
+
+struct settrans_flags
+{
+  /* The name of the node we're putting the translator on. */
+  char *node_name;
+
+  /* Flags to pass to file_set_translator.  */
+  int lookup_flags;
+  int goaway_flags;
+
+  /* Various option flags.  */
+  int passive;
+  int active;
+  int keep_active;
+  int pause;
+  int kill_active;
+  int orphan;
+  int start;
+  int stack;
+  int excl;
+  int timeout;
+  char *pid_file;
+  char *underlying_node_name;
+  int underlying_lookup_flags;
+  char **chroot_command;
+  char *chroot_chdir;
+
+  /* The translator's arg vector, in '\0' separated format.  */
+  char *argz;
+  size_t argz_len;
+};
+typedef struct settrans_flags *settrans_flags_t;
+
+/* Initialize the flags to be used. */
+void settrans_flags_init (settrans_flags_t flags);
+
+/* Release the memory allocated. */
+void settrans_flags_cleanup (settrans_flags_t flags);
+
+/* Create the struct containing the flags and initialize them.
+   If a memory allocation error occurs, ENOMEM is returned,
+   otherwise 0.*/
+error_t settrans_flags_create (settrans_flags_t *flags);
+
+/* Set a translator according to the flags passed. On success return 0. */
+error_t settrans(settrans_flags_t flags);
+
+#endif /* _HURD_UTIL_H */
diff --git a/libhurdutil/settrans.c b/libhurdutil/settrans.c
new file mode 100644
index 0000000..eeaa964
--- /dev/null
+++ b/libhurdutil/settrans.c
@@ -0,0 +1,377 @@
+/* settrans.c - Set a file's translator.
+
+   Copyright (C) 1995,96,97,98,2001,02,13,14,16
+   Free Software Foundation, Inc.
+   Written by Miles Bader <mi...@gnu.org>
+   Written by Manolis Fragkiskos Ragkousis <manolis...@gmail.com>.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <hurd.h>
+#include <error.h>
+#include <argz.h>
+#include <hurd/fshelp.h>
+#include <hurd/lookup.h>
+
+
+#include "hurdutil.h"
+
+#define DEFAULT_TIMEOUT 60
+
+/* Authentication of the current process.  */
+uid_t *uids;
+gid_t *gids;
+size_t uids_len, gids_len;
+
+/* Initialize and populate the uids and gids vectors.  */
+error_t
+get_credentials (void)
+{
+  /* Fetch uids...  */
+  uids_len = geteuids (0, 0);
+  if (uids_len < 0)
+    return errno;
+
+  uids = malloc (uids_len * sizeof (uid_t));
+  if (! uids)
+    return ENOMEM;
+
+  uids_len = geteuids (uids_len, uids);
+  if (uids_len < 0)
+    return errno;
+
+  /* ... and gids.  */
+  gids_len = getgroups (0, 0);
+  if (gids_len < 0)
+    return errno;
+
+  gids = malloc (gids_len * sizeof (gid_t));
+  if (! uids)
+    return ENOMEM;
+
+  gids_len = getgroups (gids_len, gids);
+  if (gids_len < 0)
+    return errno;
+
+  return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+void
+settrans_flags_init (settrans_flags_t flags)
+{
+  flags->node_name = 0;
+
+  flags->lookup_flags = O_NOTRANS;
+  flags->goaway_flags = 0;
+
+  flags->passive = 0;
+  flags->active = 0;
+  flags->keep_active = 0;
+  flags->pause = 0;
+  flags->kill_active = 0;
+  flags->orphan = 0;
+  flags->start = 0;
+  flags->stack = 0;
+  flags->excl = 0;
+  flags->timeout = DEFAULT_TIMEOUT * 1000; /* ms */
+  flags->pid_file = 0;
+  flags->underlying_node_name = NULL;
+  flags->chroot_command = 0;
+  flags->chroot_chdir = "/";
+
+  flags->argz;
+  flags->argz_len;
+}
+
+void
+settrans_flags_cleanup (settrans_flags_t flags)
+{
+  free(flags);
+}
+
+error_t
+settrans_flags_create (settrans_flags_t *flags)
+{
+  *flags = malloc (sizeof (struct settrans_flags));
+  if (*flags == NULL)
+    return ENOMEM;
+
+  settrans_flags_init(*flags);
+
+  return 0;
+}
+
+error_t settrans(settrans_flags_t flags)
+{
+  error_t err;
+
+  /* The filesystem node we're putting a translator on.  */
+  char *node_name = flags->node_name;
+  file_t node;
+
+  /* The translator's arg vector, in '\0' separated format.  */
+  char *argz = flags->argz;
+  size_t argz_len = flags->argz_len;
+
+  /* The control port for any active translator we start up.  */
+  fsys_t active_control = MACH_PORT_NULL;
+
+  /* Flags to pass to file_set_translator.  */
+  int active_flags = 0;
+  int passive_flags = 0;
+  int lookup_flags = flags->lookup_flags;
+  int goaway_flags = flags->goaway_flags;
+
+  /* Various option flags.  */
+  int passive = flags->passive;
+  int active = flags->active;
+  int keep_active = flags->keep_active;
+  int pause = flags->pause;
+  int kill_active = flags->kill_active;
+  int orphan = flags->orphan;
+  int start = flags->start;
+  int stack = flags->stack;
+  char *pid_file = flags->pid_file;
+  int excl = flags->excl;
+  int timeout = flags->timeout; /* ms */
+  char *underlying_node_name = flags->underlying_node_name;
+  int underlying_lookup_flags = flags->underlying_lookup_flags;
+  char **chroot_command = flags->chroot_command;
+  char *chroot_chdir = flags->chroot_chdir;
+
+  if (stack)
+    {
+      underlying_node_name = node_name;
+      underlying_lookup_flags = lookup_flags && ~O_NOTRANS;
+    }
+  else
+    underlying_lookup_flags = lookup_flags;
+
+
+  if (!active && !passive && !chroot_command)
+    passive = 1;		/* By default, set the passive translator.  */
+
+  if (passive)
+    passive_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0);
+  if (active)
+    active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0)
+      | (orphan ? FS_TRANS_ORPHAN : 0);
+
+  if (passive && !active)
+    {
+      /* When setting just the passive, decide what to do with any active.  */
+      if (kill_active)
+        /* Make it go away.  */
+        active_flags = FS_TRANS_SET;
+      else if (! keep_active)
+        /* Ensure that there isn't one.  */
+        active_flags = FS_TRANS_SET | FS_TRANS_EXCL;
+    }
+
+  if (start)
+    {
+      /* Retrieve the passive translator record in argz.  */
+      mach_port_t node = file_name_lookup (node_name, lookup_flags, 0);
+      if (node == MACH_PORT_NULL)
+        error (4, errno, "%s", node_name);
+
+      char buf[1024];
+      argz = buf;
+      argz_len = sizeof (buf);
+
+      err = file_get_translator (node, &argz, &argz_len);
+      if (err == EINVAL)
+        error (4, 0, "%s: no passive translator record found", node_name);
+      if (err)
+        error (4, err, "%s", node_name);
+
+      mach_port_deallocate (mach_task_self (), node);
+    }
+
+  if ((active || chroot_command) && argz_len > 0)
+    {
+      /* Error during file lookup; we use this to avoid duplicating error
+         messages.  */
+      error_t open_err = 0;
+
+      /* The callback to start_translator opens NODE as a side effect.  */
+      error_t open_node (int flags,
+                         mach_port_t *underlying,
+                         mach_msg_type_name_t *underlying_type,
+                         task_t task, void *cookie)
+      {
+        if (pause)
+          {
+            fprintf (stderr, "Translator pid: %d\nPausing...",
+                     task2pid (task));
+            getchar ();
+          }
+
+        if (pid_file != NULL)
+          {
+            FILE *h;
+            h = fopen (pid_file, "w");
+            if (h == NULL)
+              error (4, errno, "Failed to open pid file");
+
+            fprintf (h, "%i\n", task2pid (task));
+            fclose (h);
+          }
+
+        node = file_name_lookup (node_name, flags | lookup_flags, 0666);
+        if (node == MACH_PORT_NULL)
+          {
+            open_err = errno;
+            return open_err;
+          }
+
+        if (underlying_node_name)
+          {
+            *underlying = file_name_lookup (underlying_node_name,
+                                            flags | underlying_lookup_flags,
+                                            0666);
+            if (! MACH_PORT_VALID (*underlying))
+              {
+                /* For the error message.  */
+                node_name = underlying_node_name;
+                open_err = errno;
+                return open_err;
+              }
+          }
+        else
+          *underlying = node;
+        *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+        return 0;
+      }
+      err = fshelp_start_translator (open_node, NULL, argz, argz, argz_len,
+                                     timeout, &active_control);
+      if (err)
+        /* If ERR is due to a problem opening the translated node, we print
+           that name, otherwise, the name of the translator.  */
+        error(4, err, "%s", (err == open_err) ? node_name : argz);
+    }
+  else
+    {
+      node = file_name_lookup(node_name, lookup_flags, 0666);
+      if (node == MACH_PORT_NULL)
+        error(1, errno, "%s", node_name);
+    }
+
+  if (active || passive)
+    {
+      err = file_set_translator (node,
+                                 passive_flags, active_flags, goaway_flags,
+                                 argz, argz_len,
+                                 active_control, MACH_MSG_TYPE_COPY_SEND);
+      if (err)
+        {
+          error (5, err, "%s", node_name);
+        }
+
+    }
+
+  if (chroot_command)
+    {
+      pid_t child;
+      int status;
+      switch ((child = fork ()))
+        {
+        case -1:
+          error (6, errno, "fork");
+
+        case 0:; /* Child.  */
+          /* We will act as the parent filesystem would for a lookup
+             of the active translator's root node, then use this port
+             as our root directory while we exec the command.  */
+
+          char retry_name[1024];	/* XXX */
+          retry_type do_retry;
+          mach_port_t root;
+          file_t executable;
+          char *prefixed_name;
+
+          err = get_credentials ();
+          if (err)
+            error (6, err, "getting credentials");
+
+          err = fsys_getroot (active_control,
+                              MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
+                              uids, uids_len, gids, gids_len, 0,
+                              &do_retry, retry_name, &root);
+          mach_port_deallocate (mach_task_self (), active_control);
+          if (err)
+            error (6, err, "fsys_getroot");
+          err = hurd_file_name_lookup_retry (&_hurd_ports_use, &getdport, 0,
+                                             do_retry, retry_name, 0, 0,
+                                             &root);
+          if (err)
+            error (6, err, "cannot resolve root port");
+
+          if (setcrdir (root))
+            error (7, errno, "cannot install root port");
+          mach_port_deallocate (mach_task_self (), root);
+          if (chdir (chroot_chdir))
+            error (8, errno, "%s", chroot_chdir);
+
+          /* Lookup executable in PATH.  */
+          executable = file_name_path_lookup (chroot_command[0],
+                                              getenv ("PATH"),
+                                              O_EXEC, 0,
+                                              &prefixed_name);
+          if (MACH_PORT_VALID (executable))
+            {
+              err = mach_port_deallocate (mach_task_self (), executable);
+              assert_perror (err);
+              if (prefixed_name)
+                chroot_command[0] = prefixed_name;
+            }
+
+          execvp (chroot_command[0], chroot_command);
+          error (8, errno, "cannot execute %s", chroot_command[0]);
+          break;
+
+        default: /* Parent.  */
+          if (waitpid (child, &status, 0) != child)
+            error (8, errno, "waitpid on %d", child);
+
+          err = fsys_goaway (active_control, goaway_flags);
+          if (err && err != EBUSY)
+            error (9, err, "fsys_goaway");
+
+          if (WIFSIGNALED (status))
+            error (WTERMSIG (status) + 128, 0,
+                   "%s for child %d", strsignal (WTERMSIG (status)), child);
+          if (WEXITSTATUS (status) != 0)
+            error (WEXITSTATUS (status), 0,
+                   "Error %d for child %d", WEXITSTATUS (status), child);
+        }
+    }
+  return 0;
+}
diff --git a/utils/Makefile b/utils/Makefile
index d2ef9e8..be10cf6 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -34,7 +34,7 @@ SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
 	nullauth.c match-options.c msgids.c rpcscan.c
 
 OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
-HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
+HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc hurdutil
 LDLIBS += -lpthread
 login-LDLIBS = -lutil -lcrypt
 addauth-LDLIBS = -lcrypt
@@ -60,7 +60,7 @@ storeinfo storecat storeread: ../libstore/libstore.a
 ftpcp ftpdir: ../libftpconn/libftpconn.a
 mount umount: ../libihash/libihash.a
 settrans: ../libfshelp/libfshelp.a ../libihash/libihash.a \
-	../libports/libports.a
+	../libports/libports.a ../libhurdutil/libhurdutil.a
 ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \
   devprobe vminfo addauth rmauth setauth unsu ftpcp ftpdir storeread \
   storecat msgport mount umount nullauth rpctrace: \
diff --git a/utils/settrans.c b/utils/settrans.c
index e01906b..9c6eba1 100644
--- a/utils/settrans.c
+++ b/utils/settrans.c
@@ -1,8 +1,9 @@
 /* Set a file's translator.
 
    Copyright (C) 1995,96,97,98,2001,02,13,14
-     Free Software Foundation, Inc.
+   Free Software Foundation, Inc.
    Written by Miles Bader <mi...@gnu.org>
+   Revised by Manolis Fragkiskos Ragkousis <manolis...@gmail.com>.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -38,424 +39,181 @@
 #include <hurd/lookup.h>
 #include <hurd/fsys.h>
 
-
-const char *argp_program_version = STANDARD_HURD_VERSION (settrans);
-
-#define DEFAULT_TIMEOUT 60
+#include <hurd/hurdutil.h>
 
 #define _STRINGIFY(arg) #arg
 #define STRINGIFY(arg) _STRINGIFY (arg)
 
 #define OPT_CHROOT_CHDIR	-1
+#define OPT_STACK           -2
 
 static struct argp_option options[] =
-{
-  {"active",      'a', 0, 0, "Start TRANSLATOR and set it as NODE's active translator" },
-  {"start",       's', 0, 0, "Start the translator specified by the NODE's passive translator record and set it as NODE's active translator" },
-  {"passive",     'p', 0, 0, "Change NODE's passive translator record (default)" },
-  {"create",      'c', 0, 0, "Create NODE if it doesn't exist" },
-  {"dereference", 'L', 0, 0, "If a translator exists, put the new one on top"},
-  {"pid-file",    'F', "FILENAME", 0, "When starting an active translator,"
+  {
+    {"active",      'a', 0, 0, "Start TRANSLATOR and set it as NODE's active translator" },
+    {"start",       's', 0, 0, "Start the translator specified by the NODE's passive translator record and set it as NODE's active translator" },
+    {"passive",     'p', 0, 0, "Change NODE's passive translator record (default)" },
+    {"create",      'c', 0, 0, "Create NODE if it doesn't exist" },
+    {"dereference", 'L', 0, 0, "If a translator exists, put the new one on top"},
+    {"pid-file",    'F', "FILENAME", 0, "When starting an active translator,"
      " write its pid to this file"},
-  {"pause",       'P', 0, 0, "When starting an active translator, prompt and"
+    {"pause",       'P', 0, 0, "When starting an active translator, prompt and"
      " wait for a newline on stdin before completing the startup handshake"},
-  {"timeout",     't',"SEC",0, "Timeout for translator startup, in seconds"
+    {"timeout",     't',"SEC",0, "Timeout for translator startup, in seconds"
      " (default " STRINGIFY (DEFAULT_TIMEOUT) "); 0 means no timeout"},
-  {"exclusive",   'x', 0, 0, "Only set the translator if there is not one already"},
-  {"orphan",      'o', 0, 0, "Disconnect old translator from the filesystem "
-			     "(do not ask it to go away)"},
-  {"underlying",  'U', "NODE", 0, "Open NODE and hand it to the translator "
-				  "as the underlying node"},
-
-  {"chroot",      'C', 0, 0,
-   "Instead of setting the node's translator, take following arguments up to"
-   " `--' and run that command chroot'd to the translated node."},
-  {"chroot-chdir",      OPT_CHROOT_CHDIR, "DIR", 0,
-   "Change to DIR before running the chrooted command.  "
-   "DIR must be an absolute path."},
-
-  {0,0,0,0, "When setting the passive translator, if there's an active translator:"},
-  {"goaway",      'g', 0, 0, "Ask the active translator to go away"},
-  {"keep-active", 'k', 0, 0, "Leave any existing active translator running"},
-
-  {0,0,0,0, "When an active translator is told to go away:"},
-  {"recursive",   'R', 0, 0, "Shutdown its children too"},
-  {"force",       'f', 0, 0, "Ask it to ignore current users and shutdown "
-			     "anyway." },
-  {"nosync",      'S', 0, 0, "Don't sync it before killing it"},
-
-  {0, 0}
-};
+    {"exclusive",   'x', 0, 0, "Only set the translator if there is not one already"},
+    {"orphan",      'o', 0, 0, "Disconnect old translator from the filesystem "
+     "(do not ask it to go away)"},
+    {"underlying",  'U', "NODE", 0, "Open NODE and hand it to the translator "
+     "as the underlying node"},
+    {"stack", OPT_STACK, 0, 0, "Replace an existing translator, but keep it "
+     "running, and put the new one on top"},
+
+    {"chroot",      'C', 0, 0,
+     "Instead of setting the node's translator, take following arguments up to"
+     " `--' and run that command chroot'd to the translated node."},
+    {"chroot-chdir",      OPT_CHROOT_CHDIR, "DIR", 0,
+     "Change to DIR before running the chrooted command.  "
+     "DIR must be an absolute path."},
+
+    {0,0,0,0, "When setting the passive translator, if there's an active translator:"},
+    {"goaway",      'g', 0, 0, "Ask the active translator to go away"},
+    {"keep-active", 'k', 0, 0, "Leave any existing active translator running"},
+
+    {0,0,0,0, "When an active translator is told to go away:"},
+    {"recursive",   'R', 0, 0, "Shutdown its children too"},
+    {"force",       'f', 0, 0, "Ask it to ignore current users and shutdown "
+     "anyway." },
+    {"nosync",      'S', 0, 0, "Don't sync it before killing it"},
+
+    {0, 0}
+  };
 static char *args_doc = "NODE [TRANSLATOR ARG...]";
 static char *doc = "Set the passive/active translator on NODE."
-"\vBy default the passive translator is set.";
+  "\vBy default the passive translator is set.";
 
-/* Authentication of the current process.  */
-uid_t *uids;
-gid_t *gids;
-size_t uids_len, gids_len;
-
-/* Initialize and populate the uids and gids vectors.  */
-error_t
-get_credentials (void)
-{
-  /* Fetch uids...  */
-  uids_len = geteuids (0, 0);
-  if (uids_len < 0)
-    return errno;
-
-  uids = malloc (uids_len * sizeof (uid_t));
-  if (! uids)
-    return ENOMEM;
-
-  uids_len = geteuids (uids_len, uids);
-  if (uids_len < 0)
-    return errno;
-
-  /* ... and gids.  */
-  gids_len = getgroups (0, 0);
-  if (gids_len < 0)
-    return errno;
-
-  gids = malloc (gids_len * sizeof (gid_t));
-  if (! uids)
-    return ENOMEM;
 
-  gids_len = getgroups (gids_len, gids);
-  if (gids_len < 0)
-    return errno;
-
-  return 0;
-}
-
-/* ---------------------------------------------------------------- */
 
 int
 main(int argc, char *argv[])
 {
   error_t err;
 
-  /* The filesystem node we're putting a translator on.  */
-  char *node_name = 0;
-  file_t node;
-
-  /* The translator's arg vector, in '\0' separated format.  */
-  char *argz = 0;
-  size_t argz_len = 0;
-
-  /* The control port for any active translator we start up.  */
-  fsys_t active_control = MACH_PORT_NULL;
-
-  /* Flags to pass to file_set_translator.  */
-  int active_flags = 0;
-  int passive_flags = 0;
-  int lookup_flags = O_NOTRANS;
-  int goaway_flags = 0;
-
-  /* Various option flags.  */
-  int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0,
-      orphan = 0;
-  int start = 0;
-  char *pid_file = NULL;
-  int excl = 0;
-  int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
-  char *underlying_node_name = NULL;
-  char **chroot_command = 0;
-  char *chroot_chdir = "/";
+  /* The flags to be used to create the translator. */
+  settrans_flags_t flags;
+
+  settrans_flags_create(&flags);
 
   /* Parse our options...  */
   error_t parse_opt (int key, char *arg, struct argp_state *state)
-    {
-      switch (key)
-	{
-	case ARGP_KEY_ARG:
-	  if (state->arg_num == 0)
-	    node_name = arg;
-	  else			/* command */
-	    {
-	      if (start)
-		argp_error (state, "both --start and TRANSLATOR given");
-
-	      error_t err =
-		argz_create (state->argv + state->next - 1, &argz, &argz_len);
-	      if (err)
-		error(3, err, "Can't create options vector");
-	      state->next = state->argc; /* stop parsing */
-	    }
-	  break;
-
-	case ARGP_KEY_NO_ARGS:
-	  argp_usage (state);
-	  return EINVAL;
-
-	case 'a': active = 1; break;
-	case 's':
-	  start = 1;
-	  active = 1;	/* start implies active */
-	  break;
-	case 'p': passive = 1; break;
-	case 'k': keep_active = 1; break;
-	case 'g': kill_active = 1; break;
-	case 'x': excl = 1; break;
-	case 'P': pause = 1; break;
-	case 'F':
-	  pid_file = strdup (arg);
-	  if (pid_file == NULL)
-	    error(3, ENOMEM, "Failed to duplicate argument");
-	  break;
-
-	case 'o': orphan = 1; break;
-	case 'U':
-	  underlying_node_name = strdup (arg);
-	  if (underlying_node_name == NULL)
-	    error(3, ENOMEM, "Failed to duplicate argument");
-	  break;
-
-	case 'C':
-	  if (chroot_command)
-	    {
-	      argp_error (state, "--chroot given twice");
-	      return EINVAL;
-	    }
-	  chroot_command = &state->argv[state->next];
-	  while (state->next < state->argc)
-	    {
-	      if (!strcmp (state->argv[state->next], "--"))
-		{
-		  state->argv[state->next++] = 0;
-		  if (chroot_command[0] == 0)
-		    {
-		      argp_error (state,
-				  "--chroot must be followed by a command");
-		      return EINVAL;
-		    }
-		  return 0;
-		}
-	      ++state->next;
-	    }
-	  argp_error (state, "--chroot command must be terminated with `--'");
-	  return EINVAL;
-
-	case OPT_CHROOT_CHDIR:
-	  if (arg[0] != '/')
-	    argp_error (state, "--chroot-chdir must be absolute");
-	  chroot_chdir = arg;
-	  break;
-
-	case 'c': lookup_flags |= O_CREAT; break;
-	case 'L': lookup_flags &= ~O_NOTRANS; break;
-
-	case 'R': goaway_flags |= FSYS_GOAWAY_RECURSE; break;
-	case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break;
-	case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break;
-
-	  /* Use atof so the user can specifiy fractional timeouts.  */
-	case 't': timeout = atof (arg) * 1000.0; break;
-
-	default:
-	  return ARGP_ERR_UNKNOWN;
-	}
-      return 0;
-    }
+  {
+    switch (key)
+      {
+      case ARGP_KEY_ARG:
+        if (state->arg_num == 0)
+          flags->node_name = arg;
+        else			/* command */
+          {
+            if (flags->start)
+              argp_error (state, "both --start and TRANSLATOR given");
+
+            error_t err =
+              argz_create (state->argv + state->next - 1, &flags->argz, &flags->argz_len);
+            if (err)
+              error(3, err, "Can't create options vector");
+            state->next = state->argc; /* stop parsing */
+          }
+        break;
+
+      case ARGP_KEY_NO_ARGS:
+        argp_usage (state);
+        return EINVAL;
+
+      case 'a': flags->active = 1; break;
+      case 's':
+        flags->start = 1;
+        flags->active = 1;	/* start implies active */
+        break;
+      case OPT_STACK:
+        flags->stack = 1;
+        flags->active = 1;   /* stack implies active */
+        flags->orphan = 1;   /* stack implies orphan */
+        break;
+      case 'p': flags->passive = 1; break;
+      case 'k': flags->keep_active = 1; break;
+      case 'g': flags->kill_active = 1; break;
+      case 'x': flags->excl = 1; break;
+      case 'P': flags->pause = 1; break;
+      case 'F':
+        flags->pid_file = strdup (arg);
+        if (flags->pid_file == NULL)
+          error(3, ENOMEM, "Failed to duplicate argument");
+        break;
+
+      case 'o': flags->orphan = 1; break;
+      case 'U':
+        flags->underlying_node_name = strdup (arg);
+        if (flags->underlying_node_name == NULL)
+          error(3, ENOMEM, "Failed to duplicate argument");
+        break;
+
+      case 'C':
+        if (flags->chroot_command)
+          {
+            argp_error (state, "--chroot given twice");
+            return EINVAL;
+          }
+        flags->chroot_command = &state->argv[state->next];
+        while (state->next < state->argc)
+          {
+            if (!strcmp (state->argv[state->next], "--"))
+              {
+                state->argv[state->next++] = 0;
+                if (flags->chroot_command[0] == 0)
+                  {
+                    argp_error (state,
+                                "--chroot must be followed by a command");
+                    return EINVAL;
+                  }
+                return 0;
+              }
+            ++state->next;
+          }
+        argp_error (state, "--chroot command must be terminated with `--'");
+        return EINVAL;
+
+      case OPT_CHROOT_CHDIR:
+        if (arg[0] != '/')
+          argp_error (state, "--chroot-chdir must be absolute");
+        flags->chroot_chdir = arg;
+        break;
+
+      case 'c': flags->lookup_flags |= O_CREAT; break;
+      case 'L': flags->lookup_flags &= ~O_NOTRANS; break;
+
+      case 'R': flags->goaway_flags |= FSYS_GOAWAY_RECURSE; break;
+      case 'S': flags->goaway_flags |= FSYS_GOAWAY_NOSYNC; break;
+      case 'f': flags->goaway_flags |= FSYS_GOAWAY_FORCE; break;
+
+        /* Use atof so the user can specifiy fractional timeouts.  */
+      case 't': flags->timeout = atof (arg) * 1000.0; break;
+
+      default:
+        return ARGP_ERR_UNKNOWN;
+      }
+    return 0;
+  }
   struct argp argp = {options, parse_opt, args_doc, doc};
 
   argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
 
-  if (!active && !passive && !chroot_command)
-    passive = 1;		/* By default, set the passive translator.  */
-
-  if (passive)
-    passive_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0);
-  if (active)
-    active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0)
-		   | (orphan ? FS_TRANS_ORPHAN : 0);
-
-  if (passive && !active)
-    {
-      /* When setting just the passive, decide what to do with any active.  */
-      if (kill_active)
-	/* Make it go away.  */
-	active_flags = FS_TRANS_SET;
-      else if (! keep_active)
-	/* Ensure that there isn't one.  */
-	active_flags = FS_TRANS_SET | FS_TRANS_EXCL;
-    }
-
-  if (start)
+  err = settrans(flags);
+  if (err)
     {
-      /* Retrieve the passive translator record in argz.  */
-      mach_port_t node = file_name_lookup (node_name, lookup_flags, 0);
-      if (node == MACH_PORT_NULL)
-	error (4, errno, "%s", node_name);
-
-      char buf[1024];
-      argz = buf;
-      argz_len = sizeof (buf);
-
-      err = file_get_translator (node, &argz, &argz_len);
-      if (err == EINVAL)
-	error (4, 0, "%s: no passive translator record found", node_name);
-      if (err)
-	error (4, err, "%s", node_name);
-
-      mach_port_deallocate (mach_task_self (), node);
+      settrans_flags_cleanup(flags);
+      error(1, err, "Could not set translator");
     }
 
-  if ((active || chroot_command) && argz_len > 0)
-    {
-      /* Error during file lookup; we use this to avoid duplicating error
-	 messages.  */
-      error_t open_err = 0;
-
-      /* The callback to start_translator opens NODE as a side effect.  */
-      error_t open_node (int flags,
-			 mach_port_t *underlying,
-			 mach_msg_type_name_t *underlying_type,
-			 task_t task, void *cookie)
-	{
-	  if (pause)
-	    {
-	      fprintf (stderr, "Translator pid: %d\nPausing...",
-	               task2pid (task));
-	      getchar ();
-	    }
-
-	  if (pid_file != NULL)
-	    {
-	      FILE *h;
-	      h = fopen (pid_file, "w");
-	      if (h == NULL)
-		error (4, errno, "Failed to open pid file");
-
-	      fprintf (h, "%i\n", task2pid (task));
-	      fclose (h);
-	    }
-
-	  node = file_name_lookup (node_name, flags | lookup_flags, 0666);
-	  if (node == MACH_PORT_NULL)
-	    {
-	      open_err = errno;
-	      return open_err;
-	    }
-
-	  if (underlying_node_name)
-	    {
-	      *underlying = file_name_lookup (underlying_node_name,
-					      flags | lookup_flags, 0666);
-	      if (! MACH_PORT_VALID (*underlying))
-		{
-		  /* For the error message.  */
-		  node_name = underlying_node_name;
-		  open_err = errno;
-		  return open_err;
-		}
-	    }
-	  else
-	    *underlying = node;
-	  *underlying_type = MACH_MSG_TYPE_COPY_SEND;
-
-	  return 0;
-	}
-      err = fshelp_start_translator (open_node, NULL, argz, argz, argz_len,
-				     timeout, &active_control);
-      if (err)
-	/* If ERR is due to a problem opening the translated node, we print
-	   that name, otherwise, the name of the translator.  */
-	error(4, err, "%s", (err == open_err) ? node_name : argz);
-    }
-  else
-    {
-      node = file_name_lookup(node_name, lookup_flags, 0666);
-      if (node == MACH_PORT_NULL)
-	error(1, errno, "%s", node_name);
-    }
-
-  if (active || passive)
-    {
-      err = file_set_translator (node,
-				 passive_flags, active_flags, goaway_flags,
-				 argz, argz_len,
-				 active_control, MACH_MSG_TYPE_COPY_SEND);
-      if (err)
-	error (5, err, "%s", node_name);
-    }
-
-  if (chroot_command)
-    {
-      pid_t child;
-      int status;
-      switch ((child = fork ()))
-	{
-	case -1:
-	  error (6, errno, "fork");
-
-	case 0:; /* Child.  */
-	  /* We will act as the parent filesystem would for a lookup
-	     of the active translator's root node, then use this port
-	     as our root directory while we exec the command.  */
-
-	  char retry_name[1024];	/* XXX */
-	  retry_type do_retry;
-	  mach_port_t root;
-	  file_t executable;
-	  char *prefixed_name;
-
-	  err = get_credentials ();
-	  if (err)
-	    error (6, err, "getting credentials");
-
-	  err = fsys_getroot (active_control,
-			      MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
-			      uids, uids_len, gids, gids_len, 0,
-			      &do_retry, retry_name, &root);
-	  mach_port_deallocate (mach_task_self (), active_control);
-	  if (err)
-	    error (6, err, "fsys_getroot");
-	  err = hurd_file_name_lookup_retry (&_hurd_ports_use, &getdport, 0,
-					     do_retry, retry_name, 0, 0,
-					     &root);
-	  if (err)
-	    error (6, err, "cannot resolve root port");
-
-	  if (setcrdir (root))
-	    error (7, errno, "cannot install root port");
-	  mach_port_deallocate (mach_task_self (), root);
-	  if (chdir (chroot_chdir))
-	    error (8, errno, "%s", chroot_chdir);
-
-	  /* Lookup executable in PATH.  */
-	  executable = file_name_path_lookup (chroot_command[0],
-					      getenv ("PATH"),
-					      O_EXEC, 0,
-					      &prefixed_name);
-	  if (MACH_PORT_VALID (executable))
-	    {
-	      err = mach_port_deallocate (mach_task_self (), executable);
-	      assert_perror (err);
-	      if (prefixed_name)
-		chroot_command[0] = prefixed_name;
-	    }
-
-	  execvp (chroot_command[0], chroot_command);
-	  error (8, errno, "cannot execute %s", chroot_command[0]);
-	  break;
-
-	default: /* Parent.  */
-	  if (waitpid (child, &status, 0) != child)
-	    error (8, errno, "waitpid on %d", child);
-
-	  err = fsys_goaway (active_control, goaway_flags);
-	  if (err && err != EBUSY)
-	    error (9, err, "fsys_goaway");
-
-	  if (WIFSIGNALED (status))
-	    error (WTERMSIG (status) + 128, 0,
-		   "%s for child %d", strsignal (WTERMSIG (status)), child);
-	  if (WEXITSTATUS (status) != 0)
-	    error (WEXITSTATUS (status), 0,
-		   "Error %d for child %d", WEXITSTATUS (status), child);
-	}
-    }
+  settrans_flags_cleanup(flags);
 
   return 0;
 }
-- 
2.8.2

Reply via email to