libixp

git clone git://oldgit.suckless.org/libixp/
Log | Files | Refs | LICENSE

commit 6e7e6eba4610988e0e48c91992ac5b6fd3e82b0e
parent 1cd1e0ea108a56ccbf4b97f5e46a391f7ff8a76d
Author: Kris Maglione <kris@suckless.org>
Date:   Sun, 23 May 2010 12:43:19 -0400

Cleanup the root dir a bit.

Diffstat:
.hgignore | 11+++++++++++
Makefile | 2+-
lib/Makefile | 7+++++++
lib/libixp/LICENSE | 21+++++++++++++++++++++
lib/libixp/Makefile | 23+++++++++++++++++++++++
lib/libixp/README | 14++++++++++++++
lib/libixp/client.c | 676+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/convert.c | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/error.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/map.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/message.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/request.c | 550+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/rpc.c | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/server.c | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/socket.c | 278+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/srv_util.c | 457+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/thread.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/timer.c | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/transport.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp/util.c | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp_pthread/Makefile | 10++++++++++
lib/libixp_pthread/thread_pthread.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp_rubythread/Makefile | 11+++++++++++
lib/libixp_rubythread/thread_ruby.c | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/libixp_task/Makefile | 11+++++++++++
lib/libixp_task/thread_task.c | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
libixp/LICENSE | 21---------------------
libixp/Makefile | 23-----------------------
libixp/README | 14--------------
libixp/client.c | 676-------------------------------------------------------------------------------
libixp/convert.c | 200-------------------------------------------------------------------------------
libixp/error.c | 103-------------------------------------------------------------------------------
libixp/map.c | 132-------------------------------------------------------------------------------
libixp/message.c | 205-------------------------------------------------------------------------------
libixp/request.c | 550-------------------------------------------------------------------------------
libixp/rpc.c | 262-------------------------------------------------------------------------------
libixp/server.c | 165-------------------------------------------------------------------------------
libixp/socket.c | 278-------------------------------------------------------------------------------
libixp/srv_util.c | 457-------------------------------------------------------------------------------
libixp/thread.c | 97-------------------------------------------------------------------------------
libixp/timer.c | 139-------------------------------------------------------------------------------
libixp/transport.c | 97-------------------------------------------------------------------------------
libixp/util.c | 240-------------------------------------------------------------------------------
libixp_pthread/Makefile | 10----------
libixp_pthread/thread_pthread.c | 184-------------------------------------------------------------------------------
libixp_rubythread/Makefile | 11-----------
libixp_rubythread/thread_ruby.c | 281-------------------------------------------------------------------------------
libixp_task/Makefile | 11-----------
libixp_task/thread_task.c | 179-------------------------------------------------------------------------------
49 files changed, 4354 insertions(+), 4336 deletions(-)

diff --git a/.hgignore b/.hgignore @@ -0,0 +1,11 @@ +syntax: regexp +(^|/)\.((.*\.)?sw.|depend|hgignore)$ +(^|/)(tags|mkfile|diff)$ +\.([oa]|out|o_pic|so|pyc|pyo|diff)$ +\.(diff|orig|rej|bak)$ +\.(aux|idx|ilg|ind|log|toc)$ +^(pkg|src)/ +/bak/ +syntax: glob +config.local.mk +*.pkg.tar.?z diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ ROOT=. include ${ROOT}/mk/hdr.mk -DIRS = ${COMPONENTS} \ +DIRS = lib \ cmd \ include \ man diff --git a/lib/Makefile b/lib/Makefile @@ -0,0 +1,7 @@ +ROOT=.. +include $(ROOT)/mk/hdr.mk + +DIRS = $(COMPONENTS) + +include $(ROOT)/mk/dir.mk + diff --git a/lib/libixp/LICENSE b/lib/libixp/LICENSE @@ -0,0 +1,21 @@ + +© 2005-2006 Anselm R. Garbe <garbeam@gmail.com> +© 2006-2009 Kris Maglione <maglione.k at Gmail> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/libixp/Makefile b/lib/libixp/Makefile @@ -0,0 +1,23 @@ +ROOT= ../.. +include $(ROOT)/mk/hdr.mk +include $(ROOT)/mk/ixp.mk + +TARG = libixp + +OBJ = client \ + convert \ + error \ + map \ + message \ + request \ + rpc \ + server \ + srv_util \ + socket \ + thread \ + timer \ + transport \ + util + +include ${ROOT}/mk/lib.mk + diff --git a/lib/libixp/README b/lib/libixp/README @@ -0,0 +1,14 @@ +libixp - simple 9P client-/server-library +=============================== +libixp is an extremly simple, stand-alone 9P library. + + +Installation +------------ +Edit config.mk to match your local setup. libixp is installed into +/usr/local by default. + +Afterwards enter the following command to build and install libixp +(if necessary as root): + + $ make clean install diff --git a/lib/libixp/client.c b/lib/libixp/client.c @@ -0,0 +1,676 @@ +/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#include "ixp_local.h" + +#define nelem(ary) (sizeof(ary) / sizeof(*ary)) + +enum { + RootFid = 1, +}; + +static int +min(int a, int b) { + if(a < b) + return a; + return b; +} + +static IxpCFid* +getfid(IxpClient *c) { + IxpCFid *f; + + thread->lock(&c->lk); + f = c->freefid; + if(f != nil) + c->freefid = f->next; + else { + f = emallocz(sizeof *f); + f->client = c; + f->fid = ++c->lastfid; + thread->initmutex(&f->iolock); + } + f->next = nil; + f->open = 0; + thread->unlock(&c->lk); + return f; +} + +static void +putfid(IxpCFid *f) { + IxpClient *c; + + c = f->client; + thread->lock(&c->lk); + if(f->fid == c->lastfid) { + c->lastfid--; + thread->mdestroy(&f->iolock); + free(f); + }else { + f->next = c->freefid; + c->freefid = f; + } + thread->unlock(&c->lk); +} + +static int +dofcall(IxpClient *c, Fcall *fcall) { + Fcall *ret; + + ret = muxrpc(c, fcall); + if(ret == nil) + return 0; + if(ret->hdr.type == RError) { + werrstr("%s", ret->error.ename); + goto fail; + } + if(ret->hdr.type != (fcall->hdr.type^1)) { + werrstr("received mismatched fcall"); + goto fail; + } + memcpy(fcall, ret, sizeof *fcall); + free(ret); + return 1; +fail: + ixp_freefcall(fcall); + free(ret); + return 0; +} + +/** + * Function: ixp_unmount + * + * Unmounts the client P<c> and frees its data structures. + */ +void +ixp_unmount(IxpClient *c) { + IxpCFid *f; + + shutdown(c->fd, SHUT_RDWR); + close(c->fd); + + muxfree(c); + + while((f = c->freefid)) { + c->freefid = f->next; + thread->mdestroy(&f->iolock); + free(f); + } + free(c->rmsg.data); + free(c->wmsg.data); + free(c); +} + +static void +allocmsg(IxpClient *c, int n) { + c->rmsg.size = n; + c->wmsg.size = n; + c->rmsg.data = erealloc(c->rmsg.data, n); + c->wmsg.data = erealloc(c->wmsg.data, n); +} + +/** + * Function: ixp_mountfd + * Function: ixp_mount + * Function: ixp_nsmount + * + * Params: + * fd - A file descriptor which is already connected + * to a 9P server. + * address - An address (in Plan 9 resource fomat) at + * which to connect to a 9P server. + * name - The name of a socket in the process's canonical + * namespace directory. + * + * Initiate a 9P connection with the server at P<address>, + * connected to on P<fd>, or under the process's namespace + * directory as P<name>. + * + * Returns: + * A pointer to a new 9P client. + */ + +IxpClient* +ixp_mountfd(int fd) { + IxpClient *c; + Fcall fcall; + + c = emallocz(sizeof *c); + c->fd = fd; + + muxinit(c); + + allocmsg(c, 256); + c->lastfid = RootFid; + /* Override tag matching on TVersion */ + c->mintag = IXP_NOTAG; + c->maxtag = IXP_NOTAG+1; + + fcall.hdr.type = TVersion; + fcall.version.msize = IXP_MAX_MSG; + fcall.version.version = IXP_VERSION; + + if(dofcall(c, &fcall) == 0) { + ixp_unmount(c); + return nil; + } + + if(strcmp(fcall.version.version, IXP_VERSION) + || fcall.version.msize > IXP_MAX_MSG) { + werrstr("bad 9P version response"); + ixp_unmount(c); + return nil; + } + + c->mintag = 0; + c->maxtag = 255; + c->msize = fcall.version.msize; + + allocmsg(c, fcall.version.msize); + ixp_freefcall(&fcall); + + fcall.hdr.type = TAttach; + fcall.hdr.fid = RootFid; + fcall.tattach.afid = IXP_NOFID; + fcall.tattach.uname = getenv("USER"); + fcall.tattach.aname = ""; + if(dofcall(c, &fcall) == 0) { + ixp_unmount(c); + return nil; + } + + return c; +} + +IxpClient* +ixp_mount(const char *address) { + int fd; + + fd = ixp_dial(address); + if(fd < 0) + return nil; + return ixp_mountfd(fd); +} + +IxpClient* +ixp_nsmount(const char *name) { + char *address; + IxpClient *c; + + address = ixp_namespace(); + if(address) + address = ixp_smprint("unix!%s/%s", address, name); + if(address == nil) + return nil; + c = ixp_mount(address); + free(address); + return c; +} + +static IxpCFid* +walk(IxpClient *c, const char *path) { + IxpCFid *f; + char *p; + Fcall fcall; + int n; + + p = estrdup(path); + n = tokenize(fcall.twalk.wname, nelem(fcall.twalk.wname), p, '/'); + f = getfid(c); + + fcall.hdr.type = TWalk; + fcall.hdr.fid = RootFid; + fcall.twalk.nwname = n; + fcall.twalk.newfid = f->fid; + if(dofcall(c, &fcall) == 0) + goto fail; + if(fcall.rwalk.nwqid < n) { + werrstr("File does not exist"); + if(fcall.rwalk.nwqid == 0) + werrstr("Protocol botch"); + goto fail; + } + + f->qid = fcall.rwalk.wqid[n-1]; + + ixp_freefcall(&fcall); + free(p); + return f; +fail: + putfid(f); + free(p); + return nil; +} + +static IxpCFid* +walkdir(IxpClient *c, char *path, const char **rest) { + char *p; + + p = path + strlen(path) - 1; + assert(p >= path); + while(*p == '/') + *p-- = '\0'; + + while((p > path) && (*p != '/')) + p--; + if(*p != '/') { + werrstr("bad path"); + return nil; + } + + *p++ = '\0'; + *rest = p; + return walk(c, path); +} + +static int +clunk(IxpCFid *f) { + IxpClient *c; + Fcall fcall; + int ret; + + c = f->client; + + fcall.hdr.type = TClunk; + fcall.hdr.fid = f->fid; + ret = dofcall(c, &fcall); + if(ret) + putfid(f); + ixp_freefcall(&fcall); + return ret; +} + +/** + * Function: ixp_remove + * + * Params: + * path - The path of the file to remove. + * + * Removes a file or directory from the remote server. + * + * Returns: + * ixp_remove returns 0 on failure, 1 on success. + */ + +int +ixp_remove(IxpClient *c, const char *path) { + Fcall fcall; + IxpCFid *f; + int ret; + + if((f = walk(c, path)) == nil) + return 0; + + fcall.hdr.type = TRemove; + fcall.hdr.fid = f->fid;; + ret = dofcall(c, &fcall); + ixp_freefcall(&fcall); + putfid(f); + + return ret; +} + +static void +initfid(IxpCFid *f, Fcall *fcall) { + f->open = 1; + f->offset = 0; + f->iounit = fcall->ropen.iounit; + if(f->iounit == 0 || fcall->ropen.iounit > f->client->msize-24) + f->iounit = f->client->msize-24; + f->qid = fcall->ropen.qid; +} + +/** + * Function: ixp_create + * Function: ixp_open + * + * Params: + * path - The path of the file to open or create. + * perm - The permissions with which to create the new + * file. These will be ANDed with those of the + * parent directory by the server. + * mode - The file's open mode. + * + * ixp_open and ixp_create each open a file at P<path>. + * P<mode> must include OREAD, OWRITE, or ORDWR, and may + * include any of the modes specified in 9pmodes(3). + * ixp_create, additionally, creates a file at P<path> if it + * doesn't already exist. + * + * Returns: + * A pointer on which to operate on the newly + * opened file. + */ + +IxpCFid* +ixp_create(IxpClient *c, const char *path, uint perm, uchar mode) { + Fcall fcall; + IxpCFid *f; + char *tpath;; + + tpath = estrdup(path); + + f = walkdir(c, tpath, &path); + if(f == nil) + goto done; + + fcall.hdr.type = TCreate; + fcall.hdr.fid = f->fid; + fcall.tcreate.name = (char*)(uintptr_t)path; + fcall.tcreate.perm = perm; + fcall.tcreate.mode = mode; + + if(dofcall(c, &fcall) == 0) { + clunk(f); + f = nil; + goto done; + } + + initfid(f, &fcall); + f->mode = mode; + + ixp_freefcall(&fcall); + +done: + free(tpath); + return f; +} + +IxpCFid* +ixp_open(IxpClient *c, const char *path, uchar mode) { + Fcall fcall; + IxpCFid *f; + + f = walk(c, path); + if(f == nil) + return nil; + + fcall.hdr.type = TOpen; + fcall.hdr.fid = f->fid; + fcall.topen.mode = mode; + + if(dofcall(c, &fcall) == 0) { + clunk(f); + return nil; + } + + initfid(f, &fcall); + f->mode = mode; + + ixp_freefcall(&fcall); + return f; +} + +/** + * Function: ixp_close + * + * Closes the file pointed to by P<f> and frees its + * associated data structures; + * + * Returns: + * Returns 1 on success, and zero on failure. + */ + +int +ixp_close(IxpCFid *f) { + return clunk(f); +} + +static Stat* +_stat(IxpClient *c, ulong fid) { + IxpMsg msg; + Fcall fcall; + Stat *stat; + + fcall.hdr.type = TStat; + fcall.hdr.fid = fid; + if(dofcall(c, &fcall) == 0) + return nil; + + msg = ixp_message((char*)fcall.rstat.stat, fcall.rstat.nstat, MsgUnpack); + + stat = emalloc(sizeof *stat); + ixp_pstat(&msg, stat); + ixp_freefcall(&fcall); + if(msg.pos > msg.end) { + free(stat); + stat = nil; + } + return stat; +} + +/** + * Function: ixp_stat + * Function: ixp_fstat + * + * Params: + * path - The path of the file to stat. + * f - A CFid of an open file to stat. + * + * Stats the file at P<path> or pointed to by P<f>. + * + * Returns: + * Returns a Stat structure, which must be freed by + * the caller with free(3). + * + * S<Stat> + */ + +Stat* +ixp_stat(IxpClient *c, const char *path) { + Stat *stat; + IxpCFid *f; + + f = walk(c, path); + if(f == nil) + return nil; + + stat = _stat(c, f->fid); + clunk(f); + return stat; +} + +Stat* +ixp_fstat(IxpCFid *f) { + return _stat(f->client, f->fid); +} + +static long +_pread(IxpCFid *f, char *buf, long count, vlong offset) { + Fcall fcall; + int n, len; + + len = 0; + while(len < count) { + n = min(count-len, f->iounit); + + fcall.hdr.type = TRead; + fcall.hdr.fid = f->fid; + fcall.tread.offset = offset; + fcall.tread.count = n; + if(dofcall(f->client, &fcall) == 0) + return -1; + if(fcall.rread.count > n) + return -1; + + memcpy(buf+len, fcall.rread.data, fcall.rread.count); + offset += fcall.rread.count; + len += fcall.rread.count; + + ixp_freefcall(&fcall); + if(fcall.rread.count < n) + break; + } + return len; +} + +/** + * Function: ixp_read + * Function: ixp_pread + * + * Params: + * buf - A buffer in which to store the read data. + * count - The number of bytes to read. + * offset - The offset at which to begin reading. + * + * ixp_read and ixp_pread each read P<count> bytes of data + * from the file pointed to by P<f>, into P<buf>. ixp_read + * begins reading at its stored offset, and increments it by + * the number of bytes read. ixp_pread reads beginning at + * P<offset> and does not alter C<f>'s stored offset. + * + * Returns: + * These functions return the number of bytes read on + * success and -1 on failure. + */ + +long +ixp_read(IxpCFid *f, void *buf, long count) { + int n; + + thread->lock(&f->iolock); + n = _pread(f, buf, count, f->offset); + if(n > 0) + f->offset += n; + thread->unlock(&f->iolock); + return n; +} + +long +ixp_pread(IxpCFid *f, void *buf, long count, vlong offset) { + int n; + + thread->lock(&f->iolock); + n = _pread(f, buf, count, offset); + thread->unlock(&f->iolock); + return n; +} + +static long +_pwrite(IxpCFid *f, const void *buf, long count, vlong offset) { + Fcall fcall; + int n, len; + + len = 0; + do { + n = min(count-len, f->iounit); + fcall.hdr.type = TWrite; + fcall.hdr.fid = f->fid; + fcall.twrite.offset = offset; + fcall.twrite.data = (char*)buf + len; + fcall.twrite.count = n; + if(dofcall(f->client, &fcall) == 0) + return -1; + + offset += fcall.rwrite.count; + len += fcall.rwrite.count; + + ixp_freefcall(&fcall); + if(fcall.rwrite.count < n) + break; + } while(len < count); + return len; +} + +/** + * Function: ixp_write + * Function: ixp_pwrite + * + * Params: + * buf - A buffer holding the contents to store. + * count - The number of bytes to store. + * offset - The offset at which to write the data. + * + * ixp_write and ixp_pwrite each write P<count> bytes of + * data stored in P<buf> to the file pointed to by C<f>. + * ixp_write writes its data at its stored offset, and + * increments it by P<count>. ixp_pwrite writes its data a + * P<offset> and does not alter C<f>'s stored offset. + * + * Returns: + * These functions return the number of bytes actually + * written. Any value less than P<count> must be considered + * a failure. + */ + +long +ixp_write(IxpCFid *f, const void *buf, long count) { + int n; + + thread->lock(&f->iolock); + n = _pwrite(f, buf, count, f->offset); + if(n > 0) + f->offset += n; + thread->unlock(&f->iolock); + return n; +} + +long +ixp_pwrite(IxpCFid *f, const void *buf, long count, vlong offset) { + int n; + + thread->lock(&f->iolock); + n = _pwrite(f, buf, count, offset); + thread->unlock(&f->iolock); + return n; +} + +/** + * Function: ixp_vprint + * Function: ixp_print + * Variable: ixp_vsmprint + * + * Params: + * fmt - The string with which to format the data. + * ap - A va_list holding the arguments to the format + * string. + * ... - The arguments to the format string. + * + * These functions act like the standard formatted IO + * functions. They write the result of the formatting to the + * file pointed to by C<f>. + * + * V<ixp_vsmprint> may be set to a function which will + * format its arguments and return a null terminated string + * allocated with malloc(3). + * + * Returns: + * These functions return the number of bytes written. + * There is currently no way to detect failure. + */ + +int +ixp_vprint(IxpCFid *f, const char *fmt, va_list ap) { + char *buf; + int n; + + buf = ixp_vsmprint(fmt, ap); + if(buf == nil) + return -1; + + n = ixp_write(f, buf, strlen(buf)); + free(buf); + return n; +} + +int +ixp_print(IxpCFid *f, const char *fmt, ...) { + va_list ap; + int n; + + va_start(ap, fmt); + n = ixp_vprint(f, fmt, ap); + va_end(ap); + + return n; +} + diff --git a/lib/libixp/convert.c b/lib/libixp/convert.c @@ -0,0 +1,200 @@ +/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "ixp_local.h" + +enum { + SByte = 1, + SWord = 2, + SDWord = 4, + SQWord = 8, +}; + +static void +ixp_puint(IxpMsg *msg, uint size, ulong *val) { + uchar *pos; + int v; + + if(msg->pos + size <= msg->end) { + pos = (uchar*)msg->pos; + switch(msg->mode) { + case MsgPack: + v = *val; + switch(size) { + case SDWord: + pos[3] = v>>24; + pos[2] = v>>16; + case SWord: + pos[1] = v>>8; + case SByte: + pos[0] = v; + break; + } + case MsgUnpack: + v = 0; + switch(size) { + case SDWord: + v |= pos[3]<<24; + v |= pos[2]<<16; + case SWord: + v |= pos[1]<<8; + case SByte: + v |= pos[0]; + break; + } + *val = v; + } + } + msg->pos += size; +} + +void +ixp_pu32(IxpMsg *msg, ulong *val) { + ixp_puint(msg, SDWord, val); +} +void +ixp_pu8(IxpMsg *msg, uchar *val) { + ulong v; + + v = *val; + ixp_puint(msg, SByte, &v); + *val = (uchar)v; +} +void +ixp_pu16(IxpMsg *msg, ushort *val) { + ulong v; + + v = *val; + ixp_puint(msg, SWord, &v); + *val = (ushort)v; +} +void +ixp_pu64(IxpMsg *msg, uvlong *val) { + ulong vl, vb; + + vl = (uint)*val; + vb = (uint)(*val>>32); + ixp_puint(msg, SDWord, &vl); + ixp_puint(msg, SDWord, &vb); + *val = vl | ((uvlong)vb<<32); +} + +void +ixp_pstring(IxpMsg *msg, char **s) { + ushort len; + + if(msg->mode == MsgPack) + len = strlen(*s); + ixp_pu16(msg, &len); + + if(msg->pos + len <= msg->end) { + if(msg->mode == MsgUnpack) { + *s = emalloc(len + 1); + memcpy(*s, msg->pos, len); + (*s)[len] = '\0'; + }else + memcpy(msg->pos, *s, len); + } + msg->pos += len; +} + +void +ixp_pstrings(IxpMsg *msg, ushort *num, char *strings[]) { + char *s; + uint i, size; + ushort len; + + ixp_pu16(msg, num); + if(*num > IXP_MAX_WELEM) { + msg->pos = msg->end+1; + return; + } + + SET(s); + if(msg->mode == MsgUnpack) { + s = msg->pos; + size = 0; + for(i=0; i < *num; i++) { + ixp_pu16(msg, &len); + msg->pos += len; + size += len; + if(msg->pos > msg->end) + return; + } + msg->pos = s; + size += *num; + s = emalloc(size); + } + + for(i=0; i < *num; i++) { + if(msg->mode == MsgPack) + len = strlen(strings[i]); + ixp_pu16(msg, &len); + + if(msg->mode == MsgUnpack) { + memcpy(s, msg->pos, len); + strings[i] = (char*)s; + s += len; + msg->pos += len; + *s++ = '\0'; + }else + ixp_pdata(msg, &strings[i], len); + } +} + +void +ixp_pdata(IxpMsg *msg, char **data, uint len) { + if(msg->pos + len <= msg->end) { + if(msg->mode == MsgUnpack) { + *data = emalloc(len); + memcpy(*data, msg->pos, len); + }else + memcpy(msg->pos, *data, len); + } + msg->pos += len; +} + +void +ixp_pqid(IxpMsg *msg, Qid *qid) { + ixp_pu8(msg, &qid->type); + ixp_pu32(msg, &qid->version); + ixp_pu64(msg, &qid->path); +} + +void +ixp_pqids(IxpMsg *msg, ushort *num, Qid qid[]) { + int i; + + ixp_pu16(msg, num); + if(*num > IXP_MAX_WELEM) { + msg->pos = msg->end+1; + return; + } + + for(i = 0; i < *num; i++) + ixp_pqid(msg, &qid[i]); +} + +void +ixp_pstat(IxpMsg *msg, Stat *stat) { + ushort size; + + if(msg->mode == MsgPack) + size = ixp_sizeof_stat(stat) - 2; + + ixp_pu16(msg, &size); + ixp_pu16(msg, &stat->type); + ixp_pu32(msg, &stat->dev); + ixp_pqid(msg, &stat->qid); + ixp_pu32(msg, &stat->mode); + ixp_pu32(msg, &stat->atime); + ixp_pu32(msg, &stat->mtime); + ixp_pu64(msg, &stat->length); + ixp_pstring(msg, &stat->name); + ixp_pstring(msg, &stat->uid); + ixp_pstring(msg, &stat->gid); + ixp_pstring(msg, &stat->muid); +} diff --git a/lib/libixp/error.c b/lib/libixp/error.c @@ -0,0 +1,103 @@ +/* Public Domain --Kris Maglione */ +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "ixp_local.h" + +static int +_vsnprint(char *buf, int n, const char *fmt, va_list ap) { + return vsnprintf(buf, n, fmt, ap); +} + +static char* +_vsmprint(const char *fmt, va_list ap) { + va_list al; + char *buf = ""; + int n; + + va_copy(al, ap); + n = vsnprintf(buf, 0, fmt, al); + va_end(al); + + buf = malloc(++n); + if(buf) + vsnprintf(buf, n, fmt, ap); + return buf; +} + +int (*ixp_vsnprint)(char*, int, const char*, va_list) = _vsnprint; +char* (*ixp_vsmprint)(const char*, va_list) = _vsmprint; + +/* Approach to errno handling taken from Plan 9 Port. */ +enum { + EPLAN9 = 0x19283745, +}; + +/** + * Function: ixp_errbuf + * Function: ixp_errstr + * Function: ixp_rerrstr + * Function: ixp_werrstr + * + * Params: + * buf - The buffer to read and/or fill. + * n - The size of the buffer. + * fmt - A format string with which to write the * errstr. + * ... - Arguments to P<fmt>. + * + * These functions simulate Plan 9's errstr functionality. + * They replace errno in libixp. Note that these functions + * are not internationalized. + * + * F<ixp_errbuf> returns the errstr buffer for the current + * thread. F<ixp_rerrstr> fills P<buf> with the data from + * the current thread's error buffer, while F<ixp_errstr> + * exchanges P<buf>'s contents with those of the current + * thread's error buffer. F<ixp_werrstr> is takes a format + * string from which to construct an errstr. + * + * Returns: + * F<ixp_errbuf> returns the current thread's error + * string buffer. + */ +char* +ixp_errbuf() { + char *errbuf; + + errbuf = thread->errbuf(); + if(errno == EINTR) + strncpy(errbuf, "interrupted", IXP_ERRMAX); + else if(errno != EPLAN9) + strncpy(errbuf, strerror(errno), IXP_ERRMAX); + return errbuf; +} + +void +errstr(char *buf, int n) { + char tmp[IXP_ERRMAX]; + + strncpy(tmp, buf, sizeof tmp); + rerrstr(buf, n); + strncpy(thread->errbuf(), tmp, IXP_ERRMAX); + errno = EPLAN9; +} + +void +rerrstr(char *buf, int n) { + strncpy(buf, ixp_errbuf(), n); +} + +void +werrstr(const char *fmt, ...) { + char tmp[IXP_ERRMAX]; + va_list ap; + + va_start(ap, fmt); + ixp_vsnprint(tmp, sizeof tmp, fmt, ap); + va_end(ap); + strncpy(thread->errbuf(), tmp, IXP_ERRMAX); + errno = EPLAN9; +} + diff --git a/lib/libixp/map.c b/lib/libixp/map.c @@ -0,0 +1,132 @@ +/* Written by Kris Maglione */ +/* Public domain */ +#include <stdlib.h> +#include "ixp_local.h" + +/* Edit s/^([a-zA-Z].*)\n([a-z].*) {/\1 \2;/g x/^([^a-zA-Z]|static|$)/-+d s/ (\*map|val|*str)//g */ + +struct MapEnt { + ulong hash; + const char* key; + void* val; + MapEnt* next; +}; + +MapEnt *NM; + +static void +insert(MapEnt **e, ulong val, const char *key) { + MapEnt *te; + + te = emallocz(sizeof *te); + te->hash = val; + te->key = key; + te->next = *e; + *e = te; +} + +static MapEnt** +map_getp(Map *map, ulong val, bool create, bool *exists) { + MapEnt **e; + + e = &map->bucket[val%map->nhash]; + for(; *e; e = &(*e)->next) + if((*e)->hash >= val) break; + if(exists) + *exists = *e && (*e)->hash == val; + + if(*e == nil || (*e)->hash != val) { + if(create) + insert(e, val, nil); + else + e = &NM; + } + return e; +} + +void +ixp_mapfree(Map *map, void (*destroy)(void*)) { + int i; + MapEnt *e; + + thread->wlock(&map->lock); + for(i=0; i < map->nhash; i++) + while((e = map->bucket[i])) { + map->bucket[i] = e->next; + if(destroy) + destroy(e->val); + free(e); + } + thread->wunlock(&map->lock); + thread->rwdestroy(&map->lock); +} + +void +ixp_mapexec(Map *map, void (*run)(void*, void*), void *context) { + int i; + MapEnt *e; + + thread->rlock(&map->lock); + for(i=0; i < map->nhash; i++) + for(e=map->bucket[i]; e; e=e->next) + run(context, e->val); + thread->runlock(&map->lock); +} + +void +ixp_mapinit(Map *map, MapEnt **buckets, int nbuckets) { + + map->bucket = buckets; + map->nhash = nbuckets; + + thread->initrwlock(&map->lock); +} + +bool +ixp_mapinsert(Map *map, ulong key, void *val, bool overwrite) { + MapEnt *e; + bool existed, res; + + res = true; + thread->wlock(&map->lock); + e = *map_getp(map, key, true, &existed); + if(existed && !overwrite) + res = false; + else + e->val = val; + thread->wunlock(&map->lock); + return res; +} + +void* +ixp_mapget(Map *map, ulong val) { + MapEnt *e; + void *res; + + thread->rlock(&map->lock); + e = *map_getp(map, val, false, nil); + res = e ? e->val : nil; + thread->runlock(&map->lock); + return res; +} + +void* +ixp_maprm(Map *map, ulong val) { + MapEnt **e, *te; + void *ret; + + ret = nil; + thread->wlock(&map->lock); + e = map_getp(map, val, false, nil); + if(*e) { + te = *e; + ret = te->val; + *e = te->next; + thread->wunlock(&map->lock); + free(te); + } + else + thread->wunlock(&map->lock); + return ret; +} + diff --git a/lib/libixp/message.c b/lib/libixp/message.c @@ -0,0 +1,205 @@ +/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "ixp_local.h" + +enum { + SByte = 1, + SWord = 2, + SDWord = 4, + SQWord = 8, +}; + +#define SString(s) (SWord + strlen(s)) +enum { + SQid = SByte + SDWord + SQWord, +}; + +IxpMsg +ixp_message(char *data, uint length, uint mode) { + IxpMsg m; + + m.data = data; + m.pos = data; + m.end = data + length; + m.size = length; + m.mode = mode; + return m; +} + +void +ixp_freestat(Stat *s) { + free(s->name); + free(s->uid); + free(s->gid); + free(s->muid); + s->name = s->uid = s->gid = s->muid = nil; +} + +void +ixp_freefcall(Fcall *fcall) { + switch(fcall->hdr.type) { + case RStat: + free(fcall->rstat.stat); + fcall->rstat.stat = nil; + break; + case RRead: + free(fcall->rread.data); + fcall->rread.data = nil; + break; + case RVersion: + free(fcall->version.version); + fcall->version.version = nil; + break; + case RError: + free(fcall->error.ename); + fcall->error.ename = nil; + break; + } +} + +ushort +ixp_sizeof_stat(Stat * stat) { + return SWord /* size */ + + SWord /* type */ + + SDWord /* dev */ + + SQid /* qid */ + + 3 * SDWord /* mode, atime, mtime */ + + SQWord /* length */ + + SString(stat->name) + + SString(stat->uid) + + SString(stat->gid) + + SString(stat->muid); +} + +void +ixp_pfcall(IxpMsg *msg, Fcall *fcall) { + ixp_pu8(msg, &fcall->hdr.type); + ixp_pu16(msg, &fcall->hdr.tag); + + switch (fcall->hdr.type) { + case TVersion: + case RVersion: + ixp_pu32(msg, &fcall->version.msize); + ixp_pstring(msg, &fcall->version.version); + break; + case TAuth: + ixp_pu32(msg, &fcall->tauth.afid); + ixp_pstring(msg, &fcall->tauth.uname); + ixp_pstring(msg, &fcall->tauth.aname); + break; + case RAuth: + ixp_pqid(msg, &fcall->rauth.aqid); + break; + case RAttach: + ixp_pqid(msg, &fcall->rattach.qid); + break; + case TAttach: + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pu32(msg, &fcall->tattach.afid); + ixp_pstring(msg, &fcall->tattach.uname); + ixp_pstring(msg, &fcall->tattach.aname); + break; + case RError: + ixp_pstring(msg, &fcall->error.ename); + break; + case TFlush: + ixp_pu16(msg, &fcall->tflush.oldtag); + break; + case TWalk: + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pu32(msg, &fcall->twalk.newfid); + ixp_pstrings(msg, &fcall->twalk.nwname, fcall->twalk.wname); + break; + case RWalk: + ixp_pqids(msg, &fcall->rwalk.nwqid, fcall->rwalk.wqid); + break; + case TOpen: + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pu8(msg, &fcall->topen.mode); + break; + case ROpen: + case RCreate: + ixp_pqid(msg, &fcall->ropen.qid); + ixp_pu32(msg, &fcall->ropen.iounit); + break; + case TCreate: + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pstring(msg, &fcall->tcreate.name); + ixp_pu32(msg, &fcall->tcreate.perm); + ixp_pu8(msg, &fcall->tcreate.mode); + break; + case TRead: + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pu64(msg, &fcall->tread.offset); + ixp_pu32(msg, &fcall->tread.count); + break; + case RRead: + ixp_pu32(msg, &fcall->rread.count); + ixp_pdata(msg, &fcall->rread.data, fcall->rread.count); + break; + case TWrite: + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pu64(msg, &fcall->twrite.offset); + ixp_pu32(msg, &fcall->twrite.count); + ixp_pdata(msg, &fcall->twrite.data, fcall->twrite.count); + break; + case RWrite: + ixp_pu32(msg, &fcall->rwrite.count); + break; + case TClunk: + case TRemove: + case TStat: + ixp_pu32(msg, &fcall->hdr.fid); + break; + case RStat: + ixp_pu16(msg, &fcall->rstat.nstat); + ixp_pdata(msg, (char**)&fcall->rstat.stat, fcall->rstat.nstat); + break; + case TWStat: { + ushort size; + ixp_pu32(msg, &fcall->hdr.fid); + ixp_pu16(msg, &size); + ixp_pstat(msg, &fcall->twstat.stat); + break; + } + } +} + +uint +ixp_fcall2msg(IxpMsg *msg, Fcall *fcall) { + ulong size; + + msg->end = msg->data + msg->size; + msg->pos = msg->data + SDWord; + msg->mode = MsgPack; + ixp_pfcall(msg, fcall); + + if(msg->pos > msg->end) + return 0; + + msg->end = msg->pos; + size = msg->end - msg->data; + + msg->pos = msg->data; + ixp_pu32(msg, &size); + + msg->pos = msg->data; + return size; +} + +uint +ixp_msg2fcall(IxpMsg *msg, Fcall *fcall) { + msg->pos = msg->data + SDWord; + msg->mode = MsgUnpack; + ixp_pfcall(msg, fcall); + + if(msg->pos > msg->end) + return 0; + + return msg->pos - msg->data; +} + diff --git a/lib/libixp/request.c b/lib/libixp/request.c @@ -0,0 +1,550 @@ +/* Copyright ©2006-2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include "ixp_local.h" + +static void handlereq(Ixp9Req *r); + +static void +_printfcall(Fcall *f) { + USED(f); +} +void (*ixp_printfcall)(Fcall*) = _printfcall; + +static int +min(int a, int b) { + if(a < b) + return a; + return b; +} + +static char + Eduptag[] = "tag in use", + Edupfid[] = "fid in use", + Enofunc[] = "function not implemented", + Eopen[] = "fid is already open", + Enofile[] = "file does not exist", + Enoread[] = "file not open for reading", + Enofid[] = "fid does not exist", + Enotag[] = "tag does not exist", + Enotdir[] = "not a directory", + Eintr[] = "interrupted", + Eisdir[] = "cannot perform operation on a directory"; + +enum { + TAG_BUCKETS = 61, + FID_BUCKETS = 61, +}; + +struct Ixp9Conn { + Map tagmap; + Map fidmap; + MapEnt* taghash[TAG_BUCKETS]; + MapEnt* fidhash[FID_BUCKETS]; + Ixp9Srv* srv; + IxpConn* conn; + IxpMutex rlock; + IxpMutex wlock; + IxpMsg rmsg; + IxpMsg wmsg; + int ref; +}; + +static void +decref_p9conn(Ixp9Conn *p9conn) { + thread->lock(&p9conn->wlock); + if(--p9conn->ref > 0) { + thread->unlock(&p9conn->wlock); + return; + } + thread->unlock(&p9conn->wlock); + + assert(p9conn->conn == nil); + + thread->mdestroy(&p9conn->rlock); + thread->mdestroy(&p9conn->wlock); + + ixp_mapfree(&p9conn->tagmap, nil); + ixp_mapfree(&p9conn->fidmap, nil); + + free(p9conn->rmsg.data); + free(p9conn->wmsg.data); + free(p9conn); +} + +static void* +createfid(Map *map, int fid, Ixp9Conn *p9conn) { + Fid *f; + + f = emallocz(sizeof *f); + p9conn->ref++; + f->conn = p9conn; + f->fid = fid; + f->omode = -1; + f->map = map; + if(ixp_mapinsert(map, fid, f, false)) + return f; + free(f); + return nil; +} + +static int +destroyfid(Ixp9Conn *p9conn, ulong fid) { + Fid *f; + + f = ixp_maprm(&p9conn->fidmap, fid); + if(f == nil) + return 0; + + if(p9conn->srv->freefid) + p9conn->srv->freefid(f); + + decref_p9conn(p9conn); + free(f); + return 1; +} + +static void +handlefcall(IxpConn *c) { + Fcall fcall = {0}; + Ixp9Conn *p9conn; + Ixp9Req *req; + + p9conn = c->aux; + + thread->lock(&p9conn->rlock); + if(ixp_recvmsg(c->fd, &p9conn->rmsg) == 0) + goto Fail; + if(ixp_msg2fcall(&p9conn->rmsg, &fcall) == 0) + goto Fail; + thread->unlock(&p9conn->rlock); + + req = emallocz(sizeof *req); + p9conn->ref++; + req->conn = p9conn; + req->srv = p9conn->srv; + req->ifcall = fcall; + p9conn->conn = c; + + if(!ixp_mapinsert(&p9conn->tagmap, fcall.hdr.tag, req, false)) { + respond(req, Eduptag); + return; + } + + handlereq(req); + return; + +Fail: + thread->unlock(&p9conn->rlock); + ixp_hangup(c); + return; +} + +static void +handlereq(Ixp9Req *r) { + Ixp9Conn *p9conn; + Ixp9Srv *srv; + + p9conn = r->conn; + srv = p9conn->srv; + + ixp_printfcall(&r->ifcall); + + switch(r->ifcall.hdr.type) { + default: + respond(r, Enofunc); + break; + case TVersion: + if(!strcmp(r->ifcall.version.version, "9P")) + r->ofcall.version.version = "9P"; + else if(!strcmp(r->ifcall.version.version, "9P2000")) + r->ofcall.version.version = "9P2000"; + else + r->ofcall.version.version = "unknown"; + r->ofcall.version.msize = r->ifcall.version.msize; + respond(r, nil); + break; + case TAttach: + if(!(r->fid = createfid(&p9conn->fidmap, r->ifcall.hdr.fid, p9conn))) { + respond(r, Edupfid); + return; + } + /* attach is a required function */ + srv->attach(r); + break; + case TClunk: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if(!srv->clunk) { + respond(r, nil); + return; + } + srv->clunk(r); + break; + case TFlush: + if(!(r->oldreq = ixp_mapget(&p9conn->tagmap, r->ifcall.tflush.oldtag))) { + respond(r, Enotag); + return; + } + if(!srv->flush) { + respond(r, Enofunc); + return; + } + srv->flush(r); + break; + case TCreate: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if(r->fid->omode != -1) { + respond(r, Eopen); + return; + } + if(!(r->fid->qid.type&QTDIR)) { + respond(r, Enotdir); + return; + } + if(!p9conn->srv->create) { + respond(r, Enofunc); + return; + } + p9conn->srv->create(r); + break; + case TOpen: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if((r->fid->qid.type&QTDIR) && (r->ifcall.topen.mode|P9_ORCLOSE) != (P9_OREAD|P9_ORCLOSE)) { + respond(r, Eisdir); + return; + } + r->ofcall.ropen.qid = r->fid->qid; + if(!p9conn->srv->open) { + respond(r, Enofunc); + return; + } + p9conn->srv->open(r); + break; + case TRead: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if(r->fid->omode == -1 || r->fid->omode == P9_OWRITE) { + respond(r, Enoread); + return; + } + if(!p9conn->srv->read) { + respond(r, Enofunc); + return; + } + p9conn->srv->read(r); + break; + case TRemove: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if(!p9conn->srv->remove) { + respond(r, Enofunc); + return; + } + p9conn->srv->remove(r); + break; + case TStat: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if(!p9conn->srv->stat) { + respond(r, Enofunc); + return; + } + p9conn->srv->stat(r); + break; + case TWalk: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if(r->fid->omode != -1) { + respond(r, "cannot walk from an open fid"); + return; + } + if(r->ifcall.twalk.nwname && !(r->fid->qid.type&QTDIR)) { + respond(r, Enotdir); + return; + } + if((r->ifcall.hdr.fid != r->ifcall.twalk.newfid)) { + if(!(r->newfid = createfid(&p9conn->fidmap, r->ifcall.twalk.newfid, p9conn))) { + respond(r, Edupfid); + return; + } + }else + r->newfid = r->fid; + if(!p9conn->srv->walk) { + respond(r, Enofunc); + return; + } + p9conn->srv->walk(r); + break; + case TWrite: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if((r->fid->omode&3) != P9_OWRITE && (r->fid->omode&3) != P9_ORDWR) { + respond(r, "write on fid not opened for writing"); + return; + } + if(!p9conn->srv->write) { + respond(r, Enofunc); + return; + } + p9conn->srv->write(r); + break; + case TWStat: + if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { + respond(r, Enofid); + return; + } + if((ushort)~r->ifcall.twstat.stat.type) { + respond(r, "wstat of type"); + return; + } + if((uint)~r->ifcall.twstat.stat.dev) { + respond(r, "wstat of dev"); + return; + } + if((uchar)~r->ifcall.twstat.stat.qid.type || (ulong)~r->ifcall.twstat.stat.qid.version || (uvlong)~r->ifcall.twstat.stat.qid.path) { + respond(r, "wstat of qid"); + return; + } + if(r->ifcall.twstat.stat.muid && r->ifcall.twstat.stat.muid[0]) { + respond(r, "wstat of muid"); + return; + } + if((ulong)~r->ifcall.twstat.stat.mode && ((r->ifcall.twstat.stat.mode&DMDIR)>>24) != r->fid->qid.type&QTDIR) { + respond(r, "wstat on DMDIR bit"); + return; + } + if(!p9conn->srv->wstat) { + respond(r, Enofunc); + return; + } + p9conn->srv->wstat(r); + break; + /* Still to be implemented: auth */ + } +} + +void +respond(Ixp9Req *r, const char *error) { + Ixp9Conn *p9conn; + int msize; + + p9conn = r->conn; + + switch(r->ifcall.hdr.type) { + default: + if(!error) + assert(!"Respond called on unsupported fcall type"); + break; + case TVersion: + assert(error == nil); + free(r->ifcall.version.version); + + thread->lock(&p9conn->rlock); + thread->lock(&p9conn->wlock); + msize = min(r->ofcall.version.msize, IXP_MAX_MSG); + p9conn->rmsg.data = erealloc(p9conn->rmsg.data, msize); + p9conn->wmsg.data = erealloc(p9conn->wmsg.data, msize); + p9conn->rmsg.size = msize; + p9conn->wmsg.size = msize; + thread->unlock(&p9conn->wlock); + thread->unlock(&p9conn->rlock); + r->ofcall.version.msize = msize; + break; + case TAttach: + if(error) + destroyfid(p9conn, r->fid->fid); + free(r->ifcall.tattach.uname); + free(r->ifcall.tattach.aname); + break; + case TOpen: + case TCreate: + if(!error) { + r->ofcall.ropen.iounit = p9conn->rmsg.size - 24; + r->fid->iounit = r->ofcall.ropen.iounit; + r->fid->omode = r->ifcall.topen.mode; + r->fid->qid = r->ofcall.ropen.qid; + } + free(r->ifcall.tcreate.name); + break; + case TWalk: + if(error || r->ofcall.rwalk.nwqid < r->ifcall.twalk.nwname) { + if(r->ifcall.hdr.fid != r->ifcall.twalk.newfid && r->newfid) + destroyfid(p9conn, r->newfid->fid); + if(!error && r->ofcall.rwalk.nwqid == 0) + error = Enofile; + }else{ + if(r->ofcall.rwalk.nwqid == 0) + r->newfid->qid = r->fid->qid; + else + r->newfid->qid = r->ofcall.rwalk.wqid[r->ofcall.rwalk.nwqid-1]; + } + free(*r->ifcall.twalk.wname); + break; + case TWrite: + free(r->ifcall.twrite.data); + break; + case TRemove: + if(r->fid) + destroyfid(p9conn, r->fid->fid); + break; + case TClunk: + if(r->fid) + destroyfid(p9conn, r->fid->fid); + break; + case TFlush: + if((r->oldreq = ixp_mapget(&p9conn->tagmap, r->ifcall.tflush.oldtag))) + respond(r->oldreq, Eintr); + break; + case TWStat: + ixp_freestat(&r->ifcall.twstat.stat); + break; + case TRead: + case TStat: + break; + /* Still to be implemented: auth */ + } + + r->ofcall.hdr.tag = r->ifcall.hdr.tag; + + if(error == nil) + r->ofcall.hdr.type = r->ifcall.hdr.type + 1; + else { + r->ofcall.hdr.type = RError; + r->ofcall.error.ename = (char*)error; + } + + ixp_printfcall(&r->ofcall); + + ixp_maprm(&p9conn->tagmap, r->ifcall.hdr.tag);; + + if(p9conn->conn) { + thread->lock(&p9conn->wlock); + msize = ixp_fcall2msg(&p9conn->wmsg, &r->ofcall); + if(ixp_sendmsg(p9conn->conn->fd, &p9conn->wmsg) != msize) + ixp_hangup(p9conn->conn); + thread->unlock(&p9conn->wlock); + } + + switch(r->ofcall.hdr.type) { + case RStat: + free(r->ofcall.rstat.stat); + break; + case RRead: + free(r->ofcall.rread.data); + break; + } + free(r); + decref_p9conn(p9conn); +} + +/* Flush a pending request */ +static void +voidrequest(void *context, void *arg) { + Ixp9Req *orig_req, *flush_req; + Ixp9Conn *conn; + + orig_req = arg; + conn = orig_req->conn; + conn->ref++; + + flush_req = emallocz(sizeof *orig_req); + flush_req->ifcall.hdr.type = TFlush; + flush_req->ifcall.hdr.tag = IXP_NOTAG; + flush_req->ifcall.tflush.oldtag = orig_req->ifcall.hdr.tag; + flush_req->conn = conn; + + flush_req->aux = *(void**)context; + *(void**)context = flush_req; +} + +/* Clunk an open Fid */ +static void +voidfid(void *context, void *arg) { + Ixp9Conn *p9conn; + Ixp9Req *clunk_req; + Fid *fid; + + fid = arg; + p9conn = fid->conn; + p9conn->ref++; + + clunk_req = emallocz(sizeof *clunk_req); + clunk_req->ifcall.hdr.type = TClunk; + clunk_req->ifcall.hdr.tag = IXP_NOTAG; + clunk_req->ifcall.hdr.fid = fid->fid; + clunk_req->fid = fid; + clunk_req->conn = p9conn; + + clunk_req->aux = *(void**)context; + *(void**)context = clunk_req; +} + +static void +cleanupconn(IxpConn *c) { + Ixp9Conn *p9conn; + Ixp9Req *req, *r; + + p9conn = c->aux; + p9conn->conn = nil; + req = nil; + if(p9conn->ref > 1) { + ixp_mapexec(&p9conn->fidmap, voidfid, &req); + ixp_mapexec(&p9conn->tagmap, voidrequest, &req); + } + while((r = req)) { + req = r->aux; + r->aux = nil; + handlereq(r); + } + decref_p9conn(p9conn); +} + +/* Handle incoming 9P connections */ +void +serve_9pcon(IxpConn *c) { + Ixp9Conn *p9conn; + int fd; + + fd = accept(c->fd, nil, nil); + if(fd < 0) + return; + + p9conn = emallocz(sizeof *p9conn); + p9conn->ref++; + p9conn->srv = c->aux; + p9conn->rmsg.size = 1024; + p9conn->wmsg.size = 1024; + p9conn->rmsg.data = emalloc(p9conn->rmsg.size); + p9conn->wmsg.data = emalloc(p9conn->wmsg.size); + + ixp_mapinit(&p9conn->tagmap, p9conn->taghash, nelem(p9conn->taghash)); + ixp_mapinit(&p9conn->fidmap, p9conn->fidhash, nelem(p9conn->fidhash)); + thread->initmutex(&p9conn->rlock); + thread->initmutex(&p9conn->wlock); + + ixp_listen(c->srv, fd, p9conn, handlefcall, cleanupconn); +} diff --git a/lib/libixp/rpc.c b/lib/libixp/rpc.c @@ -0,0 +1,262 @@ +/* From Plan 9's libmux. + * Copyright (c) 2003 Russ Cox, Massachusetts Institute of Technology + * Distributed under the same terms as libixp. + */ +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "ixp_local.h" + +static int gettag(IxpClient*, IxpRpc*); +static void puttag(IxpClient*, IxpRpc*); +static void enqueue(IxpClient*, IxpRpc*); +static void dequeue(IxpClient*, IxpRpc*); + +void +muxinit(IxpClient *mux) +{ + mux->tagrend.mutex = &mux->lk; + mux->sleep.next = &mux->sleep; + mux->sleep.prev = &mux->sleep; + thread->initmutex(&mux->lk); + thread->initmutex(&mux->rlock); + thread->initmutex(&mux->wlock); + thread->initrendez(&mux->tagrend); +} + +void +muxfree(IxpClient *mux) +{ + thread->mdestroy(&mux->lk); + thread->mdestroy(&mux->rlock); + thread->mdestroy(&mux->wlock); + thread->rdestroy(&mux->tagrend); + free(mux->wait); +} + +static void +initrpc(IxpClient *mux, IxpRpc *r) +{ + r->mux = mux; + r->waiting = 1; + r->r.mutex = &mux->lk; + r->p = nil; + thread->initrendez(&r->r); +} + +static void +freemuxrpc(IxpRpc *r) +{ + thread->rdestroy(&r->r); +} + +static int +sendrpc(IxpRpc *r, Fcall *f) +{ + int ret; + IxpClient *mux; + + ret = 0; + mux = r->mux; + /* assign the tag, add selves to response queue */ + thread->lock(&mux->lk); + r->tag = gettag(mux, r); + f->hdr.tag = r->tag; + enqueue(mux, r); + thread->unlock(&mux->lk); + + thread->lock(&mux->wlock); + if(!ixp_fcall2msg(&mux->wmsg, f) || !ixp_sendmsg(mux->fd, &mux->wmsg)) { + /* werrstr("settag/send tag %d: %r", tag); fprint(2, "%r\n"); */ + thread->lock(&mux->lk); + dequeue(mux, r); + puttag(mux, r); + thread->unlock(&mux->lk); + ret = -1; + } + thread->unlock(&mux->wlock); + return ret; +} + +static Fcall* +muxrecv(IxpClient *mux) +{ + Fcall *f; + + f = nil; + thread->lock(&mux->rlock); + if(ixp_recvmsg(mux->fd, &mux->rmsg) == 0) + goto fail; + f = emallocz(sizeof *f); + if(ixp_msg2fcall(&mux->rmsg, f) == 0) { + free(f); + f = nil; + } +fail: + thread->unlock(&mux->rlock); + return f; +} + +static void +dispatchandqlock(IxpClient *mux, Fcall *f) +{ + int tag; + IxpRpc *r2; + + tag = f->hdr.tag - mux->mintag; + thread->lock(&mux->lk); + /* hand packet to correct sleeper */ + if(tag < 0 || tag >= mux->mwait) { + fprintf(stderr, "libixp: recieved unfeasible tag: %d (min: %d, max: %d)\n", f->hdr.tag, mux->mintag, mux->mintag+mux->mwait); + goto fail; + } + r2 = mux->wait[tag]; + if(r2 == nil || r2->prev == nil) { + fprintf(stderr, "libixp: recieved message with bad tag\n"); + goto fail; + } + r2->p = f; + dequeue(mux, r2); + thread->wake(&r2->r); + return; +fail: + ixp_freefcall(f); + free(f); +} + +static void +electmuxer(IxpClient *mux) +{ + IxpRpc *rpc; + + /* if there is anyone else sleeping, wake them to mux */ + for(rpc=mux->sleep.next; rpc != &mux->sleep; rpc = rpc->next){ + if(!rpc->async){ + mux->muxer = rpc; + thread->wake(&rpc->r); + return; + } + } + mux->muxer = nil; +} + +Fcall* +muxrpc(IxpClient *mux, Fcall *tx) +{ + IxpRpc r; + Fcall *p; + + initrpc(mux, &r); + if(sendrpc(&r, tx) < 0) + return nil; + + thread->lock(&mux->lk); + /* wait for our packet */ + while(mux->muxer && mux->muxer != &r && !r.p) + thread->sleep(&r.r); + + /* if not done, there's no muxer; start muxing */ + if(!r.p){ + assert(mux->muxer == nil || mux->muxer == &r); + mux->muxer = &r; + while(!r.p){ + thread->unlock(&mux->lk); + p = muxrecv(mux); + if(p == nil){ + /* eof -- just give up and pass the buck */ + thread->lock(&mux->lk); + dequeue(mux, &r); + break; + } + dispatchandqlock(mux, p); + } + electmuxer(mux); + } + p = r.p; + puttag(mux, &r); + thread->unlock(&mux->lk); + if(p == nil) + werrstr("unexpected eof"); + return p; +} + +static void +enqueue(IxpClient *mux, IxpRpc *r) +{ + r->next = mux->sleep.next; + r->prev = &mux->sleep; + r->next->prev = r; + r->prev->next = r; +} + +static void +dequeue(IxpClient *mux, IxpRpc *r) +{ + r->next->prev = r->prev; + r->prev->next = r->next; + r->prev = nil; + r->next = nil; +} + +static int +gettag(IxpClient *mux, IxpRpc *r) +{ + int i, mw; + IxpRpc **w; + + for(;;){ + /* wait for a free tag */ + while(mux->nwait == mux->mwait){ + if(mux->mwait < mux->maxtag-mux->mintag){ + mw = mux->mwait; + if(mw == 0) + mw = 1; + else + mw <<= 1; + w = realloc(mux->wait, mw * sizeof *w); + if(w == nil) + return -1; + memset(w+mux->mwait, 0, (mw-mux->mwait) * sizeof *w); + mux->wait = w; + mux->freetag = mux->mwait; + mux->mwait = mw; + break; + } + thread->sleep(&mux->tagrend); + } + + i=mux->freetag; + if(mux->wait[i] == 0) + goto Found; + for(; i<mux->mwait; i++) + if(mux->wait[i] == 0) + goto Found; + for(i=0; i<mux->freetag; i++) + if(mux->wait[i] == 0) + goto Found; + /* should not fall out of while without free tag */ + abort(); + } + +Found: + mux->nwait++; + mux->wait[i] = r; + r->tag = i+mux->mintag; + return r->tag; +} + +static void +puttag(IxpClient *mux, IxpRpc *r) +{ + int i; + + i = r->tag - mux->mintag; + assert(mux->wait[i] == r); + mux->wait[i] = nil; + mux->nwait--; + mux->freetag = i; + thread->wake(&mux->tagrend); + freemuxrpc(r); +} + diff --git a/lib/libixp/server.c b/lib/libixp/server.c @@ -0,0 +1,165 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include "ixp_local.h" + +/** + * Function: ixp_listen + * + * Params: + * fs - The file descriptor on which to listen. + * aux - A piece of data to store in the connection's + * T<IxpConn> data structure. + * read - The function to call when the connection has + * data available to read. + * close - A cleanup function to call when the + * connection is closed. + * + * Starts the server P<s> listening on P<fd>. The optional + * callbacks are called as described, with the connections + * T<IxpConn> data structure as their arguments. + * + * Returns: + * Returns the connection's new T<IxpConn> data + * structure. + * + * S<IxpConn> + */ +IxpConn* +ixp_listen(IxpServer *s, int fd, void *aux, + void (*read)(IxpConn *c), + void (*close)(IxpConn *c) + ) { + IxpConn *c; + + c = emallocz(sizeof *c); + c->fd = fd; + c->aux = aux; + c->srv = s; + c->read = read; + c->close = close; + c->next = s->conn; + s->conn = c; + return c; +} + +/** + * Function: ixp_hangup + * Function: ixp_server_close + * + * ixp_hangup closes a connection, and stops the server + * listening on it. It calls the connection's close + * function, if it exists. ixp_server_close calls ixp_hangup + * on all of the connections on which the server is + * listening. + */ + +void +ixp_hangup(IxpConn *c) { + IxpServer *s; + IxpConn **tc; + + s = c->srv; + for(tc=&s->conn; *tc; tc=&(*tc)->next) + if(*tc == c) break; + assert(*tc == c); + + *tc = c->next; + c->closed = 1; + if(c->close) + c->close(c); + else + shutdown(c->fd, SHUT_RDWR); + + close(c->fd); + free(c); +} + +void +ixp_server_close(IxpServer *s) { + IxpConn *c, *next; + + for(c = s->conn; c; c = next) { + next = c->next; + ixp_hangup(c); + } +} + +static void +prepare_select(IxpServer *s) { + IxpConn *c; + + FD_ZERO(&s->rd); + for(c = s->conn; c; c = c->next) + if(c->read) { + if(s->maxfd < c->fd) + s->maxfd = c->fd; + FD_SET(c->fd, &s->rd); + } +} + +static void +handle_conns(IxpServer *s) { + IxpConn *c, *n; + for(c = s->conn; c; c = n) { + n = c->next; + if(FD_ISSET(c->fd, &s->rd)) + c->read(c); + } +} + +/** + * Function: ixp_serverloop + * + * Enters the main loop of the server. Exits when + * P<s>->running becomes false, or when select(2) returns an + * error other than EINTR. + * + * S<IxpServer> + * + * Returns: + * Returns 0 when the loop exits normally, and 1 when + * it exits on error. V<errno> or the return value of + * ixp_errbuf(3) may be inspected. + * + */ + +int +ixp_serverloop(IxpServer *s) { + timeval *tvp; + timeval tv; + long timeout; + int r; + + s->running = 1; + thread->initmutex(&s->lk); + while(s->running) { + if(s->preselect) + s->preselect(s); + + tvp = nil; + timeout = ixp_nexttimer(s); + if(timeout > 0) { + tv.tv_sec = timeout/1000; + tv.tv_usec = timeout%1000 * 1000; + tvp = &tv; + } + + prepare_select(s); + r = thread->select(s->maxfd + 1, &s->rd, 0, 0, tvp); + if(r < 0) { + if(errno == EINTR) + continue; + return 1; + } + handle_conns(s); + } + return 0; +} + diff --git a/lib/libixp/socket.c b/lib/libixp/socket.c @@ -0,0 +1,278 @@ +/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> + * Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ +#include <errno.h> +#include <netdb.h> +#include <netinet/in.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include "ixp_local.h" + +/* Note: These functions modify the strings that they are passed. + * The lookup function duplicates the original string, so it is + * not modified. + */ + +/* From FreeBSD's sys/su.h */ +#define SUN_LEN(su) \ + (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) + +typedef struct addrinfo addrinfo; +typedef struct sockaddr sockaddr; +typedef struct sockaddr_un sockaddr_un; +typedef struct sockaddr_in sockaddr_in; + +static char* +get_port(char *addr) { + char *s; + + s = strchr(addr, '!'); + if(s == nil) { + werrstr("no port provided"); + return nil; + } + + *s++ = '\0'; + if(*s == '\0') { + werrstr("invalid port number"); + return nil; + } + return s; +} + +static int +sock_unix(char *address, sockaddr_un *sa, socklen_t *salen) { + int fd; + + memset(sa, 0, sizeof *sa); + + sa->sun_family = AF_UNIX; + strncpy(sa->sun_path, address, sizeof sa->sun_path); + *salen = SUN_LEN(sa); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd < 0) + return -1; + return fd; +} + +static int +dial_unix(char *address) { + sockaddr_un sa; + socklen_t salen; + int fd; + + fd = sock_unix(address, &sa, &salen); + if(fd == -1) + return fd; + + if(connect(fd, (sockaddr*) &sa, salen)) { + close(fd); + return -1; + } + return fd; +} + +static int +announce_unix(char *file) { + const int yes = 1; + sockaddr_un sa; + socklen_t salen; + int fd; + + signal(SIGPIPE, SIG_IGN); + + fd = sock_unix(file, &sa, &salen); + if(fd == -1) + return fd; + + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof yes) < 0) + goto fail; + + unlink(file); + if(bind(fd, (sockaddr*)&sa, salen) < 0) + goto fail; + + chmod(file, S_IRWXU); + if(listen(fd, IXP_MAX_CACHE) < 0) + goto fail; + + return fd; + +fail: + close(fd); + return -1; +} + +static addrinfo* +alookup(char *host, int announce) { + addrinfo hints, *ret; + char *port; + int err; + + /* Truncates host at '!' */ + port = get_port(host); + if(port == nil) + return nil; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + if(announce) { + hints.ai_flags = AI_PASSIVE; + if(!strcmp(host, "*")) + host = nil; + } + + err = getaddrinfo(host, port, &hints, &ret); + if(err) { + werrstr("getaddrinfo: %s", gai_strerror(err)); + return nil; + } + return ret; +} + +static int +ai_socket(addrinfo *ai) { + return socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); +} + +static int +dial_tcp(char *host) { + addrinfo *ai, *aip; + int fd; + + aip = alookup(host, 0); + if(aip == nil) + return -1; + + SET(fd); + for(ai = aip; ai; ai = ai->ai_next) { + fd = ai_socket(ai); + if(fd == -1) { + werrstr("socket: %s", strerror(errno)); + continue; + } + + if(connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) + break; + + werrstr("connect: %s", strerror(errno)); + close(fd); + fd = -1; + } + + freeaddrinfo(aip); + return fd; +} + +static int +announce_tcp(char *host) { + addrinfo *ai, *aip; + int fd; + + aip = alookup(host, 1); + if(aip == nil) + return -1; + + /* Probably don't need to loop */ + SET(fd); + for(ai = aip; ai; ai = ai->ai_next) { + fd = ai_socket(ai); + if(fd == -1) + continue; + + if(bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) + goto fail; + + if(listen(fd, IXP_MAX_CACHE) < 0) + goto fail; + break; + fail: + close(fd); + fd = -1; + } + + freeaddrinfo(aip); + return fd; +} + +typedef struct addrtab addrtab; +static +struct addrtab { + char *type; + int (*fn)(char*); +} dtab[] = { + {"tcp", dial_tcp}, + {"unix", dial_unix}, + {0, 0} +}, atab[] = { + {"tcp", announce_tcp}, + {"unix", announce_unix}, + {0, 0} +}; + +static int +lookup(const char *address, addrtab *tab) { + char *addr, *type; + int ret; + + ret = -1; + type = estrdup(address); + + addr = strchr(type, '!'); + if(addr == nil) + werrstr("no address type defined"); + else { + *addr++ = '\0'; + for(; tab->type; tab++) + if(strcmp(tab->type, type) == 0) break; + if(tab->type == nil) + werrstr("unsupported address type"); + else + ret = tab->fn(addr); + } + + free(type); + return ret; +} + +/** + * Function: ixp_dial + * Function: ixp_announce + * + * Params: + * address - An address on which to connect or listen, + * specified in the Plan 9 resources + * specification format + * (<protocol>!address[!<port>]) + * + * These functions hide some of the ugliness of Berkely + * Sockets. ixp_dial connects to the resource at P<address>, + * while ixp_announce begins listening on P<address>. + * + * Returns: + * These functions return file descriptors on success, + * and -1 on failure. ixp_errbuf(3) may be inspected on + * failure. + */ + +int +ixp_dial(const char *address) { + return lookup(address, dtab); +} + +int +ixp_announce(const char *address) { + return lookup(address, atab); +} + diff --git a/lib/libixp/srv_util.c b/lib/libixp/srv_util.c @@ -0,0 +1,457 @@ +/* Copyright ©2006-2008 Kris Maglione <fbsdaemon at gmail dot com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "ixp_local.h" + +typedef void* IxpFileIdU; + +static char + Enofile[] = "file not found"; + +#include "ixp_srvutil.h" + +struct IxpQueue { + IxpQueue* link; + char* dat; + long len; +}; + +/* Macros */ +#define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF)) + +/* Global Vars */ +/***************/ +static IxpFileId* free_fileid; + +/* Utility Functions */ +/** + * Obtain an empty, reference counted IxpFileId struct. + */ +IxpFileId* +ixp_srv_getfile(void) { + IxpFileId *file; + int i; + + if(!free_fileid) { + i = 15; + file = emallocz(i * sizeof *file); + for(; i; i--) { + file->next = free_fileid; + free_fileid = file++; + } + } + file = free_fileid; + free_fileid = file->next; + file->p = nil; + file->volatil = 0; + file->nref = 1; + file->next = nil; + file->pending = false; + return file; +} + +/** + * Decrease the reference count of the given IxpFileId, + * and push it onto the free list when it reaches 0; + */ +void +ixp_srv_freefile(IxpFileId *f) { + if(--f->nref) + return; + free(f->tab.name); + f->next = free_fileid; + free_fileid = f; +} + +/** + * Increase the reference count of every IxpFileId linked + * to 'f'. + */ +IxpFileId* +ixp_srv_clonefiles(IxpFileId *f) { + IxpFileId *r; + + r = emalloc(sizeof *r); + memcpy(r, f, sizeof *r); + r->tab.name = estrdup(r->tab.name); + r->nref = 1; + for(f=f->next; f; f=f->next) + assert(f->nref++); + return r; +} + +void +ixp_srv_readbuf(Ixp9Req *req, char *buf, uint len) { + + if(req->ifcall.io.offset >= len) + return; + + len -= req->ifcall.io.offset; + if(len > req->ifcall.io.count) + len = req->ifcall.io.count; + req->ofcall.io.data = emalloc(len); + memcpy(req->ofcall.io.data, buf + req->ifcall.io.offset, len); + req->ofcall.io.count = len; +} + +void +ixp_srv_writebuf(Ixp9Req *req, char **buf, uint *len, uint max) { + IxpFileId *file; + char *p; + uint offset, count; + + file = req->fid->aux; + + offset = req->ifcall.io.offset; + if(file->tab.perm & DMAPPEND) + offset = *len; + + if(offset > *len || req->ifcall.io.count == 0) { + req->ofcall.io.count = 0; + return; + } + + count = req->ifcall.io.count; + if(max && (offset + count > max)) + count = max - offset; + + *len = offset + count; + if(max == 0) + *buf = erealloc(*buf, *len + 1); + p = *buf; + + memcpy(p+offset, req->ifcall.io.data, count); + req->ofcall.io.count = count; + p[offset+count] = '\0'; +} + +/** + * Ensure that the data member of 'r' is null terminated, + * removing any new line from its end. + */ +void +ixp_srv_data2cstring(Ixp9Req *req) { + char *p, *q; + uint i; + + i = req->ifcall.io.count; + p = req->ifcall.io.data; + if(i && p[i - 1] == '\n') + i--; + q = memchr(p, '\0', i); + if(q) + i = q - p; + + p = erealloc(req->ifcall.io.data, i+1); + p[i] = '\0'; + req->ifcall.io.data = p; +} + +char* +ixp_srv_writectl(Ixp9Req *req, char* (*fn)(void*, IxpMsg*)) { + char *err, *s, *p, c; + IxpFileId *file; + IxpMsg msg; + + file = req->fid->aux; + + ixp_srv_data2cstring(req); + s = req->ifcall.io.data; + + err = nil; + c = *s; + while(c != '\0') { + while(*s == '\n') + s++; + p = s; + while(*p != '\0' && *p != '\n') + p++; + c = *p; + *p = '\0'; + + msg = ixp_message(s, p-s, 0); + s = fn(file->p, &msg); + if(s) + err = s; + s = p + 1; + } + return err; +} + +void +ixp_pending_respond(Ixp9Req *req) { + IxpFileId *file; + IxpPendingLink *p; + IxpRequestLink *req_link; + IxpQueue *queue; + + file = req->fid->aux; + assert(file->pending); + p = file->p; + if(p->queue) { + queue = p->queue; + p->queue = queue->link; + req->ofcall.io.data = queue->dat; + req->ofcall.io.count = queue->len; + if(req->aux) { + req_link = req->aux; + req_link->next->prev = req_link->prev; + req_link->prev->next = req_link->next; + free(req_link); + } + respond(req, nil); + free(queue); + }else { + req_link = emallocz(sizeof *req_link); + req_link->req = req; + req_link->next = &p->pending->req; + req_link->prev = req_link->next->prev; + req_link->next->prev = req_link; + req_link->prev->next = req_link; + req->aux = req_link; + } +} + +void +ixp_pending_write(IxpPending *pending, char *dat, long n) { + IxpRequestLink req_link; + IxpQueue **qp, *queue; + IxpPendingLink *pp; + IxpRequestLink *rp; + + if(n == 0) + return; + + if(pending->req.next == nil) { + pending->req.next = &pending->req; + pending->req.prev = &pending->req; + pending->fids.prev = &pending->fids; + pending->fids.next = &pending->fids; + } + + for(pp=pending->fids.next; pp != &pending->fids; pp=pp->next) { + for(qp=&pp->queue; *qp; qp=&qp[0]->link) + ; + queue = emallocz(sizeof *queue); + queue->dat = emalloc(n); + memcpy(queue->dat, dat, n); + queue->len = n; + *qp = queue; + } + + req_link.next = &req_link; + req_link.prev = &req_link; + if(pending->req.next != &pending->req) { + req_link.next = pending->req.next; + req_link.prev = pending->req.prev; + pending->req.prev = &pending->req; + pending->req.next = &pending->req; + } + req_link.prev->next = &req_link; + req_link.next->prev = &req_link; + + while((rp = req_link.next) != &req_link) + ixp_pending_respond(rp->req); +} + +void +ixp_pending_pushfid(IxpPending *pending, IxpFid *fid) { + IxpPendingLink *pend_link; + IxpFileId *file; + + if(pending->req.next == nil) { + pending->req.next = &pending->req; + pending->req.prev = &pending->req; + pending->fids.prev = &pending->fids; + pending->fids.next = &pending->fids; + } + + file = fid->aux; + pend_link = emallocz(sizeof *pend_link); + pend_link->fid = fid; + pend_link->pending = pending; + pend_link->next = &pending->fids; + pend_link->prev = pend_link->next->prev; + pend_link->next->prev = pend_link; + pend_link->prev->next = pend_link; + file->pending = true; + file->p = pend_link; +} + +static void +pending_flush(Ixp9Req *req) { + IxpFileId *file; + IxpRequestLink *req_link; + + file = req->fid->aux; + if(file->pending) { + req_link = req->aux; + if(req_link) { + req_link->prev->next = req_link->next; + req_link->next->prev = req_link->prev; + free(req_link); + } + } +} + +void +ixp_pending_flush(Ixp9Req *req) { + + pending_flush(req->oldreq); +} + +bool +ixp_pending_clunk(Ixp9Req *req) { + IxpPending *pending; + IxpPendingLink *pend_link; + IxpRequestLink *req_link; + Ixp9Req *r; + IxpFileId *file; + IxpQueue *queue; + bool more; + + file = req->fid->aux; + pend_link = file->p; + + pending = pend_link->pending; + for(req_link=pending->req.next; req_link != &pending->req;) { + r = req_link->req; + req_link = req_link->next; + if(r->fid == pend_link->fid) { + pending_flush(r); + respond(r, "interrupted"); + } + } + + pend_link->prev->next = pend_link->next; + pend_link->next->prev = pend_link->prev; + + while((queue = pend_link->queue)) { + pend_link->queue = queue->link; + free(queue->dat); + free(queue); + } + more = (pend_link->pending->fids.next == &pend_link->pending->fids); + free(pend_link); + respond(req, nil); + return more; +} + +bool +ixp_srv_verifyfile(IxpFileId *file, IxpLookupFn lookup) { + IxpFileId *tfile; + int ret; + + if(!file->next) + return true; + + ret = false; + if(ixp_srv_verifyfile(file->next, lookup)) { + tfile = lookup(file->next, file->tab.name); + if(tfile) { + if(!tfile->volatil || tfile->p == file->p) + ret = true; + ixp_srv_freefile(tfile); + } + } + return ret; +} + +void +ixp_srv_readdir(Ixp9Req *req, IxpLookupFn lookup, void (*dostat)(IxpStat*, IxpFileId*)) { + IxpMsg msg; + IxpFileId *file, *tfile; + IxpStat stat; + char *buf; + ulong size, n; + uvlong offset; + + file = req->fid->aux; + + size = req->ifcall.io.count; + if(size > req->fid->iounit) + size = req->fid->iounit; + buf = emallocz(size); + msg = ixp_message(buf, size, MsgPack); + + file = lookup(file, nil); + tfile = file; + /* Note: The first file is ".", so we skip it. */ + offset = 0; + for(file=file->next; file; file=file->next) { + dostat(&stat, file); + n = ixp_sizeof_stat(&stat); + if(offset >= req->ifcall.io.offset) { + if(size < n) + break; + ixp_pstat(&msg, &stat); + size -= n; + } + offset += n; + } + while((file = tfile)) { + tfile=tfile->next; + ixp_srv_freefile(file); + } + req->ofcall.io.count = msg.pos - msg.data; + req->ofcall.io.data = msg.data; + respond(req, nil); +} + +void +ixp_srv_walkandclone(Ixp9Req *req, IxpLookupFn lookup) { + IxpFileId *file, *tfile; + int i; + + file = ixp_srv_clonefiles(req->fid->aux); + for(i=0; i < req->ifcall.twalk.nwname; i++) { + if(!strcmp(req->ifcall.twalk.wname[i], "..")) { + if(file->next) { + tfile=file; + file=file->next; + ixp_srv_freefile(tfile); + } + }else{ + tfile = lookup(file, req->ifcall.twalk.wname[i]); + if(!tfile) + break; + assert(!tfile->next); + if(strcmp(req->ifcall.twalk.wname[i], ".")) { + tfile->next = file; + file = tfile; + } + } + req->ofcall.rwalk.wqid[i].type = file->tab.qtype; + req->ofcall.rwalk.wqid[i].path = QID(file->tab.type, file->id); + } + /* There should be a way to do this on freefid() */ + if(i < req->ifcall.twalk.nwname) { + while((tfile = file)) { + file=file->next; + ixp_srv_freefile(tfile); + } + respond(req, Enofile); + return; + } + /* Remove refs for req->fid if no new fid */ + if(req->ifcall.hdr.fid == req->ifcall.twalk.newfid) { + tfile = req->fid->aux; + req->fid->aux = file; + while((file = tfile)) { + tfile = tfile->next; + ixp_srv_freefile(file); + } + }else + req->newfid->aux = file; + req->ofcall.rwalk.nwqid = i; + respond(req, nil); +} + diff --git a/lib/libixp/thread.c b/lib/libixp/thread.c @@ -0,0 +1,97 @@ +/* Public Domain --Kris Maglione */ +#include <unistd.h> +#include "ixp_local.h" + +static IxpThread ixp_nothread; +IxpThread*ixp_thread = &ixp_nothread; + +static char* +errbuf(void) { + static char errbuf[IXP_ERRMAX]; + + return errbuf; +} + +static void +mvoid(IxpMutex *m) { + USED(m); + return; +} + +static int +mtrue(IxpMutex *m) { + USED(m); + return 1; +} + +static int +mfalse(IxpMutex *m) { + USED(m); + return 0; +} + +static void +rwvoid(IxpRWLock *rw) { + USED(rw); + return; +} + +static int +rwtrue(IxpRWLock *rw) { + USED(rw); + return 1; +} + +static int +rwfalse(IxpRWLock *m) { + USED(m); + return 0; +} + +static void +rvoid(IxpRendez *r) { + USED(r); + return; +} + +static int +rfalse(IxpRendez *r) { + USED(r); + return 0; +} + +static void +rsleep(IxpRendez *r) { + USED(r); + eprint("rsleep called when not implemented\n"); +} + +static IxpThread ixp_nothread = { + /* RWLock */ + .initrwlock = rwfalse, + .rlock = rwvoid, + .runlock = rwvoid, + .canrlock = rwtrue, + .wlock = rwvoid, + .wunlock = rwvoid, + .canwlock = rwtrue, + .rwdestroy = rwvoid, + /* Mutex */ + .initmutex = mfalse, + .lock = mvoid, + .unlock = mvoid, + .canlock = mtrue, + .mdestroy = mvoid, + /* Rendez */ + .initrendez = rfalse, + .sleep = rsleep, + .wake = rfalse, + .wakeall = rfalse, + .rdestroy = rvoid, + /* Other */ + .errbuf = errbuf, + .read = read, + .write = write, + .select = select, +}; + diff --git a/lib/libixp/timer.c b/lib/libixp/timer.c @@ -0,0 +1,139 @@ +/* Copyright ©2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <stdlib.h> +#include <sys/time.h> +#include "ixp_local.h" + +/* This really needn't be threadsafe, as it has little use in + * threaded programs, but it is, nonetheless. + */ + +static long lastid = 1; + +/** + * Function: ixp_msec + * + * Returns: + * Returns the time since the Epoch in milliseconds. + * Be aware that this may overflow. + */ +long +ixp_msec(void) { + timeval tv; + + if(gettimeofday(&tv, 0) < 0) + return -1; + return tv.tv_sec*1000 + tv.tv_usec/1000; +} + +/** + * Function: ixp_settimer + * + * Params: + * msec - The timeout in milliseconds. + * fn - The function to call after P<msec> milliseconds + * have elapsed. + * aux - An arbitrary argument to pass to P<fn> when it + * is called. + * + * Initializes a callback-based timer to be triggerred after + * P<msec> milliseconds. The timer is passed its id number + * and the value of P<aux>. + * + * Returns: + * Returns the new timer's unique id number. + */ +long +ixp_settimer(IxpServer *s, long msec, void (*fn)(long, void*), void *aux) { + Timer **tp; + Timer *t; + long time; + + time = ixp_msec(); + if(time == -1) + return -1; + msec += time; + + t = emallocz(sizeof *t); + thread->lock(&s->lk); + t->id = lastid++; + t->msec = msec; + t->fn = fn; + t->aux = aux; + + for(tp=&s->timer; *tp; tp=&tp[0]->link) + if(tp[0]->msec < msec) + break; + t->link = *tp; + *tp = t; + thread->unlock(&s->lk); + return t->id; +} + +/** + * Function: ixp_unsettimer + * + * Params: + * id - The id number of the timer to void. + * + * Voids the timer identified by P<id>. + * + * Returns: + * Returns true if a timer was stopped, false + * otherwise. + */ +int +ixp_unsettimer(IxpServer *s, long id) { + Timer **tp; + Timer *t; + + thread->lock(&s->lk); + for(tp=&s->timer; (t=*tp); tp=&t->link) + if(t->id == id) + break; + if(t) { + *tp = t->link; + free(t); + } + thread->unlock(&s->lk); + return t != nil; +} + +/** + * Function: ixp_nexttimer + * + * Triggers any timers whose timeouts have ellapsed. This is + * primarilly intended to be called from libixp's select + * loop. + * + * Returns: + * Returns the number of milliseconds until the next + * timer's timeout. + */ +long +ixp_nexttimer(IxpServer *s) { + Timer *t; + long time, ret; + + SET(time); + thread->lock(&s->lk); + while((t = s->timer)) { + time = ixp_msec(); + if(t->msec > time) + break; + s->timer = t->link; + + thread->unlock(&s->lk); + t->fn(t->id, t->aux); + free(t); + thread->lock(&s->lk); + } + ret = 0; + if(t) + ret = t->msec - time; + thread->unlock(&s->lk); + return ret; +} + diff --git a/lib/libixp/transport.c b/lib/libixp/transport.c @@ -0,0 +1,97 @@ +/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include "ixp_local.h" + +static int +mread(int fd, IxpMsg *msg, uint count) { + int r, n; + + n = msg->end - msg->pos; + if(n <= 0) { + werrstr("buffer full"); + return -1; + } + if(n > count) + n = count; + + r = thread->read(fd, msg->pos, n); + if(r > 0) + msg->pos += r; + return r; +} + +static int +readn(int fd, IxpMsg *msg, uint count) { + uint num; + int r; + + num = count; + while(num > 0) { + r = mread(fd, msg, num); + if(r == -1 && errno == EINTR) + continue; + if(r == 0) { + werrstr("broken pipe: %s", ixp_errbuf()); + return count - num; + } + num -= r; + } + return count - num; +} + +uint +ixp_sendmsg(int fd, IxpMsg *msg) { + int r; + + msg->pos = msg->data; + while(msg->pos < msg->end) { + r = thread->write(fd, msg->pos, msg->end - msg->pos); + if(r < 1) { + if(errno == EINTR) + continue; + werrstr("broken pipe: %s", ixp_errbuf()); + return 0; + } + msg->pos += r; + } + return msg->pos - msg->data; +} + +uint +ixp_recvmsg(int fd, IxpMsg *msg) { + enum { SSize = 4 }; + ulong msize, size; + + msg->mode = MsgUnpack; + msg->pos = msg->data; + msg->end = msg->data + msg->size; + if(readn(fd, msg, SSize) != SSize) + return 0; + + msg->pos = msg->data; + ixp_pu32(msg, &msize); + + size = msize - SSize; + if(size >= msg->end - msg->pos) { + werrstr("message too large"); + return 0; + } + if(readn(fd, msg, size) != size) { + werrstr("message incomplete"); + return 0; + } + + msg->end = msg->pos; + return msize; +} + diff --git a/lib/libixp/util.c b/lib/libixp/util.c @@ -0,0 +1,240 @@ +/* Written by Kris Maglione <fbsdaemon at gmail dot com> */ +/* Public domain */ +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include "ixp_local.h" + +char* +ixp_smprint(const char *fmt, ...) { + va_list ap; + char *s; + + va_start(ap, fmt); + s = ixp_vsmprint(fmt, ap); + va_end(ap); + if(s == nil) + ixp_werrstr("no memory"); + return s; +} + +static char* +_user(void) { + static char *user; + struct passwd *pw; + + if(user == nil) { + pw = getpwuid(getuid()); + if(pw) + user = strdup(pw->pw_name); + } + if(user == nil) + user = "none"; + return user; +} + +static int +rmkdir(char *path, int mode) { + char *p; + int ret; + char c; + + for(p = path+1; ; p++) { + c = *p; + if((c == '/') || (c == '\0')) { + *p = '\0'; + ret = mkdir(path, mode); + if((ret == -1) && (errno != EEXIST)) { + ixp_werrstr("Can't create path '%s': %s", path, ixp_errbuf()); + return 0; + } + *p = c; + } + if(c == '\0') + break; + } + return 1; +} + +static char* +ns_display(void) { + char *path, *disp; + struct stat st; + + disp = getenv("DISPLAY"); + if(disp == nil || disp[0] == '\0') { + ixp_werrstr("$DISPLAY is unset"); + return nil; + } + + disp = estrdup(disp); + path = &disp[strlen(disp) - 2]; + if(path > disp && !strcmp(path, ".0")) + *path = '\0'; + + path = ixp_smprint("/tmp/ns.%s.%s", _user(), disp); + free(disp); + + if(!rmkdir(path, 0700)) + ; + else if(stat(path, &st)) + ixp_werrstr("Can't stat ns_path '%s': %s", path, ixp_errbuf()); + else if(getuid() != st.st_uid) + ixp_werrstr("ns_path '%s' exists but is not owned by you", path); + else if((st.st_mode & 077) && chmod(path, st.st_mode & ~077)) + ixp_werrstr("Namespace path '%s' exists, but has wrong permissions: %s", path, ixp_errbuf()); + else + return path; + free(path); + return nil; +} + +/** + * Function: ixp_namespace + * + * Returns the path of the canonical 9p namespace directory. + * Either the value of $NAMESPACE, if it's set, or, roughly, + * /tmp/ns.${USER}.${DISPLAY:%.0=%}. In the latter case, the + * directory is created if it doesn't exist, and it is + * ensured to be owned by the current user, with no group or + * other permissions. + * + * Returns: + * A statically allocated string which must not be freed + * or altered by the caller. The same value is returned + * upon successive calls. + */ +/* Not especially threadsafe. */ +char* +ixp_namespace(void) { + static char *namespace; + + if(namespace == nil) + namespace = getenv("NAMESPACE"); + if(namespace == nil) + namespace = ns_display(); + return namespace; +} + +void +eprint(const char *fmt, ...) { + va_list ap; + int err; + + err = errno; + fprintf(stderr, "libixp: fatal: "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if(fmt[strlen(fmt)-1] == ':') + fprintf(stderr, " %s\n", strerror(err)); + else + fprintf(stderr, "\n"); + + exit(1); +} + +/* Can't malloc */ +static void +mfatal(char *name, uint size) { + const char + couldnot[] = "libixp: fatal: Could not ", + paren[] = "() ", + bytes[] = " bytes\n"; + char sizestr[8]; + int i; + + i = sizeof sizestr; + do { + sizestr[--i] = '0' + (size%10); + size /= 10; + } while(size > 0); + + write(1, couldnot, sizeof(couldnot)-1); + write(1, name, strlen(name)); + write(1, paren, sizeof(paren)-1); + write(1, sizestr+i, sizeof(sizestr)-i); + write(1, bytes, sizeof(bytes)-1); + + exit(1); +} + +void* +emalloc(uint size) { + void *ret = malloc(size); + if(!ret) + mfatal("malloc", size); + return ret; +} + +void* +emallocz(uint size) { + void *ret = emalloc(size); + memset(ret, 0, size); + return ret; +} + +void* +erealloc(void *ptr, uint size) { + void *ret = realloc(ptr, size); + if(!ret) + mfatal("realloc", size); + return ret; +} + +char* +estrdup(const char *str) { + void *ret = strdup(str); + if(!ret) + mfatal("strdup", strlen(str)); + return ret; +} + +uint +tokenize(char *res[], uint reslen, char *str, char delim) { + char *s; + uint i; + + i = 0; + s = str; + while(i < reslen && *s) { + while(*s == delim) + *(s++) = '\0'; + if(*s) + res[i++] = s; + while(*s && *s != delim) + s++; + } + return i; +} + +uint +strlcat(char *dst, const char *src, uint size) { + const char *s; + char *d; + int n, len; + + d = dst; + s = src; + n = size; + while(n-- > 0 && *d != '\0') + d++; + len = n; + + while(*s != '\0' && n-- > 0) + *d++ = *s++; + while(*s++ != '\0') + n--; + if(len > 0) + *d = '\0'; + return size - n - 1; +} + diff --git a/lib/libixp_pthread/Makefile b/lib/libixp_pthread/Makefile @@ -0,0 +1,10 @@ +ROOT= ../.. +include ${ROOT}/mk/hdr.mk +include ${ROOT}/mk/ixp.mk + +TARG = libixp_pthread + +OBJ = thread_pthread + +include ${ROOT}/mk/lib.mk + diff --git a/lib/libixp_pthread/thread_pthread.c b/lib/libixp_pthread/thread_pthread.c @@ -0,0 +1,184 @@ +/* Written by Kris Maglione <fbsdaemon@gmail.com> */ +/* Public domain */ +#define _XOPEN_SOURCE 600 +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <unistd.h> +#include "ixp_local.h" + +static IxpThread ixp_pthread; +static pthread_key_t errstr_k; + +int +ixp_pthread_init() { + int ret; + + ret = pthread_key_create(&errstr_k, free); + if(ret) { + werrstr("can't create TLS value: %s", ixp_errbuf()); + return 1; + } + + ixp_thread = &ixp_pthread; + return 0; +} + +static char* +errbuf(void) { + char *ret; + + ret = pthread_getspecific(errstr_k); + if(ret == nil) { + ret = emallocz(IXP_ERRMAX); + pthread_setspecific(errstr_k, (void*)ret); + } + return ret; +} + +static void +mlock(IxpMutex *m) { + pthread_mutex_lock(m->aux); +} + +static int +mcanlock(IxpMutex *m) { + return !pthread_mutex_trylock(m->aux); +} + +static void +munlock(IxpMutex *m) { + pthread_mutex_unlock(m->aux); +} + +static void +mdestroy(IxpMutex *m) { + pthread_mutex_destroy(m->aux); + free(m->aux); +} + +static int +initmutex(IxpMutex *m) { + pthread_mutex_t *mutex; + + mutex = emalloc(sizeof *mutex); + if(pthread_mutex_init(mutex, nil)) { + free(mutex); + return 1; + } + + m->aux = mutex; + return 0; +} + +static void +rlock(IxpRWLock *rw) { + pthread_rwlock_rdlock(rw->aux); +} + +static int +canrlock(IxpRWLock *rw) { + return !pthread_rwlock_tryrdlock(rw->aux); +} + +static void +wlock(IxpRWLock *rw) { + pthread_rwlock_rdlock(rw->aux); +} + +static int +canwlock(IxpRWLock *rw) { + return !pthread_rwlock_tryrdlock(rw->aux); +} + +static void +rwunlock(IxpRWLock *rw) { + pthread_rwlock_unlock(rw->aux); +} + +static void +rwdestroy(IxpRWLock *rw) { + pthread_rwlock_destroy(rw->aux); + free(rw->aux); +} + +static int +initrwlock(IxpRWLock *rw) { + pthread_rwlock_t *rwlock; + + rwlock = emalloc(sizeof *rwlock); + if(pthread_rwlock_init(rwlock, nil)) { + free(rwlock); + return 1; + } + + rw->aux = rwlock; + return 0; +} + +static void +rsleep(IxpRendez *r) { + pthread_cond_wait(r->aux, r->mutex->aux); +} + +static int +rwake(IxpRendez *r) { + pthread_cond_signal(r->aux); + return 0; +} + +static int +rwakeall(IxpRendez *r) { + pthread_cond_broadcast(r->aux); + return 0; +} + +static void +rdestroy(IxpRendez *r) { + pthread_cond_destroy(r->aux); + free(r->aux); +} + +static int +initrendez(IxpRendez *r) { + pthread_cond_t *cond; + + cond = emalloc(sizeof *cond); + if(pthread_cond_init(cond, nil)) { + free(cond); + return 1; + } + + r->aux = cond; + return 0; +} + +static IxpThread ixp_pthread = { + /* Mutex */ + .initmutex = initmutex, + .lock = mlock, + .canlock = mcanlock, + .unlock = munlock, + .mdestroy = mdestroy, + /* RWLock */ + .initrwlock = initrwlock, + .rlock = rlock, + .canrlock = canrlock, + .wlock = wlock, + .canwlock = canwlock, + .runlock = rwunlock, + .wunlock = rwunlock, + .rwdestroy = rwdestroy, + /* Rendez */ + .initrendez = initrendez, + .sleep = rsleep, + .wake = rwake, + .wakeall = rwakeall, + .rdestroy = rdestroy, + /* Other */ + .errbuf = errbuf, + .read = read, + .write = write, + .select = select, +}; + diff --git a/lib/libixp_rubythread/Makefile b/lib/libixp_rubythread/Makefile @@ -0,0 +1,11 @@ +ROOT= ../.. +include ${ROOT}/mk/hdr.mk +include ${ROOT}/mk/ixp.mk + +CFLAGS += ${RUBYINC} + +TARG = libixp_rubythread +OBJ = thread_ruby + +include ${ROOT}/mk/lib.mk + diff --git a/lib/libixp_rubythread/thread_ruby.c b/lib/libixp_rubythread/thread_ruby.c @@ -0,0 +1,281 @@ +/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <ruby.h> +#include "ixp_local.h" + +static IxpThread ixp_rthread; +static char RWLock[]; + +int +ixp_rubyinit(void) { + rb_require("thread.rb"); + rb_eval_string(RWLock); + ixp_thread = &ixp_rthread; + return 0; +} + +static char* +errbuf(void) { + static ID key; + volatile VALUE val; + + if(key == 0L) + key = rb_intern("_ixp_errbuf"); + + val = rb_thread_local_aref(rb_thread_current(), key); + if(NIL_P(val)) { + val = rb_str_new(nil, IXP_ERRMAX); + rb_thread_local_aset(rb_thread_current(), key, val); + } + + Check_Type(val, T_STRING); + return RSTRING(val)->ptr; +} + +static void +save(char *eval, void **place) { + *place = (void*)rb_eval_string(eval); + rb_gc_register_address((VALUE*)place); +} + +static void +unsave(void **place) { + rb_gc_unregister_address((VALUE*)place); +} + +#define call(obj, meth, ...) rb_funcall((VALUE)obj, rb_intern(meth), __VA_ARGS__) + +/* Mutex */ +static int +initmutex(IxpMutex *m) { + save("Mutex.new", &m->aux); + return 0; +} + +static void +mdestroy(IxpMutex *m) { + unsave(&m->aux); +} + +static void +mlock(IxpMutex *m) { + call(m->aux, "lock", 0); +} + +static int +mcanlock(IxpMutex *m) { + return call(m->aux, "trylock", 0); +} + +static void +munlock(IxpMutex *m) { + call(m->aux, "unlock", 0); +} + +/* RWLock */ +static int +initrwlock(IxpRWLock *rw) { + save("RWLock.new", &rw->aux); + return 0; +} + +static void +rwdestroy(IxpRWLock *rw) { + unsave(&rw->aux); +} + +static void +rlock(IxpRWLock *rw) { + call(rw->aux, "rdlock", 0); +} + +static int +canrlock(IxpRWLock *rw) { + return call(rw->aux, "tryrdlock", 0) == Qtrue; +} + +static void +wlock(IxpRWLock *rw) { + call(rw->aux, "wrlock", 0); +} + +static int +canwlock(IxpRWLock *rw) { + return call(rw->aux, "trywrlock", 0) == Qtrue; +} + +static void +rwunlock(IxpRWLock *rw) { + call(rw->aux, "unlock", 0); +} + +/* Rendez */ +static int +initrendez(IxpRendez *r) { + save("ConditionVariable.new", &r->aux); + return 0; +} + +static void +rdestroy(IxpRendez *r) { + unsave(&r->aux); +} + +static void +rsleep(IxpRendez *r) { + call(r->aux, "wait", 1, (VALUE)r->mutex->aux); +} + +static int +rwake(IxpRendez *r) { + call(r->aux, "signal", 0); + return 0; +} + +static int +rwakeall(IxpRendez *r) { + call(r->aux, "broadcast", 0); + return 0; +} + +/* Yielding IO */ +static ssize_t +_read(int fd, void *buf, size_t size) { + int n; + + rb_thread_wait_fd(fd); + n = read(fd, buf, size); + + if(n < 0 && errno == EINTR) + rb_thread_schedule(); + return n; +} + +static ssize_t +_write(int fd, const void *buf, size_t size) { + int n; + + rb_thread_fd_writable(fd); + n = write(fd, buf, size); + + if(n < 0 && errno == EINTR) + rb_thread_schedule(); + return n; +} + +static IxpThread ixp_rthread = { + /* Mutex */ + .initmutex = initmutex, + .lock = mlock, + .canlock = mcanlock, + .unlock = munlock, + .mdestroy = mdestroy, + /* RWLock */ + .initrwlock = initrwlock, + .rlock = rlock, + .canrlock = canrlock, + .wlock = wlock, + .canwlock = canwlock, + .runlock = rwunlock, + .wunlock = rwunlock, + .rwdestroy = rwdestroy, + /* Rendez */ + .initrendez = initrendez, + .sleep = rsleep, + .wake = rwake, + .wakeall = rwakeall, + .rdestroy = rdestroy, + /* Other */ + .errbuf = errbuf, + .read = _read, + .write = _write, + .select = rb_thread_select, +}; + +static char RWLock[] = + "class RWLock \n" + " def initialize \n" + " @rdqueue = [] \n" + " @wrqueue = [] \n" + " @wrheld = nil \n" + " @rdheld = [] \n" + " end \n" + " \n" + " def rdlock \n" + " cr = Thread.critical \n" + " while (Thread.critical = true; @wrheld != nil && @rwheld != Thread.current)\n" + " @rdqueue.push Thread.current \n" + " Thread.stop \n" + " end \n" + " @wrheld = nil \n" + " @rdheld.push Thread.current \n" + " \n" + " @rdqueue.each {|t| t.wakeup} \n" + " Thread.critical = cr \n" + " self \n" + " end \n" + " \n" + " def wrlock \n" + " cr = Thread.critical \n" + " while (Thread.critical = true; \n" + " !@rdheld.empty? || (@wrheld != Thread.current && @wrheld != nil)) \n" + " @wrqueue.push Thread.current \n" + " Thread.stop \n" + " end \n" + " @wrheld = Thread.current \n" + " Thread.critical = cr \n" + " self \n" + " end \n" + " \n" + " \n" + " def tryrdlock \n" + " cr = Thread.critical \n" + " if @wrheld == nil \n" + " rdlock \n" + " true \n" + " else \n" + " false \n" + " end \n" + " ensure \n" + " Thread.critical = cr \n" + " end \n" + " \n" + " def trywrlock \n" + " cr = Thread.critical \n" + " if @wrheld == nil && @rdheld.empty? \n" + " wrlock \n" + " true \n" + " else \n" + " false \n" + " end \n" + " ensure \n" + " Thread.critical = cr \n" + " end \n" + " \n" + " def unlock \n" + " cr = Thread.critical \n" + " Thread.critical = true \n" + " \n" + " if @rdheld.include?(Thread.current) \n" + " @rdheld.remove!(Thread.current) \n" + " raise if @wrheld \n" + " elsif @rwheld != Thread.current \n" + " raise \n" + " end \n" + " \n" + " @wrheld = nil \n" + " if !@rwqueue.empty? && @rdheld.empty? \n" + " @wrheld = @wrqueue.shift \n" + " elsif !@rdqueue.empty \n" + " @wrheld = @rdqueue.shift \n" + " end \n" + " @wrheld.wakeup if @wrheld \n" + " ensure \n" + " Thread.critical = cr \n" + " end \n" + "end \n"; + diff --git a/lib/libixp_task/Makefile b/lib/libixp_task/Makefile @@ -0,0 +1,11 @@ +ROOT= ../.. +include ${ROOT}/mk/hdr.mk +include ${ROOT}/mk/ixp.mk + +CFLAGS += ${TASKINC} + +TARG = libixp_task +OBJ = thread_task + +include ${ROOT}/mk/lib.mk + diff --git a/lib/libixp_task/thread_task.c b/lib/libixp_task/thread_task.c @@ -0,0 +1,179 @@ +/* Written by Kris Maglione <fbsdaemon@gmail.com> */ +/* Public domain */ +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <task.h> +#include "ixp_local.h" + +static IxpThread ixp_task; + +int +ixp_taskinit() { + ixp_thread = &ixp_task; + return 0; +} + +static char* +errbuf(void) { + void **p; + + p = taskdata(); + if(*p == nil) + *p = emallocz(IXP_ERRMAX); + return *p; +} + +/* Mutex */ +static int +initmutex(IxpMutex *m) { + m->aux = emallocz(sizeof(QLock)); + return 0; +} + +static void +mdestroy(IxpMutex *m) { + free(m->aux); + m->aux = nil; +} + +static void +mlock(IxpMutex *m) { + qlock(m->aux); +} + +static int +mcanlock(IxpMutex *m) { + return canqlock(m->aux); +} + +static void +munlock(IxpMutex *m) { + qunlock(m->aux); +} + +/* RWLock */ +static int +initrwlock(IxpRWLock *rw) { + rw->aux = emallocz(sizeof(RWLock)); + return 0; +} + +static void +rwdestroy(IxpRWLock *rw) { + free(rw->aux); + rw->aux = nil; +} + +static void +_rlock(IxpRWLock *rw) { + rlock(rw->aux); +} + +static int +_canrlock(IxpRWLock *rw) { + return canrlock(rw->aux); +} + +static void +_wlock(IxpRWLock *rw) { + wlock(rw->aux); +} + +static int +_canwlock(IxpRWLock *rw) { + return canwlock(rw->aux); +} + +static void +_runlock(IxpRWLock *rw) { + runlock(rw->aux); +} + +static void +_wunlock(IxpRWLock *rw) { + wunlock(rw->aux); +} + +/* Rendez */ +static int +initrendez(IxpRendez *r) { + r->aux = emallocz(sizeof(Rendez)); + return 0; +} + +static void +rdestroy(IxpRendez *r) { + free(r->aux); + r->aux = nil; +} + +static void +rsleep(IxpRendez *r) { + Rendez *rz; + + rz = r->aux; + rz->l = r->mutex->aux; + tasksleep(rz); +} + +static int +rwake(IxpRendez *r) { + Rendez *rz; + + rz = r->aux; + rz->l = r->mutex->aux; + return taskwakeup(rz); +} + +static int +rwakeall(IxpRendez *r) { + Rendez *rz; + + rz = r->aux; + rz->l = r->mutex->aux; + return taskwakeupall(rz); +} + +/* Yielding IO */ +static ssize_t +_read(int fd, void *buf, size_t size) { + fdnoblock(fd); + return fdread(fd, buf, size); +} + +static ssize_t +_write(int fd, const void *buf, size_t size) { + fdnoblock(fd); + return fdwrite(fd, (void*)buf, size); +} + +static IxpThread ixp_task = { + /* Mutex */ + .initmutex = initmutex, + .lock = mlock, + .canlock = mcanlock, + .unlock = munlock, + .mdestroy = mdestroy, + /* RWLock */ + .initrwlock = initrwlock, + .rlock = _rlock, + .canrlock = _canrlock, + .wlock = _wlock, + .canwlock = _canwlock, + .runlock = _runlock, + .wunlock = _wunlock, + .rwdestroy = rwdestroy, + /* Rendez */ + .initrendez = initrendez, + .sleep = rsleep, + .wake = rwake, + .wakeall = rwakeall, + .rdestroy = rdestroy, + /* Other */ + .errbuf = errbuf, + .read = _read, + .write = _write, + .select = select, /* wrong */ +}; + diff --git a/libixp/LICENSE b/libixp/LICENSE @@ -1,21 +0,0 @@ - -© 2005-2006 Anselm R. Garbe <garbeam@gmail.com> -© 2006-2009 Kris Maglione <maglione.k at Gmail> - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/libixp/Makefile b/libixp/Makefile @@ -1,23 +0,0 @@ -ROOT= .. -include $(ROOT)/mk/hdr.mk -include $(ROOT)/mk/ixp.mk - -TARG = libixp - -OBJ = client \ - convert \ - error \ - map \ - message \ - request \ - rpc \ - server \ - srv_util \ - socket \ - thread \ - timer \ - transport \ - util - -include ${ROOT}/mk/lib.mk - diff --git a/libixp/README b/libixp/README @@ -1,14 +0,0 @@ -libixp - simple 9P client-/server-library -=============================== -libixp is an extremly simple, stand-alone 9P library. - - -Installation ------------- -Edit config.mk to match your local setup. libixp is installed into -/usr/local by default. - -Afterwards enter the following command to build and install libixp -(if necessary as root): - - $ make clean install diff --git a/libixp/client.c b/libixp/client.c @@ -1,676 +0,0 @@ -/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> -#include "ixp_local.h" - -#define nelem(ary) (sizeof(ary) / sizeof(*ary)) - -enum { - RootFid = 1, -}; - -static int -min(int a, int b) { - if(a < b) - return a; - return b; -} - -static IxpCFid* -getfid(IxpClient *c) { - IxpCFid *f; - - thread->lock(&c->lk); - f = c->freefid; - if(f != nil) - c->freefid = f->next; - else { - f = emallocz(sizeof *f); - f->client = c; - f->fid = ++c->lastfid; - thread->initmutex(&f->iolock); - } - f->next = nil; - f->open = 0; - thread->unlock(&c->lk); - return f; -} - -static void -putfid(IxpCFid *f) { - IxpClient *c; - - c = f->client; - thread->lock(&c->lk); - if(f->fid == c->lastfid) { - c->lastfid--; - thread->mdestroy(&f->iolock); - free(f); - }else { - f->next = c->freefid; - c->freefid = f; - } - thread->unlock(&c->lk); -} - -static int -dofcall(IxpClient *c, Fcall *fcall) { - Fcall *ret; - - ret = muxrpc(c, fcall); - if(ret == nil) - return 0; - if(ret->hdr.type == RError) { - werrstr("%s", ret->error.ename); - goto fail; - } - if(ret->hdr.type != (fcall->hdr.type^1)) { - werrstr("received mismatched fcall"); - goto fail; - } - memcpy(fcall, ret, sizeof *fcall); - free(ret); - return 1; -fail: - ixp_freefcall(fcall); - free(ret); - return 0; -} - -/** - * Function: ixp_unmount - * - * Unmounts the client P<c> and frees its data structures. - */ -void -ixp_unmount(IxpClient *c) { - IxpCFid *f; - - shutdown(c->fd, SHUT_RDWR); - close(c->fd); - - muxfree(c); - - while((f = c->freefid)) { - c->freefid = f->next; - thread->mdestroy(&f->iolock); - free(f); - } - free(c->rmsg.data); - free(c->wmsg.data); - free(c); -} - -static void -allocmsg(IxpClient *c, int n) { - c->rmsg.size = n; - c->wmsg.size = n; - c->rmsg.data = erealloc(c->rmsg.data, n); - c->wmsg.data = erealloc(c->wmsg.data, n); -} - -/** - * Function: ixp_mountfd - * Function: ixp_mount - * Function: ixp_nsmount - * - * Params: - * fd - A file descriptor which is already connected - * to a 9P server. - * address - An address (in Plan 9 resource fomat) at - * which to connect to a 9P server. - * name - The name of a socket in the process's canonical - * namespace directory. - * - * Initiate a 9P connection with the server at P<address>, - * connected to on P<fd>, or under the process's namespace - * directory as P<name>. - * - * Returns: - * A pointer to a new 9P client. - */ - -IxpClient* -ixp_mountfd(int fd) { - IxpClient *c; - Fcall fcall; - - c = emallocz(sizeof *c); - c->fd = fd; - - muxinit(c); - - allocmsg(c, 256); - c->lastfid = RootFid; - /* Override tag matching on TVersion */ - c->mintag = IXP_NOTAG; - c->maxtag = IXP_NOTAG+1; - - fcall.hdr.type = TVersion; - fcall.version.msize = IXP_MAX_MSG; - fcall.version.version = IXP_VERSION; - - if(dofcall(c, &fcall) == 0) { - ixp_unmount(c); - return nil; - } - - if(strcmp(fcall.version.version, IXP_VERSION) - || fcall.version.msize > IXP_MAX_MSG) { - werrstr("bad 9P version response"); - ixp_unmount(c); - return nil; - } - - c->mintag = 0; - c->maxtag = 255; - c->msize = fcall.version.msize; - - allocmsg(c, fcall.version.msize); - ixp_freefcall(&fcall); - - fcall.hdr.type = TAttach; - fcall.hdr.fid = RootFid; - fcall.tattach.afid = IXP_NOFID; - fcall.tattach.uname = getenv("USER"); - fcall.tattach.aname = ""; - if(dofcall(c, &fcall) == 0) { - ixp_unmount(c); - return nil; - } - - return c; -} - -IxpClient* -ixp_mount(const char *address) { - int fd; - - fd = ixp_dial(address); - if(fd < 0) - return nil; - return ixp_mountfd(fd); -} - -IxpClient* -ixp_nsmount(const char *name) { - char *address; - IxpClient *c; - - address = ixp_namespace(); - if(address) - address = ixp_smprint("unix!%s/%s", address, name); - if(address == nil) - return nil; - c = ixp_mount(address); - free(address); - return c; -} - -static IxpCFid* -walk(IxpClient *c, const char *path) { - IxpCFid *f; - char *p; - Fcall fcall; - int n; - - p = estrdup(path); - n = tokenize(fcall.twalk.wname, nelem(fcall.twalk.wname), p, '/'); - f = getfid(c); - - fcall.hdr.type = TWalk; - fcall.hdr.fid = RootFid; - fcall.twalk.nwname = n; - fcall.twalk.newfid = f->fid; - if(dofcall(c, &fcall) == 0) - goto fail; - if(fcall.rwalk.nwqid < n) { - werrstr("File does not exist"); - if(fcall.rwalk.nwqid == 0) - werrstr("Protocol botch"); - goto fail; - } - - f->qid = fcall.rwalk.wqid[n-1]; - - ixp_freefcall(&fcall); - free(p); - return f; -fail: - putfid(f); - free(p); - return nil; -} - -static IxpCFid* -walkdir(IxpClient *c, char *path, const char **rest) { - char *p; - - p = path + strlen(path) - 1; - assert(p >= path); - while(*p == '/') - *p-- = '\0'; - - while((p > path) && (*p != '/')) - p--; - if(*p != '/') { - werrstr("bad path"); - return nil; - } - - *p++ = '\0'; - *rest = p; - return walk(c, path); -} - -static int -clunk(IxpCFid *f) { - IxpClient *c; - Fcall fcall; - int ret; - - c = f->client; - - fcall.hdr.type = TClunk; - fcall.hdr.fid = f->fid; - ret = dofcall(c, &fcall); - if(ret) - putfid(f); - ixp_freefcall(&fcall); - return ret; -} - -/** - * Function: ixp_remove - * - * Params: - * path - The path of the file to remove. - * - * Removes a file or directory from the remote server. - * - * Returns: - * ixp_remove returns 0 on failure, 1 on success. - */ - -int -ixp_remove(IxpClient *c, const char *path) { - Fcall fcall; - IxpCFid *f; - int ret; - - if((f = walk(c, path)) == nil) - return 0; - - fcall.hdr.type = TRemove; - fcall.hdr.fid = f->fid;; - ret = dofcall(c, &fcall); - ixp_freefcall(&fcall); - putfid(f); - - return ret; -} - -static void -initfid(IxpCFid *f, Fcall *fcall) { - f->open = 1; - f->offset = 0; - f->iounit = fcall->ropen.iounit; - if(f->iounit == 0 || fcall->ropen.iounit > f->client->msize-24) - f->iounit = f->client->msize-24; - f->qid = fcall->ropen.qid; -} - -/** - * Function: ixp_create - * Function: ixp_open - * - * Params: - * path - The path of the file to open or create. - * perm - The permissions with which to create the new - * file. These will be ANDed with those of the - * parent directory by the server. - * mode - The file's open mode. - * - * ixp_open and ixp_create each open a file at P<path>. - * P<mode> must include OREAD, OWRITE, or ORDWR, and may - * include any of the modes specified in 9pmodes(3). - * ixp_create, additionally, creates a file at P<path> if it - * doesn't already exist. - * - * Returns: - * A pointer on which to operate on the newly - * opened file. - */ - -IxpCFid* -ixp_create(IxpClient *c, const char *path, uint perm, uchar mode) { - Fcall fcall; - IxpCFid *f; - char *tpath;; - - tpath = estrdup(path); - - f = walkdir(c, tpath, &path); - if(f == nil) - goto done; - - fcall.hdr.type = TCreate; - fcall.hdr.fid = f->fid; - fcall.tcreate.name = (char*)(uintptr_t)path; - fcall.tcreate.perm = perm; - fcall.tcreate.mode = mode; - - if(dofcall(c, &fcall) == 0) { - clunk(f); - f = nil; - goto done; - } - - initfid(f, &fcall); - f->mode = mode; - - ixp_freefcall(&fcall); - -done: - free(tpath); - return f; -} - -IxpCFid* -ixp_open(IxpClient *c, const char *path, uchar mode) { - Fcall fcall; - IxpCFid *f; - - f = walk(c, path); - if(f == nil) - return nil; - - fcall.hdr.type = TOpen; - fcall.hdr.fid = f->fid; - fcall.topen.mode = mode; - - if(dofcall(c, &fcall) == 0) { - clunk(f); - return nil; - } - - initfid(f, &fcall); - f->mode = mode; - - ixp_freefcall(&fcall); - return f; -} - -/** - * Function: ixp_close - * - * Closes the file pointed to by P<f> and frees its - * associated data structures; - * - * Returns: - * Returns 1 on success, and zero on failure. - */ - -int -ixp_close(IxpCFid *f) { - return clunk(f); -} - -static Stat* -_stat(IxpClient *c, ulong fid) { - IxpMsg msg; - Fcall fcall; - Stat *stat; - - fcall.hdr.type = TStat; - fcall.hdr.fid = fid; - if(dofcall(c, &fcall) == 0) - return nil; - - msg = ixp_message((char*)fcall.rstat.stat, fcall.rstat.nstat, MsgUnpack); - - stat = emalloc(sizeof *stat); - ixp_pstat(&msg, stat); - ixp_freefcall(&fcall); - if(msg.pos > msg.end) { - free(stat); - stat = nil; - } - return stat; -} - -/** - * Function: ixp_stat - * Function: ixp_fstat - * - * Params: - * path - The path of the file to stat. - * f - A CFid of an open file to stat. - * - * Stats the file at P<path> or pointed to by P<f>. - * - * Returns: - * Returns a Stat structure, which must be freed by - * the caller with free(3). - * - * S<Stat> - */ - -Stat* -ixp_stat(IxpClient *c, const char *path) { - Stat *stat; - IxpCFid *f; - - f = walk(c, path); - if(f == nil) - return nil; - - stat = _stat(c, f->fid); - clunk(f); - return stat; -} - -Stat* -ixp_fstat(IxpCFid *f) { - return _stat(f->client, f->fid); -} - -static long -_pread(IxpCFid *f, char *buf, long count, vlong offset) { - Fcall fcall; - int n, len; - - len = 0; - while(len < count) { - n = min(count-len, f->iounit); - - fcall.hdr.type = TRead; - fcall.hdr.fid = f->fid; - fcall.tread.offset = offset; - fcall.tread.count = n; - if(dofcall(f->client, &fcall) == 0) - return -1; - if(fcall.rread.count > n) - return -1; - - memcpy(buf+len, fcall.rread.data, fcall.rread.count); - offset += fcall.rread.count; - len += fcall.rread.count; - - ixp_freefcall(&fcall); - if(fcall.rread.count < n) - break; - } - return len; -} - -/** - * Function: ixp_read - * Function: ixp_pread - * - * Params: - * buf - A buffer in which to store the read data. - * count - The number of bytes to read. - * offset - The offset at which to begin reading. - * - * ixp_read and ixp_pread each read P<count> bytes of data - * from the file pointed to by P<f>, into P<buf>. ixp_read - * begins reading at its stored offset, and increments it by - * the number of bytes read. ixp_pread reads beginning at - * P<offset> and does not alter C<f>'s stored offset. - * - * Returns: - * These functions return the number of bytes read on - * success and -1 on failure. - */ - -long -ixp_read(IxpCFid *f, void *buf, long count) { - int n; - - thread->lock(&f->iolock); - n = _pread(f, buf, count, f->offset); - if(n > 0) - f->offset += n; - thread->unlock(&f->iolock); - return n; -} - -long -ixp_pread(IxpCFid *f, void *buf, long count, vlong offset) { - int n; - - thread->lock(&f->iolock); - n = _pread(f, buf, count, offset); - thread->unlock(&f->iolock); - return n; -} - -static long -_pwrite(IxpCFid *f, const void *buf, long count, vlong offset) { - Fcall fcall; - int n, len; - - len = 0; - do { - n = min(count-len, f->iounit); - fcall.hdr.type = TWrite; - fcall.hdr.fid = f->fid; - fcall.twrite.offset = offset; - fcall.twrite.data = (char*)buf + len; - fcall.twrite.count = n; - if(dofcall(f->client, &fcall) == 0) - return -1; - - offset += fcall.rwrite.count; - len += fcall.rwrite.count; - - ixp_freefcall(&fcall); - if(fcall.rwrite.count < n) - break; - } while(len < count); - return len; -} - -/** - * Function: ixp_write - * Function: ixp_pwrite - * - * Params: - * buf - A buffer holding the contents to store. - * count - The number of bytes to store. - * offset - The offset at which to write the data. - * - * ixp_write and ixp_pwrite each write P<count> bytes of - * data stored in P<buf> to the file pointed to by C<f>. - * ixp_write writes its data at its stored offset, and - * increments it by P<count>. ixp_pwrite writes its data a - * P<offset> and does not alter C<f>'s stored offset. - * - * Returns: - * These functions return the number of bytes actually - * written. Any value less than P<count> must be considered - * a failure. - */ - -long -ixp_write(IxpCFid *f, const void *buf, long count) { - int n; - - thread->lock(&f->iolock); - n = _pwrite(f, buf, count, f->offset); - if(n > 0) - f->offset += n; - thread->unlock(&f->iolock); - return n; -} - -long -ixp_pwrite(IxpCFid *f, const void *buf, long count, vlong offset) { - int n; - - thread->lock(&f->iolock); - n = _pwrite(f, buf, count, offset); - thread->unlock(&f->iolock); - return n; -} - -/** - * Function: ixp_vprint - * Function: ixp_print - * Variable: ixp_vsmprint - * - * Params: - * fmt - The string with which to format the data. - * ap - A va_list holding the arguments to the format - * string. - * ... - The arguments to the format string. - * - * These functions act like the standard formatted IO - * functions. They write the result of the formatting to the - * file pointed to by C<f>. - * - * V<ixp_vsmprint> may be set to a function which will - * format its arguments and return a null terminated string - * allocated with malloc(3). - * - * Returns: - * These functions return the number of bytes written. - * There is currently no way to detect failure. - */ - -int -ixp_vprint(IxpCFid *f, const char *fmt, va_list ap) { - char *buf; - int n; - - buf = ixp_vsmprint(fmt, ap); - if(buf == nil) - return -1; - - n = ixp_write(f, buf, strlen(buf)); - free(buf); - return n; -} - -int -ixp_print(IxpCFid *f, const char *fmt, ...) { - va_list ap; - int n; - - va_start(ap, fmt); - n = ixp_vprint(f, fmt, ap); - va_end(ap); - - return n; -} - diff --git a/libixp/convert.c b/libixp/convert.c @@ -1,200 +0,0 @@ -/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "ixp_local.h" - -enum { - SByte = 1, - SWord = 2, - SDWord = 4, - SQWord = 8, -}; - -static void -ixp_puint(IxpMsg *msg, uint size, ulong *val) { - uchar *pos; - int v; - - if(msg->pos + size <= msg->end) { - pos = (uchar*)msg->pos; - switch(msg->mode) { - case MsgPack: - v = *val; - switch(size) { - case SDWord: - pos[3] = v>>24; - pos[2] = v>>16; - case SWord: - pos[1] = v>>8; - case SByte: - pos[0] = v; - break; - } - case MsgUnpack: - v = 0; - switch(size) { - case SDWord: - v |= pos[3]<<24; - v |= pos[2]<<16; - case SWord: - v |= pos[1]<<8; - case SByte: - v |= pos[0]; - break; - } - *val = v; - } - } - msg->pos += size; -} - -void -ixp_pu32(IxpMsg *msg, ulong *val) { - ixp_puint(msg, SDWord, val); -} -void -ixp_pu8(IxpMsg *msg, uchar *val) { - ulong v; - - v = *val; - ixp_puint(msg, SByte, &v); - *val = (uchar)v; -} -void -ixp_pu16(IxpMsg *msg, ushort *val) { - ulong v; - - v = *val; - ixp_puint(msg, SWord, &v); - *val = (ushort)v; -} -void -ixp_pu64(IxpMsg *msg, uvlong *val) { - ulong vl, vb; - - vl = (uint)*val; - vb = (uint)(*val>>32); - ixp_puint(msg, SDWord, &vl); - ixp_puint(msg, SDWord, &vb); - *val = vl | ((uvlong)vb<<32); -} - -void -ixp_pstring(IxpMsg *msg, char **s) { - ushort len; - - if(msg->mode == MsgPack) - len = strlen(*s); - ixp_pu16(msg, &len); - - if(msg->pos + len <= msg->end) { - if(msg->mode == MsgUnpack) { - *s = emalloc(len + 1); - memcpy(*s, msg->pos, len); - (*s)[len] = '\0'; - }else - memcpy(msg->pos, *s, len); - } - msg->pos += len; -} - -void -ixp_pstrings(IxpMsg *msg, ushort *num, char *strings[]) { - char *s; - uint i, size; - ushort len; - - ixp_pu16(msg, num); - if(*num > IXP_MAX_WELEM) { - msg->pos = msg->end+1; - return; - } - - SET(s); - if(msg->mode == MsgUnpack) { - s = msg->pos; - size = 0; - for(i=0; i < *num; i++) { - ixp_pu16(msg, &len); - msg->pos += len; - size += len; - if(msg->pos > msg->end) - return; - } - msg->pos = s; - size += *num; - s = emalloc(size); - } - - for(i=0; i < *num; i++) { - if(msg->mode == MsgPack) - len = strlen(strings[i]); - ixp_pu16(msg, &len); - - if(msg->mode == MsgUnpack) { - memcpy(s, msg->pos, len); - strings[i] = (char*)s; - s += len; - msg->pos += len; - *s++ = '\0'; - }else - ixp_pdata(msg, &strings[i], len); - } -} - -void -ixp_pdata(IxpMsg *msg, char **data, uint len) { - if(msg->pos + len <= msg->end) { - if(msg->mode == MsgUnpack) { - *data = emalloc(len); - memcpy(*data, msg->pos, len); - }else - memcpy(msg->pos, *data, len); - } - msg->pos += len; -} - -void -ixp_pqid(IxpMsg *msg, Qid *qid) { - ixp_pu8(msg, &qid->type); - ixp_pu32(msg, &qid->version); - ixp_pu64(msg, &qid->path); -} - -void -ixp_pqids(IxpMsg *msg, ushort *num, Qid qid[]) { - int i; - - ixp_pu16(msg, num); - if(*num > IXP_MAX_WELEM) { - msg->pos = msg->end+1; - return; - } - - for(i = 0; i < *num; i++) - ixp_pqid(msg, &qid[i]); -} - -void -ixp_pstat(IxpMsg *msg, Stat *stat) { - ushort size; - - if(msg->mode == MsgPack) - size = ixp_sizeof_stat(stat) - 2; - - ixp_pu16(msg, &size); - ixp_pu16(msg, &stat->type); - ixp_pu32(msg, &stat->dev); - ixp_pqid(msg, &stat->qid); - ixp_pu32(msg, &stat->mode); - ixp_pu32(msg, &stat->atime); - ixp_pu32(msg, &stat->mtime); - ixp_pu64(msg, &stat->length); - ixp_pstring(msg, &stat->name); - ixp_pstring(msg, &stat->uid); - ixp_pstring(msg, &stat->gid); - ixp_pstring(msg, &stat->muid); -} diff --git a/libixp/error.c b/libixp/error.c @@ -1,103 +0,0 @@ -/* Public Domain --Kris Maglione */ -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "ixp_local.h" - -static int -_vsnprint(char *buf, int n, const char *fmt, va_list ap) { - return vsnprintf(buf, n, fmt, ap); -} - -static char* -_vsmprint(const char *fmt, va_list ap) { - va_list al; - char *buf = ""; - int n; - - va_copy(al, ap); - n = vsnprintf(buf, 0, fmt, al); - va_end(al); - - buf = malloc(++n); - if(buf) - vsnprintf(buf, n, fmt, ap); - return buf; -} - -int (*ixp_vsnprint)(char*, int, const char*, va_list) = _vsnprint; -char* (*ixp_vsmprint)(const char*, va_list) = _vsmprint; - -/* Approach to errno handling taken from Plan 9 Port. */ -enum { - EPLAN9 = 0x19283745, -}; - -/** - * Function: ixp_errbuf - * Function: ixp_errstr - * Function: ixp_rerrstr - * Function: ixp_werrstr - * - * Params: - * buf - The buffer to read and/or fill. - * n - The size of the buffer. - * fmt - A format string with which to write the * errstr. - * ... - Arguments to P<fmt>. - * - * These functions simulate Plan 9's errstr functionality. - * They replace errno in libixp. Note that these functions - * are not internationalized. - * - * F<ixp_errbuf> returns the errstr buffer for the current - * thread. F<ixp_rerrstr> fills P<buf> with the data from - * the current thread's error buffer, while F<ixp_errstr> - * exchanges P<buf>'s contents with those of the current - * thread's error buffer. F<ixp_werrstr> is takes a format - * string from which to construct an errstr. - * - * Returns: - * F<ixp_errbuf> returns the current thread's error - * string buffer. - */ -char* -ixp_errbuf() { - char *errbuf; - - errbuf = thread->errbuf(); - if(errno == EINTR) - strncpy(errbuf, "interrupted", IXP_ERRMAX); - else if(errno != EPLAN9) - strncpy(errbuf, strerror(errno), IXP_ERRMAX); - return errbuf; -} - -void -errstr(char *buf, int n) { - char tmp[IXP_ERRMAX]; - - strncpy(tmp, buf, sizeof tmp); - rerrstr(buf, n); - strncpy(thread->errbuf(), tmp, IXP_ERRMAX); - errno = EPLAN9; -} - -void -rerrstr(char *buf, int n) { - strncpy(buf, ixp_errbuf(), n); -} - -void -werrstr(const char *fmt, ...) { - char tmp[IXP_ERRMAX]; - va_list ap; - - va_start(ap, fmt); - ixp_vsnprint(tmp, sizeof tmp, fmt, ap); - va_end(ap); - strncpy(thread->errbuf(), tmp, IXP_ERRMAX); - errno = EPLAN9; -} - diff --git a/libixp/map.c b/libixp/map.c @@ -1,132 +0,0 @@ -/* Written by Kris Maglione */ -/* Public domain */ -#include <stdlib.h> -#include "ixp_local.h" - -/* Edit s/^([a-zA-Z].*)\n([a-z].*) {/\1 \2;/g x/^([^a-zA-Z]|static|$)/-+d s/ (\*map|val|*str)//g */ - -struct MapEnt { - ulong hash; - const char* key; - void* val; - MapEnt* next; -}; - -MapEnt *NM; - -static void -insert(MapEnt **e, ulong val, const char *key) { - MapEnt *te; - - te = emallocz(sizeof *te); - te->hash = val; - te->key = key; - te->next = *e; - *e = te; -} - -static MapEnt** -map_getp(Map *map, ulong val, bool create, bool *exists) { - MapEnt **e; - - e = &map->bucket[val%map->nhash]; - for(; *e; e = &(*e)->next) - if((*e)->hash >= val) break; - if(exists) - *exists = *e && (*e)->hash == val; - - if(*e == nil || (*e)->hash != val) { - if(create) - insert(e, val, nil); - else - e = &NM; - } - return e; -} - -void -ixp_mapfree(Map *map, void (*destroy)(void*)) { - int i; - MapEnt *e; - - thread->wlock(&map->lock); - for(i=0; i < map->nhash; i++) - while((e = map->bucket[i])) { - map->bucket[i] = e->next; - if(destroy) - destroy(e->val); - free(e); - } - thread->wunlock(&map->lock); - thread->rwdestroy(&map->lock); -} - -void -ixp_mapexec(Map *map, void (*run)(void*, void*), void *context) { - int i; - MapEnt *e; - - thread->rlock(&map->lock); - for(i=0; i < map->nhash; i++) - for(e=map->bucket[i]; e; e=e->next) - run(context, e->val); - thread->runlock(&map->lock); -} - -void -ixp_mapinit(Map *map, MapEnt **buckets, int nbuckets) { - - map->bucket = buckets; - map->nhash = nbuckets; - - thread->initrwlock(&map->lock); -} - -bool -ixp_mapinsert(Map *map, ulong key, void *val, bool overwrite) { - MapEnt *e; - bool existed, res; - - res = true; - thread->wlock(&map->lock); - e = *map_getp(map, key, true, &existed); - if(existed && !overwrite) - res = false; - else - e->val = val; - thread->wunlock(&map->lock); - return res; -} - -void* -ixp_mapget(Map *map, ulong val) { - MapEnt *e; - void *res; - - thread->rlock(&map->lock); - e = *map_getp(map, val, false, nil); - res = e ? e->val : nil; - thread->runlock(&map->lock); - return res; -} - -void* -ixp_maprm(Map *map, ulong val) { - MapEnt **e, *te; - void *ret; - - ret = nil; - thread->wlock(&map->lock); - e = map_getp(map, val, false, nil); - if(*e) { - te = *e; - ret = te->val; - *e = te->next; - thread->wunlock(&map->lock); - free(te); - } - else - thread->wunlock(&map->lock); - return ret; -} - diff --git a/libixp/message.c b/libixp/message.c @@ -1,205 +0,0 @@ -/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include "ixp_local.h" - -enum { - SByte = 1, - SWord = 2, - SDWord = 4, - SQWord = 8, -}; - -#define SString(s) (SWord + strlen(s)) -enum { - SQid = SByte + SDWord + SQWord, -}; - -IxpMsg -ixp_message(char *data, uint length, uint mode) { - IxpMsg m; - - m.data = data; - m.pos = data; - m.end = data + length; - m.size = length; - m.mode = mode; - return m; -} - -void -ixp_freestat(Stat *s) { - free(s->name); - free(s->uid); - free(s->gid); - free(s->muid); - s->name = s->uid = s->gid = s->muid = nil; -} - -void -ixp_freefcall(Fcall *fcall) { - switch(fcall->hdr.type) { - case RStat: - free(fcall->rstat.stat); - fcall->rstat.stat = nil; - break; - case RRead: - free(fcall->rread.data); - fcall->rread.data = nil; - break; - case RVersion: - free(fcall->version.version); - fcall->version.version = nil; - break; - case RError: - free(fcall->error.ename); - fcall->error.ename = nil; - break; - } -} - -ushort -ixp_sizeof_stat(Stat * stat) { - return SWord /* size */ - + SWord /* type */ - + SDWord /* dev */ - + SQid /* qid */ - + 3 * SDWord /* mode, atime, mtime */ - + SQWord /* length */ - + SString(stat->name) - + SString(stat->uid) - + SString(stat->gid) - + SString(stat->muid); -} - -void -ixp_pfcall(IxpMsg *msg, Fcall *fcall) { - ixp_pu8(msg, &fcall->hdr.type); - ixp_pu16(msg, &fcall->hdr.tag); - - switch (fcall->hdr.type) { - case TVersion: - case RVersion: - ixp_pu32(msg, &fcall->version.msize); - ixp_pstring(msg, &fcall->version.version); - break; - case TAuth: - ixp_pu32(msg, &fcall->tauth.afid); - ixp_pstring(msg, &fcall->tauth.uname); - ixp_pstring(msg, &fcall->tauth.aname); - break; - case RAuth: - ixp_pqid(msg, &fcall->rauth.aqid); - break; - case RAttach: - ixp_pqid(msg, &fcall->rattach.qid); - break; - case TAttach: - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pu32(msg, &fcall->tattach.afid); - ixp_pstring(msg, &fcall->tattach.uname); - ixp_pstring(msg, &fcall->tattach.aname); - break; - case RError: - ixp_pstring(msg, &fcall->error.ename); - break; - case TFlush: - ixp_pu16(msg, &fcall->tflush.oldtag); - break; - case TWalk: - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pu32(msg, &fcall->twalk.newfid); - ixp_pstrings(msg, &fcall->twalk.nwname, fcall->twalk.wname); - break; - case RWalk: - ixp_pqids(msg, &fcall->rwalk.nwqid, fcall->rwalk.wqid); - break; - case TOpen: - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pu8(msg, &fcall->topen.mode); - break; - case ROpen: - case RCreate: - ixp_pqid(msg, &fcall->ropen.qid); - ixp_pu32(msg, &fcall->ropen.iounit); - break; - case TCreate: - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pstring(msg, &fcall->tcreate.name); - ixp_pu32(msg, &fcall->tcreate.perm); - ixp_pu8(msg, &fcall->tcreate.mode); - break; - case TRead: - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pu64(msg, &fcall->tread.offset); - ixp_pu32(msg, &fcall->tread.count); - break; - case RRead: - ixp_pu32(msg, &fcall->rread.count); - ixp_pdata(msg, &fcall->rread.data, fcall->rread.count); - break; - case TWrite: - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pu64(msg, &fcall->twrite.offset); - ixp_pu32(msg, &fcall->twrite.count); - ixp_pdata(msg, &fcall->twrite.data, fcall->twrite.count); - break; - case RWrite: - ixp_pu32(msg, &fcall->rwrite.count); - break; - case TClunk: - case TRemove: - case TStat: - ixp_pu32(msg, &fcall->hdr.fid); - break; - case RStat: - ixp_pu16(msg, &fcall->rstat.nstat); - ixp_pdata(msg, (char**)&fcall->rstat.stat, fcall->rstat.nstat); - break; - case TWStat: { - ushort size; - ixp_pu32(msg, &fcall->hdr.fid); - ixp_pu16(msg, &size); - ixp_pstat(msg, &fcall->twstat.stat); - break; - } - } -} - -uint -ixp_fcall2msg(IxpMsg *msg, Fcall *fcall) { - ulong size; - - msg->end = msg->data + msg->size; - msg->pos = msg->data + SDWord; - msg->mode = MsgPack; - ixp_pfcall(msg, fcall); - - if(msg->pos > msg->end) - return 0; - - msg->end = msg->pos; - size = msg->end - msg->data; - - msg->pos = msg->data; - ixp_pu32(msg, &size); - - msg->pos = msg->data; - return size; -} - -uint -ixp_msg2fcall(IxpMsg *msg, Fcall *fcall) { - msg->pos = msg->data + SDWord; - msg->mode = MsgUnpack; - ixp_pfcall(msg, fcall); - - if(msg->pos > msg->end) - return 0; - - return msg->pos - msg->data; -} - diff --git a/libixp/request.c b/libixp/request.c @@ -1,550 +0,0 @@ -/* Copyright ©2006-2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> -#include "ixp_local.h" - -static void handlereq(Ixp9Req *r); - -static void -_printfcall(Fcall *f) { - USED(f); -} -void (*ixp_printfcall)(Fcall*) = _printfcall; - -static int -min(int a, int b) { - if(a < b) - return a; - return b; -} - -static char - Eduptag[] = "tag in use", - Edupfid[] = "fid in use", - Enofunc[] = "function not implemented", - Eopen[] = "fid is already open", - Enofile[] = "file does not exist", - Enoread[] = "file not open for reading", - Enofid[] = "fid does not exist", - Enotag[] = "tag does not exist", - Enotdir[] = "not a directory", - Eintr[] = "interrupted", - Eisdir[] = "cannot perform operation on a directory"; - -enum { - TAG_BUCKETS = 61, - FID_BUCKETS = 61, -}; - -struct Ixp9Conn { - Map tagmap; - Map fidmap; - MapEnt* taghash[TAG_BUCKETS]; - MapEnt* fidhash[FID_BUCKETS]; - Ixp9Srv* srv; - IxpConn* conn; - IxpMutex rlock; - IxpMutex wlock; - IxpMsg rmsg; - IxpMsg wmsg; - int ref; -}; - -static void -decref_p9conn(Ixp9Conn *p9conn) { - thread->lock(&p9conn->wlock); - if(--p9conn->ref > 0) { - thread->unlock(&p9conn->wlock); - return; - } - thread->unlock(&p9conn->wlock); - - assert(p9conn->conn == nil); - - thread->mdestroy(&p9conn->rlock); - thread->mdestroy(&p9conn->wlock); - - ixp_mapfree(&p9conn->tagmap, nil); - ixp_mapfree(&p9conn->fidmap, nil); - - free(p9conn->rmsg.data); - free(p9conn->wmsg.data); - free(p9conn); -} - -static void* -createfid(Map *map, int fid, Ixp9Conn *p9conn) { - Fid *f; - - f = emallocz(sizeof *f); - p9conn->ref++; - f->conn = p9conn; - f->fid = fid; - f->omode = -1; - f->map = map; - if(ixp_mapinsert(map, fid, f, false)) - return f; - free(f); - return nil; -} - -static int -destroyfid(Ixp9Conn *p9conn, ulong fid) { - Fid *f; - - f = ixp_maprm(&p9conn->fidmap, fid); - if(f == nil) - return 0; - - if(p9conn->srv->freefid) - p9conn->srv->freefid(f); - - decref_p9conn(p9conn); - free(f); - return 1; -} - -static void -handlefcall(IxpConn *c) { - Fcall fcall = {0}; - Ixp9Conn *p9conn; - Ixp9Req *req; - - p9conn = c->aux; - - thread->lock(&p9conn->rlock); - if(ixp_recvmsg(c->fd, &p9conn->rmsg) == 0) - goto Fail; - if(ixp_msg2fcall(&p9conn->rmsg, &fcall) == 0) - goto Fail; - thread->unlock(&p9conn->rlock); - - req = emallocz(sizeof *req); - p9conn->ref++; - req->conn = p9conn; - req->srv = p9conn->srv; - req->ifcall = fcall; - p9conn->conn = c; - - if(!ixp_mapinsert(&p9conn->tagmap, fcall.hdr.tag, req, false)) { - respond(req, Eduptag); - return; - } - - handlereq(req); - return; - -Fail: - thread->unlock(&p9conn->rlock); - ixp_hangup(c); - return; -} - -static void -handlereq(Ixp9Req *r) { - Ixp9Conn *p9conn; - Ixp9Srv *srv; - - p9conn = r->conn; - srv = p9conn->srv; - - ixp_printfcall(&r->ifcall); - - switch(r->ifcall.hdr.type) { - default: - respond(r, Enofunc); - break; - case TVersion: - if(!strcmp(r->ifcall.version.version, "9P")) - r->ofcall.version.version = "9P"; - else if(!strcmp(r->ifcall.version.version, "9P2000")) - r->ofcall.version.version = "9P2000"; - else - r->ofcall.version.version = "unknown"; - r->ofcall.version.msize = r->ifcall.version.msize; - respond(r, nil); - break; - case TAttach: - if(!(r->fid = createfid(&p9conn->fidmap, r->ifcall.hdr.fid, p9conn))) { - respond(r, Edupfid); - return; - } - /* attach is a required function */ - srv->attach(r); - break; - case TClunk: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if(!srv->clunk) { - respond(r, nil); - return; - } - srv->clunk(r); - break; - case TFlush: - if(!(r->oldreq = ixp_mapget(&p9conn->tagmap, r->ifcall.tflush.oldtag))) { - respond(r, Enotag); - return; - } - if(!srv->flush) { - respond(r, Enofunc); - return; - } - srv->flush(r); - break; - case TCreate: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if(r->fid->omode != -1) { - respond(r, Eopen); - return; - } - if(!(r->fid->qid.type&QTDIR)) { - respond(r, Enotdir); - return; - } - if(!p9conn->srv->create) { - respond(r, Enofunc); - return; - } - p9conn->srv->create(r); - break; - case TOpen: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if((r->fid->qid.type&QTDIR) && (r->ifcall.topen.mode|P9_ORCLOSE) != (P9_OREAD|P9_ORCLOSE)) { - respond(r, Eisdir); - return; - } - r->ofcall.ropen.qid = r->fid->qid; - if(!p9conn->srv->open) { - respond(r, Enofunc); - return; - } - p9conn->srv->open(r); - break; - case TRead: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if(r->fid->omode == -1 || r->fid->omode == P9_OWRITE) { - respond(r, Enoread); - return; - } - if(!p9conn->srv->read) { - respond(r, Enofunc); - return; - } - p9conn->srv->read(r); - break; - case TRemove: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if(!p9conn->srv->remove) { - respond(r, Enofunc); - return; - } - p9conn->srv->remove(r); - break; - case TStat: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if(!p9conn->srv->stat) { - respond(r, Enofunc); - return; - } - p9conn->srv->stat(r); - break; - case TWalk: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if(r->fid->omode != -1) { - respond(r, "cannot walk from an open fid"); - return; - } - if(r->ifcall.twalk.nwname && !(r->fid->qid.type&QTDIR)) { - respond(r, Enotdir); - return; - } - if((r->ifcall.hdr.fid != r->ifcall.twalk.newfid)) { - if(!(r->newfid = createfid(&p9conn->fidmap, r->ifcall.twalk.newfid, p9conn))) { - respond(r, Edupfid); - return; - } - }else - r->newfid = r->fid; - if(!p9conn->srv->walk) { - respond(r, Enofunc); - return; - } - p9conn->srv->walk(r); - break; - case TWrite: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if((r->fid->omode&3) != P9_OWRITE && (r->fid->omode&3) != P9_ORDWR) { - respond(r, "write on fid not opened for writing"); - return; - } - if(!p9conn->srv->write) { - respond(r, Enofunc); - return; - } - p9conn->srv->write(r); - break; - case TWStat: - if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { - respond(r, Enofid); - return; - } - if((ushort)~r->ifcall.twstat.stat.type) { - respond(r, "wstat of type"); - return; - } - if((uint)~r->ifcall.twstat.stat.dev) { - respond(r, "wstat of dev"); - return; - } - if((uchar)~r->ifcall.twstat.stat.qid.type || (ulong)~r->ifcall.twstat.stat.qid.version || (uvlong)~r->ifcall.twstat.stat.qid.path) { - respond(r, "wstat of qid"); - return; - } - if(r->ifcall.twstat.stat.muid && r->ifcall.twstat.stat.muid[0]) { - respond(r, "wstat of muid"); - return; - } - if((ulong)~r->ifcall.twstat.stat.mode && ((r->ifcall.twstat.stat.mode&DMDIR)>>24) != r->fid->qid.type&QTDIR) { - respond(r, "wstat on DMDIR bit"); - return; - } - if(!p9conn->srv->wstat) { - respond(r, Enofunc); - return; - } - p9conn->srv->wstat(r); - break; - /* Still to be implemented: auth */ - } -} - -void -respond(Ixp9Req *r, const char *error) { - Ixp9Conn *p9conn; - int msize; - - p9conn = r->conn; - - switch(r->ifcall.hdr.type) { - default: - if(!error) - assert(!"Respond called on unsupported fcall type"); - break; - case TVersion: - assert(error == nil); - free(r->ifcall.version.version); - - thread->lock(&p9conn->rlock); - thread->lock(&p9conn->wlock); - msize = min(r->ofcall.version.msize, IXP_MAX_MSG); - p9conn->rmsg.data = erealloc(p9conn->rmsg.data, msize); - p9conn->wmsg.data = erealloc(p9conn->wmsg.data, msize); - p9conn->rmsg.size = msize; - p9conn->wmsg.size = msize; - thread->unlock(&p9conn->wlock); - thread->unlock(&p9conn->rlock); - r->ofcall.version.msize = msize; - break; - case TAttach: - if(error) - destroyfid(p9conn, r->fid->fid); - free(r->ifcall.tattach.uname); - free(r->ifcall.tattach.aname); - break; - case TOpen: - case TCreate: - if(!error) { - r->ofcall.ropen.iounit = p9conn->rmsg.size - 24; - r->fid->iounit = r->ofcall.ropen.iounit; - r->fid->omode = r->ifcall.topen.mode; - r->fid->qid = r->ofcall.ropen.qid; - } - free(r->ifcall.tcreate.name); - break; - case TWalk: - if(error || r->ofcall.rwalk.nwqid < r->ifcall.twalk.nwname) { - if(r->ifcall.hdr.fid != r->ifcall.twalk.newfid && r->newfid) - destroyfid(p9conn, r->newfid->fid); - if(!error && r->ofcall.rwalk.nwqid == 0) - error = Enofile; - }else{ - if(r->ofcall.rwalk.nwqid == 0) - r->newfid->qid = r->fid->qid; - else - r->newfid->qid = r->ofcall.rwalk.wqid[r->ofcall.rwalk.nwqid-1]; - } - free(*r->ifcall.twalk.wname); - break; - case TWrite: - free(r->ifcall.twrite.data); - break; - case TRemove: - if(r->fid) - destroyfid(p9conn, r->fid->fid); - break; - case TClunk: - if(r->fid) - destroyfid(p9conn, r->fid->fid); - break; - case TFlush: - if((r->oldreq = ixp_mapget(&p9conn->tagmap, r->ifcall.tflush.oldtag))) - respond(r->oldreq, Eintr); - break; - case TWStat: - ixp_freestat(&r->ifcall.twstat.stat); - break; - case TRead: - case TStat: - break; - /* Still to be implemented: auth */ - } - - r->ofcall.hdr.tag = r->ifcall.hdr.tag; - - if(error == nil) - r->ofcall.hdr.type = r->ifcall.hdr.type + 1; - else { - r->ofcall.hdr.type = RError; - r->ofcall.error.ename = (char*)error; - } - - ixp_printfcall(&r->ofcall); - - ixp_maprm(&p9conn->tagmap, r->ifcall.hdr.tag);; - - if(p9conn->conn) { - thread->lock(&p9conn->wlock); - msize = ixp_fcall2msg(&p9conn->wmsg, &r->ofcall); - if(ixp_sendmsg(p9conn->conn->fd, &p9conn->wmsg) != msize) - ixp_hangup(p9conn->conn); - thread->unlock(&p9conn->wlock); - } - - switch(r->ofcall.hdr.type) { - case RStat: - free(r->ofcall.rstat.stat); - break; - case RRead: - free(r->ofcall.rread.data); - break; - } - free(r); - decref_p9conn(p9conn); -} - -/* Flush a pending request */ -static void -voidrequest(void *context, void *arg) { - Ixp9Req *orig_req, *flush_req; - Ixp9Conn *conn; - - orig_req = arg; - conn = orig_req->conn; - conn->ref++; - - flush_req = emallocz(sizeof *orig_req); - flush_req->ifcall.hdr.type = TFlush; - flush_req->ifcall.hdr.tag = IXP_NOTAG; - flush_req->ifcall.tflush.oldtag = orig_req->ifcall.hdr.tag; - flush_req->conn = conn; - - flush_req->aux = *(void**)context; - *(void**)context = flush_req; -} - -/* Clunk an open Fid */ -static void -voidfid(void *context, void *arg) { - Ixp9Conn *p9conn; - Ixp9Req *clunk_req; - Fid *fid; - - fid = arg; - p9conn = fid->conn; - p9conn->ref++; - - clunk_req = emallocz(sizeof *clunk_req); - clunk_req->ifcall.hdr.type = TClunk; - clunk_req->ifcall.hdr.tag = IXP_NOTAG; - clunk_req->ifcall.hdr.fid = fid->fid; - clunk_req->fid = fid; - clunk_req->conn = p9conn; - - clunk_req->aux = *(void**)context; - *(void**)context = clunk_req; -} - -static void -cleanupconn(IxpConn *c) { - Ixp9Conn *p9conn; - Ixp9Req *req, *r; - - p9conn = c->aux; - p9conn->conn = nil; - req = nil; - if(p9conn->ref > 1) { - ixp_mapexec(&p9conn->fidmap, voidfid, &req); - ixp_mapexec(&p9conn->tagmap, voidrequest, &req); - } - while((r = req)) { - req = r->aux; - r->aux = nil; - handlereq(r); - } - decref_p9conn(p9conn); -} - -/* Handle incoming 9P connections */ -void -serve_9pcon(IxpConn *c) { - Ixp9Conn *p9conn; - int fd; - - fd = accept(c->fd, nil, nil); - if(fd < 0) - return; - - p9conn = emallocz(sizeof *p9conn); - p9conn->ref++; - p9conn->srv = c->aux; - p9conn->rmsg.size = 1024; - p9conn->wmsg.size = 1024; - p9conn->rmsg.data = emalloc(p9conn->rmsg.size); - p9conn->wmsg.data = emalloc(p9conn->wmsg.size); - - ixp_mapinit(&p9conn->tagmap, p9conn->taghash, nelem(p9conn->taghash)); - ixp_mapinit(&p9conn->fidmap, p9conn->fidhash, nelem(p9conn->fidhash)); - thread->initmutex(&p9conn->rlock); - thread->initmutex(&p9conn->wlock); - - ixp_listen(c->srv, fd, p9conn, handlefcall, cleanupconn); -} diff --git a/libixp/rpc.c b/libixp/rpc.c @@ -1,262 +0,0 @@ -/* From Plan 9's libmux. - * Copyright (c) 2003 Russ Cox, Massachusetts Institute of Technology - * Distributed under the same terms as libixp. - */ -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include "ixp_local.h" - -static int gettag(IxpClient*, IxpRpc*); -static void puttag(IxpClient*, IxpRpc*); -static void enqueue(IxpClient*, IxpRpc*); -static void dequeue(IxpClient*, IxpRpc*); - -void -muxinit(IxpClient *mux) -{ - mux->tagrend.mutex = &mux->lk; - mux->sleep.next = &mux->sleep; - mux->sleep.prev = &mux->sleep; - thread->initmutex(&mux->lk); - thread->initmutex(&mux->rlock); - thread->initmutex(&mux->wlock); - thread->initrendez(&mux->tagrend); -} - -void -muxfree(IxpClient *mux) -{ - thread->mdestroy(&mux->lk); - thread->mdestroy(&mux->rlock); - thread->mdestroy(&mux->wlock); - thread->rdestroy(&mux->tagrend); - free(mux->wait); -} - -static void -initrpc(IxpClient *mux, IxpRpc *r) -{ - r->mux = mux; - r->waiting = 1; - r->r.mutex = &mux->lk; - r->p = nil; - thread->initrendez(&r->r); -} - -static void -freemuxrpc(IxpRpc *r) -{ - thread->rdestroy(&r->r); -} - -static int -sendrpc(IxpRpc *r, Fcall *f) -{ - int ret; - IxpClient *mux; - - ret = 0; - mux = r->mux; - /* assign the tag, add selves to response queue */ - thread->lock(&mux->lk); - r->tag = gettag(mux, r); - f->hdr.tag = r->tag; - enqueue(mux, r); - thread->unlock(&mux->lk); - - thread->lock(&mux->wlock); - if(!ixp_fcall2msg(&mux->wmsg, f) || !ixp_sendmsg(mux->fd, &mux->wmsg)) { - /* werrstr("settag/send tag %d: %r", tag); fprint(2, "%r\n"); */ - thread->lock(&mux->lk); - dequeue(mux, r); - puttag(mux, r); - thread->unlock(&mux->lk); - ret = -1; - } - thread->unlock(&mux->wlock); - return ret; -} - -static Fcall* -muxrecv(IxpClient *mux) -{ - Fcall *f; - - f = nil; - thread->lock(&mux->rlock); - if(ixp_recvmsg(mux->fd, &mux->rmsg) == 0) - goto fail; - f = emallocz(sizeof *f); - if(ixp_msg2fcall(&mux->rmsg, f) == 0) { - free(f); - f = nil; - } -fail: - thread->unlock(&mux->rlock); - return f; -} - -static void -dispatchandqlock(IxpClient *mux, Fcall *f) -{ - int tag; - IxpRpc *r2; - - tag = f->hdr.tag - mux->mintag; - thread->lock(&mux->lk); - /* hand packet to correct sleeper */ - if(tag < 0 || tag >= mux->mwait) { - fprintf(stderr, "libixp: recieved unfeasible tag: %d (min: %d, max: %d)\n", f->hdr.tag, mux->mintag, mux->mintag+mux->mwait); - goto fail; - } - r2 = mux->wait[tag]; - if(r2 == nil || r2->prev == nil) { - fprintf(stderr, "libixp: recieved message with bad tag\n"); - goto fail; - } - r2->p = f; - dequeue(mux, r2); - thread->wake(&r2->r); - return; -fail: - ixp_freefcall(f); - free(f); -} - -static void -electmuxer(IxpClient *mux) -{ - IxpRpc *rpc; - - /* if there is anyone else sleeping, wake them to mux */ - for(rpc=mux->sleep.next; rpc != &mux->sleep; rpc = rpc->next){ - if(!rpc->async){ - mux->muxer = rpc; - thread->wake(&rpc->r); - return; - } - } - mux->muxer = nil; -} - -Fcall* -muxrpc(IxpClient *mux, Fcall *tx) -{ - IxpRpc r; - Fcall *p; - - initrpc(mux, &r); - if(sendrpc(&r, tx) < 0) - return nil; - - thread->lock(&mux->lk); - /* wait for our packet */ - while(mux->muxer && mux->muxer != &r && !r.p) - thread->sleep(&r.r); - - /* if not done, there's no muxer; start muxing */ - if(!r.p){ - assert(mux->muxer == nil || mux->muxer == &r); - mux->muxer = &r; - while(!r.p){ - thread->unlock(&mux->lk); - p = muxrecv(mux); - if(p == nil){ - /* eof -- just give up and pass the buck */ - thread->lock(&mux->lk); - dequeue(mux, &r); - break; - } - dispatchandqlock(mux, p); - } - electmuxer(mux); - } - p = r.p; - puttag(mux, &r); - thread->unlock(&mux->lk); - if(p == nil) - werrstr("unexpected eof"); - return p; -} - -static void -enqueue(IxpClient *mux, IxpRpc *r) -{ - r->next = mux->sleep.next; - r->prev = &mux->sleep; - r->next->prev = r; - r->prev->next = r; -} - -static void -dequeue(IxpClient *mux, IxpRpc *r) -{ - r->next->prev = r->prev; - r->prev->next = r->next; - r->prev = nil; - r->next = nil; -} - -static int -gettag(IxpClient *mux, IxpRpc *r) -{ - int i, mw; - IxpRpc **w; - - for(;;){ - /* wait for a free tag */ - while(mux->nwait == mux->mwait){ - if(mux->mwait < mux->maxtag-mux->mintag){ - mw = mux->mwait; - if(mw == 0) - mw = 1; - else - mw <<= 1; - w = realloc(mux->wait, mw * sizeof *w); - if(w == nil) - return -1; - memset(w+mux->mwait, 0, (mw-mux->mwait) * sizeof *w); - mux->wait = w; - mux->freetag = mux->mwait; - mux->mwait = mw; - break; - } - thread->sleep(&mux->tagrend); - } - - i=mux->freetag; - if(mux->wait[i] == 0) - goto Found; - for(; i<mux->mwait; i++) - if(mux->wait[i] == 0) - goto Found; - for(i=0; i<mux->freetag; i++) - if(mux->wait[i] == 0) - goto Found; - /* should not fall out of while without free tag */ - abort(); - } - -Found: - mux->nwait++; - mux->wait[i] = r; - r->tag = i+mux->mintag; - return r->tag; -} - -static void -puttag(IxpClient *mux, IxpRpc *r) -{ - int i; - - i = r->tag - mux->mintag; - assert(mux->wait[i] == r); - mux->wait[i] = nil; - mux->nwait--; - mux->freetag = i; - thread->wake(&mux->tagrend); - freemuxrpc(r); -} - diff --git a/libixp/server.c b/libixp/server.c @@ -1,165 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <errno.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <unistd.h> -#include "ixp_local.h" - -/** - * Function: ixp_listen - * - * Params: - * fs - The file descriptor on which to listen. - * aux - A piece of data to store in the connection's - * T<IxpConn> data structure. - * read - The function to call when the connection has - * data available to read. - * close - A cleanup function to call when the - * connection is closed. - * - * Starts the server P<s> listening on P<fd>. The optional - * callbacks are called as described, with the connections - * T<IxpConn> data structure as their arguments. - * - * Returns: - * Returns the connection's new T<IxpConn> data - * structure. - * - * S<IxpConn> - */ -IxpConn* -ixp_listen(IxpServer *s, int fd, void *aux, - void (*read)(IxpConn *c), - void (*close)(IxpConn *c) - ) { - IxpConn *c; - - c = emallocz(sizeof *c); - c->fd = fd; - c->aux = aux; - c->srv = s; - c->read = read; - c->close = close; - c->next = s->conn; - s->conn = c; - return c; -} - -/** - * Function: ixp_hangup - * Function: ixp_server_close - * - * ixp_hangup closes a connection, and stops the server - * listening on it. It calls the connection's close - * function, if it exists. ixp_server_close calls ixp_hangup - * on all of the connections on which the server is - * listening. - */ - -void -ixp_hangup(IxpConn *c) { - IxpServer *s; - IxpConn **tc; - - s = c->srv; - for(tc=&s->conn; *tc; tc=&(*tc)->next) - if(*tc == c) break; - assert(*tc == c); - - *tc = c->next; - c->closed = 1; - if(c->close) - c->close(c); - else - shutdown(c->fd, SHUT_RDWR); - - close(c->fd); - free(c); -} - -void -ixp_server_close(IxpServer *s) { - IxpConn *c, *next; - - for(c = s->conn; c; c = next) { - next = c->next; - ixp_hangup(c); - } -} - -static void -prepare_select(IxpServer *s) { - IxpConn *c; - - FD_ZERO(&s->rd); - for(c = s->conn; c; c = c->next) - if(c->read) { - if(s->maxfd < c->fd) - s->maxfd = c->fd; - FD_SET(c->fd, &s->rd); - } -} - -static void -handle_conns(IxpServer *s) { - IxpConn *c, *n; - for(c = s->conn; c; c = n) { - n = c->next; - if(FD_ISSET(c->fd, &s->rd)) - c->read(c); - } -} - -/** - * Function: ixp_serverloop - * - * Enters the main loop of the server. Exits when - * P<s>->running becomes false, or when select(2) returns an - * error other than EINTR. - * - * S<IxpServer> - * - * Returns: - * Returns 0 when the loop exits normally, and 1 when - * it exits on error. V<errno> or the return value of - * ixp_errbuf(3) may be inspected. - * - */ - -int -ixp_serverloop(IxpServer *s) { - timeval *tvp; - timeval tv; - long timeout; - int r; - - s->running = 1; - thread->initmutex(&s->lk); - while(s->running) { - if(s->preselect) - s->preselect(s); - - tvp = nil; - timeout = ixp_nexttimer(s); - if(timeout > 0) { - tv.tv_sec = timeout/1000; - tv.tv_usec = timeout%1000 * 1000; - tvp = &tv; - } - - prepare_select(s); - r = thread->select(s->maxfd + 1, &s->rd, 0, 0, tvp); - if(r < 0) { - if(errno == EINTR) - continue; - return 1; - } - handle_conns(s); - } - return 0; -} - diff --git a/libixp/socket.c b/libixp/socket.c @@ -1,278 +0,0 @@ -/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> - * Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ -#include <errno.h> -#include <netdb.h> -#include <netinet/in.h> -#include <signal.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> -#include "ixp_local.h" - -/* Note: These functions modify the strings that they are passed. - * The lookup function duplicates the original string, so it is - * not modified. - */ - -/* From FreeBSD's sys/su.h */ -#define SUN_LEN(su) \ - (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) - -typedef struct addrinfo addrinfo; -typedef struct sockaddr sockaddr; -typedef struct sockaddr_un sockaddr_un; -typedef struct sockaddr_in sockaddr_in; - -static char* -get_port(char *addr) { - char *s; - - s = strchr(addr, '!'); - if(s == nil) { - werrstr("no port provided"); - return nil; - } - - *s++ = '\0'; - if(*s == '\0') { - werrstr("invalid port number"); - return nil; - } - return s; -} - -static int -sock_unix(char *address, sockaddr_un *sa, socklen_t *salen) { - int fd; - - memset(sa, 0, sizeof *sa); - - sa->sun_family = AF_UNIX; - strncpy(sa->sun_path, address, sizeof sa->sun_path); - *salen = SUN_LEN(sa); - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if(fd < 0) - return -1; - return fd; -} - -static int -dial_unix(char *address) { - sockaddr_un sa; - socklen_t salen; - int fd; - - fd = sock_unix(address, &sa, &salen); - if(fd == -1) - return fd; - - if(connect(fd, (sockaddr*) &sa, salen)) { - close(fd); - return -1; - } - return fd; -} - -static int -announce_unix(char *file) { - const int yes = 1; - sockaddr_un sa; - socklen_t salen; - int fd; - - signal(SIGPIPE, SIG_IGN); - - fd = sock_unix(file, &sa, &salen); - if(fd == -1) - return fd; - - if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof yes) < 0) - goto fail; - - unlink(file); - if(bind(fd, (sockaddr*)&sa, salen) < 0) - goto fail; - - chmod(file, S_IRWXU); - if(listen(fd, IXP_MAX_CACHE) < 0) - goto fail; - - return fd; - -fail: - close(fd); - return -1; -} - -static addrinfo* -alookup(char *host, int announce) { - addrinfo hints, *ret; - char *port; - int err; - - /* Truncates host at '!' */ - port = get_port(host); - if(port == nil) - return nil; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - - if(announce) { - hints.ai_flags = AI_PASSIVE; - if(!strcmp(host, "*")) - host = nil; - } - - err = getaddrinfo(host, port, &hints, &ret); - if(err) { - werrstr("getaddrinfo: %s", gai_strerror(err)); - return nil; - } - return ret; -} - -static int -ai_socket(addrinfo *ai) { - return socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); -} - -static int -dial_tcp(char *host) { - addrinfo *ai, *aip; - int fd; - - aip = alookup(host, 0); - if(aip == nil) - return -1; - - SET(fd); - for(ai = aip; ai; ai = ai->ai_next) { - fd = ai_socket(ai); - if(fd == -1) { - werrstr("socket: %s", strerror(errno)); - continue; - } - - if(connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) - break; - - werrstr("connect: %s", strerror(errno)); - close(fd); - fd = -1; - } - - freeaddrinfo(aip); - return fd; -} - -static int -announce_tcp(char *host) { - addrinfo *ai, *aip; - int fd; - - aip = alookup(host, 1); - if(aip == nil) - return -1; - - /* Probably don't need to loop */ - SET(fd); - for(ai = aip; ai; ai = ai->ai_next) { - fd = ai_socket(ai); - if(fd == -1) - continue; - - if(bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) - goto fail; - - if(listen(fd, IXP_MAX_CACHE) < 0) - goto fail; - break; - fail: - close(fd); - fd = -1; - } - - freeaddrinfo(aip); - return fd; -} - -typedef struct addrtab addrtab; -static -struct addrtab { - char *type; - int (*fn)(char*); -} dtab[] = { - {"tcp", dial_tcp}, - {"unix", dial_unix}, - {0, 0} -}, atab[] = { - {"tcp", announce_tcp}, - {"unix", announce_unix}, - {0, 0} -}; - -static int -lookup(const char *address, addrtab *tab) { - char *addr, *type; - int ret; - - ret = -1; - type = estrdup(address); - - addr = strchr(type, '!'); - if(addr == nil) - werrstr("no address type defined"); - else { - *addr++ = '\0'; - for(; tab->type; tab++) - if(strcmp(tab->type, type) == 0) break; - if(tab->type == nil) - werrstr("unsupported address type"); - else - ret = tab->fn(addr); - } - - free(type); - return ret; -} - -/** - * Function: ixp_dial - * Function: ixp_announce - * - * Params: - * address - An address on which to connect or listen, - * specified in the Plan 9 resources - * specification format - * (<protocol>!address[!<port>]) - * - * These functions hide some of the ugliness of Berkely - * Sockets. ixp_dial connects to the resource at P<address>, - * while ixp_announce begins listening on P<address>. - * - * Returns: - * These functions return file descriptors on success, - * and -1 on failure. ixp_errbuf(3) may be inspected on - * failure. - */ - -int -ixp_dial(const char *address) { - return lookup(address, dtab); -} - -int -ixp_announce(const char *address) { - return lookup(address, atab); -} - diff --git a/libixp/srv_util.c b/libixp/srv_util.c @@ -1,457 +0,0 @@ -/* Copyright ©2006-2008 Kris Maglione <fbsdaemon at gmail dot com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <ctype.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include "ixp_local.h" - -typedef void* IxpFileIdU; - -static char - Enofile[] = "file not found"; - -#include "ixp_srvutil.h" - -struct IxpQueue { - IxpQueue* link; - char* dat; - long len; -}; - -/* Macros */ -#define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF)) - -/* Global Vars */ -/***************/ -static IxpFileId* free_fileid; - -/* Utility Functions */ -/** - * Obtain an empty, reference counted IxpFileId struct. - */ -IxpFileId* -ixp_srv_getfile(void) { - IxpFileId *file; - int i; - - if(!free_fileid) { - i = 15; - file = emallocz(i * sizeof *file); - for(; i; i--) { - file->next = free_fileid; - free_fileid = file++; - } - } - file = free_fileid; - free_fileid = file->next; - file->p = nil; - file->volatil = 0; - file->nref = 1; - file->next = nil; - file->pending = false; - return file; -} - -/** - * Decrease the reference count of the given IxpFileId, - * and push it onto the free list when it reaches 0; - */ -void -ixp_srv_freefile(IxpFileId *f) { - if(--f->nref) - return; - free(f->tab.name); - f->next = free_fileid; - free_fileid = f; -} - -/** - * Increase the reference count of every IxpFileId linked - * to 'f'. - */ -IxpFileId* -ixp_srv_clonefiles(IxpFileId *f) { - IxpFileId *r; - - r = emalloc(sizeof *r); - memcpy(r, f, sizeof *r); - r->tab.name = estrdup(r->tab.name); - r->nref = 1; - for(f=f->next; f; f=f->next) - assert(f->nref++); - return r; -} - -void -ixp_srv_readbuf(Ixp9Req *req, char *buf, uint len) { - - if(req->ifcall.io.offset >= len) - return; - - len -= req->ifcall.io.offset; - if(len > req->ifcall.io.count) - len = req->ifcall.io.count; - req->ofcall.io.data = emalloc(len); - memcpy(req->ofcall.io.data, buf + req->ifcall.io.offset, len); - req->ofcall.io.count = len; -} - -void -ixp_srv_writebuf(Ixp9Req *req, char **buf, uint *len, uint max) { - IxpFileId *file; - char *p; - uint offset, count; - - file = req->fid->aux; - - offset = req->ifcall.io.offset; - if(file->tab.perm & DMAPPEND) - offset = *len; - - if(offset > *len || req->ifcall.io.count == 0) { - req->ofcall.io.count = 0; - return; - } - - count = req->ifcall.io.count; - if(max && (offset + count > max)) - count = max - offset; - - *len = offset + count; - if(max == 0) - *buf = erealloc(*buf, *len + 1); - p = *buf; - - memcpy(p+offset, req->ifcall.io.data, count); - req->ofcall.io.count = count; - p[offset+count] = '\0'; -} - -/** - * Ensure that the data member of 'r' is null terminated, - * removing any new line from its end. - */ -void -ixp_srv_data2cstring(Ixp9Req *req) { - char *p, *q; - uint i; - - i = req->ifcall.io.count; - p = req->ifcall.io.data; - if(i && p[i - 1] == '\n') - i--; - q = memchr(p, '\0', i); - if(q) - i = q - p; - - p = erealloc(req->ifcall.io.data, i+1); - p[i] = '\0'; - req->ifcall.io.data = p; -} - -char* -ixp_srv_writectl(Ixp9Req *req, char* (*fn)(void*, IxpMsg*)) { - char *err, *s, *p, c; - IxpFileId *file; - IxpMsg msg; - - file = req->fid->aux; - - ixp_srv_data2cstring(req); - s = req->ifcall.io.data; - - err = nil; - c = *s; - while(c != '\0') { - while(*s == '\n') - s++; - p = s; - while(*p != '\0' && *p != '\n') - p++; - c = *p; - *p = '\0'; - - msg = ixp_message(s, p-s, 0); - s = fn(file->p, &msg); - if(s) - err = s; - s = p + 1; - } - return err; -} - -void -ixp_pending_respond(Ixp9Req *req) { - IxpFileId *file; - IxpPendingLink *p; - IxpRequestLink *req_link; - IxpQueue *queue; - - file = req->fid->aux; - assert(file->pending); - p = file->p; - if(p->queue) { - queue = p->queue; - p->queue = queue->link; - req->ofcall.io.data = queue->dat; - req->ofcall.io.count = queue->len; - if(req->aux) { - req_link = req->aux; - req_link->next->prev = req_link->prev; - req_link->prev->next = req_link->next; - free(req_link); - } - respond(req, nil); - free(queue); - }else { - req_link = emallocz(sizeof *req_link); - req_link->req = req; - req_link->next = &p->pending->req; - req_link->prev = req_link->next->prev; - req_link->next->prev = req_link; - req_link->prev->next = req_link; - req->aux = req_link; - } -} - -void -ixp_pending_write(IxpPending *pending, char *dat, long n) { - IxpRequestLink req_link; - IxpQueue **qp, *queue; - IxpPendingLink *pp; - IxpRequestLink *rp; - - if(n == 0) - return; - - if(pending->req.next == nil) { - pending->req.next = &pending->req; - pending->req.prev = &pending->req; - pending->fids.prev = &pending->fids; - pending->fids.next = &pending->fids; - } - - for(pp=pending->fids.next; pp != &pending->fids; pp=pp->next) { - for(qp=&pp->queue; *qp; qp=&qp[0]->link) - ; - queue = emallocz(sizeof *queue); - queue->dat = emalloc(n); - memcpy(queue->dat, dat, n); - queue->len = n; - *qp = queue; - } - - req_link.next = &req_link; - req_link.prev = &req_link; - if(pending->req.next != &pending->req) { - req_link.next = pending->req.next; - req_link.prev = pending->req.prev; - pending->req.prev = &pending->req; - pending->req.next = &pending->req; - } - req_link.prev->next = &req_link; - req_link.next->prev = &req_link; - - while((rp = req_link.next) != &req_link) - ixp_pending_respond(rp->req); -} - -void -ixp_pending_pushfid(IxpPending *pending, IxpFid *fid) { - IxpPendingLink *pend_link; - IxpFileId *file; - - if(pending->req.next == nil) { - pending->req.next = &pending->req; - pending->req.prev = &pending->req; - pending->fids.prev = &pending->fids; - pending->fids.next = &pending->fids; - } - - file = fid->aux; - pend_link = emallocz(sizeof *pend_link); - pend_link->fid = fid; - pend_link->pending = pending; - pend_link->next = &pending->fids; - pend_link->prev = pend_link->next->prev; - pend_link->next->prev = pend_link; - pend_link->prev->next = pend_link; - file->pending = true; - file->p = pend_link; -} - -static void -pending_flush(Ixp9Req *req) { - IxpFileId *file; - IxpRequestLink *req_link; - - file = req->fid->aux; - if(file->pending) { - req_link = req->aux; - if(req_link) { - req_link->prev->next = req_link->next; - req_link->next->prev = req_link->prev; - free(req_link); - } - } -} - -void -ixp_pending_flush(Ixp9Req *req) { - - pending_flush(req->oldreq); -} - -bool -ixp_pending_clunk(Ixp9Req *req) { - IxpPending *pending; - IxpPendingLink *pend_link; - IxpRequestLink *req_link; - Ixp9Req *r; - IxpFileId *file; - IxpQueue *queue; - bool more; - - file = req->fid->aux; - pend_link = file->p; - - pending = pend_link->pending; - for(req_link=pending->req.next; req_link != &pending->req;) { - r = req_link->req; - req_link = req_link->next; - if(r->fid == pend_link->fid) { - pending_flush(r); - respond(r, "interrupted"); - } - } - - pend_link->prev->next = pend_link->next; - pend_link->next->prev = pend_link->prev; - - while((queue = pend_link->queue)) { - pend_link->queue = queue->link; - free(queue->dat); - free(queue); - } - more = (pend_link->pending->fids.next == &pend_link->pending->fids); - free(pend_link); - respond(req, nil); - return more; -} - -bool -ixp_srv_verifyfile(IxpFileId *file, IxpLookupFn lookup) { - IxpFileId *tfile; - int ret; - - if(!file->next) - return true; - - ret = false; - if(ixp_srv_verifyfile(file->next, lookup)) { - tfile = lookup(file->next, file->tab.name); - if(tfile) { - if(!tfile->volatil || tfile->p == file->p) - ret = true; - ixp_srv_freefile(tfile); - } - } - return ret; -} - -void -ixp_srv_readdir(Ixp9Req *req, IxpLookupFn lookup, void (*dostat)(IxpStat*, IxpFileId*)) { - IxpMsg msg; - IxpFileId *file, *tfile; - IxpStat stat; - char *buf; - ulong size, n; - uvlong offset; - - file = req->fid->aux; - - size = req->ifcall.io.count; - if(size > req->fid->iounit) - size = req->fid->iounit; - buf = emallocz(size); - msg = ixp_message(buf, size, MsgPack); - - file = lookup(file, nil); - tfile = file; - /* Note: The first file is ".", so we skip it. */ - offset = 0; - for(file=file->next; file; file=file->next) { - dostat(&stat, file); - n = ixp_sizeof_stat(&stat); - if(offset >= req->ifcall.io.offset) { - if(size < n) - break; - ixp_pstat(&msg, &stat); - size -= n; - } - offset += n; - } - while((file = tfile)) { - tfile=tfile->next; - ixp_srv_freefile(file); - } - req->ofcall.io.count = msg.pos - msg.data; - req->ofcall.io.data = msg.data; - respond(req, nil); -} - -void -ixp_srv_walkandclone(Ixp9Req *req, IxpLookupFn lookup) { - IxpFileId *file, *tfile; - int i; - - file = ixp_srv_clonefiles(req->fid->aux); - for(i=0; i < req->ifcall.twalk.nwname; i++) { - if(!strcmp(req->ifcall.twalk.wname[i], "..")) { - if(file->next) { - tfile=file; - file=file->next; - ixp_srv_freefile(tfile); - } - }else{ - tfile = lookup(file, req->ifcall.twalk.wname[i]); - if(!tfile) - break; - assert(!tfile->next); - if(strcmp(req->ifcall.twalk.wname[i], ".")) { - tfile->next = file; - file = tfile; - } - } - req->ofcall.rwalk.wqid[i].type = file->tab.qtype; - req->ofcall.rwalk.wqid[i].path = QID(file->tab.type, file->id); - } - /* There should be a way to do this on freefid() */ - if(i < req->ifcall.twalk.nwname) { - while((tfile = file)) { - file=file->next; - ixp_srv_freefile(tfile); - } - respond(req, Enofile); - return; - } - /* Remove refs for req->fid if no new fid */ - if(req->ifcall.hdr.fid == req->ifcall.twalk.newfid) { - tfile = req->fid->aux; - req->fid->aux = file; - while((file = tfile)) { - tfile = tfile->next; - ixp_srv_freefile(file); - } - }else - req->newfid->aux = file; - req->ofcall.rwalk.nwqid = i; - respond(req, nil); -} - diff --git a/libixp/thread.c b/libixp/thread.c @@ -1,97 +0,0 @@ -/* Public Domain --Kris Maglione */ -#include <unistd.h> -#include "ixp_local.h" - -static IxpThread ixp_nothread; -IxpThread*ixp_thread = &ixp_nothread; - -static char* -errbuf(void) { - static char errbuf[IXP_ERRMAX]; - - return errbuf; -} - -static void -mvoid(IxpMutex *m) { - USED(m); - return; -} - -static int -mtrue(IxpMutex *m) { - USED(m); - return 1; -} - -static int -mfalse(IxpMutex *m) { - USED(m); - return 0; -} - -static void -rwvoid(IxpRWLock *rw) { - USED(rw); - return; -} - -static int -rwtrue(IxpRWLock *rw) { - USED(rw); - return 1; -} - -static int -rwfalse(IxpRWLock *m) { - USED(m); - return 0; -} - -static void -rvoid(IxpRendez *r) { - USED(r); - return; -} - -static int -rfalse(IxpRendez *r) { - USED(r); - return 0; -} - -static void -rsleep(IxpRendez *r) { - USED(r); - eprint("rsleep called when not implemented\n"); -} - -static IxpThread ixp_nothread = { - /* RWLock */ - .initrwlock = rwfalse, - .rlock = rwvoid, - .runlock = rwvoid, - .canrlock = rwtrue, - .wlock = rwvoid, - .wunlock = rwvoid, - .canwlock = rwtrue, - .rwdestroy = rwvoid, - /* Mutex */ - .initmutex = mfalse, - .lock = mvoid, - .unlock = mvoid, - .canlock = mtrue, - .mdestroy = mvoid, - /* Rendez */ - .initrendez = rfalse, - .sleep = rsleep, - .wake = rfalse, - .wakeall = rfalse, - .rdestroy = rvoid, - /* Other */ - .errbuf = errbuf, - .read = read, - .write = write, - .select = select, -}; - diff --git a/libixp/timer.c b/libixp/timer.c @@ -1,139 +0,0 @@ -/* Copyright ©2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <stdlib.h> -#include <sys/time.h> -#include "ixp_local.h" - -/* This really needn't be threadsafe, as it has little use in - * threaded programs, but it is, nonetheless. - */ - -static long lastid = 1; - -/** - * Function: ixp_msec - * - * Returns: - * Returns the time since the Epoch in milliseconds. - * Be aware that this may overflow. - */ -long -ixp_msec(void) { - timeval tv; - - if(gettimeofday(&tv, 0) < 0) - return -1; - return tv.tv_sec*1000 + tv.tv_usec/1000; -} - -/** - * Function: ixp_settimer - * - * Params: - * msec - The timeout in milliseconds. - * fn - The function to call after P<msec> milliseconds - * have elapsed. - * aux - An arbitrary argument to pass to P<fn> when it - * is called. - * - * Initializes a callback-based timer to be triggerred after - * P<msec> milliseconds. The timer is passed its id number - * and the value of P<aux>. - * - * Returns: - * Returns the new timer's unique id number. - */ -long -ixp_settimer(IxpServer *s, long msec, void (*fn)(long, void*), void *aux) { - Timer **tp; - Timer *t; - long time; - - time = ixp_msec(); - if(time == -1) - return -1; - msec += time; - - t = emallocz(sizeof *t); - thread->lock(&s->lk); - t->id = lastid++; - t->msec = msec; - t->fn = fn; - t->aux = aux; - - for(tp=&s->timer; *tp; tp=&tp[0]->link) - if(tp[0]->msec < msec) - break; - t->link = *tp; - *tp = t; - thread->unlock(&s->lk); - return t->id; -} - -/** - * Function: ixp_unsettimer - * - * Params: - * id - The id number of the timer to void. - * - * Voids the timer identified by P<id>. - * - * Returns: - * Returns true if a timer was stopped, false - * otherwise. - */ -int -ixp_unsettimer(IxpServer *s, long id) { - Timer **tp; - Timer *t; - - thread->lock(&s->lk); - for(tp=&s->timer; (t=*tp); tp=&t->link) - if(t->id == id) - break; - if(t) { - *tp = t->link; - free(t); - } - thread->unlock(&s->lk); - return t != nil; -} - -/** - * Function: ixp_nexttimer - * - * Triggers any timers whose timeouts have ellapsed. This is - * primarilly intended to be called from libixp's select - * loop. - * - * Returns: - * Returns the number of milliseconds until the next - * timer's timeout. - */ -long -ixp_nexttimer(IxpServer *s) { - Timer *t; - long time, ret; - - SET(time); - thread->lock(&s->lk); - while((t = s->timer)) { - time = ixp_msec(); - if(t->msec > time) - break; - s->timer = t->link; - - thread->unlock(&s->lk); - t->fn(t->id, t->aux); - free(t); - thread->lock(&s->lk); - } - ret = 0; - if(t) - ret = t->msec - time; - thread->unlock(&s->lk); - return ret; -} - diff --git a/libixp/transport.c b/libixp/transport.c @@ -1,97 +0,0 @@ -/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> -#include "ixp_local.h" - -static int -mread(int fd, IxpMsg *msg, uint count) { - int r, n; - - n = msg->end - msg->pos; - if(n <= 0) { - werrstr("buffer full"); - return -1; - } - if(n > count) - n = count; - - r = thread->read(fd, msg->pos, n); - if(r > 0) - msg->pos += r; - return r; -} - -static int -readn(int fd, IxpMsg *msg, uint count) { - uint num; - int r; - - num = count; - while(num > 0) { - r = mread(fd, msg, num); - if(r == -1 && errno == EINTR) - continue; - if(r == 0) { - werrstr("broken pipe: %s", ixp_errbuf()); - return count - num; - } - num -= r; - } - return count - num; -} - -uint -ixp_sendmsg(int fd, IxpMsg *msg) { - int r; - - msg->pos = msg->data; - while(msg->pos < msg->end) { - r = thread->write(fd, msg->pos, msg->end - msg->pos); - if(r < 1) { - if(errno == EINTR) - continue; - werrstr("broken pipe: %s", ixp_errbuf()); - return 0; - } - msg->pos += r; - } - return msg->pos - msg->data; -} - -uint -ixp_recvmsg(int fd, IxpMsg *msg) { - enum { SSize = 4 }; - ulong msize, size; - - msg->mode = MsgUnpack; - msg->pos = msg->data; - msg->end = msg->data + msg->size; - if(readn(fd, msg, SSize) != SSize) - return 0; - - msg->pos = msg->data; - ixp_pu32(msg, &msize); - - size = msize - SSize; - if(size >= msg->end - msg->pos) { - werrstr("message too large"); - return 0; - } - if(readn(fd, msg, size) != size) { - werrstr("message incomplete"); - return 0; - } - - msg->end = msg->pos; - return msize; -} - diff --git a/libixp/util.c b/libixp/util.c @@ -1,240 +0,0 @@ -/* Written by Kris Maglione <fbsdaemon at gmail dot com> */ -/* Public domain */ -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <pwd.h> -#include "ixp_local.h" - -char* -ixp_smprint(const char *fmt, ...) { - va_list ap; - char *s; - - va_start(ap, fmt); - s = ixp_vsmprint(fmt, ap); - va_end(ap); - if(s == nil) - ixp_werrstr("no memory"); - return s; -} - -static char* -_user(void) { - static char *user; - struct passwd *pw; - - if(user == nil) { - pw = getpwuid(getuid()); - if(pw) - user = strdup(pw->pw_name); - } - if(user == nil) - user = "none"; - return user; -} - -static int -rmkdir(char *path, int mode) { - char *p; - int ret; - char c; - - for(p = path+1; ; p++) { - c = *p; - if((c == '/') || (c == '\0')) { - *p = '\0'; - ret = mkdir(path, mode); - if((ret == -1) && (errno != EEXIST)) { - ixp_werrstr("Can't create path '%s': %s", path, ixp_errbuf()); - return 0; - } - *p = c; - } - if(c == '\0') - break; - } - return 1; -} - -static char* -ns_display(void) { - char *path, *disp; - struct stat st; - - disp = getenv("DISPLAY"); - if(disp == nil || disp[0] == '\0') { - ixp_werrstr("$DISPLAY is unset"); - return nil; - } - - disp = estrdup(disp); - path = &disp[strlen(disp) - 2]; - if(path > disp && !strcmp(path, ".0")) - *path = '\0'; - - path = ixp_smprint("/tmp/ns.%s.%s", _user(), disp); - free(disp); - - if(!rmkdir(path, 0700)) - ; - else if(stat(path, &st)) - ixp_werrstr("Can't stat ns_path '%s': %s", path, ixp_errbuf()); - else if(getuid() != st.st_uid) - ixp_werrstr("ns_path '%s' exists but is not owned by you", path); - else if((st.st_mode & 077) && chmod(path, st.st_mode & ~077)) - ixp_werrstr("Namespace path '%s' exists, but has wrong permissions: %s", path, ixp_errbuf()); - else - return path; - free(path); - return nil; -} - -/** - * Function: ixp_namespace - * - * Returns the path of the canonical 9p namespace directory. - * Either the value of $NAMESPACE, if it's set, or, roughly, - * /tmp/ns.${USER}.${DISPLAY:%.0=%}. In the latter case, the - * directory is created if it doesn't exist, and it is - * ensured to be owned by the current user, with no group or - * other permissions. - * - * Returns: - * A statically allocated string which must not be freed - * or altered by the caller. The same value is returned - * upon successive calls. - */ -/* Not especially threadsafe. */ -char* -ixp_namespace(void) { - static char *namespace; - - if(namespace == nil) - namespace = getenv("NAMESPACE"); - if(namespace == nil) - namespace = ns_display(); - return namespace; -} - -void -eprint(const char *fmt, ...) { - va_list ap; - int err; - - err = errno; - fprintf(stderr, "libixp: fatal: "); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if(fmt[strlen(fmt)-1] == ':') - fprintf(stderr, " %s\n", strerror(err)); - else - fprintf(stderr, "\n"); - - exit(1); -} - -/* Can't malloc */ -static void -mfatal(char *name, uint size) { - const char - couldnot[] = "libixp: fatal: Could not ", - paren[] = "() ", - bytes[] = " bytes\n"; - char sizestr[8]; - int i; - - i = sizeof sizestr; - do { - sizestr[--i] = '0' + (size%10); - size /= 10; - } while(size > 0); - - write(1, couldnot, sizeof(couldnot)-1); - write(1, name, strlen(name)); - write(1, paren, sizeof(paren)-1); - write(1, sizestr+i, sizeof(sizestr)-i); - write(1, bytes, sizeof(bytes)-1); - - exit(1); -} - -void* -emalloc(uint size) { - void *ret = malloc(size); - if(!ret) - mfatal("malloc", size); - return ret; -} - -void* -emallocz(uint size) { - void *ret = emalloc(size); - memset(ret, 0, size); - return ret; -} - -void* -erealloc(void *ptr, uint size) { - void *ret = realloc(ptr, size); - if(!ret) - mfatal("realloc", size); - return ret; -} - -char* -estrdup(const char *str) { - void *ret = strdup(str); - if(!ret) - mfatal("strdup", strlen(str)); - return ret; -} - -uint -tokenize(char *res[], uint reslen, char *str, char delim) { - char *s; - uint i; - - i = 0; - s = str; - while(i < reslen && *s) { - while(*s == delim) - *(s++) = '\0'; - if(*s) - res[i++] = s; - while(*s && *s != delim) - s++; - } - return i; -} - -uint -strlcat(char *dst, const char *src, uint size) { - const char *s; - char *d; - int n, len; - - d = dst; - s = src; - n = size; - while(n-- > 0 && *d != '\0') - d++; - len = n; - - while(*s != '\0' && n-- > 0) - *d++ = *s++; - while(*s++ != '\0') - n--; - if(len > 0) - *d = '\0'; - return size - n - 1; -} - diff --git a/libixp_pthread/Makefile b/libixp_pthread/Makefile @@ -1,10 +0,0 @@ -ROOT= .. -include ${ROOT}/mk/hdr.mk -include ${ROOT}/mk/ixp.mk - -TARG = libixp_pthread - -OBJ = thread_pthread - -include ${ROOT}/mk/lib.mk - diff --git a/libixp_pthread/thread_pthread.c b/libixp_pthread/thread_pthread.c @@ -1,184 +0,0 @@ -/* Written by Kris Maglione <fbsdaemon@gmail.com> */ -/* Public domain */ -#define _XOPEN_SOURCE 600 -#include <errno.h> -#include <pthread.h> -#include <stdlib.h> -#include <unistd.h> -#include "ixp_local.h" - -static IxpThread ixp_pthread; -static pthread_key_t errstr_k; - -int -ixp_pthread_init() { - int ret; - - ret = pthread_key_create(&errstr_k, free); - if(ret) { - werrstr("can't create TLS value: %s", ixp_errbuf()); - return 1; - } - - ixp_thread = &ixp_pthread; - return 0; -} - -static char* -errbuf(void) { - char *ret; - - ret = pthread_getspecific(errstr_k); - if(ret == nil) { - ret = emallocz(IXP_ERRMAX); - pthread_setspecific(errstr_k, (void*)ret); - } - return ret; -} - -static void -mlock(IxpMutex *m) { - pthread_mutex_lock(m->aux); -} - -static int -mcanlock(IxpMutex *m) { - return !pthread_mutex_trylock(m->aux); -} - -static void -munlock(IxpMutex *m) { - pthread_mutex_unlock(m->aux); -} - -static void -mdestroy(IxpMutex *m) { - pthread_mutex_destroy(m->aux); - free(m->aux); -} - -static int -initmutex(IxpMutex *m) { - pthread_mutex_t *mutex; - - mutex = emalloc(sizeof *mutex); - if(pthread_mutex_init(mutex, nil)) { - free(mutex); - return 1; - } - - m->aux = mutex; - return 0; -} - -static void -rlock(IxpRWLock *rw) { - pthread_rwlock_rdlock(rw->aux); -} - -static int -canrlock(IxpRWLock *rw) { - return !pthread_rwlock_tryrdlock(rw->aux); -} - -static void -wlock(IxpRWLock *rw) { - pthread_rwlock_rdlock(rw->aux); -} - -static int -canwlock(IxpRWLock *rw) { - return !pthread_rwlock_tryrdlock(rw->aux); -} - -static void -rwunlock(IxpRWLock *rw) { - pthread_rwlock_unlock(rw->aux); -} - -static void -rwdestroy(IxpRWLock *rw) { - pthread_rwlock_destroy(rw->aux); - free(rw->aux); -} - -static int -initrwlock(IxpRWLock *rw) { - pthread_rwlock_t *rwlock; - - rwlock = emalloc(sizeof *rwlock); - if(pthread_rwlock_init(rwlock, nil)) { - free(rwlock); - return 1; - } - - rw->aux = rwlock; - return 0; -} - -static void -rsleep(IxpRendez *r) { - pthread_cond_wait(r->aux, r->mutex->aux); -} - -static int -rwake(IxpRendez *r) { - pthread_cond_signal(r->aux); - return 0; -} - -static int -rwakeall(IxpRendez *r) { - pthread_cond_broadcast(r->aux); - return 0; -} - -static void -rdestroy(IxpRendez *r) { - pthread_cond_destroy(r->aux); - free(r->aux); -} - -static int -initrendez(IxpRendez *r) { - pthread_cond_t *cond; - - cond = emalloc(sizeof *cond); - if(pthread_cond_init(cond, nil)) { - free(cond); - return 1; - } - - r->aux = cond; - return 0; -} - -static IxpThread ixp_pthread = { - /* Mutex */ - .initmutex = initmutex, - .lock = mlock, - .canlock = mcanlock, - .unlock = munlock, - .mdestroy = mdestroy, - /* RWLock */ - .initrwlock = initrwlock, - .rlock = rlock, - .canrlock = canrlock, - .wlock = wlock, - .canwlock = canwlock, - .runlock = rwunlock, - .wunlock = rwunlock, - .rwdestroy = rwdestroy, - /* Rendez */ - .initrendez = initrendez, - .sleep = rsleep, - .wake = rwake, - .wakeall = rwakeall, - .rdestroy = rdestroy, - /* Other */ - .errbuf = errbuf, - .read = read, - .write = write, - .select = select, -}; - diff --git a/libixp_rubythread/Makefile b/libixp_rubythread/Makefile @@ -1,11 +0,0 @@ -ROOT= .. -include ${ROOT}/mk/hdr.mk -include ${ROOT}/mk/ixp.mk - -CFLAGS += ${RUBYINC} - -TARG = libixp_rubythread -OBJ = thread_ruby - -include ${ROOT}/mk/lib.mk - diff --git a/libixp_rubythread/thread_ruby.c b/libixp_rubythread/thread_ruby.c @@ -1,281 +0,0 @@ -/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> -#include <ruby.h> -#include "ixp_local.h" - -static IxpThread ixp_rthread; -static char RWLock[]; - -int -ixp_rubyinit(void) { - rb_require("thread.rb"); - rb_eval_string(RWLock); - ixp_thread = &ixp_rthread; - return 0; -} - -static char* -errbuf(void) { - static ID key; - volatile VALUE val; - - if(key == 0L) - key = rb_intern("_ixp_errbuf"); - - val = rb_thread_local_aref(rb_thread_current(), key); - if(NIL_P(val)) { - val = rb_str_new(nil, IXP_ERRMAX); - rb_thread_local_aset(rb_thread_current(), key, val); - } - - Check_Type(val, T_STRING); - return RSTRING(val)->ptr; -} - -static void -save(char *eval, void **place) { - *place = (void*)rb_eval_string(eval); - rb_gc_register_address((VALUE*)place); -} - -static void -unsave(void **place) { - rb_gc_unregister_address((VALUE*)place); -} - -#define call(obj, meth, ...) rb_funcall((VALUE)obj, rb_intern(meth), __VA_ARGS__) - -/* Mutex */ -static int -initmutex(IxpMutex *m) { - save("Mutex.new", &m->aux); - return 0; -} - -static void -mdestroy(IxpMutex *m) { - unsave(&m->aux); -} - -static void -mlock(IxpMutex *m) { - call(m->aux, "lock", 0); -} - -static int -mcanlock(IxpMutex *m) { - return call(m->aux, "trylock", 0); -} - -static void -munlock(IxpMutex *m) { - call(m->aux, "unlock", 0); -} - -/* RWLock */ -static int -initrwlock(IxpRWLock *rw) { - save("RWLock.new", &rw->aux); - return 0; -} - -static void -rwdestroy(IxpRWLock *rw) { - unsave(&rw->aux); -} - -static void -rlock(IxpRWLock *rw) { - call(rw->aux, "rdlock", 0); -} - -static int -canrlock(IxpRWLock *rw) { - return call(rw->aux, "tryrdlock", 0) == Qtrue; -} - -static void -wlock(IxpRWLock *rw) { - call(rw->aux, "wrlock", 0); -} - -static int -canwlock(IxpRWLock *rw) { - return call(rw->aux, "trywrlock", 0) == Qtrue; -} - -static void -rwunlock(IxpRWLock *rw) { - call(rw->aux, "unlock", 0); -} - -/* Rendez */ -static int -initrendez(IxpRendez *r) { - save("ConditionVariable.new", &r->aux); - return 0; -} - -static void -rdestroy(IxpRendez *r) { - unsave(&r->aux); -} - -static void -rsleep(IxpRendez *r) { - call(r->aux, "wait", 1, (VALUE)r->mutex->aux); -} - -static int -rwake(IxpRendez *r) { - call(r->aux, "signal", 0); - return 0; -} - -static int -rwakeall(IxpRendez *r) { - call(r->aux, "broadcast", 0); - return 0; -} - -/* Yielding IO */ -static ssize_t -_read(int fd, void *buf, size_t size) { - int n; - - rb_thread_wait_fd(fd); - n = read(fd, buf, size); - - if(n < 0 && errno == EINTR) - rb_thread_schedule(); - return n; -} - -static ssize_t -_write(int fd, const void *buf, size_t size) { - int n; - - rb_thread_fd_writable(fd); - n = write(fd, buf, size); - - if(n < 0 && errno == EINTR) - rb_thread_schedule(); - return n; -} - -static IxpThread ixp_rthread = { - /* Mutex */ - .initmutex = initmutex, - .lock = mlock, - .canlock = mcanlock, - .unlock = munlock, - .mdestroy = mdestroy, - /* RWLock */ - .initrwlock = initrwlock, - .rlock = rlock, - .canrlock = canrlock, - .wlock = wlock, - .canwlock = canwlock, - .runlock = rwunlock, - .wunlock = rwunlock, - .rwdestroy = rwdestroy, - /* Rendez */ - .initrendez = initrendez, - .sleep = rsleep, - .wake = rwake, - .wakeall = rwakeall, - .rdestroy = rdestroy, - /* Other */ - .errbuf = errbuf, - .read = _read, - .write = _write, - .select = rb_thread_select, -}; - -static char RWLock[] = - "class RWLock \n" - " def initialize \n" - " @rdqueue = [] \n" - " @wrqueue = [] \n" - " @wrheld = nil \n" - " @rdheld = [] \n" - " end \n" - " \n" - " def rdlock \n" - " cr = Thread.critical \n" - " while (Thread.critical = true; @wrheld != nil && @rwheld != Thread.current)\n" - " @rdqueue.push Thread.current \n" - " Thread.stop \n" - " end \n" - " @wrheld = nil \n" - " @rdheld.push Thread.current \n" - " \n" - " @rdqueue.each {|t| t.wakeup} \n" - " Thread.critical = cr \n" - " self \n" - " end \n" - " \n" - " def wrlock \n" - " cr = Thread.critical \n" - " while (Thread.critical = true; \n" - " !@rdheld.empty? || (@wrheld != Thread.current && @wrheld != nil)) \n" - " @wrqueue.push Thread.current \n" - " Thread.stop \n" - " end \n" - " @wrheld = Thread.current \n" - " Thread.critical = cr \n" - " self \n" - " end \n" - " \n" - " \n" - " def tryrdlock \n" - " cr = Thread.critical \n" - " if @wrheld == nil \n" - " rdlock \n" - " true \n" - " else \n" - " false \n" - " end \n" - " ensure \n" - " Thread.critical = cr \n" - " end \n" - " \n" - " def trywrlock \n" - " cr = Thread.critical \n" - " if @wrheld == nil && @rdheld.empty? \n" - " wrlock \n" - " true \n" - " else \n" - " false \n" - " end \n" - " ensure \n" - " Thread.critical = cr \n" - " end \n" - " \n" - " def unlock \n" - " cr = Thread.critical \n" - " Thread.critical = true \n" - " \n" - " if @rdheld.include?(Thread.current) \n" - " @rdheld.remove!(Thread.current) \n" - " raise if @wrheld \n" - " elsif @rwheld != Thread.current \n" - " raise \n" - " end \n" - " \n" - " @wrheld = nil \n" - " if !@rwqueue.empty? && @rdheld.empty? \n" - " @wrheld = @wrqueue.shift \n" - " elsif !@rdqueue.empty \n" - " @wrheld = @rdqueue.shift \n" - " end \n" - " @wrheld.wakeup if @wrheld \n" - " ensure \n" - " Thread.critical = cr \n" - " end \n" - "end \n"; - diff --git a/libixp_task/Makefile b/libixp_task/Makefile @@ -1,11 +0,0 @@ -ROOT= .. -include ${ROOT}/mk/hdr.mk -include ${ROOT}/mk/ixp.mk - -CFLAGS += ${TASKINC} - -TARG = libixp_task -OBJ = thread_task - -include ${ROOT}/mk/lib.mk - diff --git a/libixp_task/thread_task.c b/libixp_task/thread_task.c @@ -1,179 +0,0 @@ -/* Written by Kris Maglione <fbsdaemon@gmail.com> */ -/* Public domain */ -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> -#include <task.h> -#include "ixp_local.h" - -static IxpThread ixp_task; - -int -ixp_taskinit() { - ixp_thread = &ixp_task; - return 0; -} - -static char* -errbuf(void) { - void **p; - - p = taskdata(); - if(*p == nil) - *p = emallocz(IXP_ERRMAX); - return *p; -} - -/* Mutex */ -static int -initmutex(IxpMutex *m) { - m->aux = emallocz(sizeof(QLock)); - return 0; -} - -static void -mdestroy(IxpMutex *m) { - free(m->aux); - m->aux = nil; -} - -static void -mlock(IxpMutex *m) { - qlock(m->aux); -} - -static int -mcanlock(IxpMutex *m) { - return canqlock(m->aux); -} - -static void -munlock(IxpMutex *m) { - qunlock(m->aux); -} - -/* RWLock */ -static int -initrwlock(IxpRWLock *rw) { - rw->aux = emallocz(sizeof(RWLock)); - return 0; -} - -static void -rwdestroy(IxpRWLock *rw) { - free(rw->aux); - rw->aux = nil; -} - -static void -_rlock(IxpRWLock *rw) { - rlock(rw->aux); -} - -static int -_canrlock(IxpRWLock *rw) { - return canrlock(rw->aux); -} - -static void -_wlock(IxpRWLock *rw) { - wlock(rw->aux); -} - -static int -_canwlock(IxpRWLock *rw) { - return canwlock(rw->aux); -} - -static void -_runlock(IxpRWLock *rw) { - runlock(rw->aux); -} - -static void -_wunlock(IxpRWLock *rw) { - wunlock(rw->aux); -} - -/* Rendez */ -static int -initrendez(IxpRendez *r) { - r->aux = emallocz(sizeof(Rendez)); - return 0; -} - -static void -rdestroy(IxpRendez *r) { - free(r->aux); - r->aux = nil; -} - -static void -rsleep(IxpRendez *r) { - Rendez *rz; - - rz = r->aux; - rz->l = r->mutex->aux; - tasksleep(rz); -} - -static int -rwake(IxpRendez *r) { - Rendez *rz; - - rz = r->aux; - rz->l = r->mutex->aux; - return taskwakeup(rz); -} - -static int -rwakeall(IxpRendez *r) { - Rendez *rz; - - rz = r->aux; - rz->l = r->mutex->aux; - return taskwakeupall(rz); -} - -/* Yielding IO */ -static ssize_t -_read(int fd, void *buf, size_t size) { - fdnoblock(fd); - return fdread(fd, buf, size); -} - -static ssize_t -_write(int fd, const void *buf, size_t size) { - fdnoblock(fd); - return fdwrite(fd, (void*)buf, size); -} - -static IxpThread ixp_task = { - /* Mutex */ - .initmutex = initmutex, - .lock = mlock, - .canlock = mcanlock, - .unlock = munlock, - .mdestroy = mdestroy, - /* RWLock */ - .initrwlock = initrwlock, - .rlock = _rlock, - .canrlock = _canrlock, - .wlock = _wlock, - .canwlock = _canwlock, - .runlock = _runlock, - .wunlock = _wunlock, - .rwdestroy = rwdestroy, - /* Rendez */ - .initrendez = initrendez, - .sleep = rsleep, - .wake = rwake, - .wakeall = rwakeall, - .rdestroy = rdestroy, - /* Other */ - .errbuf = errbuf, - .read = _read, - .write = _write, - .select = select, /* wrong */ -}; -