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-2005 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
51 #include <sys/syscall.h>
61 #include <sys/types.h>
65 #include <sys/queue.h>
85 * Tracing callback record.
87 struct trace_callback {
88 TAILQ_ENTRY(trace_callback) links;
89 void (*f)(const char *file, const char *func, int line);
93 * Initialize a tracing callback record.
95 #define TCB_INIT(__tcb, __f) \
101 * Trace queue head record.
103 TAILQ_HEAD(trace_q, trace_callback);
106 * The entry and exit queue heads, and queue pointers.
108 static struct trace_q _sysio_entry_trace_head;
109 void *_sysio_entry_trace_q = &_sysio_entry_trace_head;
110 static struct trace_q _sysio_exit_trace_head;
111 void *_sysio_exit_trace_q = &_sysio_exit_trace_head;
115 * White space characters.
117 #define IGNORE_WHITE " \t\r\n"
120 * Check if long overflows integer range.
122 #if LONG_MAX <= INT_MAX
123 #define _irecheck(_l, _e) \
124 ((_l) == LONG_MAX && (_e) == ERANGE)
126 #define _irecheck(_l, _e) \
131 * In sysio_init we'll allow simple comments, strings outside {}
132 * delimited by COMMENT_INTRO, and '\n' or '\0'
134 #define COMMENT_INTRO '#'
137 * Sysio library initialization. Must be called before anything else in the
145 extern int _sysio_sockets_init(void);
150 * Initialize tracing callback queues.
152 TAILQ_INIT(&_sysio_entry_trace_head);
153 TAILQ_INIT(&_sysio_exit_trace_head);
156 err = _sysio_ioctx_init();
159 err = _sysio_i_init();
162 err = _sysio_mount_init();
166 err = _sysio_dev_init();
170 err = _sysio_stdfd_init();
175 err = _sysio_sockets_init();
185 * Unlike all other _sysio routines, this one returns with errno
186 * set. It also returns the error, as usual.
192 * Sysio library shutdown.
198 if (!(_sysio_fd_close_all() == 0 &&
199 _sysio_unmount_all() == 0))
203 _sysio_fd_shutdown();
205 _sysio_fssw_shutdown();
208 struct trace_callback *tcb;
211 * Empty the trace queues and free the entries.
213 while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL) {
214 TAILQ_REMOVE(&_sysio_entry_trace_head, tcb, links);
217 while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL) {
218 TAILQ_REMOVE(&_sysio_exit_trace_head, tcb, links);
228 #if !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF)
230 * Print a string to allocated memory.
233 vasprintf(char **strp, const char *fmt, va_list ap)
243 if (!(s = malloc(siz))) {
249 n = vsnprintf (s, siz, fmt, aq);
251 if (n > -1 && (size_t )n < siz)
253 if (n > -1) /* glibc 2.1 */
254 siz = n+1; /* precise */
256 siz *= 2; /* twice the old */
257 if (!(s = realloc (s, siz)))
267 asprintf(char **strp, const char *fmt, ...)
273 n = vasprintf(strp, fmt, ap);
278 #endif /* !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF) */
281 _sysio_cwrite(const char *buf, size_t len)
286 (void )syscall(SYSIO_SYS_write, STDERR_FILENO, buf, len);
294 _sysio_cprintf(const char *fmt, ...)
302 len = vasprintf(&buf, fmt, ap);
306 _sysio_cwrite(buf, len);
311 * Register a trace callback.
313 * The pointer to the trace record is returned.
316 _sysio_register_trace(void *q,
317 void (*f)(const char *file,
321 struct trace_callback *tcb;
323 tcb = malloc(sizeof(struct trace_callback));
327 TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
332 * Remove a registered trace callback.
335 _sysio_remove_trace(void *q, void *p)
338 TAILQ_REMOVE((struct trace_q *)q, (struct trace_callback *)p, links);
344 * Run a trace queue, making all the callbacks.
346 _sysio_run_trace_q(void *q,
351 struct trace_callback *tcb;
353 tcb = ((struct trace_q *)q)->tqh_first;
355 (*tcb->f)(file, func, line);
356 tcb = tcb->links.tqe_next;
361 _sysio_trace_entry(const char *file __IS_UNUSED,
363 int line __IS_UNUSED)
366 _sysio_cprintf("+ENTER+ %s\n", func);
370 _sysio_trace_exit(const char *file __IS_UNUSED,
372 int line __IS_UNUSED)
375 _sysio_cprintf("+EXIT+ %s\n", func);
377 #endif /* defined(SYSIO_TRACING) */
380 * (kind of)Duplicates strtok function.
382 * Given a buffer, returns the longest string
383 * that does not contain any delim characters. Will
384 * remove ws and any characters in the ignore string.
387 * The parameter controlling acceptance controls whether a positive
388 * match for some delimiter be made or not. If set, then either a delimiter
389 * or NUL character is success.
393 _sysio_get_token(const char *buf,
403 * Find the first occurance of delim, recording how many
404 * characters lead up to it. Ignore indicated characters.
407 while ((c = *buf) != '\0') {
419 if (strchr(delim, c) != NULL) {
423 if (strchr(ignore, c) != NULL)
432 *tbuf = '\0'; /* NUL term */
437 * Parse and record named arguments given as `name = value', comma-separated
440 * NB: Alters the passed buffer.
443 _sysio_get_args(char *buf, struct option_value_info *vec)
447 struct option_value_info *v;
451 (char *)_sysio_get_token(buf,
457 (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
464 (char *)_sysio_get_token(nxt,
471 for (v = vec; v->ovi_name; v++)
472 if (strcmp(v->ovi_name, name) == 0)
476 v->ovi_value = value;
483 parse_mm(const char *s, dev_t *devp)
489 ul = strtoul(s, &cp, 0);
490 if (*cp != '+' || ul > USHRT_MAX)
493 s = (const char *)++cp;
494 ul = strtoul(s, &cp, 0);
495 if (*cp != '\0' || ul > USHRT_MAX)
503 * Performs the creat command for the namespace assembly
505 * NB: Alters the passed buffer.
511 struct option_value_info v[] = {
512 { "ft", NULL }, /* file type */
513 { "nm", NULL }, /* name */
514 { "pm", NULL }, /* permissions */
515 { "ow", NULL }, /* owner */
516 { "gr", NULL }, /* group */
517 { "mm", NULL }, /* major + minor */
518 { "str", NULL }, /* file data */
524 struct pnode *dir, *pno;
526 struct intent intent;
531 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
536 perms = strtol(v[2].ovi_value, (char **)&cp, 0);
539 (perms == LONG_MAX && errno == ERANGE) ||
540 ((unsigned)perms & ~07777))
542 if (v[3].ovi_value) {
543 owner = strtol(v[3].ovi_value, (char **)&cp, 0);
545 ((owner == LONG_MIN || owner == LONG_MAX)
550 if (v[4].ovi_value) {
551 group = strtol(v[4].ovi_value, (char **)&cp, 0);
553 ((group == LONG_MIN || group == LONG_MAX) &&
559 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
563 if (strcmp(v[0].ovi_value, "dir") == 0) {
564 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
566 _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
569 if (pno->p_base->pb_ino)
571 else if (IS_RDONLY(pno->p_parent,
572 pno->p_parent->p_base->pb_ino))
577 ino = pno->p_parent->p_base->pb_ino;
578 err = (*ino->i_ops.inop_mkdir)(pno, mode);
581 } else if (strcmp(v[0].ovi_value, "chr") == 0) {
582 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
585 INTENT_INIT(&intent, INT_CREAT, &mode, 0);
587 _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
590 if (pno->p_base->pb_ino)
592 else if (IS_RDONLY(pno->p_parent,
593 pno->p_parent->p_base->pb_ino))
598 ino = pno->p_parent->p_base->pb_ino;
599 err = (*ino->i_ops.inop_mknod)(pno, mode, dev);
602 } else if (strcmp(v[0].ovi_value, "blk") == 0) {
604 * We don't support block special files yet.
607 } else if (strcmp(v[0].ovi_value, "file") == 0) {
612 INTENT_INIT(&intent, INT_CREAT, &mode, &i);
614 _sysio_namei(dir, v[1].ovi_value, ND_NEGOK, &intent, &pno);
617 err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
622 ino = pno->p_base->pb_ino;
623 if (!err && v[6].ovi_value) {
625 struct intnl_xtvec xtvec;
626 struct ioctx io_context;
629 * Deposit optional file content.
631 iovec.iov_base = v[6].ovi_value;
632 iovec.iov_len = strlen(v[6].ovi_value);
634 xtvec.xtv_len = iovec.iov_len;
635 IOCTX_INIT(&io_context,
641 _sysio_ioctx_enter(&io_context);
643 (*ino->i_ops.inop_write)(pno->p_base->pb_ino,
648 cc = _sysio_ioctx_wait(&io_context);
651 else if ((size_t )cc != iovec.iov_len)
652 err = -EIO; /* huh? */
654 _sysio_ioctx_complete(&io_context);
656 i = (*ino->i_ops.inop_close)(ino);
669 * NB: The passed buffer is altered.
675 struct option_value_info v[] = {
676 { "dev", NULL }, /* source (type:dev) */
677 { "dir", NULL }, /* target dir */
678 { "fl", NULL }, /* flags */
679 { "da", NULL }, /* mount data */
687 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
688 !(v[0].ovi_value && v[1].ovi_value))
691 (char *)_sysio_get_token(v[0].ovi_value,
695 name = v[0].ovi_value);
697 if (v[2].ovi_value) {
703 flags = strtoul(v[2].ovi_value, &cp, 0);
704 if (*cp || (flags == ULONG_MAX && errno == ERANGE))
708 if (strlen(v[1].ovi_value) == 1 && v[1].ovi_value[0] == PATH_SEPARATOR) {
710 * Aha! It's root they want. Have to do that special.
712 return _sysio_mount_root(ty, name, flags, v[3].ovi_value);
715 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
717 return _sysio_mount(dir,
730 * NB: Alters the passed buffer.
736 struct option_value_info v[] = {
737 { "dir", NULL }, /* directory */
741 struct pnode *dir, *pno;
744 if (_sysio_get_args(args, v) - args != (ssize_t )len || !v[0].ovi_value)
747 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
749 err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
752 err = _sysio_p_chdir(pno);
762 * NB: Alters passed buffer.
768 struct option_value_info v[] = {
769 { "src", NULL }, /* path */
770 { "pm", NULL }, /* perms */
775 struct intnl_stat stbuf;
777 struct pnode *dir, *pno;
780 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
781 !(v[0].ovi_value && v[1].ovi_value))
783 perms = strtol(v[1].ovi_value, &cp, 0);
786 (perms == LONG_MAX && errno == ERANGE) ||
787 ((unsigned)perms & ~07777))
789 (void )memset(&stbuf, 0, sizeof(stbuf));
790 stbuf.st_mode = (mode_t)perms;
792 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
794 err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
797 err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
807 struct option_value_info v[] = {
808 { "nm", NULL }, /* path */
809 { "fd", NULL }, /* fildes */
810 { "m", NULL }, /* mode */
818 struct pnode *dir, *pno;
819 struct intent intent;
824 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
825 !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
827 l = strtol(v[1].ovi_value, (char **)&cp, 0);
828 if (*cp || l < 0 || _irecheck(l, errno))
831 ul = strtoul(v[1].ovi_value, (char **)&cp, 0);
833 (ul == ULONG_MAX && errno == ERANGE))
835 m = (mode_t )ul & (O_RDONLY|O_WRONLY|O_RDWR);
837 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
839 INTENT_INIT(&intent, INT_OPEN, &m, NULL);
841 err = _sysio_namei(dir, v[0].ovi_value, 0, &intent, &pno);
846 err = _sysio_open(pno, m, 0);
849 fil = _sysio_fnew(pno->p_base->pb_ino, m);
854 err = _sysio_fd_set(fil, fd, 1);
868 * Execute the given cmd.
870 * NB: Buf is altered.
873 do_command(char *buf)
879 args = (char *)_sysio_get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
881 if (strcmp("creat", cmd) == 0)
882 return do_creat(args);
883 if (strcmp("mnt", cmd) == 0)
886 if (strcmp("cd", cmd) == 0)
889 if (strcmp("chmd", cmd) == 0)
890 return do_chmd(args);
891 if (strcmp("open", cmd) == 0)
892 return do_open(args);
902 _sysio_boot_tracing(const char *arg)
906 static struct trace_callback
912 l = strtol(arg, (char **)&cp, 0);
913 if (*cp || !(l == 0 || l == 1))
919 _sysio_register_trace(_sysio_entry_trace_q,
925 _sysio_register_trace(_sysio_exit_trace_q,
931 _sysio_remove_trace(_sysio_entry_trace_q, entcb);
934 _sysio_remove_trace(_sysio_exit_trace_q, exitcb);
942 * Initialize the namespace.
945 _sysio_boot_namespace(const char *arg)
952 * Allocate token buffer.
955 tok = malloc(len ? len : 1);
962 * Discard leading white space.
964 while ((c = *arg) != '\0' && strchr(IGNORE_WHITE, c))
966 if (COMMENT_INTRO == c) {
967 while (*arg && (*arg != '\n')) {
984 (char *)_sysio_get_token(arg + 1,
997 err = do_command(tok);
1003 _sysio_cprintf("+NS init+ failed at expr %u (last = %s): %s\n",
1005 tok && *tok ? tok : "NULL",
1014 * Set deferred initial working directory.
1017 _sysio_boot_cwd(const char *arg)
1020 _sysio_init_cwd = arg;
1026 * Given an identifier and it's arguments, perform optional initializations.
1029 _sysio_boot(const char *opt, const char *arg)
1031 struct option_value_info vec[] = {
1033 { "trace", NULL }, /* tracing? */
1035 { "namespace", NULL }, /* init namespace? */
1037 { "cwd", NULL }, /* init working dir */
1041 struct option_value_info *v;
1043 static int (*f[])(const char *) = {
1045 _sysio_boot_tracing,
1047 _sysio_boot_namespace,
1051 NULL /* can't happen */
1054 for (v = vec, u = 0; v->ovi_name; v++, u++)
1055 if (strcmp(v->ovi_name, opt) == 0)
1059 return (*f[u])(arg);