libixp

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

commit 5299fd2143f757e40e6cadc2bbcba9d8ca0bd13e
parent 6c0c63babdc6cf260c1c710bafee567afb0846db
Author: Anselm R. Garbe <arg@10kloc.org>
Date:   Thu, 12 Oct 2006 14:58:16 +0200

added ixpc

Diffstat:
Makefile | 20++++++++++++++++----
client.c | 28++++++++++++++++++++++++++--
config.mk | 2+-
ixp.h | 1+
ixpc.1 | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ixpc.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
util.c | 9+++++++++
7 files changed, 408 insertions(+), 7 deletions(-)

diff --git a/Makefile b/Makefile @@ -5,9 +5,11 @@ include config.mk SRC = client.c convert.c intmap.c message.c request.c server.c socket.c \ transport.c util.c +SRCIXPC = ixpc.c OBJ = ${SRC:.c=.o} +OBJIXPC = ${SRCIXPC:.c=.o} -all: options libixp.a +all: options libixp.a ixpc options: @echo libixp build options: @@ -26,16 +28,20 @@ libixp.a: ${OBJ} @echo AR $@ @${AR} $@ ${OBJ} @${RANLIB} $@ + +ixpc: ${OBJIXPC} + @echo LD $@ + @${LD} -o $@ ${OBJIXPC} ${LDFLAGS} -lixp @strip $@ clean: @echo cleaning - @rm -f libixp.a ${OBJ} libixp-${VERSION}.tar.gz + @rm -f ixpc libixp.a ${OBJ} ${OBJIXPC} 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} + @cp -R LICENSE LICENSE.p9p Makefile README config.mk ixp.h ${SRC} ${SRCIXPC} libixp-${VERSION} @tar -cf libixp-${VERSION}.tar libixp-${VERSION} @gzip libixp-${VERSION}.tar @rm -rf libixp-${VERSION} @@ -47,13 +53,19 @@ install: all @chmod 644 ${DESTDIR}${PREFIX}/include/ixp.h @echo installing library to ${DESTDIR}${PREFIX}/lib @mkdir -p ${DESTDIR}${PREFIX}/lib - @cp -f ssid ${DESTDIR}${PREFIX}/lib + @cp -f libixp.a ${DESTDIR}${PREFIX}/lib @chmod 644 ${DESTDIR}${PREFIX}/lib/libixp.a + @echo installing ixpc to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ixpc ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/ixpc uninstall: @echo removing header file from ${DESTDIR}${PREFIX}/include @rm -f ${DESTDIR}${PREFIX}/include/ixp.h @echo removing library file from ${DESTDIR}${PREFIX}/lib @rm -f ${DESTDIR}${PREFIX}/lib/libixp.a + @echo removing ipx client from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/ixpc .PHONY: all options clean dist install uninstall diff --git a/client.c b/client.c @@ -9,6 +9,31 @@ #include <sys/types.h> #include <unistd.h> +static unsigned int +tokenize(char **result, unsigned int reslen, char *str, char delim) { + char *p, *n; + unsigned int i = 0; + + if(!str) + return 0; + for(n = str; *n == delim; n++); + p = n; + for(i = 0; *n != 0;) { + if(i == reslen) + return i; + if(*n == delim) { + *n = 0; + if(strlen(p)) + result[i++] = p; + p = ++n; + } else + n++; + } + if((i < reslen) && (p < n) && strlen(p)) + result[i++] = p; + return i; /* number of tokens */ +} + int ixp_client_do_fcall(IXPClient *c) { static unsigned char msg[IXP_MAX_MSG]; @@ -101,8 +126,7 @@ ixp_client_walk(IXPClient *c, unsigned int newfid, char *filepath) { c->ifcall.newfid = newfid; if(filepath) { c->ifcall.name = filepath; - c->ifcall.nwname = - cext_tokenize(wname, IXP_MAX_WELEM, c->ifcall.name, '/'); + c->ifcall.nwname = tokenize(wname, IXP_MAX_WELEM, c->ifcall.name, '/'); for(i = 0; i < c->ifcall.nwname; i++) c->ifcall.wname[i] = wname[i]; } diff --git a/config.mk b/config.mk @@ -8,7 +8,7 @@ PREFIX = /usr/local # includes and libs INCS = -I. -I/usr/include -LIBS = -L/usr/lib -lc +LIBS = -L/usr/lib -lc -L. # flags CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\" diff --git a/ixp.h b/ixp.h @@ -371,4 +371,5 @@ extern unsigned int ixp_recv_message(int fd, void *msg, unsigned int msglen, cha extern void * ixp_emalloc(unsigned int size); extern void * ixp_emallocz(unsigned int size); extern void ixp_eprint(const char *errstr, ...); +extern void * ixp_erealloc(void *ptr, unsigned int size); extern char * ixp_estrdup(const char *str); diff --git a/ixpc.1 b/ixpc.1 @@ -0,0 +1,83 @@ +.TH IXPC 1 ixpc-VERSION +.SH NAME +ixpc \- ixp client +.SH SYNOPSIS +.B ixpc +.RB [ \-a +.IR address ] +.I action +.I file +.br +.B ixpc +.B \-v +.SH DESCRIPTION +.SS Overview +.B ixpc +is a client to access a 9P file server from the command line or from shell +scripts. It can be used to configure +.BR wmii (1). +.SS Options +.TP +.BI \-a " address" +Lets you specify the address to which +.B ixpc +will establish a connection. If this option is not supplied, and the +environment variable IXP_ADDRESS is set, +.B ixpc +will use this value as its address. Currently, the address can only be a +unix socket file or a tcp socket. The syntax for +.I address +is taken (along with many other profound ideas) from the Plan 9 operating +system and has the form +.BR unix!/path/to/socket +for unix socket files, and +.BR tcp!hostname!port +for tcp sockets. +.TP +.B \-v +Prints version information to stdout, then exits. +.TP +The syntax of the actions is as follows: +.TP +.B write +Writes the supplied data from stdin to +.IR file, +overwriting any previous data. The data to be written is arbitrary +and only gains meaning (and restrictions) when it is interpreted by +.BR wmiiwm (1). +See +.B EXAMPLES +below. +.TP +.B create +Creates file or directory but does not write any data. If the file exists, +nothing is done. +.TP +.B read +Reads file or directory contents +.TP +.B remove +Removes file or directory tree +.SH ENVIRONMENT +.TP +IXP_ADDRESS +See above. +.SH EXAMPLES +.TP +.B ixpc read / +This prints the root directory of the wmii filesystem. For more information +about the contents of this filesystem, see +.BR wmiiwm (1). +.TP +.B echo -n quit | ixpc write /ctl +Write 'quit' to the main control file of the wmii filesystem, effectively +leaving wmii. +.TP +.B echo -n view 2 | ixpc write /ctl +Bring into view all clients tagged '2'. To learn about clients and +tags, see +.BR wmii (1). +.SH SEE ALSO +.BR wmii (1) + +http://www.cs.bell-labs.com/sys/man/5/INDEX.html diff --git a/ixpc.c b/ixpc.c @@ -0,0 +1,272 @@ +/* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ +#include <ixp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <time.h> + +static IXPClient c = { 0 }; + +static void +write_data(unsigned int fid) { + void *data = ixp_emallocz(c.ofcall.iounit); + unsigned long long offset = 0; + unsigned int len = 0; + + while((len = read(0, data, c.ofcall.iounit)) > 0) { + if(ixp_client_write(&c, fid, offset, len, data) != len) { + fprintf(stderr, "ixpc: cannot write file: %s\n", c.errstr); + break; + } + offset += len; + } + if(offset == 0) /* do an explicit empty write when no writing has been done yet */ + if(ixp_client_write(&c, fid, offset, 0, 0) != 0) + fprintf(stderr, "ixpc: cannot write file: %s\n", c.errstr); + free(data); +} + +static int +xcreate(char *file) { + unsigned int fid; + char *p = strrchr(file, '/'); + + if(!p) + p = file; + fid = c.root_fid << 2; + /* walk to bottom-most directory */ + *p = 0; + if(ixp_client_walk(&c, fid, file) == -1) { + fprintf(stderr, "ixpc: cannot walk to '%s': %s\n", file, c.errstr); + return -1; + } + p++; + if(ixp_client_create(&c, fid, p, IXP_DMWRITE, IXP_OWRITE) == -1) { + fprintf(stderr, "ixpc: cannot create file '%s': %s\n", p, c.errstr); + return -1; + } + if(!(c.ofcall.qid.type&P9DMDIR)) + write_data(fid); + return ixp_client_close(&c, fid); +} + +static int +xwrite(char *file, unsigned char mode) { + /* open */ + unsigned int fid = c.root_fid << 2; + if(ixp_client_walkopen(&c, fid, file, mode) == -1) { + fprintf(stderr, "ixpc: cannot open file '%s': %s\n", file, c.errstr); + return -1; + } + write_data(fid); + return ixp_client_close(&c, fid); +} + +static int +comp_stat(const void *s1, const void *s2) { + Stat *st1 = (Stat *)s1; + Stat *st2 = (Stat *)s2; + return strcmp(st1->name, st2->name); +} + +static void +setrwx(long m, char *s) { + static char *modes[] = { + "---", + "--x", + "-w-", + "-wx", + "r--", + "r-x", + "rw-", + "rwx", + }; + strncpy(s, modes[m], 3); +} + +static char * +str_of_mode(unsigned int mode) { + static char buf[16]; + + if(mode & IXP_DMDIR) + buf[0]='d'; + else + buf[0]='-'; + buf[1]='-'; + setrwx((mode >> 6) & 7, &buf[2]); + setrwx((mode >> 3) & 7, &buf[5]); + setrwx((mode >> 0) & 7, &buf[8]); + buf[11] = 0; + return buf; +} + +static char * +str_of_time(unsigned int val) { + static char buf[32]; + time_t t = (time_t)(int)val; + char *tstr = ctime(&t); + + strncpy(buf, tstr ? tstr : "in v a l id ", sizeof(buf)); + buf[strlen(buf) - 1] = 0; + return buf; +} + +static void +print_stat(Stat *s, int details) { + if(details) + fprintf(stdout, "%s %s %s %5llu %s %s\n", str_of_mode(s->mode), + s->uid, s->gid, s->length, str_of_time(s->mtime), s->name); + else { + if(s->mode & IXP_DMDIR) + fprintf(stdout, "%s/\n", s->name); + else + fprintf(stdout, "%s\n", s->name); + } +} + +static void +xls(void *result, unsigned int msize, int details) { + unsigned int n = 0, i = 0; + unsigned char *p = result; + Stat *dir; + static Stat stat; + + do { + ixp_unpack_stat(&p, NULL, &stat); + n++; + } + while(p - (unsigned char*)result < msize); + dir = (Stat *)ixp_emallocz(sizeof(Stat) * n); + p = result; + do { + ixp_unpack_stat(&p, NULL, &dir[i++]); + } + while(p - (unsigned char*)result < msize); + qsort(dir, n, sizeof(Stat), comp_stat); + for(i = 0; i < n; i++) + print_stat(&dir[i], details); + free(dir); + fflush(stdout); +} + +static int +xdir(char *file, int details) { + unsigned int fid = c.root_fid << 2; + /* XXX: buffer overflow */ + Stat *s = ixp_emallocz(sizeof(Stat)); + unsigned char *buf; + int count; + static unsigned char result[IXP_MAX_MSG]; + void *data = NULL; + unsigned long long offset = 0; + + if(ixp_client_stat(&c, fid, file) == -1) { + fprintf(stderr, "ixpc: cannot stat file '%s': %s\n", file, c.errstr); + return -1; + } + buf = c.ofcall.stat; + ixp_unpack_stat(&buf, NULL, s); + if(!(s->mode & IXP_DMDIR)) { + print_stat(s, details); + fflush(stdout); + return 0; + } + /* directory */ + if(ixp_client_open(&c, fid, IXP_OREAD) == -1) { + fprintf(stderr, "ixpc: cannot open directory '%s': %s\n", file, c.errstr); + return -1; + } + while((count = ixp_client_read(&c, fid, offset, result, IXP_MAX_MSG)) > 0) { + data = ixp_erealloc(data, offset + count); + memcpy(data + offset, result, count); + offset += count; + } + if(count == -1) { + fprintf(stderr, "ixpc: cannot read directory '%s': %s\n", file, c.errstr); + return -1; + } + if(data) + xls(data, offset + count, details); + return ixp_client_close(&c, fid); +} + +static int +xread(char *file) { + unsigned int fid = c.root_fid << 2; + int count; + static unsigned char result[IXP_MAX_MSG]; + unsigned long long offset = 0; + + if(ixp_client_walkopen(&c, fid, file, IXP_OREAD) == -1) { + fprintf(stderr, "ixpc: cannot open file '%s': %s\n", file, c.errstr); + return -1; + } + while((count = ixp_client_read(&c, fid, offset, result, IXP_MAX_MSG)) > 0) { + write(1, result, count); + offset += count; + } + if(count == -1) { + fprintf(stderr, "ixpc: cannot read file/directory '%s': %s\n", file, c.errstr); + return -1; + } + return ixp_client_close(&c, fid); +} + +static int +xremove(char *file) { + unsigned int fid; + + fid = c.root_fid << 2; + if(ixp_client_remove(&c, fid, file) == -1) { + fprintf(stderr, "ixpc: cannot remove file '%s': %s\n", file, c.errstr); + return -1; + } + return 0; +} + +int +main(int argc, char *argv[]) { + int ret = 0, i = 0, details = 0; + char *cmd, *file, *address = getenv("IXP_ADDRESS"); + + /* command line args */ + if(argc < 2) + ixp_eprint("usage: ixpc [-a <address>] [-v] create | read | ls [-l] | remove | write <file>\n"); + for(i = 1; i < argc; i++) + if(!strncmp(argv[i], "-v", 3)) { + fputs("ixpc-"VERSION", (C)opyright MMIV-MMVI Anselm R. Garbe\n", stdout); + exit(EXIT_SUCCESS); + } + else if(!strncmp(argv[i], "-a", 3)) + address = argv[++i]; + cmd = argv[argc - 2]; + file = argv[argc - 1]; + if((details = !strncmp(cmd, "-l", 3))) { + if(argc < 3 || strncmp(argv[argc - 3], "ls", 3)) + ixp_eprint("usage: ixpc [-a <address>] [-v] create | read | ls [-l] | remove | write <file>\n"); + cmd = argv[argc - 3]; + } + if(!address) + ixp_eprint("ixpc: error: $IXP_ADDRESS not set\n"); + if(ixp_client_dial(&c, address, getpid()) == -1) + ixp_eprint("ixpc: %s\n", c.errstr); + if(!strncmp(cmd, "create", 7)) + ret = xcreate(file); + else if(!strncmp(cmd, "ls", 3)) + ret = xdir(file, details); + else if(!strncmp(cmd, "read", 5)) + ret = xread(file); + else if(!strncmp(cmd, "remove", 7)) + ret = xremove(file); + else if(!strncmp(cmd, "write", 6)) + ret = xwrite(file, IXP_OWRITE); + else + ixp_eprint("usage: ixpc [-a <address>] [-v] create | read | ls [-l] | remove | write <file>\n"); + /* close socket */ + ixp_client_hangup(&c); + return ret; +} diff --git a/util.c b/util.c @@ -37,6 +37,15 @@ ixp_eprint(const char *errstr, ...) { exit(EXIT_FAILURE); } +void * +ixp_erealloc(void *ptr, unsigned int size) { + void *res = realloc(ptr, size); + + if(!res) + ixp_eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + char * ixp_estrdup(const char *str) { void *res = strdup(str);