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();
224 struct trace_callback *tcb;
227 * Empty the trace queues and free the entries.
229 while ((tcb = _sysio_entry_trace_head.tqh_first) != NULL)
230 _sysio_remove_trace(&_sysio_entry_trace_head, tcb);
231 while ((tcb = _sysio_exit_trace_head.tqh_first) != NULL)
232 _sysio_remove_trace(&_sysio_exit_trace_head, tcb);
240 #if !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF)
242 * Print a string to allocated memory.
245 vasprintf(char **strp, const char *fmt, va_list ap)
255 if (!(s = malloc(siz))) {
261 n = vsnprintf (s, siz, fmt, aq);
263 if (n > -1 && (size_t )n < siz)
265 if (n > -1) /* glibc 2.1 */
266 siz = n+1; /* precise */
268 siz *= 2; /* twice the old */
269 if (!(s = realloc (s, siz)))
279 asprintf(char **strp, const char *fmt, ...)
285 n = vasprintf(strp, fmt, ap);
290 #endif /* !(defined(_HAVE_ASPRINTF) && _HAVE_ASPRINTF) */
293 _sysio_cwrite(const char *buf, size_t len)
298 (void )syscall(SYSIO_SYS_write, STDERR_FILENO, buf, len);
306 _sysio_cprintf(const char *fmt, ...)
314 len = vasprintf(&buf, fmt, ap);
318 _sysio_cwrite(buf, len);
323 * Register a trace callback.
325 * The pointer to the trace record is returned.
328 _sysio_register_trace(void *q,
329 void (*f)(const char *file,
334 void (*destructor)(void *data))
336 struct trace_callback *tcb;
338 tcb = malloc(sizeof(struct trace_callback));
341 TCB_INIT(tcb, f, data, destructor);
342 TAILQ_INSERT_TAIL((struct trace_q *)q, tcb, links);
347 * Remove a registered trace callback.
350 _sysio_remove_trace(void *q, void *p)
352 struct trace_callback *tcb;
354 tcb = (struct trace_callback *)p;
357 (*tcb->destructor)(tcb->data);
358 TAILQ_REMOVE((struct trace_q *)q, tcb, links);
364 * Run a trace queue, making all the callbacks.
366 _sysio_run_trace_q(void *q,
371 struct trace_callback *tcb;
373 tcb = ((struct trace_q *)q)->tqh_first;
375 (*tcb->f)(file, func, line, tcb->data);
376 tcb = tcb->links.tqe_next;
381 _sysio_trace_entry(const char *file __IS_UNUSED,
383 int line __IS_UNUSED,
384 void *data __IS_UNUSED)
387 _sysio_cprintf("+ENTER+ %s\n", func);
391 _sysio_trace_exit(const char *file __IS_UNUSED,
393 int line __IS_UNUSED,
394 void *data __IS_UNUSED)
397 _sysio_cprintf("+EXIT+ %s\n", func);
399 #endif /* defined(SYSIO_TRACING) */
402 * (kind of)Duplicates strtok function.
404 * Given a buffer, returns the longest string
405 * that does not contain any delim characters. Will
406 * remove ws and any characters in the ignore string.
409 * The parameter controlling acceptance controls whether a positive
410 * match for some delimiter be made or not. If set, then either a delimiter
411 * or NUL character is success.
415 _sysio_get_token(const char *buf,
425 * Find the first occurance of delim, recording how many
426 * characters lead up to it. Ignore indicated characters.
429 while ((c = *buf) != '\0') {
441 if (strchr(delim, c) != NULL) {
445 if (strchr(ignore, c) != NULL)
454 *tbuf = '\0'; /* NUL term */
459 * Parse and record named arguments given as `name = value', comma-separated
462 * NB: Alters the passed buffer.
465 _sysio_get_args(char *buf, struct option_value_info *vec)
469 struct option_value_info *v;
473 (char *)_sysio_get_token(buf,
479 (nxt != buf && *name == '\0' && buf + strlen(buf) == nxt)) {
486 (char *)_sysio_get_token(nxt,
493 for (v = vec; v->ovi_name; v++)
494 if (strcmp(v->ovi_name, name) == 0)
498 v->ovi_value = value;
505 parse_mm(const char *s, dev_t *devp)
511 ul = strtoul(s, &cp, 0);
512 if (*cp != '+' || ul > USHRT_MAX)
515 s = (const char *)++cp;
516 ul = strtoul(s, &cp, 0);
517 if (*cp != '\0' || ul > USHRT_MAX)
525 * Performs the creat command for the namespace assembly
527 * NB: Alters the passed buffer.
533 struct option_value_info v[] = {
534 { "ft", NULL }, /* file type */
535 { "nm", NULL }, /* name */
536 { "pm", NULL }, /* permissions */
537 { "ow", NULL }, /* owner */
538 { "gr", NULL }, /* group */
539 { "mm", NULL }, /* major + minor */
540 { "str", NULL }, /* file data */
546 struct pnode *dir, *pno;
548 struct intent intent;
562 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
567 perms = strtol(v[2].ovi_value, (char **)&cp, 0);
570 (perms == LONG_MAX && errno == ERANGE) ||
571 ((unsigned)perms & ~07777))
573 if (v[3].ovi_value) {
574 owner = strtol(v[3].ovi_value, (char **)&cp, 0);
576 ((owner == LONG_MIN || owner == LONG_MAX)
581 if (v[4].ovi_value) {
582 group = strtol(v[4].ovi_value, (char **)&cp, 0);
584 ((group == LONG_MIN || group == LONG_MAX) &&
590 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
594 * Init, get the operation, setup the intent.
599 if (strcmp(v[0].ovi_value, "dir") == 0) {
601 INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
602 } else if (strcmp(v[0].ovi_value, "chr") == 0) {
605 INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
606 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
608 } else if (strcmp(v[0].ovi_value, "blk") == 0) {
611 INTENT_INIT(&intent, INT_CREAT, &mode, NULL);
612 if (!(v[5].ovi_value && parse_mm(v[5].ovi_value, &dev) == 0))
614 } else if (strcmp(v[0].ovi_value, "file") == 0) {
616 intent_mode = O_CREAT|O_EXCL;
617 INTENT_INIT(&intent, INT_CREAT, &mode, &intent_mode);
624 * Lookup the given path.
629 ND_NEGOK|ND_NOPERMCHECK,
640 err = _sysio_mkdir(pno, mode);
644 err = _sysio_mknod(pno, mode, dev);
647 err = _sysio_open(pno, O_CREAT|O_EXCL, mode);
650 ino = pno->p_base->pb_ino;
651 if (v[6].ovi_value) {
653 struct intnl_xtvec xtvec;
654 struct ioctx io_context;
657 * Deposit optional file content.
659 iovec.iov_base = v[6].ovi_value;
660 iovec.iov_len = strlen(v[6].ovi_value);
662 xtvec.xtv_len = iovec.iov_len;
663 IOCTX_INIT(&io_context,
669 _sysio_ioctx_enter(&io_context);
671 (*ino->i_ops.inop_write)(pno->p_base->pb_ino,
676 cc = _sysio_ioctx_wait(&io_context);
679 else if ((size_t )cc != iovec.iov_len)
680 err = -EIO; /* huh? */
682 _sysio_ioctx_complete(&io_context);
684 i = (*ino->i_ops.inop_close)(ino);
699 * NB: The passed buffer is altered.
705 struct option_value_info v[] = {
706 { "dev", NULL }, /* source (type:dev) */
707 { "dir", NULL }, /* target dir */
708 { "fl", NULL }, /* flags */
709 { "da", NULL }, /* mount data */
717 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
718 !(v[0].ovi_value && v[1].ovi_value))
721 (char *)_sysio_get_token(v[0].ovi_value,
725 name = v[0].ovi_value);
727 if (v[2].ovi_value) {
733 flags = strtoul(v[2].ovi_value, &cp, 0);
734 if (*cp || (flags == ULONG_MAX && errno == ERANGE))
738 if (strlen(v[1].ovi_value) == 1 && v[1].ovi_value[0] == PATH_SEPARATOR) {
740 * Aha! It's root they want. Have to do that special.
742 return _sysio_mount_root(ty, name, flags, v[3].ovi_value);
745 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
747 return _sysio_mount(dir,
760 * NB: Alters the passed buffer.
766 struct option_value_info v[] = {
767 { "dir", NULL }, /* directory */
771 struct pnode *dir, *pno;
774 if (_sysio_get_args(args, v) - args != (ssize_t )len || !v[0].ovi_value)
777 if (!(dir = _sysio_cwd) && !(dir = _sysio_root)) {
779 * We have no namespace yet. They really need to give us
780 * something to work with.
784 err = _sysio_namei(dir, v[0].ovi_value, 0, NULL, &pno);
787 err = _sysio_p_chdir(pno);
797 * NB: Alters passed buffer.
803 struct option_value_info v[] = {
804 { "src", NULL }, /* path */
805 { "pm", NULL }, /* perms */
810 struct intnl_stat stbuf;
812 struct pnode *dir, *pno;
815 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
816 !(v[0].ovi_value && v[1].ovi_value))
818 perms = strtol(v[1].ovi_value, &cp, 0);
821 (perms == LONG_MAX && errno == ERANGE) ||
822 ((unsigned)perms & ~07777))
824 (void )memset(&stbuf, 0, sizeof(stbuf));
825 stbuf.st_mode = (mode_t)perms;
827 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
829 err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, NULL, &pno);
832 err = _sysio_setattr(pno, pno->p_base->pb_ino, SETATTR_MODE, &stbuf);
842 struct option_value_info v[] = {
843 { "nm", NULL }, /* path */
844 { "fd", NULL }, /* fildes */
845 { "m", NULL }, /* mode */
853 struct pnode *dir, *pno;
854 struct intent intent;
859 if (_sysio_get_args(args, v) - args != (ssize_t )len ||
860 !(v[0].ovi_value && v[1].ovi_value && v[2].ovi_value))
862 l = strtol(v[1].ovi_value, (char **)&cp, 0);
863 if (*cp || l < 0 || _irecheck(l, errno))
866 ul = strtoul(v[1].ovi_value, (char **)&cp, 0);
868 (ul == ULONG_MAX && errno == ERANGE))
870 m = (mode_t )ul & (O_RDONLY|O_WRONLY|O_RDWR);
872 if (!(dir = _sysio_cwd) && !(dir = _sysio_root))
874 INTENT_INIT(&intent, INT_OPEN, &m, NULL);
876 err = _sysio_namei(dir, v[0].ovi_value, ND_NOPERMCHECK, &intent, &pno);
881 err = _sysio_open(pno, m, 0);
884 fil = _sysio_fnew(pno->p_base->pb_ino, m);
889 err = _sysio_fd_set(fil, fd, 1);
903 * Execute the given cmd.
905 * NB: Buf is altered.
908 do_command(char *buf)
914 args = (char *)_sysio_get_token(buf, 1, ",", IGNORE_WHITE, cmd = buf);
916 if (strcmp("creat", cmd) == 0)
917 return do_creat(args);
918 if (strcmp("mnt", cmd) == 0)
921 if (strcmp("cd", cmd) == 0)
924 if (strcmp("chmd", cmd) == 0)
925 return do_chmd(args);
926 if (strcmp("open", cmd) == 0)
927 return do_open(args);
937 _sysio_boot_tracing(const char *arg)
941 static struct trace_callback
947 l = strtol(arg, (char **)&cp, 0);
948 if (*cp || !(l == 0 || l == 1))
954 _sysio_register_trace(_sysio_entry_trace_q,
962 _sysio_register_trace(_sysio_exit_trace_q,
970 _sysio_remove_trace(_sysio_entry_trace_q, entcb);
973 _sysio_remove_trace(_sysio_exit_trace_q, exitcb);
981 * Initialize the namespace.
984 _sysio_boot_namespace(const char *arg)
991 * Allocate token buffer.
994 tok = malloc(len ? len : 1);
1001 * Discard leading white space.
1003 while ((c = *arg) != '\0' && strchr(IGNORE_WHITE, c))
1005 if (COMMENT_INTRO == c) {
1009 while (*arg && (*arg != '\n')) {
1026 (char *)_sysio_get_token(arg + 1,
1039 err = do_command(tok);
1043 #ifdef SYSIO_TRACING
1045 _sysio_cprintf("+NS init+ failed at expr %u (last = %s): %s\n",
1047 tok && *tok ? tok : "NULL",
1054 #ifdef DEFER_INIT_CWD
1056 * Set deferred initial working directory.
1059 _sysio_boot_cwd(const char *arg)
1062 _sysio_init_cwd = arg;
1068 * Given an identifier and it's arguments, perform optional initializations.
1071 _sysio_boot(const char *opt, const char *arg)
1073 struct option_value_info vec[] = {
1074 #ifdef SYSIO_TRACING
1075 { "trace", NULL }, /* tracing? */
1077 { "namespace", NULL }, /* init namespace? */
1078 #ifdef DEFER_INIT_CWD
1079 { "cwd", NULL }, /* init working dir */
1083 struct option_value_info *v;
1085 static int (*f[])(const char *) = {
1086 #ifdef SYSIO_TRACING
1087 _sysio_boot_tracing,
1089 _sysio_boot_namespace,
1090 #ifdef DEFER_INIT_CWD
1093 NULL /* can't happen */
1096 for (v = vec, u = 0; v->ovi_name; v++, u++)
1097 if (strcmp(v->ovi_name, opt) == 0)
1101 return (*f[u])(arg);