* 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-2005 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
#define _BSD_SOURCE
+#if SYSIO_TRACING
+#include <stdio.h>
+#endif
#include <stdlib.h>
+#if SYSIO_TRACING
+#include <sys/syscall.h>
+#endif
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#if 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"
+#if SYSIO_TRACING
+#include "native.h"
+#endif
#include "inode.h"
#include "fs.h"
#include "mount.h"
#include "stdfd.h"
#endif
+#if SYSIO_TRACING
+
+/*
+ * Tracing callback record.
+ */
+struct trace_callback {
+ TAILQ_ENTRY(trace_callback) links;
+ void (*f)(const char *file, const char *func, int line);
+};
+
+/*
+ * Initialize a tracing callback record.
+ */
+#define TCB_INIT(__tcb, __f) \
+ do { \
+ (__tcb)->f = (__f); \
+ } 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
+
+/*
* 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
+
+#if SYSIO_TRACING
+ /*
+ * Initialize tracing callback queues.
+ */
+ TAILQ_INIT(&_sysio_entry_trace_head);
+ TAILQ_INIT(&_sysio_exit_trace_head);
#endif
err = _sysio_ioctx_init();
_sysio_fd_shutdown();
_sysio_i_shutdown();
_sysio_fssw_shutdown();
+#if SYSIO_TRACING
+ {
+ struct trace_callback *tcb;
+
+ /*
+ * Empty the trace queues and free the entries.
+ */
+ while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL) {
+ TAILQ_REMOVE(&_sysio_entry_trace_head, tcb, links);
+ free(tcb);
+ }
+ while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL) {
+ TAILQ_REMOVE(&_sysio_exit_trace_head, tcb, links);
+ free(tcb);
+ }
+ }
+#endif
#endif
}
+#if 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))
+{
+ struct trace_callback *tcb;
+
+ tcb = malloc(sizeof(struct trace_callback));
+ if (!tcb)
+ return NULL;
+ TCB_INIT(tcb, f);
+ TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
+ return tcb;
+}
+
+/*
+ * Remove a registered trace callback.
+ */
+void
+_sysio_remove_trace(void *q, void *p)
+{
+
+ TAILQ_REMOVE((struct trace_q *)q, (struct trace_callback *)p, links);
+ free(p);
+}
+
+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 = tcb->links.tqe_next;
+ }
+}
+
+static void
+_sysio_trace_entry(const char *file __IS_UNUSED,
+ const char *func,
+ int line __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)
+{
+
+ _sysio_cprintf("+EXIT+ %s\n", func);
+}
+#endif /* defined(SYSIO_TRACING) */
+
/*
* (kind of)Duplicates strtok function.
*
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))
if (pno)
P_RELE(pno);
return err;
-
-#undef _irecheck
}
/*
return -EINVAL;
}
+#if 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);
+ if (entcb == NULL)
+ return -errno;
+ if (exitcb == NULL)
+ exitcb =
+ _sysio_register_trace(_sysio_exit_trace_q,
+ _sysio_trace_exit);
+ 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' &&
+ while ((c = *arg) != '\0' &&
!(c == '{' || strchr(IGNORE_WHITE, c) == NULL))
- buf++;
+ arg++;
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
+#if 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;
}
+
+#if 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[] = {
+#if SYSIO_TRACING
+ { "trace", NULL }, /* tracing? */
+#endif
+ { "namespace", NULL }, /* init namespace? */
+#if DEFER_INIT_CWD
+ { "cwd", NULL }, /* init working dir */
+#endif
+ { NULL, NULL }
+ };
+ struct option_value_info *v;
+ unsigned u;
+ static int (*f[])(const char *) = {
+#if SYSIO_TRACING
+ _sysio_boot_tracing,
+#endif
+ _sysio_boot_namespace,
+#if 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);
+}