* 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.
*/
{
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();
_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.
*
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 ||
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;
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;
}
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;
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);
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))
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;
if (pno)
P_RELE(pno);
return err;
-
-#undef _irecheck
}
/*
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 != '{') {
/*
* 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.
*/
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);
+}