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);