2 * This Cplant(TM) source code is the property of Sandia National
5 * This Cplant(TM) source code is copyrighted by Sandia National
8 * The redistribution of this Cplant(TM) source code is subject to the
9 * terms of the GNU Lesser General Public License
10 * (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
12 * Cplant(TM) Copyright 1998-2006 Sandia Corporation.
13 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14 * license for use of this work by or on behalf of the US Government.
15 * Export of this program may require a license from the United States
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 * Questions or comments about this library should be sent to:
37 * Sandia National Laboratories, New Mexico
39 * Albuquerque, NM 87185-1110
52 #if defined(_BSD_SOURCE) || defined(SYSIO_TRACING)
53 #include <sys/syscall.h>
63 #include <sys/types.h>
67 #include <sys/queue.h>
87 * Tracing callback record.
89 struct trace_callback {
90 TAILQ_ENTRY(trace_callback) links; /* trace list links */
91 void (*f)(const char *file, /* callback function */
95 void *data; /* callback data */
96 void (*destructor)(void *data); /* data destructor */
100 * Initialize a tracing callback record.
102 #define TCB_INIT(__tcb, __f, __d, __destroy) \
104 (__tcb)->f = (__f); \
105 (__tcb)->data = (__d); \
106 (__tcb)->destructor = (__destroy); \
110 * Trace queue head record.
112 TAILQ_HEAD(trace_q, trace_callback);
115 * The entry and exit queue heads, and queue pointers.
117 static struct trace_q _sysio_entry_trace_head;
118 void *_sysio_entry_trace_q = &_sysio_entry_trace_head;
119 static struct trace_q _sysio_exit_trace_head;
120 void *_sysio_exit_trace_q = &_sysio_exit_trace_head;
124 * White space characters.
126 #define IGNORE_WHITE " \t\r\n"
129 * Check if long overflows integer range.
131 #if LONG_MAX <= INT_MAX
132 #define _irecheck(_l, _e) \
133 ((_l) == LONG_MAX && (_e) == ERANGE)
135 #define _irecheck(_l, _e) \
140 * In sysio_init we'll allow simple comments, strings outside {}
141 * delimited by COMMENT_INTRO, and '\n' or '\0'
143 #define COMMENT_INTRO '#'
146 * In sysio_init we'll allow simple comments, strings outside {}
147 * delimited by COMMENT_INTRO, and '\n' or '\0'
149 #define COMMENT_INTRO '#'
152 * Sysio library initialization. Must be called before anything else in the
160 extern int _sysio_sockets_init(void);
165 * Initialize tracing callback queues.
167 TAILQ_INIT(&_sysio_entry_trace_head);
168 TAILQ_INIT(&_sysio_exit_trace_head);
171 err = _sysio_ioctx_init();
174 err = _sysio_i_init();
177 err = _sysio_mount_init();
181 err = _sysio_dev_init();
185 err = _sysio_stdfd_init();
190 err = _sysio_sockets_init();
200 * Unlike all other _sysio routines, this one returns with errno
201 * set. It also returns the error, as usual.
207 * Sysio library shutdown.
213 if (!(_sysio_fd_close_all() == 0 &&
214 _sysio_unmount_all() == 0))
217 #ifdef ZERO_SUM_MEMORY
218 _sysio_fd_shutdown();
220 _sysio_fssw_shutdown();
221 _sysio_access_shutdown();
222 free(incore_dir_template);
225 struct trace_callback *tcb;
228 * Empty the trace queues and free the entries.
230 while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL)
231 _sysio_remove_trace(&_sysio_entry_trace_head, tcb);
232 while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL)
233 _sysio_remove_trace(&_sysio_exit_trace_head, tcb);
241 #if !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF)
243 * Print a string to allocated memory.
246 vasprintf(char **strp, const char *fmt, va_list ap)
256 if (!(s = malloc(siz))) {
262 n = vsnprintf (s, siz, fmt, aq);
264 if (n > -1 && (size_t )n < siz)
266 if (n > -1) /* glibc 2.1 */
267 siz = n+1; /* precise */
269 siz *= 2; /* twice the old */
270 if (!(s = realloc (s, siz)))
280 asprintf(char **strp, const char *fmt, ...)
286 n = vasprintf(strp, fmt, ap);
291 #endif /* !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF) */
294 _sysio_cwrite(const char *buf, size_t len)
299 (void )syscall(SYSIO_SYS_write, STDERR_FILENO, buf, len);
307 _sysio_cprintf(const char *fmt, ...)
315 len = vasprintf(&buf, fmt, ap);
319 _sysio_cwrite(buf, len);
324 * Register a trace callback.
326 * The pointer to the trace record is returned.
329 _sysio_register_trace(void *q,
330 void (*f)(const char *file,
335 void (*destructor)(void *data))
337 struct trace_callback *tcb;
339 tcb = malloc(sizeof(struct trace_callback));
342 TCB_INIT(tcb, f, data, destructor);
343 TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
348 * Remove a registered trace callback.
351 _sysio_remove_trace(void *q, void *p)
353 struct trace_callback *tcb;
355 tcb = (struct trace_callback *)p;
358 (*tcb->destructor)(tcb->data);
359 TAILQ_REMOVE((struct trace_q *)q, tcb, links);
365 * Run a trace queue, making all the callbacks.
367 _sysio_run_trace_q(void *q,
372 struct trace_callback *tcb;
374 tcb = ((struct trace_q *)q)->tqh_first;
376 (*tcb->f)(file, func, line, tcb->data);
377 tcb = tcb->links.tqe_next;
382 _sysio_trace_entry(const char *file __IS_UNUSED,
384 int line __IS_UNUSED,
385 void *data __IS_UNUSED)
388 _sysio_cprintf("+ENTER+ %s\n", func);
392 _sysio_trace_exit(const char *file __IS_UNUSED,
394 int line __IS_UNUSED,
395 void *data __IS_UNUSED)
398 _sysio_cprintf("+EXIT+ %s\n", func);
400 #endif /* defined(SYSIO_TRACING) */
403 * (kind of)Duplicates strtok function.
405 * Given a buffer, returns the longest string
406 * that does not contain any delim characters. Will
407 * remove ws and any characters in the ignore string.
410 * The parameter controlling acceptance controls whether a positive
411 * match for some delimiter be made or not. If set, then either a delimiter
412 * or NUL character is success.
416 _sysio_get_token(const char *buf,
426 * Find the first occurance of delim, recording how many
427 * characters lead up to it. Ignore indicated characters.
430 while ((c = *buf) != '\0') {
442 if (strchr(delim, c) != NULL) {
446 if (strchr(ignore, c) != NULL)
455 *tbuf = '\0'; /* NUL term */
460 * Parse and record named arguments given as `name = value', comma-separated
463 * NB: Alters the passed buffer.
466 _sysio_get_args(char *buf, struct option_value_info *vec)
470 struct option_value_info *v;
474 (char *)_sysio_get_token(buf,
480 (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
487 (char *)_sysio_get_token(nxt,
494 for (v = vec; v->ovi_name; v++)
495 if (strcmp(v->ovi_name, name) == 0)
499 v->ovi_value = value;
506 parse_mm(const char *s, dev_t *devp)
512 ul = strtoul(s, &cp, 0);
513 if (*cp != '+' || ul > USHRT_MAX)
516 s = (const char *)++cp;
517 ul = strtoul(s, &cp, 0);
518 if (*cp != '\0' || ul > USHRT_MAX)
526 * Performs the creat command for the namespace assembly
528 * NB: Alters the passed buffer.
534 struct option_value_info v[] = {
535 { "ft", NULL }, /* file type */
536 { "nm", NULL }, /* name */
537 { "pm", NULL }, /* permissions */
538 { "ow", NULL }, /* owner */
539 { "gr", NULL }, /* group */
540 { "mm", NULL }, /* major + minor */
541 { "str", NULL }, /* file data */
547 struct pnode *dir, *pno;
549 struct intent intent;
563 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
568 perms = strtol(v[2].ovi_value, (char **)&cp, 0);
571 (perms == LONG_MAX && errno == ERANGE) ||
572 ((unsigned)perms & ~07777))
574 if (v[3].ovi_value) {
575 owner = strtol(v[3].ovi_value, (char **)&cp, 0);
577 ((owner == LONG_MIN || owner == LONG_MAX)
582 if (v[4].ovi_value) {
583 group = strtol(v[4].ovi_value, (char **)&cp, 0);
585 ((group == LONG_MIN || group == LONG_MAX) &&
591 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
595 * Init, get the operation, setup the intent.
600 if (strcmp(v[0].ovi_value, "dir") == 0) {
602 INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
603 } else if (strcmp(v[0].ovi_value, "chr") == 0) {
606 INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
607 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
609 } else if (strcmp(v[0].ovi_value, "blk") == 0) {
612 INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
613 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
615 } else if (strcmp(v[0].ovi_value, "file") == 0) {
617 intent_mode = O_CREAT|O_EXCL;
618 INTENT_INIT(&intent, INT_CREAT, &mode, &intent_mode);
625 * Lookup the given path.
630 ND_NEGOK|ND_NOPERMCHECK,
641 err = _sysio_mkdir(pno, mode);
645 err = _sysio_mknod(pno, mode, dev);
648 err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
651 ino = pno->p_base->pb_ino;
652 if (v[6].ovi_value) {
654 struct intnl_xtvec xtvec;
655 struct ioctx io_context;
658 * Deposit optional file content.
660 iovec.iov_base = v[6].ovi_value;
661 iovec.iov_len = strlen(v[6].ovi_value);
663 xtvec.xtv_len = iovec.iov_len;
664 IOCTX_INIT(&io_context,
670 _sysio_ioctx_enter(&io_context);
672 (*ino->i_ops.inop_write)(pno->p_base->pb_ino,
677 cc = _sysio_ioctx_wait(&io_context);
680 else if ((size_t )cc != iovec.iov_len)
681 err = -EIO; /* huh? */
683 _sysio_ioctx_complete(&io_context);
685 i = (*ino->i_ops.inop_close)(ino);
700 * NB: The passed buffer is altered.
706 struct option_value_info v[] = {
707 { "dev", NULL }, /* source (type:dev) */
708 { "dir", NULL }, /* target dir */
709 { "fl", NULL }, /* flags */
710 { "da", NULL }, /* mount data */
718 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
719 !(v[0].ovi_value && v[1].ovi_value))
722 (char *)_sysio_get_token(v[0].ovi_value,
726 name = v[0].ovi_value);
728 if (v[2].ovi_value) {
734 flags = strtoul(v[2].ovi_value, &cp, 0);
735 if (*cp || (flags == ULONG_MAX && errno == ERANGE))
739 if (strlen(v[1].ovi_value) == 1 && v[1].ovi_value[0] == PATH_SEPARATOR) {
741 * Aha! It's root they want. Have to do that special.
743 return _sysio_mount_root(ty, name, flags, v[3].ovi_value);
746 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
748 return _sysio_mount(dir,
761 * NB: Alters the passed buffer.
767 struct option_value_info v[] = {
768 { "dir", NULL }, /* directory */
772 struct pnode *dir, *pno;
775 if (_sysio_get_args(args, v) - args != (ssize_t )len || !v[0].ovi_value)
778 if (!(dir = _sysio_cwd) && !(dir = _sysio_root)) {
780 * We have no namespace yet. They really need to give us
781 * something to work with.
785 err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
788 err = _sysio_p_chdir(pno);
798 * NB: Alters passed buffer.
804 struct option_value_info v[] = {
805 { "src", NULL }, /* path */
806 { "pm", NULL }, /* perms */
811 struct intnl_stat stbuf;
813 struct pnode *dir, *pno;
816 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
817 !(v[0].ovi_value && v[1].ovi_value))
819 perms = strtol(v[1].ovi_value, &cp, 0);
822 (perms == LONG_MAX && errno == ERANGE) ||
823 ((unsigned)perms & ~07777))
825 (void )memset(&stbuf, 0, sizeof(stbuf));
826 stbuf.st_mode = (mode_t)perms;
828 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
830 err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, NULL, &pno);
833 err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
843 struct option_value_info v[] = {
844 { "nm", NULL }, /* path */
845 { "fd", NULL }, /* fildes */
846 { "m", NULL }, /* mode */
854 struct pnode *dir, *pno;
855 struct intent intent;
860 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
861 !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
863 l = strtol(v[1].ovi_value, (char **)&cp, 0);
864 if (*cp || l < 0 || _irecheck(l, errno))
867 ul = strtoul(v[1].ovi_value, (char **)&cp, 0);
869 (ul == ULONG_MAX && errno == ERANGE))
871 m = (mode_t )ul & (O_RDONLY|O_WRONLY|O_RDWR);
873 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
875 INTENT_INIT(&intent, INT_OPEN, &m, NULL);
877 err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, &intent, &pno);
882 err = _sysio_open(pno, m, 0);
885 fil = _sysio_fnew(pno->p_base->pb_ino, m);
890 err = _sysio_fd_set(fil, fd, 1);
904 * Execute the given cmd.
906 * NB: Buf is altered.
909 do_command(char *buf)
915 args = (char *)_sysio_get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
917 if (strcmp("creat", cmd) == 0)
918 return do_creat(args);
919 if (strcmp("mnt", cmd) == 0)
922 if (strcmp("cd", cmd) == 0)
925 if (strcmp("chmd", cmd) == 0)
926 return do_chmd(args);
927 if (strcmp("open", cmd) == 0)
928 return do_open(args);
938 _sysio_boot_tracing(const char *arg)
942 static struct trace_callback
948 l = strtol(arg, (char **)&cp, 0);
949 if (*cp || !(l == 0 || l == 1))
955 _sysio_register_trace(_sysio_entry_trace_q,
963 _sysio_register_trace(_sysio_exit_trace_q,
971 _sysio_remove_trace(_sysio_entry_trace_q, entcb);
974 _sysio_remove_trace(_sysio_exit_trace_q, exitcb);
982 * Initialize the namespace.
985 _sysio_boot_namespace(const char *arg)
992 * Allocate token buffer.
995 tok = malloc(len ? len : 1);
1002 * Discard leading white space.
1004 while ((c = *arg) != '\0' && strchr(IGNORE_WHITE, c))
1006 if (COMMENT_INTRO == c) {
1010 while (*arg && (*arg != '\n')) {
1027 (char *)_sysio_get_token(arg + 1,
1040 err = do_command(tok);
1044 #ifdef SYSIO_TRACING
1046 _sysio_cprintf("+NS init+ failed at expr %u (last = %s): %s\n",
1048 tok && *tok ? tok : "NULL",
1055 #ifdef DEFER_INIT_CWD
1057 * Set deferred initial working directory.
1060 _sysio_boot_cwd(const char *arg)
1063 _sysio_init_cwd = arg;
1069 * Given an identifier and it's arguments, perform optional initializations.
1072 _sysio_boot(const char *opt, const char *arg)
1074 struct option_value_info vec[] = {
1075 #ifdef SYSIO_TRACING
1076 { "trace", NULL }, /* tracing? */
1078 { "namespace", NULL }, /* init namespace? */
1079 #ifdef DEFER_INIT_CWD
1080 { "cwd", NULL }, /* init working dir */
1084 struct option_value_info *v;
1086 static int (*f[])(const char *) = {
1087 #ifdef SYSIO_TRACING
1088 _sysio_boot_tracing,
1090 _sysio_boot_namespace,
1091 #ifdef DEFER_INIT_CWD
1094 NULL /* can't happen */
1097 for (v = vec, u = 0; v->ovi_name; v++, u++)
1098 if (strcmp(v->ovi_name, opt) == 0)
1102 return (*f[u])(arg);