commit 4b28f22d0534b00bc7ec53d153de47645ccceb3b
Author: arg <arg@suckless.org>
Date: Thu, 12 Oct 2006 13:14:13 +0200
initial commit
Diffstat:
LICENSE | | | 23 | +++++++++++++++++++++++ |
LICENSE.p9p | | | 251 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
Makefile | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
README | | | 14 | ++++++++++++++ |
client.c | | | 220 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
config.mk | | | 23 | +++++++++++++++++++++++ |
convert.c | | | 236 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
intmap.c | | | 144 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
ixp.h | | | 374 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
message.c | | | 243 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
request.c | | | 394 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
server.c | | | 130 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
socket.c | | | 200 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
transport.c | | | 77 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
util.c | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
15 files changed, 2429 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,23 @@
+MIT/X Consortium License
+
+(C)opyright MMV-MMVI Anselm R. Garbe <garbeam@gmail.com>
+(C)opyright MMVI Sander van Dijk <sander at wmii dot de>
+(C)opyright MMVI Kris Maglione <bsdaemon at comcast dot net>
+
+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/LICENSE.p9p b/LICENSE.p9p
@@ -0,0 +1,251 @@
+The bulk of this software is derived from Plan 9 and is thus distributed
+under the Lucent Public License, Version 1.02, reproduced below.
+
+There are a few exceptions: libutf, libfmt, and libregexp are distributed
+under simpler BSD-like boilerplates. See the LICENSE files in those
+directories. There are other exceptions, also marked with LICENSE files
+in their directories.
+
+The bitmap fonts in the font/luc, font/lucm, font/lucsans, and font/pelm
+directory are copyright B&H Inc. and distributed under more restricted
+terms under agreement with B&H. See the NOTICE file in those directories.
+
+===================================================================
+
+Lucent Public License Version 1.02
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
+ Program, and
+ b. in the case of each Contributor,
+
+ i. changes to the Program, and
+ ii. additions to the Program;
+
+ where such changes and/or additions to the Program were added to the
+ Program by such Contributor itself or anyone acting on such
+ Contributor's behalf, and the Contributor explicitly consents, in
+ accordance with Section 3C, to characterization of the changes and/or
+ additions as Contributions.
+
+"Contributor" means LUCENT and any other entity that has Contributed a
+Contribution to the Program.
+
+"Distributor" means a Recipient that distributes the Program,
+modifications to the Program, or any part thereof.
+
+"Licensed Patents" mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Original Program" means the original version of the software
+accompanying this Agreement as released by LUCENT, including source
+code, object code and documentation, if any.
+
+"Program" means the Original Program and Contributions or any part
+thereof
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a. Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare derivative works of, publicly display,
+ publicly perform, distribute and sublicense the Contribution of such
+ Contributor, if any, and such derivative works, in source code and
+ object code form.
+
+ b. Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor, if
+ any, in source code and object code form. The patent license granted
+ by a Contributor shall also apply to the combination of the
+ Contribution of that Contributor and the Program if, at the time the
+ Contribution is added by the Contributor, such addition of the
+ Contribution causes such combination to be covered by the Licensed
+ Patents. The patent license granted by a Contributor shall not apply
+ to (i) any other combinations which include the Contribution, nor to
+ (ii) Contributions of other Contributors. No hardware per se is
+ licensed hereunder.
+
+ c. Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity. Each
+ Contributor disclaims any liability to Recipient for claims brought by
+ any other entity based on infringement of intellectual property rights
+ or otherwise. As a condition to exercising the rights and licenses
+ granted hereunder, each Recipient hereby assumes sole responsibility
+ to secure any other intellectual property rights needed, if any. For
+ example, if a third party patent license is required to allow
+ Recipient to distribute the Program, it is Recipient's responsibility
+ to acquire that license before distributing the Program.
+
+ d. Each Contributor represents that to its knowledge it has sufficient
+ copyright rights in its Contribution, if any, to grant the copyright
+ license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A. Distributor may choose to distribute the Program in any form under
+this Agreement or under its own license agreement, provided that:
+
+ a. it complies with the terms and conditions of this Agreement;
+
+ b. if the Program is distributed in source code or other tangible
+ form, a copy of this Agreement or Distributor's own license agreement
+ is included with each copy of the Program; and
+
+ c. if distributed under Distributor's own license agreement, such
+ license agreement:
+
+ i. effectively disclaims on behalf of all Contributors all warranties
+ and conditions, express and implied, including warranties or
+ conditions of title and non-infringement, and implied warranties or
+ conditions of merchantability and fitness for a particular purpose;
+ ii. effectively excludes on behalf of all Contributors all liability
+ for damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits; and
+ iii. states that any provisions which differ from this Agreement are
+ offered by that Contributor alone and not by any other party.
+
+B. Each Distributor must include the following in a conspicuous
+ location in the Program:
+
+ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
+ Reserved.
+
+C. In addition, each Contributor must identify itself as the
+originator of its Contribution in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution.
+Also, each Contributor must agree that the additions and/or changes
+are intended to be a Contribution. Once a Contribution is contributed,
+it may not thereafter be revoked.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Distributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for Contributors. Therefore, if a
+Distributor includes the Program in a commercial product offering,
+such Distributor ("Commercial Distributor") hereby agrees to defend
+and indemnify every Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively"Losses") arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Distributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Distributor in writing of such claim, and b)
+allow the Commercial Distributor to control, and cooperate with the
+Commercial Distributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Distributor might include the Program in a commercial
+product offering, Product X. That Distributor is then a Commercial
+Distributor. If that Commercial Distributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Distributor's responsibility
+alone. Under this section, the Commercial Distributor would have to
+defend claims against the Contributors related to those performance
+claims and warranties, and if a court requires any Contributor to pay
+any damages as a result, the Commercial Distributor must pay those
+damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. EXPORT CONTROL
+
+Recipient agrees that Recipient alone is responsible for compliance
+with the United States export administration regulations (and the
+export control laws and regulation of any other countries).
+
+8. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+LUCENT may publish new versions (including revisions) of this
+Agreement from time to time. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to
+distribute the Program (including its Contributions) under the new
+version. No one other than LUCENT has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property
+of any Contributor under this Agreement, whether expressly, by
+implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
diff --git a/Makefile b/Makefile
@@ -0,0 +1,53 @@
+# libixp - simple 9P client-/server-library
+# (C)opyright MMIV-MMVI Anselm R. Garbe
+
+include config.mk
+
+SRC = client.c convert.c intmap.c message.c request.c server.c socket.c \
+ transport.c util.c
+OBJ = ${SRC:.c=.o}
+
+all: options libixp.a
+
+options:
+ @echo libixp build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+ @echo "LD = ${LD}"
+
+.c.o:
+ @echo CC $<
+ @${CC} -c ${CFLAGS} $<
+
+${OBJ}: config.mk ixp.h
+
+libixp.a: ${OBJ}
+ @echo AR $@
+ @${AR} $@ ${OBJ}
+ @${RANLIB} $@
+ @strip $@
+
+clean:
+ @echo cleaning
+ @rm -f libixp.a ${OBJ} libixp-${VERSION}.tar.gz
+
+dist: clean
+ @echo creating dist tarball
+ @mkdir -p libixp-${VERSION}
+ @cp -R LICENSE LICENSE.p9p Makefile README config.mk ixp.h ${SRC} libixp-${VERSION}
+ @tar -cf libixp-${VERSION}.tar libixp-${VERSION}
+ @gzip libixp-${VERSION}.tar
+ @rm -rf libixp-${VERSION}
+
+install: all
+ @echo installing library to ${DESTDIR}${PREFIX}/lib
+ @mkdir -p ${DESTDIR}${PREFIX}/lib
+ @cp -f ssid ${DESTDIR}${PREFIX}/lib
+ @chmod 644 ${DESTDIR}${PREFIX}/lib/libixp.a
+
+uninstall:
+ @echo removing libraray file from ${DESTDIR}${PREFIX}/lib
+ @rm -f ${DESTDIR}${PREFIX}/lib/libixp.a
+
+.PHONY: all options clean dist install uninstall
diff --git a/README b/README
@@ -0,0 +1,14 @@
+libixp - simple 9P client-/server-library
+=========================================
+libixp is an extremly simple 9P stand-alone library.
+
+
+Installation
+------------
+Edit config.mk to match your local setup. ssid is installed into
+/usr/local by default.
+
+Afterwards enter the following command to build and install ssid
+(if necessary as root):
+
+ $ make clean install
diff --git a/client.c b/client.c
@@ -0,0 +1,220 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "ixp.h"
+
+int
+ixp_client_do_fcall(IXPClient *c)
+{
+ static unsigned char msg[IXP_MAX_MSG];
+ unsigned int msize = ixp_fcall2msg(msg, &c->ifcall, IXP_MAX_MSG);
+
+ c->errstr = 0;
+ if(ixp_send_message(c->fd, msg, msize, &c->errstr) != msize)
+ return -1;
+ if(!ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &c->errstr))
+ return -1;
+ if(!(msize = ixp_msg2fcall(&c->ofcall, msg, IXP_MAX_MSG))) {
+ c->errstr = "received bad message";
+ return -1;
+ }
+ if(c->ofcall.type == RERROR) {
+ c->errstr = c->ofcall.ename;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ixp_client_dial(IXPClient *c, char *sockfile, unsigned int rootfid)
+{
+
+ if((c->fd = ixp_connect_sock(sockfile)) < 0) {
+ c->errstr = "cannot connect server";
+ return -1;
+ }
+
+ c->ifcall.type = TVERSION;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.msize = IXP_MAX_MSG;
+ c->ifcall.version = IXP_VERSION;
+ if(ixp_client_do_fcall(c) == -1) {
+ fprintf(stderr, "error: %s\n", c->errstr);
+ ixp_client_hangup(c);
+ return -1;
+ }
+ if(strncmp(c->ofcall.version, IXP_VERSION, strlen(IXP_VERSION))) {
+ fprintf(stderr, "error: %s\n", c->errstr);
+ c->errstr = "9P versions differ";
+ ixp_client_hangup(c);
+ return -1; /* we cannot handle this version */
+ }
+ free(c->ofcall.version);
+ c->root_fid = rootfid;
+
+ c->ifcall.type = TATTACH;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = c->root_fid;
+ c->ifcall.afid = IXP_NOFID;
+ c->ifcall.uname = getenv("USER");
+ c->ifcall.aname = "";
+ if(ixp_client_do_fcall(c) == -1) {
+ fprintf(stderr, "error: %s\n", c->errstr);
+ ixp_client_hangup(c);
+ return -1;
+ }
+ c->root_qid = c->ofcall.qid;
+
+ return 0;
+}
+
+int
+ixp_client_remove(IXPClient *c, unsigned int newfid, char *filepath)
+{
+
+ if(ixp_client_walk(c, newfid, filepath) == -1)
+ return -1;
+ c->ifcall.type = TREMOVE;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = newfid;
+
+ return ixp_client_do_fcall(c);
+}
+
+int
+ixp_client_create(IXPClient *c, unsigned int dirfid, char *name,
+ unsigned int perm, unsigned char mode)
+{
+ c->ifcall.type = TCREATE;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = dirfid;
+ c->ifcall.name = name;
+ c->ifcall.perm = perm;
+ c->ifcall.mode = mode;
+ return ixp_client_do_fcall(c);
+}
+
+int
+ixp_client_walk(IXPClient *c, unsigned int newfid, char *filepath)
+{
+ unsigned int i;
+ char *wname[IXP_MAX_WELEM];
+
+ c->ifcall.type = TWALK;
+ c->ifcall.fid = c->root_fid;
+ c->ifcall.newfid = newfid;
+ if(filepath) {
+ c->ifcall.name = filepath;
+ c->ifcall.nwname =
+ cext_tokenize(wname, IXP_MAX_WELEM, c->ifcall.name, '/');
+ for(i = 0; i < c->ifcall.nwname; i++)
+ c->ifcall.wname[i] = wname[i];
+ }
+ return ixp_client_do_fcall(c);
+}
+
+int
+ixp_client_stat(IXPClient *c, unsigned int newfid, char *filepath)
+{
+
+ if(ixp_client_walk(c, newfid, filepath) == -1)
+ return -1;
+
+ c->ifcall.type = TSTAT;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = newfid;
+ return ixp_client_do_fcall(c);
+}
+
+int
+ixp_client_open(IXPClient *c, unsigned int newfid, unsigned char mode)
+{
+
+ c->ifcall.type = TOPEN;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = newfid;
+ c->ifcall.mode = mode;
+ return ixp_client_do_fcall(c);
+}
+
+int
+ixp_client_walkopen(IXPClient *c, unsigned int newfid, char *filepath,
+ unsigned char mode)
+{
+
+ if(ixp_client_walk(c, newfid, filepath) == -1)
+ return -1;
+ return ixp_client_open(c, newfid, mode);
+}
+
+int
+ixp_client_read(IXPClient *c, unsigned int fid, unsigned long long offset,
+ void *result, unsigned int res_len)
+{
+ unsigned int bytes = c->ofcall.iounit;
+
+ c->ifcall.type = TREAD;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = fid;
+ c->ifcall.offset = offset;
+ c->ifcall.count = res_len < bytes ? res_len : bytes;
+ if(ixp_client_do_fcall(c) == -1)
+ return -1;
+ memcpy(result, c->ofcall.data, c->ofcall.count);
+ free(c->ofcall.data);
+
+ return c->ofcall.count;
+}
+
+int
+ixp_client_write(IXPClient *c, unsigned int fid,
+ unsigned long long offset, unsigned int count,
+ unsigned char *data)
+{
+
+ if(count > c->ofcall.iounit) {
+ c->errstr = "iounit exceeded";
+ return -1;
+ }
+
+ c->ifcall.type = TWRITE;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = fid;
+ c->ifcall.offset = offset;
+ c->ifcall.count = count;
+ c->ifcall.data = (void *)data;
+ if(ixp_client_do_fcall(c) == -1)
+ return -1;
+
+ return c->ofcall.count;
+}
+
+int
+ixp_client_close(IXPClient *c, unsigned int fid)
+{
+
+ c->ifcall.type = TCLUNK;
+ c->ifcall.tag = IXP_NOTAG;
+ c->ifcall.fid = fid;
+ return ixp_client_do_fcall(c);
+}
+
+void
+ixp_client_hangup(IXPClient *c)
+{
+ /* session finished, now shutdown */
+ if(c->fd) {
+ shutdown(c->fd, SHUT_RDWR);
+ close(c->fd);
+ }
+}
diff --git a/config.mk b/config.mk
@@ -0,0 +1,23 @@
+# libixp version
+VERSION = 0.1
+
+# Customize below to fit your system
+
+# paths
+PREFIX = /usr/local
+
+# includes and libs
+INCS = -I. -I/usr/include
+LIBS = -L/usr/lib -lc
+
+# flags
+CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\"
+LDFLAGS = ${LIBS}
+#CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\"
+#LDFLAGS = -g ${LIBS}
+
+# compiler and linker
+AR = ar cr
+CC = cc
+LD = ${CC}
+RANLIB = ranlib
diff --git a/convert.c b/convert.c
@@ -0,0 +1,236 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "ixp.h"
+
+/* packode/unpackode stuff */
+
+void
+ixp_pack_u8(unsigned char **msg, int *msize, unsigned char val)
+{
+ if(!msize || (*msize -= 1) >= 0)
+ *(*msg)++ = val;
+}
+
+void
+ixp_unpack_u8(unsigned char **msg, int *msize, unsigned char *val)
+{
+ if(!msize || (*msize -= 1) >= 0)
+ *val = *(*msg)++;
+}
+
+void
+ixp_pack_u16(unsigned char **msg, int *msize, unsigned short val)
+{
+ if(!msize || (*msize -= 2) >= 0) {
+ *(*msg)++ = val;
+ *(*msg)++ = val >> 8;
+ }
+}
+
+void
+ixp_unpack_u16(unsigned char **msg, int *msize, unsigned short *val)
+{
+ if(!msize || (*msize -= 2) >= 0) {
+ *val = *(*msg)++;
+ *val |= *(*msg)++ << 8;
+ }
+}
+
+void
+ixp_pack_u32(unsigned char **msg, int *msize, unsigned int val)
+{
+ if(!msize || (*msize -= 4) >= 0) {
+ *(*msg)++ = val;
+ *(*msg)++ = val >> 8;
+ *(*msg)++ = val >> 16;
+ *(*msg)++ = val >> 24;
+ }
+}
+
+void
+ixp_unpack_u32(unsigned char **msg, int *msize, unsigned int *val)
+{
+ if(!msize || (*msize -= 4) >= 0) {
+ *val = *(*msg)++;
+ *val |= *(*msg)++ << 8;
+ *val |= *(*msg)++ << 16;
+ *val |= *(*msg)++ << 24;
+ }
+}
+
+void
+ixp_pack_u64(unsigned char **msg, int *msize, unsigned long long val)
+{
+ if(!msize || (*msize -= 8) >= 0) {
+ *(*msg)++ = val;
+ *(*msg)++ = val >> 8;
+ *(*msg)++ = val >> 16;
+ *(*msg)++ = val >> 24;
+ *(*msg)++ = val >> 32;
+ *(*msg)++ = val >> 40;
+ *(*msg)++ = val >> 48;
+ *(*msg)++ = val >> 56;
+ }
+}
+
+void
+ixp_unpack_u64(unsigned char **msg, int *msize, unsigned long long *val)
+{
+ if(!msize || (*msize -= 8) >= 0) {
+ *val |= *(*msg)++;
+ *val |= *(*msg)++ << 8;
+ *val |= *(*msg)++ << 16;
+ *val |= *(*msg)++ << 24;
+ *val |= (unsigned long long)*(*msg)++ << 32;
+ *val |= (unsigned long long)*(*msg)++ << 40;
+ *val |= (unsigned long long)*(*msg)++ << 48;
+ *val |= (unsigned long long)*(*msg)++ << 56;
+ }
+}
+
+void
+ixp_pack_string(unsigned char **msg, int *msize, const char *s)
+{
+ unsigned short len = s ? strlen(s) : 0;
+ ixp_pack_u16(msg, msize, len);
+ if(s)
+ ixp_pack_data(msg, msize, (void *)s, len);
+}
+
+void
+ixp_unpack_strings(unsigned char **msg, int *msize, unsigned short n, char **strings) {
+ unsigned char *s = *msg;
+ unsigned int i, size = 0;
+ unsigned short len;
+ for(i=0; i<n; i++) {
+ ixp_unpack_u16(&s, msize, &len);
+ s += len;
+ size += len + 1; /* for '\0' */
+ }
+ if(!size) {
+ /* So we don't try to free some random value */
+ *strings = NULL;
+ return;
+ }
+ s = ixp_emalloc(size);
+ for(i=0; i < n; i++) {
+ ixp_unpack_u16(msg, msize, &len);
+ if(!msize || (*msize -= len) < 0)
+ return;
+
+ memcpy(s, *msg, len);
+ s[len] = '\0';
+ strings[i] = (char *)s;
+ *msg += len;
+ s += len + 1;
+ }
+}
+
+void
+ixp_unpack_string(unsigned char **msg, int *msize, char **string, unsigned short *len)
+{
+ ixp_unpack_u16(msg, msize, len);
+ *string = NULL;
+ if (!msize || (*msize -= *len) >= 0) {
+ *string = ixp_emalloc(*len+1);
+ if(*len)
+ memcpy(*string, *msg, *len);
+ (*string)[*len] = 0;
+ *msg += *len;
+ }
+}
+
+void
+ixp_pack_data(unsigned char **msg, int *msize, unsigned char *data, unsigned int datalen)
+{
+ if(!msize || (*msize -= datalen) >= 0) {
+ memcpy(*msg, data, datalen);
+ *msg += datalen;
+ }
+}
+
+void
+ixp_unpack_data(unsigned char **msg, int *msize, unsigned char **data, unsigned int datalen)
+{
+ if(!msize || (*msize -= datalen) >= 0) {
+ *data = ixp_emallocz(datalen);
+ memcpy(*data, *msg, datalen);
+ *msg += datalen;
+ }
+}
+
+void
+ixp_pack_prefix(unsigned char *msg, unsigned int size, unsigned char id,
+ unsigned short tag)
+{
+ ixp_pack_u32(&msg, 0, size);
+ ixp_pack_u8(&msg, 0, id);
+ ixp_pack_u16(&msg, 0, tag);
+}
+
+void
+ixp_unpack_prefix(unsigned char **msg, unsigned int *size, unsigned char *id,
+ unsigned short *tag)
+{
+ int msize;
+ ixp_unpack_u32(msg, NULL, size);
+ msize = *size;
+ ixp_unpack_u8(msg, &msize, id);
+ ixp_unpack_u16(msg, &msize, tag);
+}
+
+void
+ixp_pack_qid(unsigned char **msg, int *msize, Qid * qid)
+{
+ ixp_pack_u8(msg, msize, qid->type);
+ ixp_pack_u32(msg, msize, qid->version);
+ ixp_pack_u64(msg, msize, qid->path);
+}
+
+void
+ixp_unpack_qid(unsigned char **msg, int *msize, Qid * qid)
+{
+ ixp_unpack_u8(msg, msize, &qid->type);
+ ixp_unpack_u32(msg, msize, &qid->version);
+ ixp_unpack_u64(msg, msize, &qid->path);
+}
+
+void
+ixp_pack_stat(unsigned char **msg, int *msize, Stat * stat)
+{
+ ixp_pack_u16(msg, msize, ixp_sizeof_stat(stat) - sizeof(unsigned short));
+ ixp_pack_u16(msg, msize, stat->type);
+ ixp_pack_u32(msg, msize, stat->dev);
+ ixp_pack_qid(msg, msize, &stat->qid);
+ ixp_pack_u32(msg, msize, stat->mode);
+ ixp_pack_u32(msg, msize, stat->atime);
+ ixp_pack_u32(msg, msize, stat->mtime);
+ ixp_pack_u64(msg, msize, stat->length);
+ ixp_pack_string(msg, msize, stat->name);
+ ixp_pack_string(msg, msize, stat->uid);
+ ixp_pack_string(msg, msize, stat->gid);
+ ixp_pack_string(msg, msize, stat->muid);
+}
+
+void
+ixp_unpack_stat(unsigned char **msg, int *msize, Stat * stat)
+{
+ unsigned short dummy;
+ *msg += sizeof(unsigned short);
+ ixp_unpack_u16(msg, msize, &stat->type);
+ ixp_unpack_u32(msg, msize, &stat->dev);
+ ixp_unpack_qid(msg, msize, &stat->qid);
+ ixp_unpack_u32(msg, msize, &stat->mode);
+ ixp_unpack_u32(msg, msize, &stat->atime);
+ ixp_unpack_u32(msg, msize, &stat->mtime);
+ ixp_unpack_u64(msg, msize, &stat->length);
+ ixp_unpack_string(msg, msize, &stat->name, &dummy);
+ ixp_unpack_string(msg, msize, &stat->uid, &dummy);
+ ixp_unpack_string(msg, msize, &stat->gid, &dummy);
+ ixp_unpack_string(msg, msize, &stat->muid, &dummy);
+}
diff --git a/intmap.c b/intmap.c
@@ -0,0 +1,144 @@
+/* This file is derived from src/lib9p/intmap.c from plan9port */
+/* See LICENCE.p9p for terms of use */
+#include <stdlib.h>
+#include "ixp.h"
+#define USED(v) if(v){}else{}
+
+struct Intlist {
+ unsigned long id;
+ void* aux;
+ Intlist* link;
+ unsigned int ref;
+};
+
+static unsigned long
+hashid(Intmap *map, unsigned long id)
+{
+ return id%map->nhash;
+}
+
+static void
+nop(void *v)
+{
+ USED(v);
+}
+
+void
+initmap(Intmap *m, unsigned long nhash, void *hash)
+{
+ m->nhash = nhash;
+ m->hash = hash;
+}
+
+static Intlist**
+llookup(Intmap *map, unsigned long id)
+{
+ Intlist **lf;
+
+ for(lf=&map->hash[hashid(map, id)]; *lf; lf=&(*lf)->link)
+ if((*lf)->id == id)
+ break;
+ return lf;
+}
+
+void
+freemap(Intmap *map, void (*destroy)(void*))
+{
+ int i;
+ Intlist *p, *nlink;
+
+ if(destroy == NULL)
+ destroy = nop;
+ for(i=0; i<map->nhash; i++){
+ for(p=map->hash[i]; p; p=nlink){
+ nlink = p->link;
+ destroy(p->aux);
+ free(p);
+ }
+ }
+}
+void
+execmap(Intmap *map, void (*run)(void*))
+{
+ int i;
+ Intlist *p, *nlink;
+
+ for(i=0; i<map->nhash; i++){
+ for(p=map->hash[i]; p; p=nlink){
+ nlink = p->link;
+ run(p->aux);
+ }
+ }
+}
+
+void*
+lookupkey(Intmap *map, unsigned long id)
+{
+ Intlist *f;
+ void *v;
+
+ if((f = *llookup(map, id)))
+ v = f->aux;
+ else
+ v = NULL;
+ return v;
+}
+
+void*
+insertkey(Intmap *map, unsigned long id, void *v)
+{
+ Intlist *f;
+ void *ov;
+ unsigned long h;
+
+ if((f = *llookup(map, id))){
+ /* no decrement for ov because we're returning it */
+ ov = f->aux;
+ f->aux = v;
+ }else{
+ f = ixp_emallocz(sizeof(*f));
+ f->id = id;
+ f->aux = v;
+ h = hashid(map, id);
+ f->link = map->hash[h];
+ map->hash[h] = f;
+ ov = NULL;
+ }
+ return ov;
+}
+
+int
+caninsertkey(Intmap *map, unsigned long id, void *v)
+{
+ Intlist *f;
+ int rv;
+ unsigned long h;
+
+ if(*llookup(map, id))
+ rv = 0;
+ else{
+ f = ixp_emallocz(sizeof *f);
+ f->id = id;
+ f->aux = v;
+ h = hashid(map, id);
+ f->link = map->hash[h];
+ map->hash[h] = f;
+ rv = 1;
+ }
+ return rv;
+}
+
+void*
+deletekey(Intmap *map, unsigned long id)
+{
+ Intlist **lf, *f;
+ void *ov;
+
+ if((f = *(lf = llookup(map, id)))){
+ ov = f->aux;
+ *lf = f->link;
+ free(f);
+ }else
+ ov = NULL;
+ return ov;
+}
diff --git a/ixp.h b/ixp.h
@@ -0,0 +1,374 @@
+/*
+ *(C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ *See LICENSE file for license details.
+ */
+
+#include <sys/types.h>
+
+#define IXP_VERSION "9P2000"
+#define IXP_NOTAG (unsigned short)~0U /*Dummy tag */
+#define IXP_NOFID (unsigned int)~0 /*No auth */
+
+enum { IXP_MAX_VERSION = 32,
+ IXP_MAX_ERROR = 128,
+ IXP_MAX_CACHE = 32,
+ IXP_MAX_MSG = 8192,
+ IXP_MAX_FLEN = 128,
+ IXP_MAX_ULEN = 32,
+ IXP_MAX_WELEM = 16 };
+
+/* 9P message types */
+enum { TVERSION = 100,
+ RVERSION,
+ TAUTH = 102,
+ RAUTH,
+ TATTACH = 104,
+ RATTACH,
+ TERROR = 106, /* illegal */
+ RERROR,
+ TFLUSH = 108,
+ RFLUSH,
+ TWALK = 110,
+ RWALK,
+ TOPEN = 112,
+ ROPEN,
+ TCREATE = 114,
+ RCREATE,
+ TREAD = 116,
+ RREAD,
+ TWRITE = 118,
+ RWRITE,
+ TCLUNK = 120,
+ RCLUNK,
+ TREMOVE = 122,
+ RREMOVE,
+ TSTAT = 124,
+ RSTAT,
+ TWSTAT = 126,
+ RWSTAT,
+};
+
+/* borrowed from libc.h of Plan 9 */
+enum { IXP_DMDIR = 0x80000000, /* mode bit for directories */
+ IXP_DMAPPEND = 0x40000000, /* mode bit for append only files */
+ IXP_DMEXCL = 0x20000000, /* mode bit for exclusive use files */
+ IXP_DMMOUNT = 0x10000000, /* mode bit for mounted channel */
+ IXP_DMAUTH = 0x08000000, /* mode bit for authentication file */
+ IXP_DMTMP = 0x04000000, /* mode bit for non-backed-up file */
+ IXP_DMREAD = 0x4<<6, /* mode bit for read permission */
+ IXP_DMWRITE = 0x2<<6, /* mode bit for write permission */
+ IXP_DMEXEC = 0x1<<6 /* mode bit for execute permission */
+};
+
+/* modes */
+enum { IXP_OREAD = 0x00,
+ IXP_OWRITE = 0x01,
+ IXP_ORDWR = 0x02,
+ IXP_OEXEC = 0x03,
+ IXP_OEXCL = 0x04,
+ IXP_OTRUNC = 0x10,
+ IXP_OREXEC = 0x20,
+ IXP_ORCLOSE = 0x40,
+ IXP_OAPPEND = 0x80,
+};
+
+/* qid.types */
+enum { IXP_QTDIR = 0x80,
+ IXP_QTAPPEND = 0x40,
+ IXP_QTEXCL = 0x20,
+ IXP_QTMOUNT = 0x10,
+ IXP_QTAUTH = 0x08,
+ IXP_QTTMP = 0x04,
+ IXP_QTSYMLINK = 0x02,
+ IXP_QTLINK = 0x01,
+ IXP_QTFILE = 0x00,
+};
+
+/* from libc.h in p9p */
+enum { P9OREAD = 0, /* open for read */
+ P9OWRITE = 1, /* write */
+ P9ORDWR = 2, /* read and write */
+ P9OEXEC = 3, /* execute, == read but check execute permission */
+ P9OTRUNC = 16, /* or'ed in (except for exec), truncate file first */
+ P9OCEXEC = 32, /* or'ed in, close on exec */
+ P9ORCLOSE = 64, /* or'ed in, remove on close */
+ P9ODIRECT = 128, /* or'ed in, direct access */
+ P9ONONBLOCK = 256, /* or'ed in, non-blocking call */
+ P9OEXCL = 0x1000, /* or'ed in, exclusive use (create only) */
+ P9OLOCK = 0x2000, /* or'ed in, lock after opening */
+ P9OAPPEND = 0x4000 /* or'ed in, append only */
+};
+
+/* bits in Qid.type */
+enum { P9QTDIR = 0x80, /* type bit for directories */
+ P9QTAPPEND = 0x40, /* type bit for append only files */
+ P9QTEXCL = 0x20, /* type bit for exclusive use files */
+ P9QTMOUNT = 0x10, /* type bit for mounted channel */
+ P9QTAUTH = 0x08, /* type bit for authentication file */
+ P9QTTMP = 0x04, /* type bit for non-backed-up file */
+ P9QTSYMLINK = 0x02, /* type bit for symbolic link */
+ P9QTFILE = 0x00 /* type bits for plain file */
+};
+
+/* bits in Dir.mode */
+#define P9DMDIR 0x80000000 /* mode bit for directories */
+#define P9DMAPPEND 0x40000000 /* mode bit for append only files */
+#define P9DMEXCL 0x20000000 /* mode bit for exclusive use files */
+#define P9DMMOUNT 0x10000000 /* mode bit for mounted channel */
+#define P9DMAUTH 0x08000000 /* mode bit for authentication file */
+#define P9DMTMP 0x04000000 /* mode bit for non-backed-up file */
+#define P9DMSYMLINK 0x02000000 /* mode bit for symbolic link (Unix, 9P2000.u) */
+#define P9DMDEVICE 0x00800000 /* mode bit for device file (Unix, 9P2000.u) */
+#define P9DMNAMEDPIPE 0x00200000 /* mode bit for named pipe (Unix, 9P2000.u) */
+#define P9DMSOCKET 0x00100000 /* mode bit for socket (Unix, 9P2000.u) */
+#define P9DMSETUID 0x00080000 /* mode bit for setuid (Unix, 9P2000.u) */
+#define P9DMSETGID 0x00040000 /* mode bit for setgid (Unix, 9P2000.u) */
+
+enum { P9DMREAD = 0x4, /* mode bit for read permission */
+ P9DMWRITE = 0x2, /* mode bit for write permission */
+ P9DMEXEC = 0x1 /* mode bit for execute permission */
+};
+
+
+typedef struct Qid Qid;
+struct Qid {
+ unsigned char type;
+ unsigned int version;
+ unsigned long long path;
+ /* internal use only */
+ unsigned char dir_type;
+};
+
+/* stat structure */
+typedef struct Stat {
+ unsigned short type;
+ unsigned int dev;
+ Qid qid;
+ unsigned int mode;
+ unsigned int atime;
+ unsigned int mtime;
+ unsigned long long length;
+ char *name;
+ char *uid;
+ char *gid;
+ char *muid;
+} Stat;
+
+/* from fcall(3) in plan9port */
+typedef struct Fcall {
+ unsigned char type;
+ unsigned short tag;
+ unsigned int fid;
+ union {
+ struct { /* Tversion, Rversion */
+ unsigned int msize;
+ char *version;
+ };
+ struct { /* Tflush */
+ unsigned short oldtag;
+ };
+ struct { /* Rerror */
+ char *ename;
+ };
+ struct { /* Ropen, Rcreate */
+ Qid qid; /* +Rattach */
+ unsigned int iounit;
+ };
+ struct { /* Rauth */
+ Qid aqid;
+ };
+ struct { /* Tauth, Tattach */
+ unsigned int afid;
+ char *uname;
+ char *aname;
+ };
+ struct { /* Tcreate */
+ unsigned int perm;
+ char *name;
+ unsigned char mode; /* +Topen */
+ };
+ struct { /* Twalk */
+ unsigned int newfid;
+ unsigned short nwname;
+ char *wname[IXP_MAX_WELEM];
+ };
+ struct { /* Rwalk */
+ unsigned short nwqid;
+ Qid wqid[IXP_MAX_WELEM];
+ };
+ struct { /* Twrite */
+ unsigned long long offset; /* +Tread */
+ /* +Rread */
+ unsigned int count; /* +Tread */
+ char *data;
+ };
+ struct { /* Twstat, Rstat */
+ unsigned short nstat;
+ unsigned char *stat;
+ };
+ };
+} Fcall;
+
+typedef struct IXPServer IXPServer;
+typedef struct IXPConn IXPConn;
+typedef struct Intmap Intmap;
+
+typedef struct Intlist Intlist;
+struct Intmap {
+ unsigned long nhash;
+ Intlist **hash;
+};
+
+struct IXPConn {
+ IXPServer *srv;
+ void *aux;
+ int fd;
+ void (*read) (IXPConn *);
+ void (*close) (IXPConn *);
+ char closed;
+
+ /* Implementation details */
+ /* do not use */
+ IXPConn *next;
+};
+
+struct IXPServer {
+ int running;
+ IXPConn *conn;
+ int maxfd;
+ fd_set rd;
+};
+
+typedef struct IXPClient {
+ int fd;
+ unsigned int root_fid;
+ Qid root_qid;
+ Fcall ifcall;
+ Fcall ofcall;
+ char *errstr;
+} IXPClient;
+
+typedef struct P9Conn P9Conn;
+typedef struct Fid {
+ P9Conn *conn;
+ Intmap *map;
+ char *uid;
+ void *aux;
+ unsigned long fid;
+ Qid qid;
+ signed char omode;
+} Fid;
+
+typedef struct P9Req P9Req;
+struct P9Req {
+ P9Conn *conn;
+ Fid *fid;
+ Fid *newfid;
+ P9Req *oldreq;
+ Fcall ifcall;
+ Fcall ofcall;
+ void *aux;
+};
+
+typedef struct P9Srv {
+ void (*attach)(P9Req *r);
+ void (*clunk)(P9Req *r);
+ void (*create)(P9Req *r);
+ void (*flush)(P9Req *r);
+ void (*open)(P9Req *r);
+ void (*read)(P9Req *r);
+ void (*remove)(P9Req *r);
+ void (*stat)(P9Req *r);
+ void (*walk)(P9Req *r);
+ void (*write)(P9Req *r);
+ void (*freefid)(Fid *f);
+} P9Srv;
+
+/* client.c */
+extern int ixp_client_dial(IXPClient *c, char *address, unsigned int rootfid);
+extern void ixp_client_hangup(IXPClient *c);
+extern int ixp_client_remove(IXPClient *c, unsigned int newfid, char *filepath);
+extern int ixp_client_create(IXPClient *c, unsigned int dirfid, char *name,
+ unsigned int perm, unsigned char mode);
+extern int ixp_client_walk(IXPClient *c, unsigned int newfid, char *filepath);
+extern int ixp_client_stat(IXPClient *c, unsigned int newfid, char *filepath);
+extern int ixp_client_walkopen(IXPClient *c, unsigned int newfid, char *filepath,
+ unsigned char mode);
+extern int ixp_client_open(IXPClient *c, unsigned int newfid, unsigned char mode);
+extern int ixp_client_read(IXPClient *c, unsigned int fid,
+ unsigned long long offset, void *result,
+ unsigned int res_len);
+extern int ixp_client_write(IXPClient *c, unsigned int fid,
+ unsigned long long offset,
+ unsigned int count, unsigned char *data);
+extern int ixp_client_close(IXPClient *c, unsigned int fid);
+extern int ixp_client_do_fcall(IXPClient * c);
+
+/* convert.c */
+extern void ixp_pack_u8(unsigned char **msg, int *msize, unsigned char val);
+extern void ixp_unpack_u8(unsigned char **msg, int *msize, unsigned char *val);
+extern void ixp_pack_u16(unsigned char **msg, int *msize, unsigned short val);
+extern void ixp_unpack_u16(unsigned char **msg, int *msize, unsigned short *val);
+extern void ixp_pack_u32(unsigned char **msg, int *msize, unsigned int val);
+extern void ixp_unpack_u32(unsigned char **msg, int *msize, unsigned int *val);
+extern void ixp_pack_u64(unsigned char **msg, int *msize, unsigned long long val);
+extern void ixp_unpack_u64(unsigned char **msg, int *msize, unsigned long long *val);
+extern void ixp_pack_string(unsigned char **msg, int *msize, const char *s);
+extern void ixp_unpack_strings(unsigned char **msg, int *msize, unsigned short n, char **strings);
+extern void ixp_unpack_string(unsigned char **msg, int *msize, char **string, unsigned short *len);
+extern void ixp_pack_data(unsigned char **msg, int *msize, unsigned char *data,
+ unsigned int datalen);
+extern void ixp_unpack_data(unsigned char **msg, int *msize, unsigned char **data,
+ unsigned int datalen);
+extern void ixp_pack_prefix(unsigned char *msg, unsigned int size,
+ unsigned char id, unsigned short tag);
+extern void ixp_unpack_prefix(unsigned char **msg, unsigned int *size,
+ unsigned char *id, unsigned short *tag);
+extern void ixp_pack_qid(unsigned char **msg, int *msize, Qid *qid);
+extern void ixp_unpack_qid(unsigned char **msg, int *msize, Qid *qid);
+extern void ixp_pack_stat(unsigned char **msg, int *msize, Stat *stat);
+extern void ixp_unpack_stat(unsigned char **msg, int *msize, Stat *stat);
+
+/* request.c */
+extern void respond(P9Req *r, char *error);
+extern void serve_9pcon(IXPConn *c);
+
+/* intmap.c */
+extern void initmap(Intmap *m, unsigned long nhash, void *hash);
+extern void incref_map(Intmap *m);
+extern void decref_map(Intmap *m);
+extern void freemap(Intmap *map, void (*destroy)(void*));
+extern void execmap(Intmap *map, void (*destroy)(void*));
+extern void * lookupkey(Intmap *map, unsigned long id);
+extern void * insertkey(Intmap *map, unsigned long id, void *v);
+extern int caninsertkey(Intmap *map, unsigned long id, void *v);
+extern void * deletekey(Intmap *map, unsigned long id);
+
+/* message.c */
+extern unsigned short ixp_sizeof_stat(Stat *stat);
+extern unsigned int ixp_fcall2msg(void *msg, Fcall *fcall, unsigned int msglen);
+extern unsigned int ixp_msg2fcall(Fcall *call, void *msg, unsigned int msglen);
+
+/* server.c */
+extern IXPConn *ixp_server_open_conn(IXPServer *s, int fd, void *aux,
+ void (*read)(IXPConn *c), void (*close)(IXPConn *c));
+extern void ixp_server_close_conn(IXPConn *c);
+extern char *ixp_server_loop(IXPServer *s);
+extern unsigned int ixp_server_receive_fcall(IXPConn *c, Fcall *fcall);
+extern int ixp_server_respond_fcall(IXPConn *c, Fcall *fcall);
+extern int ixp_server_respond_error(IXPConn *c, Fcall *fcall, char *errstr);
+extern void ixp_server_close(IXPServer *s);
+
+/* socket.c */
+extern int ixp_connect_sock(char *address);
+extern int ixp_create_sock(char *address, char **errstr);
+
+/* transport.c */
+extern unsigned int ixp_send_message(int fd, void *msg, unsigned int msize, char **errstr);
+extern unsigned int ixp_recv_message(int fd, void *msg, unsigned int msglen, char **errstr);
+
+/* util.c */
+extern void * ixp_emalloc(unsigned int size);
+extern void * ixp_emallocz(unsigned int size);
+extern void ixp_eprint(const char *errstr, ...);
+extern char * ixp_estrdup(const char *str);
diff --git a/message.c b/message.c
@@ -0,0 +1,243 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ixp.h"
+
+#define IXP_QIDSZ (sizeof(unsigned char) + sizeof(unsigned int)\
+ + sizeof(unsigned long long))
+
+static unsigned short
+sizeof_string(const char *s)
+{
+ return sizeof(unsigned short) + strlen(s);
+}
+
+unsigned short
+ixp_sizeof_stat(Stat * stat)
+{
+ return IXP_QIDSZ
+ + 2 * sizeof(unsigned short)
+ + 4 * sizeof(unsigned int)
+ + sizeof(unsigned long long)
+ + sizeof_string(stat->name)
+ + sizeof_string(stat->uid)
+ + sizeof_string(stat->gid)
+ + sizeof_string(stat->muid);
+}
+
+unsigned int
+ixp_fcall2msg(void *msg, Fcall *fcall, unsigned int msglen)
+{
+ unsigned int i = sizeof(unsigned char) +
+ sizeof(unsigned short) + sizeof(unsigned int);
+ int msize = msglen - i;
+ unsigned char *p = msg + i;
+
+ switch (fcall->type) {
+ case TVERSION:
+ case RVERSION:
+ ixp_pack_u32(&p, &msize, fcall->msize);
+ ixp_pack_string(&p, &msize, fcall->version);
+ break;
+ case TAUTH:
+ ixp_pack_u32(&p, &msize, fcall->afid);
+ ixp_pack_string(&p, &msize, fcall->uname);
+ ixp_pack_string(&p, &msize, fcall->aname);
+ break;
+ case RAUTH:
+ ixp_pack_qid(&p, &msize, &fcall->aqid);
+ break;
+ case RATTACH:
+ ixp_pack_qid(&p, &msize, &fcall->qid);
+ break;
+ case TATTACH:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_u32(&p, &msize, fcall->afid);
+ ixp_pack_string(&p, &msize, fcall->uname);
+ ixp_pack_string(&p, &msize, fcall->aname);
+ break;
+ case RERROR:
+ ixp_pack_string(&p, &msize, fcall->ename);
+ break;
+ case TFLUSH:
+ ixp_pack_u16(&p, &msize, fcall->oldtag);
+ break;
+ case TWALK:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_u32(&p, &msize, fcall->newfid);
+ ixp_pack_u16(&p, &msize, fcall->nwname);
+ for(i = 0; i < fcall->nwname; i++)
+ ixp_pack_string(&p, &msize, fcall->wname[i]);
+ break;
+ case RWALK:
+ ixp_pack_u16(&p, &msize, fcall->nwqid);
+ for(i = 0; i < fcall->nwqid; i++)
+ ixp_pack_qid(&p, &msize, &fcall->wqid[i]);
+ break;
+ case TOPEN:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_u8(&p, &msize, fcall->mode);
+ break;
+ case ROPEN:
+ case RCREATE:
+ ixp_pack_qid(&p, &msize, &fcall->qid);
+ ixp_pack_u32(&p, &msize, fcall->iounit);
+ break;
+ case TCREATE:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_string(&p, &msize, fcall->name);
+ ixp_pack_u32(&p, &msize, fcall->perm);
+ ixp_pack_u8(&p, &msize, fcall->mode);
+ break;
+ case TREAD:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_u64(&p, &msize, fcall->offset);
+ ixp_pack_u32(&p, &msize, fcall->count);
+ break;
+ case RREAD:
+ ixp_pack_u32(&p, &msize, fcall->count);
+ ixp_pack_data(&p, &msize, (unsigned char *)fcall->data, fcall->count);
+ break;
+ case TWRITE:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_u64(&p, &msize, fcall->offset);
+ ixp_pack_u32(&p, &msize, fcall->count);
+ ixp_pack_data(&p, &msize, (unsigned char *)fcall->data, fcall->count);
+ break;
+ case RWRITE:
+ ixp_pack_u32(&p, &msize, fcall->count);
+ break;
+ case TCLUNK:
+ case TREMOVE:
+ case TSTAT:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ break;
+ case RSTAT:
+ ixp_pack_u16(&p, &msize, fcall->nstat);
+ ixp_pack_data(&p, &msize, fcall->stat, fcall->nstat);
+ break;
+ case TWSTAT:
+ ixp_pack_u32(&p, &msize, fcall->fid);
+ ixp_pack_u16(&p, &msize, fcall->nstat);
+ ixp_pack_data(&p, &msize, fcall->stat, fcall->nstat);
+ break;
+ }
+
+ if(msize < 0)
+ return 0;
+
+ msize = msglen - msize;
+ ixp_pack_prefix(msg, msize, fcall->type, fcall->tag);
+ return msize;
+}
+
+unsigned int
+ixp_msg2fcall(Fcall *fcall, void *msg, unsigned int msglen)
+{
+ int msize;
+ unsigned int i, tsize;
+ unsigned short len;
+ unsigned char *p = msg;
+ ixp_unpack_prefix(&p, (unsigned int *)&msize, &fcall->type, &fcall->tag);
+ tsize = msize;
+
+ if(msize > msglen) /* bad message */
+ return 0;
+ switch (fcall->type) {
+ case TVERSION:
+ case RVERSION:
+ ixp_unpack_u32(&p, &msize, &fcall->msize);
+ ixp_unpack_string(&p, &msize, &fcall->version, &len);
+ break;
+ case TAUTH:
+ ixp_unpack_u32(&p, &msize, &fcall->afid);
+ ixp_unpack_string(&p, &msize, &fcall->uname, &len);
+ ixp_unpack_string(&p, &msize, &fcall->aname, &len);
+ break;
+ case RAUTH:
+ ixp_unpack_qid(&p, &msize, &fcall->aqid);
+ break;
+ case RATTACH:
+ ixp_unpack_qid(&p, &msize, &fcall->qid);
+ break;
+ case TATTACH:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_u32(&p, &msize, &fcall->afid);
+ ixp_unpack_string(&p, &msize, &fcall->uname, &len);
+ ixp_unpack_string(&p, &msize, &fcall->aname, &len);
+ break;
+ case RERROR:
+ ixp_unpack_string(&p, &msize, &fcall->ename, &len);
+ break;
+ case TFLUSH:
+ ixp_unpack_u16(&p, &msize, &fcall->oldtag);
+ break;
+ case TWALK:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_u32(&p, &msize, &fcall->newfid);
+ ixp_unpack_u16(&p, &msize, &fcall->nwname);
+ ixp_unpack_strings(&p, &msize, fcall->nwname, fcall->wname);
+ break;
+ case RWALK:
+ ixp_unpack_u16(&p, &msize, &fcall->nwqid);
+ for(i = 0; i < fcall->nwqid; i++)
+ ixp_unpack_qid(&p, &msize, &fcall->wqid[i]);
+ break;
+ case TOPEN:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_u8(&p, &msize, &fcall->mode);
+ break;
+ case ROPEN:
+ case RCREATE:
+ ixp_unpack_qid(&p, &msize, &fcall->qid);
+ ixp_unpack_u32(&p, &msize, &fcall->iounit);
+ break;
+ case TCREATE:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_string(&p, &msize, &fcall->name, &len);
+ ixp_unpack_u32(&p, &msize, &fcall->perm);
+ ixp_unpack_u8(&p, &msize, &fcall->mode);
+ break;
+ case TREAD:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_u64(&p, &msize, &fcall->offset);
+ ixp_unpack_u32(&p, &msize, &fcall->count);
+ break;
+ case RREAD:
+ ixp_unpack_u32(&p, &msize, &fcall->count);
+ ixp_unpack_data(&p, &msize, (void *)&fcall->data, fcall->count);
+ break;
+ case TWRITE:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_u64(&p, &msize, &fcall->offset);
+ ixp_unpack_u32(&p, &msize, &fcall->count);
+ ixp_unpack_data(&p, &msize, (void *)&fcall->data, fcall->count);
+ break;
+ case RWRITE:
+ ixp_unpack_u32(&p, &msize, &fcall->count);
+ break;
+ case TCLUNK:
+ case TREMOVE:
+ case TSTAT:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ break;
+ case RSTAT:
+ ixp_unpack_u16(&p, &msize, &len);
+ ixp_unpack_data(&p, &msize, &fcall->stat, len);
+ break;
+ case TWSTAT:
+ ixp_unpack_u32(&p, &msize, &fcall->fid);
+ ixp_unpack_u16(&p, &msize, &len);
+ ixp_unpack_data(&p, &msize, &fcall->stat, len);
+ break;
+ }
+
+ if(msize > 0)
+ return tsize;
+ return 0;
+}
diff --git a/request.c b/request.c
@@ -0,0 +1,394 @@
+/*
+ * (C)opyright MMVI Kris Maglione <fbsdaemon at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include "ixp.h"
+
+static void ixp_handle_req(P9Req *r);
+
+/* We use string literals rather than arrays here because
+ * they're allocated in a readonly section */
+static char
+ *Eduptag = "tag in use",
+ *Edupfid = "fid in use",
+ *Enofunc = "function not implemented",
+ *Ebotch = "9P protocol botch",
+ *Enofile = "file does not exist",
+ *Enofid = "fid does not exist",
+ *Enotag = "tag does not exist",
+ *Enotdir = "not a directory",
+ *Einterrupted = "interrupted",
+ *Eisdir = "cannot perform operation on a directory";
+
+enum { TAG_BUCKETS = 64,
+ FID_BUCKETS = 64 };
+
+struct P9Conn {
+ Intmap tagmap;
+ void *taghash[TAG_BUCKETS];
+ Intmap fidmap;
+ void *fidhash[FID_BUCKETS];
+ P9Srv *srv;
+ IXPConn *conn;
+ unsigned int msize;
+ unsigned char *buf;
+ unsigned int ref;
+};
+
+static void
+free_p9conn(P9Conn *pc) {
+ free(pc->buf);
+ free(pc);
+}
+
+static void *
+createfid(Intmap *map, int fid, P9Conn *pc) {
+ Fid *f = ixp_emallocz(sizeof(Fid));
+ f->fid = fid;
+ f->omode = -1;
+ f->map = map;
+ f->conn = pc;
+ if(caninsertkey(map, fid, f))
+ return f;
+ free(f);
+ return NULL;
+}
+
+static int
+destroyfid(P9Conn *pc, unsigned long fid) {
+ Fid *f;
+ if(!(f = deletekey(&pc->fidmap, fid)))
+ return 0;
+ if(pc->srv->freefid)
+ pc->srv->freefid(f);
+ free(f);
+ return 1;
+}
+
+void
+ixp_server_handle_fcall(IXPConn *c)
+{
+ Fcall fcall = {0};
+ P9Conn *pc = c->aux;
+ P9Req *req;
+ unsigned int msize;
+ char *errstr = NULL;
+
+ if(!(msize = ixp_recv_message(c->fd, pc->buf, pc->msize, &errstr)))
+ goto Fail;
+ if(!(msize = ixp_msg2fcall(&fcall, pc->buf, IXP_MAX_MSG)))
+ goto Fail;
+
+ req = ixp_emallocz(sizeof(P9Req));
+ req->conn = pc;
+ req->ifcall = fcall;
+ pc->conn = c;
+
+ if(lookupkey(&pc->tagmap, fcall.tag))
+ return respond(req, Eduptag);
+
+ insertkey(&pc->tagmap, fcall.tag, req);
+ return ixp_handle_req(req);
+
+Fail:
+ ixp_server_close_conn(c);
+}
+
+static void
+ixp_handle_req(P9Req *r)
+{
+ P9Conn *pc = r->conn;
+ P9Srv *srv = pc->srv;
+
+ switch(r->ifcall.type) {
+ default:
+ respond(r, Enofunc);
+ break;
+ case TVERSION:
+ if(!strncmp(r->ifcall.version, "9P", 3)) {
+ r->ofcall.version = "9P";
+ }else
+ if(!strncmp(r->ifcall.version, "9P2000", 7)) {
+ r->ofcall.version = "9P2000";
+ }else{
+ r->ofcall.version = "unknown";
+ }
+ r->ofcall.msize = r->ifcall.msize;
+ respond(r, NULL);
+ break;
+ case TATTACH:
+ if(!(r->fid = createfid(&pc->fidmap, r->ifcall.fid, pc)))
+ return respond(r, Edupfid);
+ /* attach is a required function */
+ srv->attach(r);
+ break;
+ case TCLUNK:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if(!srv->clunk)
+ return respond(r, NULL);
+ srv->clunk(r);
+ break;
+ case TFLUSH:
+ if(!(r->oldreq = lookupkey(&pc->tagmap, r->ifcall.oldtag)))
+ return respond(r, Enotag);
+ if(!srv->flush)
+ return respond(r, Enofunc);
+ srv->flush(r);
+ break;
+ case TCREATE:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if(r->fid->omode != -1)
+ return respond(r, Ebotch);
+ if(!(r->fid->qid.type&P9QTDIR))
+ return respond(r, Enotdir);
+ if(!pc->srv->create)
+ return respond(r, Enofunc);
+ pc->srv->create(r);
+ break;
+ case TOPEN:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if((r->fid->qid.type&P9QTDIR) && (r->ifcall.mode|P9ORCLOSE) != (P9OREAD|P9ORCLOSE))
+ return respond(r, Eisdir);
+ r->ofcall.qid = r->fid->qid;
+ if(!pc->srv->open)
+ return respond(r, Enofunc);
+ pc->srv->open(r);
+ break;
+ case TREAD:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if(r->fid->omode == -1 || r->fid->omode == P9OWRITE)
+ return respond(r, Ebotch);
+ if(!pc->srv->read)
+ return respond(r, Enofunc);
+ pc->srv->read(r);
+ break;
+ case TREMOVE:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if(!pc->srv->remove)
+ return respond(r, Enofunc);
+ pc->srv->remove(r);
+ break;
+ case TSTAT:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if(!pc->srv->stat)
+ return respond(r, Enofunc);
+ pc->srv->stat(r);
+ break;
+ case TWALK:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if(r->fid->omode != -1)
+ return respond(r, "cannot walk from an open fid");
+ if(r->ifcall.nwname && !(r->fid->qid.type&P9QTDIR))
+ return respond(r, Enotdir);
+ if((r->ifcall.fid != r->ifcall.newfid)) {
+ if(!(r->newfid = createfid(&pc->fidmap, r->ifcall.newfid, pc)))
+ return respond(r, Edupfid);
+ }else
+ r->newfid = r->fid;
+ if(!pc->srv->walk)
+ return respond(r, Enofunc);
+ pc->srv->walk(r);
+ break;
+ case TWRITE:
+ if(!(r->fid = lookupkey(&pc->fidmap, r->ifcall.fid)))
+ return respond(r, Enofid);
+ if((r->fid->omode&3) != P9OWRITE && (r->fid->omode&3) != P9ORDWR)
+ return respond(r, "write on fid not opened for writing");
+ if(!pc->srv->write)
+ return respond(r, Enofunc);
+ pc->srv->write(r);
+ break;
+ /* Still to be implemented: flush, wstat, auth */
+ }
+}
+
+void
+respond(P9Req *r, char *error) {
+ P9Conn *pc = r->conn;
+ switch(r->ifcall.type) {
+ default:
+ if(!error)
+ cext_assert(!"Respond called on unsupported fcall type");
+ break;
+ case TVERSION:
+ cext_assert(!error);
+ free(r->ifcall.version);
+ pc->msize = (r->ofcall.msize < IXP_MAX_MSG) ? r->ofcall.msize : IXP_MAX_MSG;
+ free(pc->buf);
+ pc->buf = ixp_emallocz(r->ofcall.msize);
+ break;
+ case TATTACH:
+ if(error)
+ destroyfid(pc, r->fid->fid);
+ free(r->ifcall.uname);
+ free(r->ifcall.aname);
+ break;
+ case TOPEN:
+ case TCREATE:
+ if(!error) {
+ r->fid->omode = r->ifcall.mode;
+ r->fid->qid = r->ofcall.qid;
+ }
+ free(r->ifcall.name);
+ r->ofcall.iounit = pc->msize - sizeof(unsigned long);
+ break;
+ case TWALK:
+ if(error || r->ofcall.nwqid < r->ifcall.nwname) {
+ if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
+ destroyfid(pc, r->newfid->fid);
+ if(!error && r->ofcall.nwqid == 0)
+ error = Enofile;
+ }else{
+ if(r->ofcall.nwqid == 0)
+ r->newfid->qid = r->fid->qid;
+ else
+ r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
+ }
+ free(*r->ifcall.wname);
+ break;
+ case TWRITE:
+ free(r->ifcall.data);
+ break;
+ case TREMOVE:
+ if(r->fid)
+ destroyfid(pc, r->fid->fid);
+ break;
+ case TCLUNK:
+ if(r->fid)
+ destroyfid(pc, r->fid->fid);
+ if(!pc->conn && r->ifcall.tag == IXP_NOTAG)
+ pc->ref--;
+ break;
+ case TFLUSH:
+ if((r->oldreq = lookupkey(&pc->tagmap, r->ifcall.oldtag)))
+ respond(r->oldreq, Einterrupted);
+ if(!pc->conn && r->ifcall.tag == IXP_NOTAG)
+ pc->ref--;
+ break;
+ case TREAD:
+ case TSTAT:
+ break;
+ /* Still to be implemented: flush, wstat, auth */
+ }
+
+ r->ofcall.tag = r->ifcall.tag;
+ if(!error)
+ r->ofcall.type = r->ifcall.type + 1;
+ else {
+ r->ofcall.type = RERROR;
+ r->ofcall.ename = error;
+ }
+
+ if(pc->conn)
+ ixp_server_respond_fcall(pc->conn, &r->ofcall);
+
+ switch(r->ofcall.type) {
+ case RSTAT:
+ free(r->ofcall.stat);
+ break;
+ case RREAD:
+ free(r->ofcall.data);
+ break;
+ }
+
+ deletekey(&pc->tagmap, r->ifcall.tag);;
+ free(r);
+
+ if(!pc->conn && pc->ref == 0)
+ free_p9conn(pc);
+}
+
+/* Pending request cleanup */
+static void
+ixp_void_request(void *t) {
+ P9Req *r, *tr;
+ P9Conn *pc;
+
+ r = t;
+ pc = r->conn;
+
+ tr = ixp_emallocz(sizeof(P9Req));
+ tr->conn = pc;
+ tr->ifcall.type = TFLUSH;
+ tr->ifcall.tag = IXP_NOTAG;
+ tr->ifcall.oldtag = r->ifcall.tag;
+ ixp_handle_req(tr);
+}
+
+/* Open FID cleanup */
+static void
+ixp_void_fid(void *t) {
+ P9Conn *pc;
+ P9Req *tr;
+ Fid *f;
+
+ f = t;
+ pc = f->conn;
+
+ tr = ixp_emallocz(sizeof(P9Req));
+ tr->fid = f;
+ tr->conn = pc;
+ tr->ifcall.type = TCLUNK;
+ tr->ifcall.tag = IXP_NOTAG;
+ tr->ifcall.fid = f->fid;
+ ixp_handle_req(tr);
+}
+
+static void
+ixp_p9conn_incref(void *r) {
+ P9Conn *pc = *(P9Conn **)r;
+ pc->ref++;
+}
+
+/* To cleanup a connction, we increase the ref count for
+ * each open FID and pending request and generate clunk and
+ * flush requests. As each request is responded to and each
+ * FID is clunked, we decrease the ref count. When the ref
+ * count is 0, we free the P9Conn and its buf. The IXPConn
+ * is taken care of in server.c */
+static void
+ixp_cleanup_conn(IXPConn *c) {
+ P9Conn *pc = c->aux;
+ pc->conn = NULL;
+ pc->ref = 1;
+ execmap(&pc->tagmap, ixp_p9conn_incref);
+ execmap(&pc->fidmap, ixp_p9conn_incref);
+ if(pc->ref > 1) {
+ execmap(&pc->tagmap, ixp_void_request);
+ execmap(&pc->fidmap, ixp_void_fid);
+ }
+ if(--pc->ref == 0)
+ free_p9conn(pc);
+}
+
+/* Handle incoming 9P connections */
+void
+serve_9pcon(IXPConn *c) {
+ int fd = accept(c->fd, NULL, NULL);
+ if(fd < 0)
+ return;
+
+ P9Conn *pc = ixp_emallocz(sizeof(P9Conn));
+ pc->srv = c->aux;
+
+ /* XXX */
+ pc->msize = 1024;
+ pc->buf = ixp_emallocz(pc->msize);
+
+ initmap(&pc->tagmap, TAG_BUCKETS, &pc->taghash);
+ initmap(&pc->fidmap, FID_BUCKETS, &pc->fidhash);
+
+ ixp_server_open_conn(c->srv, fd, pc, ixp_server_handle_fcall, ixp_cleanup_conn);
+}
diff --git a/server.c b/server.c
@@ -0,0 +1,130 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.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.h"
+
+static unsigned char *msg[IXP_MAX_MSG];
+
+IXPConn *
+ixp_server_open_conn(IXPServer *s, int fd, void *aux,
+ void (*read)(IXPConn *c), void (*close)(IXPConn *c))
+{
+ IXPConn *c = ixp_emallocz(sizeof(IXPConn));
+ c->fd = fd;
+ c->aux = aux;
+ c->srv = s;
+ c->read = read;
+ c->close = close;
+ c->next = s->conn;
+ s->conn = c;
+ return c;
+}
+
+void
+ixp_server_close_conn(IXPConn *c)
+{
+ IXPServer *s = c->srv;
+ IXPConn **tc;
+ for(tc=&s->conn; *tc && *tc != c; tc=&(*tc)->next);
+ cext_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);
+}
+
+static void
+prepare_select(IXPServer *s)
+{
+ IXPConn **c;
+ FD_ZERO(&s->rd);
+ for(c=&s->conn; *c; *c && (c=&(*c)->next)) {
+ if(s->maxfd < (*c)->fd)
+ s->maxfd = (*c)->fd;
+ if((*c)->read)
+ FD_SET((*c)->fd, &s->rd);
+ }
+}
+
+static void
+handle_conns(IXPServer *s)
+{
+ IXPConn *c, *n;
+ for((c=s->conn) && (n=c->next); c; (c=n) && (n=c->next))
+ if(FD_ISSET(c->fd, &s->rd) && c->read)
+ c->read(c);
+}
+
+char *
+ixp_server_loop(IXPServer *s)
+{
+ int r;
+ s->running = 1;
+
+ /* main loop */
+ while(s->running) {
+ prepare_select(s);
+
+ r = select(s->maxfd + 1, &s->rd, 0, 0, 0);
+ if(r == -1 && errno == EINTR)
+ continue;
+ if(r < 0)
+ return "fatal select error";
+ else if(r > 0)
+ handle_conns(s);
+ }
+ return NULL;
+}
+
+unsigned int
+ixp_server_receive_fcall(IXPConn *c, Fcall *fcall)
+{
+ unsigned int msize;
+ char *errstr = 0;
+ if(!(msize = ixp_recv_message(c->fd, msg, IXP_MAX_MSG, &errstr))) {
+ ixp_server_close_conn(c);
+ return 0;
+ }
+ return ixp_msg2fcall(fcall, msg, IXP_MAX_MSG);
+}
+
+int
+ixp_server_respond_fcall(IXPConn *c, Fcall *fcall)
+{
+ char *errstr;
+ unsigned int msize = ixp_fcall2msg(msg, fcall, IXP_MAX_MSG);
+ if(c->closed)
+ return 0;
+ if(ixp_send_message(c->fd, msg, msize, &errstr) != msize) {
+ ixp_server_close_conn(c);
+ return -1;
+ }
+ return 0;
+}
+
+void
+ixp_server_close(IXPServer *s)
+{
+ IXPConn *c, *next;
+ for(c=s->conn; c; c=next) {
+ next=c->next;
+ ixp_server_close_conn(c);
+ }
+}
diff --git a/socket.c b/socket.c
@@ -0,0 +1,200 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "ixp.h"
+
+static int
+connect_unix_sock(char *address)
+{
+ int fd = 0;
+ struct sockaddr_un addr = { 0 };
+ socklen_t su_len;
+
+ /* init */
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, address, sizeof(addr.sun_path));
+ su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
+
+ if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+ if(connect(fd, (struct sockaddr *) &addr, su_len)) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+static int
+connect_inet_sock(char *host)
+{
+ int fd = 0;
+ struct sockaddr_in addr = { 0 };
+ struct hostent *hp;
+ char *port = strrchr(host, '!');
+ unsigned int prt;
+
+ if(!port)
+ return -1;
+ *port = 0;
+ port++;
+ if(sscanf(port, "%d", &prt) != 1)
+ return -1;
+
+ /* init */
+ if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return -1;
+ hp = gethostbyname(host);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(prt);
+ bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
+
+ if(connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in))) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int
+ixp_connect_sock(char *address)
+{
+ char *p;
+
+ if((p = strchr(address, '!'))) {
+ *p = 0;
+ p++;
+
+ if(!strncmp(address, "unix", 5))
+ return connect_unix_sock(p);
+ else if(!strncmp(address, "tcp", 4))
+ return connect_inet_sock(p);
+ }
+ return -1;
+}
+
+static int
+create_inet_sock(char *host, char **errstr)
+{
+ int fd;
+ struct sockaddr_in addr = { 0 };
+ struct hostent *hp;
+ char *port = strrchr(host, '!');
+ unsigned int prt;
+
+ if(!port) {
+ *errstr = "no port provided in address";
+ return -1;
+ }
+ *port = 0;
+ port++;
+ if(sscanf(port, "%d", &prt) != 1) {
+ *errstr = "invalid port number";
+ return -1;
+ }
+ signal(SIGPIPE, SIG_IGN);
+ if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ *errstr = "cannot open socket";
+ return -1;
+ }
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(prt);
+
+ if(!strncmp(host, "*", 2))
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ else if((hp = gethostbyname(host)))
+ bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
+ else {
+ *errstr = "cannot translate hostname to an address";
+ return -1;
+ }
+
+ if(bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
+ *errstr = "cannot bind socket";
+ close(fd);
+ return -1;
+ }
+
+ if(listen(fd, IXP_MAX_CACHE) < 0) {
+ *errstr = "cannot listen on socket";
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+static int
+create_unix_sock(char *file, char **errstr)
+{
+ int fd;
+ int yes = 1;
+ struct sockaddr_un addr = { 0 };
+ socklen_t su_len;
+
+ signal(SIGPIPE, SIG_IGN);
+ if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ *errstr = "cannot open socket";
+ return -1;
+ }
+ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &yes, sizeof(yes)) < 0) {
+ *errstr = "cannot set socket options";
+ close(fd);
+ return -1;
+ }
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, file, sizeof(addr.sun_path));
+ su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
+
+ unlink(file); /* remove old socket, if any */
+ if(bind(fd, (struct sockaddr *) &addr, su_len) < 0) {
+ *errstr = "cannot bind socket";
+ close(fd);
+ return -1;
+ }
+ chmod(file, S_IRWXU);
+
+ if(listen(fd, IXP_MAX_CACHE) < 0) {
+ *errstr = "cannot listen on socket";
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int
+ixp_create_sock(char *address, char **errstr)
+{
+ char *p = strchr(address, '!');
+ char *addr, *type;
+
+ if(!p) {
+ *errstr = "no socket type defined";
+ return -1;
+ }
+ *p = 0;
+
+ addr = &p[1];
+ type = address; /* unix, inet */
+
+ if(!strncmp(type, "unix", 5))
+ return create_unix_sock(addr, errstr);
+ else if(!strncmp(type, "tcp", 4))
+ return create_inet_sock(addr, errstr);
+ else
+ *errstr = "unkown socket type";
+ return -1;
+}
diff --git a/transport.c b/transport.c
@@ -0,0 +1,77 @@
+/*
+ * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot 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.h"
+
+unsigned int
+ixp_send_message(int fd, void *msg, unsigned int msize, char **errstr)
+{
+ unsigned int num = 0;
+ int r;
+
+ /* send message */
+ while(num < msize) {
+ r = write(fd, msg + num, msize - num);
+ if(r == -1 && errno == EINTR)
+ continue;
+ if(r < 1) {
+ *errstr = "broken pipe";
+ return 0;
+ }
+ num += r;
+ }
+ return num;
+}
+
+static unsigned int
+ixp_recv_data(int fd, void *msg, unsigned int msize, char **errstr)
+{
+ unsigned int num = 0;
+ int r = 0;
+
+ /* receive data */
+ while(num < msize) {
+ r = read(fd, msg + num, msize - num);
+ if(r == -1 && errno == EINTR)
+ continue;
+ if(r < 1) {
+ *errstr = "broken pipe";
+ return 0;
+ }
+ num += r;
+ }
+ return num;
+}
+
+unsigned int
+ixp_recv_message(int fd, void *msg, unsigned int msglen, char **errstr)
+{
+ unsigned int msize;
+
+ /* receive header */
+ if(ixp_recv_data(fd, msg, sizeof(unsigned int), errstr) !=
+ sizeof(unsigned int))
+ return 0;
+ ixp_unpack_u32((void *)&msg, NULL, &msize);
+ if(msize > msglen) {
+ *errstr = "invalid message header";
+ return 0;
+ }
+ /* receive message */
+ if(ixp_recv_data(fd, msg, msize - sizeof(unsigned int), errstr)
+ != msize - sizeof(unsigned int))
+ return 0;
+ return msize;
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,47 @@
+/* (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
+ * See LICENSE file for license details.
+ */
+#include "ixp.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+void *
+ixp_emalloc(unsigned int size) {
+ void *res = malloc(size);
+
+ if(!res)
+ ixp_eprint("fatal: could not malloc() %u bytes\n", size);
+ return res;
+}
+
+void *
+ixp_emallocz(unsigned int size) {
+ void *res = calloc(1, size);
+
+ if(!res)
+ ixp_eprint("fatal: could not malloc() %u bytes\n", size);
+ return res;
+}
+
+void
+ixp_eprint(const char *errstr, ...) {
+ va_list ap;
+
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+char *
+ixp_estrdup(const char *str) {
+ void *res = strdup(str);
+
+ if(!res)
+ ixp_eprint("fatal: could not malloc() %u bytes\n", strlen(str));
+ return res;
+}