Whamcloud - gitweb
LU-571 ldlm: Remove parallel AST limitation
[fs/lustre-release.git] / libsysio / src / init.c
index ce253cc..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/uio.h>
 #include <sys/queue.h>
 
-#include "xtio.h"
 #include "sysio.h"
+#include "xtio.h"
+#ifdef SYSIO_TRACING
+#include "native.h"
+#endif
 #include "inode.h"
 #include "fs.h"
 #include "mount.h"
 #include "stdfd.h"
 #endif
 
+#ifdef SYSIO_TRACING
+
+/*
+ * Tracing callback record.
+ */
+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.
  */
@@ -81,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();
@@ -130,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.
  *
@@ -287,6 +548,15 @@ 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 (_sysio_get_args(args, v) - args != (ssize_t )len ||
@@ -319,69 +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;
+       op = 0;
        if (strcmp(v[0].ovi_value, "dir") == 0) {
-               INTENT_INIT(&intent, INT_CREAT, &mode, 0);
-               err =
-                   _sysio_namei(dir, v[1].ovi_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);
+               op = CREATE_DIR;
+               INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
        } else if (strcmp(v[0].ovi_value, "chr") == 0) {
-               if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
-                       return -EINVAL;
+               op = CREATE_CHR;
                mode |= S_IFCHR;
-               INTENT_INIT(&intent, INT_CREAT, &mode, 0);
-               err =
-                   _sysio_namei(dir, v[1].ovi_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);
+               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) {
-               /*
-                * We don't support block special files yet.
-                */
-               return -EINVAL;
+               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) {
-               int     i;
-               struct inode *ino;
+               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].ovi_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].ovi_value) {
+               if (v[6].ovi_value) {
                        struct iovec iovec;
                        struct intnl_xtvec xtvec;
                        struct ioctx io_context;
@@ -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;
 }
 
@@ -505,8 +774,13 @@ do_cd(char *args)
        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].ovi_value, 0, NULL, &pno);
        if (err)
                return err;
@@ -552,7 +826,7 @@ do_chmd(char *args)
 
        if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
                return -ENOENT;
-       err = _sysio_namei(dir, v[0].ovi_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);
@@ -581,17 +855,6 @@ do_open(char *args)
        int     err;
        struct file *fil;
 
-/*
- * 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
-
        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))
@@ -610,7 +873,7 @@ do_open(char *args)
                return -ENOENT;
        INTENT_INIT(&intent, INT_OPEN, &m, NULL);
        pno = NULL;
-       err = _sysio_namei(dir, v[0].ovi_value, 0, &intent, &pno);
+       err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, &intent, &pno);
        if (err)
                return err;
        fil = NULL;
@@ -634,8 +897,6 @@ do_open(char *args)
        if (pno)
                P_RELE(pno);
        return err;
-
-#undef _irecheck
 }
 
 /*
@@ -668,38 +929,89 @@ do_command(char *buf)
        return -EINVAL;
 }
 
+#ifdef SYSIO_TRACING
 /*
- * Given a command sequence buffer, parse it and run the given
- * commands 
+ * Set/Unset tracing.
  */
-int 
-_sysio_boot(const char *buf
-#if DEFER_INIT_CWD
-           , const char *path
+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
-          )
+
+/*
+ * Initialize the namespace.
+ */
+static int
+_sysio_boot_namespace(const char *arg)
 {
        char    c, *tok;
        ssize_t len;
        int     err;
-
-       if (!buf)
-               buf = "";
+       unsigned count;
        /*
         * Allocate token buffer.
         */
-       len = 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 != '{') {
@@ -709,16 +1021,18 @@ _sysio_boot(const char *buf
                /*
                 * Get the command.
                 */
-               buf =
-                   (char *)_sysio_get_token(buf + 1,
+               *tok = '\0';
+               arg =
+                   (char *)_sysio_get_token(arg + 1,
                                             0,
                                             "}",
                                             IGNORE_WHITE,
                                             tok);
-               if (!buf) {
+               if (!arg) {
                        err = -EINVAL;
                        break;
                }
+               count++;
                /*
                 * Perform.
                 */
@@ -726,11 +1040,63 @@ _sysio_boot(const char *buf
                if (err)
                        break;
        }
-       free(tok);
-#if DEFER_INIT_CWD
+#ifdef SYSIO_TRACING
        if (err)
-               return err;
-       _sysio_init_cwd = path;
+               _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);
+}