libixp

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

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; +}