Whamcloud - gitweb
b=20668
[fs/lustre-release.git] / libsysio / src / init.c
index 38d0794..0708a96 100644 (file)
@@ -9,7 +9,7 @@
  *    terms of the GNU Lesser General Public License
  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
  *
- *    Cplant(TM) Copyright 1998-2004 Sandia Corporation. 
+ *    Cplant(TM) Copyright 1998-2006 Sandia Corporation. 
  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
  *    license for use of this work by or on behalf of the US Government.
  *    Export of this program may require a license from the United States
  * lee@sandia.gov
  */
 
+#ifdef __linux__
 #define _BSD_SOURCE
+#endif
 
+#ifdef SYSIO_TRACING
+#include <stdio.h>
+#endif
 #include <stdlib.h>
+#if defined(_BSD_SOURCE) || defined(SYSIO_TRACING)
+#include <sys/syscall.h>
+#endif
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#ifdef SYSIO_TRACING
+#include <stdarg.h>
+#endif
 #include <limits.h>
 #include <assert.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/queue.h>
-#include <sys/uio.h>
-
-#include <sys/stat.h>
 #include <fcntl.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
 
 #include "sysio.h"
+#include "xtio.h"
+#ifdef SYSIO_TRACING
+#include "native.h"
+#endif
 #include "inode.h"
 #include "fs.h"
 #include "mount.h"
 #include "file.h"
 #include "dev.h"
-#include "xtio.h"
 
 #ifdef STDFD_DEV
 #include "stdfd.h"
 #endif
 
+#ifdef SYSIO_TRACING
+
 /*
- * The namespace assembly buffer passes args with a `name'=`value'
- * syntax. We use the following to record that in various
- * routines below.
+ * Tracing callback record.
  */
-struct named_argument {
-       const char *name;                               /* arg name */
-       char    *value;                                 /* arg value */
+struct trace_callback {
+       TAILQ_ENTRY(trace_callback) links;              /* trace list links */
+       void    (*f)(const char *file,                  /* callback function */
+                    const char *func,
+                    int line,
+                    void *data);
+       void    *data;                                  /* callback data */
+       void    (*destructor)(void *data);              /* data destructor */
 };
 
 /*
+ * Initialize a tracing callback record.
+ */
+#define TCB_INIT(__tcb, __f, __d, __destroy) \
+       do { \
+               (__tcb)->f = (__f); \
+               (__tcb)->data = (__d); \
+               (__tcb)->destructor = (__destroy); \
+       } while (0)
+
+/*
+ * Trace queue head record.
+ */
+TAILQ_HEAD(trace_q, trace_callback);
+
+/*
+ * The entry and exit queue heads, and queue pointers.
+ */
+static struct trace_q _sysio_entry_trace_head;
+void   *_sysio_entry_trace_q = &_sysio_entry_trace_head;
+static struct trace_q _sysio_exit_trace_head;
+void   *_sysio_exit_trace_q = &_sysio_exit_trace_head;
+#endif
+
+/*
  * White space characters.
  */
 #define IGNORE_WHITE           " \t\r\n"
 
 /*
+ * Check if long overflows integer range.
+ */
+#if LONG_MAX <= INT_MAX
+#define _irecheck(_l, _e) \
+       ((_l) == LONG_MAX && (_e) == ERANGE)
+#else
+#define _irecheck(_l, _e) \
+       ((_l) > INT_MAX)
+#endif
+
+/*
+ * In sysio_init we'll allow simple comments, strings outside {}
+ * delimited by COMMENT_INTRO, and '\n' or '\0'
+ */
+#define COMMENT_INTRO          '#'
+
+/*
+ * In sysio_init we'll allow simple comments, strings outside {}
+ * delimited by COMMENT_INTRO, and '\n' or '\0'
+ */
+#define COMMENT_INTRO          '#'
+
+/*
  * Sysio library initialization. Must be called before anything else in the
  * library.
  */
@@ -93,7 +157,15 @@ _sysio_init()
 {
        int     err;
 #ifdef WITH_SOCKETS
-       int     _sysio_sockets_init(void);
+       extern int _sysio_sockets_init(void);
+#endif
+
+#ifdef SYSIO_TRACING
+       /*
+        * Initialize tracing callback queues.
+        */
+       TAILQ_INIT(&_sysio_entry_trace_head);
+       TAILQ_INIT(&_sysio_exit_trace_head);
 #endif
 
        err = _sysio_ioctx_init();
@@ -115,9 +187,9 @@ _sysio_init()
                goto error;
 #endif
 #ifdef WITH_SOCKETS
-       err = _sysio_sockets_init();
-       if (err)
-               goto error;
+        err = _sysio_sockets_init();
+        if (err)
+                goto error;
 #endif
 
        goto out;
@@ -142,13 +214,190 @@ _sysio_shutdown()
              _sysio_unmount_all() == 0))
                        abort();
 
-#if ZERO_SUM_MEMORY
+#ifdef ZERO_SUM_MEMORY
        _sysio_fd_shutdown();
        _sysio_i_shutdown();
        _sysio_fssw_shutdown();
+       _sysio_access_shutdown();
+#ifdef SYSIO_TRACING
+       {
+               struct trace_callback *tcb;
+
+               /*
+                * Empty the trace queues and free the entries.
+                */
+               while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL)
+                       _sysio_remove_trace(&_sysio_entry_trace_head, tcb);
+               while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL)
+                       _sysio_remove_trace(&_sysio_exit_trace_head, tcb);
+       }
+#endif
 #endif
 }
 
+#ifdef SYSIO_TRACING
+
+#if !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF)
+/*
+ * Print a string to allocated memory.
+ */
+static int
+vasprintf(char **strp, const char *fmt, va_list ap)
+{
+       size_t  siz;
+       int     oerrno;
+       char    *s;
+       va_list aq;
+       int     n;
+
+       siz = 50;
+       oerrno = errno;
+       if (!(s = malloc(siz))) {
+               errno = oerrno;
+               return -1;
+       }
+       for (;;) {
+               va_copy(aq, ap);
+               n = vsnprintf (s, siz, fmt, aq);
+               va_end(aq);
+               if (n > -1 && (size_t )n < siz)
+                       break;
+               if (n > -1)                             /* glibc 2.1 */
+                       siz = n+1;                      /* precise */
+               else                                    /* glibc 2.0 */
+                       siz *= 2;                       /* twice the old */
+               if (!(s = realloc (s, siz)))
+                       break;
+       }
+       *strp = s;
+       errno = oerrno;
+       return n;
+}
+
+#if 0
+static int
+asprintf(char **strp, const char *fmt, ...)
+{
+       va_list ap;
+       int     n;
+
+       va_start(ap, fmt);
+       n = vasprintf(strp, fmt, ap);
+       va_end(ap);
+       return n;
+}
+#endif
+#endif /* !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF) */
+
+static void
+_sysio_cwrite(const char *buf, size_t len)
+{
+       int     oerrno;
+
+       oerrno = errno;
+       (void )syscall(SYSIO_SYS_write, STDERR_FILENO, buf, len);
+       errno = oerrno;
+}
+
+/*
+ * Console printf.
+ */
+void
+_sysio_cprintf(const char *fmt, ...)
+{
+       va_list ap;
+       int     len;
+       char    *buf;
+
+       va_start(ap, fmt);
+       buf = NULL;
+       len = vasprintf(&buf, fmt, ap);
+       va_end(ap);
+       if (len < 0)
+               return;
+       _sysio_cwrite(buf, len);
+       free(buf);
+}
+
+/*
+ * Register a trace callback.
+ *
+ * The pointer to the trace record is returned.
+ */
+void *
+_sysio_register_trace(void *q,
+                     void (*f)(const char *file,
+                               const char *func,
+                               int line,
+                               void *data),
+                     void *data,
+                     void (*destructor)(void *data))
+{
+       struct trace_callback *tcb;
+
+       tcb = malloc(sizeof(struct trace_callback));
+       if (!tcb)
+               return NULL;
+       TCB_INIT(tcb, f, data, destructor);
+       TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
+       return tcb;
+}
+
+/*
+ * Remove a registered trace callback.
+ */
+void
+_sysio_remove_trace(void *q, void *p)
+{
+       struct trace_callback *tcb;
+
+       tcb = (struct trace_callback *)p;
+
+       if (tcb->destructor)
+               (*tcb->destructor)(tcb->data);
+       TAILQ_REMOVE((struct trace_q *)q, tcb, links);
+       free(tcb);
+}
+
+void
+/*
+ * Run a trace queue, making all the callbacks.
+ */
+_sysio_run_trace_q(void *q,
+                  const char *file,
+                  const char *func,
+                  int line)
+{
+       struct trace_callback *tcb;
+
+       tcb = ((struct trace_q *)q)->tqh_first;
+       while (tcb) {
+               (*tcb->f)(file, func, line, tcb->data);
+               tcb = tcb->links.tqe_next;
+       }
+}
+
+static void
+_sysio_trace_entry(const char *file __IS_UNUSED,
+                  const char *func,
+                  int line __IS_UNUSED,
+                  void *data __IS_UNUSED)
+{
+
+       _sysio_cprintf("+ENTER+ %s\n", func);
+}
+
+static void
+_sysio_trace_exit(const char *file __IS_UNUSED,
+                 const char *func,
+                 int line __IS_UNUSED,
+                 void *data __IS_UNUSED)
+{
+
+       _sysio_cprintf("+EXIT+ %s\n", func);
+}
+#endif /* defined(SYSIO_TRACING) */
+
 /* 
  * (kind of)Duplicates strtok function.
  *
@@ -162,8 +411,8 @@ _sysio_shutdown()
  * or NUL character is success.
  *
  */
-static const char *
-get_token(const char *buf,
+const char *
+_sysio_get_token(const char *buf,
          int accepts,
          const char *delim,
          const char *ignore,
@@ -212,15 +461,20 @@ get_token(const char *buf,
  *
  * NB: Alters the passed buffer.
  */
-static char *
-get_args(char *buf, struct named_argument *vec)
+char *
+_sysio_get_args(char *buf, struct option_value_info *vec)
 {
        char    *nxt;
        char    *name, *value;
-       struct named_argument *v;
+       struct option_value_info *v;
 
        for (;;) {
-               nxt = (char *)get_token(buf, 1, "=,", IGNORE_WHITE, name = buf);
+               nxt =
+                   (char *)_sysio_get_token(buf,
+                                            1,
+                                            "=,",
+                                            IGNORE_WHITE,
+                                            name = buf);
                if (!nxt ||
                    (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
                        buf = NULL;
@@ -228,15 +482,20 @@ get_args(char *buf, struct named_argument *vec)
                }
                if (*name == '\0')
                        break;
-               buf = (char *)get_token(nxt, 1, ",", IGNORE_WHITE, value = nxt);
+               buf =
+                   (char *)_sysio_get_token(nxt,
+                                            1,
+                                            ",",
+                                            IGNORE_WHITE,
+                                            value = nxt);
                if (*value == '\0')
                        value = NULL;
-               for (v = vec; v->name; v++)
-                       if (strcmp(v->name, name) == 0)
+               for (v = vec; v->ovi_name; v++)
+                       if (strcmp(v->ovi_name, name) == 0)
                                break;
-               if (!v->name)
+               if (!v->ovi_name)
                        return NULL;
-               v->value = value;
+               v->ovi_value = value;
        }
 
        return buf;
@@ -271,7 +530,7 @@ static int
 do_creat(char *args) 
 {
        size_t  len;
-       struct named_argument v[] = {
+       struct option_value_info v[] = {
                { "ft",         NULL },                 /* file type */
                { "nm",         NULL },                 /* name */
                { "pm",         NULL },                 /* permissions */
@@ -289,29 +548,38 @@ do_creat(char *args)
        struct intent intent;
        dev_t   dev;
        int     err;
+       enum {
+               CREATE_DIR      = 1,
+               CREATE_CHR      = 2,
+               CREATE_BLK      = 3,
+               CREATE_FILE     = 4
+       } op;
+       int     intent_mode;
+       struct inode *ino;
+       int     i;
   
        len = strlen(args);
-       if (get_args(args, v) - args != (ssize_t )len ||
-           !(v[0].value &&
-             v[1].value &&
-             v[2].value))
+       if (_sysio_get_args(args, v) - args != (ssize_t )len ||
+           !(v[0].ovi_value &&
+             v[1].ovi_value &&
+             v[2].ovi_value))
                return -EINVAL;
-       perms = strtol(v[2].value, (char **)&cp, 0);
+       perms = strtol(v[2].ovi_value, (char **)&cp, 0);
        if (*cp ||
            perms < 0 ||
            (perms == LONG_MAX && errno == ERANGE) ||
            ((unsigned)perms & ~07777))
                return -EINVAL;
-       if (v[3].value) {
-               owner = strtol(v[3].value, (char **)&cp, 0);
+       if (v[3].ovi_value) {
+               owner = strtol(v[3].ovi_value, (char **)&cp, 0);
                if (*cp ||
                    ((owner == LONG_MIN || owner == LONG_MAX)
                     && errno == ERANGE))
                        return -EINVAL;
        } else
                owner = getuid();
-       if (v[4].value) {
-               group = strtol(v[4].value, (char **)&cp, 0);
+       if (v[4].ovi_value) {
+               group = strtol(v[4].ovi_value, (char **)&cp, 0);
                if (*cp ||
                    ((group == LONG_MIN || group == LONG_MAX) &&
                     errno == ERANGE))
@@ -321,66 +589,66 @@ do_creat(char *args)
 
        if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
                return -ENOENT;
+
+       /*
+        * Init, get the operation, setup the intent.
+        */
        err = 0;
        mode = perms;
-       if (strcmp(v[0].value, "dir") == 0) {
-               INTENT_INIT(&intent, INT_CREAT, &mode, 0);
-               err = _sysio_namei(dir, v[1].value, ND_NEGOK, &intent, &pno);
-               if (err)
-                       return err;
-               if (pno->p_base->pb_ino)
-                       err = -EEXIST;
-               else if (IS_RDONLY(pno->p_parent,
-                                  pno->p_parent->p_base->pb_ino))
-                       err = -EROFS;
-               else {
-                       struct inode *ino;
-
-                       ino = pno->p_parent->p_base->pb_ino;
-                       err = (*ino->i_ops.inop_mkdir)(pno, mode);
-               }
-               P_RELE(pno);
-       } else if (strcmp(v[0].value, "chr") == 0) {
-               if (!(v[5].value && parse_mm(v[5].value, &dev) == 0))
-                       return -EINVAL;
+       op = 0;
+       if (strcmp(v[0].ovi_value, "dir") == 0) {
+               op = CREATE_DIR;
+               INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
+       } else if (strcmp(v[0].ovi_value, "chr") == 0) {
+               op = CREATE_CHR;
                mode |= S_IFCHR;
-               INTENT_INIT(&intent, INT_CREAT, &mode, 0);
-               err = _sysio_namei(dir, v[1].value, ND_NEGOK, &intent, &pno);
-               if (err)
-                       return err;
-               if (pno->p_base->pb_ino)
-                       err = -EEXIST;
-               else if (IS_RDONLY(pno->p_parent,
-                                  pno->p_parent->p_base->pb_ino))
-                       err = -EROFS;
-               else {
-                       struct inode *ino;
-
-                       ino = pno->p_parent->p_base->pb_ino;
-                       err = (*ino->i_ops.inop_mknod)(pno, mode, dev);
-               }
-               P_RELE(pno);
-       } else if (strcmp(v[0].value, "blk") == 0) {
-               /*
-                * We don't support block special files yet.
-                */
-               return -EINVAL;
-       } else if (strcmp(v[0].value, "file") == 0) {
-               int     i;
-               struct inode *ino;
+               INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
+               if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
+                       err = -EINVAL;
+       } else if (strcmp(v[0].ovi_value, "blk") == 0) {
+               op = CREATE_BLK;
+               mode |= S_IFBLK;
+               INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
+               if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
+                       err = -EINVAL;
+       } else if (strcmp(v[0].ovi_value, "file") == 0) {
+               op = CREATE_FILE;
+               intent_mode = O_CREAT|O_EXCL;
+               INTENT_INIT(&intent, INT_CREAT, &mode, &intent_mode);
+       } else
+               err = -EINVAL;
+       if (err)
+               return err;
 
-               i = O_CREAT|O_EXCL;
-               INTENT_INIT(&intent, INT_CREAT, &mode, &i);
-               err = _sysio_namei(dir, v[1].value, ND_NEGOK, &intent, &pno);
-               if (err)
-                       return err;
+       /*
+        * Lookup the given path.
+        */
+       err =
+           _sysio_namei(dir,
+                        v[1].ovi_value,
+                        ND_NEGOK|ND_NOPERMCHECK,
+                        &intent,
+                        &pno);
+       if (err)
+               return err;
+
+       /*
+        * Perform.
+        */
+       switch (op) {
+       case CREATE_DIR:
+               err = _sysio_mkdir(pno, mode);
+               break;
+       case CREATE_CHR:
+       case CREATE_BLK:
+               err = _sysio_mknod(pno, mode, dev);
+               break;
+       case CREATE_FILE:
                err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
-               if (err) {
-                       P_RELE(pno);
-                       return err;
-               }
+               if (err)
+                       break;
                ino = pno->p_base->pb_ino;
-               if (!err && v[6].value) {
+               if (v[6].ovi_value) {
                        struct iovec iovec;
                        struct intnl_xtvec xtvec;
                        struct ioctx io_context;
@@ -388,13 +656,12 @@ do_creat(char *args)
                        /*
                         * Deposit optional file content.
                         */
-                       iovec.iov_base = v[6].value;
-                       iovec.iov_len = strlen(v[6].value);
+                       iovec.iov_base = v[6].ovi_value;
+                       iovec.iov_len = strlen(v[6].ovi_value);
                        xtvec.xtv_off = 0;
                        xtvec.xtv_len = iovec.iov_len;
                        IOCTX_INIT(&io_context,
                                   1,
-                                  (ioid_t )&io_context,
                                   1,
                                   ino,
                                   &iovec, 1,
@@ -417,10 +684,12 @@ do_creat(char *args)
                i = (*ino->i_ops.inop_close)(ino);
                if (!err)
                        err = i;
-               P_RELE(pno);
-       } else 
-               err = -EINVAL;
+               break;
+       default:
+               abort();
+       }
 
+       P_RELE(pno);
        return err;
 }
 
@@ -433,7 +702,7 @@ static int
 do_mnt(char *args) 
 {
        size_t  len;
-       struct named_argument v[] = {
+       struct option_value_info v[] = {
                { "dev",        NULL },                 /* source (type:dev) */
                { "dir",        NULL },                 /* target dir */
                { "fl",         NULL },                 /* flags */
@@ -445,35 +714,46 @@ do_mnt(char *args)
        struct pnode *dir;
   
        len = strlen(args);
-       if (get_args(args, v) - args != (ssize_t )len ||
-           !(v[0].value && v[1].value))
+       if (_sysio_get_args(args, v) - args != (ssize_t )len ||
+           !(v[0].ovi_value && v[1].ovi_value))
                return -EINVAL;
-       ty = (char *)get_token(v[0].value, 1, ":", "", name = v[0].value);
+       ty =
+           (char *)_sysio_get_token(v[0].ovi_value,
+                                    1,
+                                    ":",
+                                    "",
+                                    name = v[0].ovi_value);
        flags = 0;
-       if (v[2].value) {
+       if (v[2].ovi_value) {
                char    *cp;
 
                /*
                 * Optional flags.
                 */
-               flags = strtoul(v[2].value, &cp, 0);
+               flags = strtoul(v[2].ovi_value, &cp, 0);
                if (*cp || (flags == ULONG_MAX && errno == ERANGE))
                        return -EINVAL;
        }
 
-       if (strlen(v[1].value) == 1 && v[1].value[0] == PATH_SEPARATOR) {
+       if (strlen(v[1].ovi_value) == 1 && v[1].ovi_value[0] == PATH_SEPARATOR) {
                /*
                 * Aha! It's root they want. Have to do that special.
                 */
-               return _sysio_mount_root(ty, name, flags, v[3].value);
+               return _sysio_mount_root(ty, name, flags, v[3].ovi_value);
        }
 
        if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
                return -ENOENT;
-       return _sysio_mount(dir, ty, v[1].value, name, flags, v[3].value);
+       return _sysio_mount(dir,
+                           ty,
+                           v[1].ovi_value,
+                           name,
+                           flags,
+                           v[3].ovi_value);
 }
 
 
+#if 0
 /*
  * Chdir
  *
@@ -483,7 +763,7 @@ static int
 do_cd(char *args) 
 {
        size_t  len;
-       struct named_argument v[] = {
+       struct option_value_info v[] = {
                { "dir",        NULL },                 /* directory */
                { NULL,         NULL }
        };
@@ -491,12 +771,17 @@ do_cd(char *args)
        struct pnode *dir, *pno;
 
        len = strlen(args);
-       if (get_args(args, v) - args != (ssize_t )len || !v[0].value)
+       if (_sysio_get_args(args, v) - args != (ssize_t )len || !v[0].ovi_value)
                return -EINVAL;
 
-       if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
+       if (!(dir = _sysio_cwd) && !(dir = _sysio_root)) {
+               /*
+                * We have no namespace yet. They really need to give us
+                * something to work with.
+                */
                return -ENOENT;
-       err = _sysio_namei(dir, v[0].value, 0, NULL, &pno);
+       }
+       err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
        if (err)
                return err;
        err = _sysio_p_chdir(pno);
@@ -504,6 +789,7 @@ do_cd(char *args)
                P_RELE(pno);
        return err;
 }
+#endif
 
 /*
  * Does a chmod
@@ -514,7 +800,7 @@ static int
 do_chmd(char *args)
 {
        size_t  len;
-       struct named_argument v[] = {
+       struct option_value_info v[] = {
                { "src",        NULL },                 /* path */
                { "pm",         NULL },                 /* perms */
                { NULL,         NULL }
@@ -526,10 +812,10 @@ do_chmd(char *args)
        struct pnode *dir, *pno;
   
        len = strlen(args);
-       if (get_args(args, v) - args != (ssize_t )len ||
-           !(v[0].value && v[1].value))
+       if (_sysio_get_args(args, v) - args != (ssize_t )len ||
+           !(v[0].ovi_value && v[1].ovi_value))
                return -EINVAL;
-       perms = strtol(v[1].value, &cp, 0);
+       perms = strtol(v[1].ovi_value, &cp, 0);
        if (*cp ||
            perms < 0 ||
            (perms == LONG_MAX && errno == ERANGE) ||
@@ -540,7 +826,7 @@ do_chmd(char *args)
 
        if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
                return -ENOENT;
-       err = _sysio_namei(dir, v[0].value, 0, NULL, &pno);
+       err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, NULL, &pno);
        if (err)
                return err;
        err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
@@ -549,6 +835,70 @@ do_chmd(char *args)
        return err;
 }
 
+static int
+do_open(char *args)
+{
+       size_t  len;
+       struct option_value_info v[] = {
+               { "nm",         NULL },                 /* path */
+               { "fd",         NULL },                 /* fildes */
+               { "m",          NULL },                 /* mode */
+               { NULL,         NULL }
+       };
+       char    *cp;
+       long    l;
+       int     fd;
+       unsigned long ul;
+       mode_t  m;
+       struct pnode *dir, *pno;
+       struct intent intent;
+       int     err;
+       struct file *fil;
+
+       len = strlen(args);
+       if (_sysio_get_args(args, v) - args != (ssize_t )len ||
+           !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
+               return -EINVAL;
+       l = strtol(v[1].ovi_value, (char **)&cp, 0);
+       if (*cp || l < 0 || _irecheck(l, errno))
+               return -EINVAL;
+       fd = (int )l;
+       ul = strtoul(v[1].ovi_value, (char **)&cp, 0);
+       if (*cp ||
+           (ul == ULONG_MAX && errno == ERANGE))
+               return -EINVAL;
+       m = (mode_t )ul & (O_RDONLY|O_WRONLY|O_RDWR);
+
+       if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
+               return -ENOENT;
+       INTENT_INIT(&intent, INT_OPEN, &m, NULL);
+       pno = NULL;
+       err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, &intent, &pno);
+       if (err)
+               return err;
+       fil = NULL;
+       do {
+               err = _sysio_open(pno, m, 0);
+               if (err)
+                       break;
+               fil = _sysio_fnew(pno->p_base->pb_ino, m);
+               if (!fil) {
+                       err = -ENOMEM;
+                       break;
+               }
+               err = _sysio_fd_set(fil, fd, 1);
+               if (err < 0)
+                       break;
+               P_RELE(pno);
+               return 0;
+       } while (0);
+       if (fil)
+               F_RELE(fil);
+       if (pno)
+               P_RELE(pno);
+       return err;
+}
+
 /*
  * Execute the given cmd.
  *
@@ -561,44 +911,107 @@ do_command(char *buf)
        char    *args, *cmd;
 
        len = strlen(buf);
-       args = (char *)get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
+       args = (char *)_sysio_get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
        if (args) {
                if (strcmp("creat", cmd) == 0)
                        return do_creat(args);
                if (strcmp("mnt", cmd) == 0)
                        return do_mnt(args);
+#if 0
                if (strcmp("cd", cmd) == 0)
                        return do_cd(args);
+#endif
                if (strcmp("chmd", cmd) == 0)
                        return do_chmd(args);
+               if (strcmp("open", cmd) == 0)
+                       return do_open(args);
        }
        return -EINVAL;
 }
 
+#ifdef SYSIO_TRACING
+/*
+ * Set/Unset tracing.
+ */
+static int
+_sysio_boot_tracing(const char *arg)
+{
+       long    l;
+       char    *cp;
+       static struct trace_callback
+               *entcb = NULL,
+               *exitcb = NULL;
+
+       l = 0;
+       if (arg) {
+               l = strtol(arg, (char **)&cp, 0);
+               if (*cp || !(l == 0 || l == 1))
+                       return -EINVAL;
+       }
+       if (l) {
+               if (entcb == NULL)
+                       entcb =
+                           _sysio_register_trace(_sysio_entry_trace_q,
+                                                 _sysio_trace_entry,
+                                                 NULL,
+                                                 NULL);
+               if (entcb == NULL)
+                       return -errno;
+               if (exitcb == NULL)
+                       exitcb =
+                           _sysio_register_trace(_sysio_exit_trace_q,
+                                                 _sysio_trace_exit,
+                                                 NULL,
+                                                 NULL);
+               if (exitcb == NULL)
+                       return -errno;
+       } else {
+               if (entcb != NULL)
+                       _sysio_remove_trace(_sysio_entry_trace_q, entcb);
+               entcb = NULL;
+               if (exitcb != NULL)
+                       _sysio_remove_trace(_sysio_exit_trace_q, exitcb);
+               exitcb = NULL;
+       }
+       return 0;
+}
+#endif
+
 /*
- * Given a command sequence buffer, parse it and run the given
- * commands 
+ * Initialize the namespace.
  */
-int 
-_sysio_boot(const char *buf) 
+static int
+_sysio_boot_namespace(const char *arg)
 {
        char    c, *tok;
+       ssize_t len;
        int     err;
-
+       unsigned count;
        /*
         * Allocate token buffer.
         */
-       tok = malloc(strlen(buf));
+       len = strlen(arg);
+       tok = malloc(len ? len : 1);
        if (!tok)
                return -ENOMEM;
        err = 0;
+       count = 0;
        while (1) {
                /*
                 * Discard leading white space.
                 */
-               while ((c = *buf) != '\0' &&
-                      !(c == '{' || strchr(IGNORE_WHITE, c) == NULL))
-                       buf++;
+               while ((c = *arg) != '\0' && strchr(IGNORE_WHITE, c))
+                       arg++;
+               if (COMMENT_INTRO == c) {
+                       /*
+                        * Discard comment.
+                        */
+                       while (*arg && (*arg != '\n')) {
+                               ++arg;
+                       }
+                       continue;
+               }
+
                if (c == '\0')
                        break;
                if (c != '{') {
@@ -608,11 +1021,18 @@ _sysio_boot(const char *buf)
                /*
                 * Get the command.
                 */
-               buf = (char *)get_token(buf + 1, 0, "}", IGNORE_WHITE, tok);
-               if (!buf) {
+               *tok = '\0';
+               arg =
+                   (char *)_sysio_get_token(arg + 1,
+                                            0,
+                                            "}",
+                                            IGNORE_WHITE,
+                                            tok);
+               if (!arg) {
                        err = -EINVAL;
                        break;
                }
+               count++;
                /*
                 * Perform.
                 */
@@ -620,6 +1040,63 @@ _sysio_boot(const char *buf)
                if (err)
                        break;
        }
+#ifdef SYSIO_TRACING
+       if (err)
+               _sysio_cprintf("+NS init+ failed at expr %u (last = %s): %s\n", 
+                              count,
+                              tok && *tok ? tok : "NULL",
+                              strerror(-err));
+#endif
        free(tok);
        return err;
 }
+
+#ifdef DEFER_INIT_CWD
+/*
+ * Set deferred initial working directory.
+ */
+static int
+_sysio_boot_cwd(const char *arg)
+{
+
+       _sysio_init_cwd = arg;
+       return 0;
+}
+#endif
+
+/*
+ * Given an identifier and it's arguments, perform optional initializations.
+ */
+int
+_sysio_boot(const char *opt, const char *arg)
+{
+       struct option_value_info vec[] = {
+#ifdef SYSIO_TRACING
+               { "trace",      NULL },                 /* tracing? */
+#endif
+               { "namespace",  NULL },                 /* init namespace? */
+#ifdef DEFER_INIT_CWD
+               { "cwd",        NULL },                 /* init working dir */
+#endif
+               { NULL,         NULL }
+       };
+       struct option_value_info *v;
+       unsigned u;
+       static int (*f[])(const char *) = {
+#ifdef SYSIO_TRACING
+               _sysio_boot_tracing,
+#endif
+               _sysio_boot_namespace,
+#ifdef DEFER_INIT_CWD
+               _sysio_boot_cwd,
+#endif
+               NULL                                    /* can't happen */
+       };
+
+       for (v = vec, u = 0; v->ovi_name; v++, u++)
+               if (strcmp(v->ovi_name, opt) == 0)
+                       break;
+       if (!v->ovi_name)
+               return -EINVAL;
+       return (*f[u])(arg);
+}