9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] writefs.
@ 2003-10-09  4:58 Dan Cross
  0 siblings, 0 replies; only message in thread
From: Dan Cross @ 2003-10-09  4:58 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 1106 bytes --]

I wrote this a long time ago; it's imperfect, and full of bugs,
I'm sure.  It crashes fairly often, too, but seems to work for
simple things.  Conceptually, it's like what Geoff posted,
only implemented as a filesystem instead of just a simple
file descriptor in /srv.  The idea is that it could thus be run
on a central CPU server and imported all over the place,
effectively giving network write service for free.  If someone
wants to clean it up and mail it back to me, feel free; I
never finished writing the ACL stuff (I'd probably do it
differently now, anyway).  Maybe in a few weeks I'll have
some time to clean it up some more and we can stick it in
the distribution or something.

To run it, compile writefs.c and put the binary somewhere.
Run it on a CPU server when it starts up.  Copy writelstn
and write somewhere, and modify them as needed (for
instance, to import /mnt/writefs from the CPU server if needed).
Run writelstn in a window somewhere to receive messages
(I usually run it in a `win' under acme) and run `write user' to
send messages to others.

	- Dan C.


[-- Attachment #2: write --]
[-- Type: application/octet-stream, Size: 77 bytes --]

#!/bin/rc
rfork n
mount /srv/writefs /mnt/write
exec cat > /mnt/write/$1

[-- Attachment #3: writefs.c --]
[-- Type: text/plain, Size: 4909 bytes --]

/*
 *  I miss the Unix write(1) command, but I think
 *  this is a better way to do it.
 */

#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

#define	min(A, B)	((A) < (B) ? (A) : (B))
#define	STREQ(A, B)	((A)[0] == (B)[0] && strcmp(A, B) == 0)

//  Messages are consumed by readers either when they're
//  posted, or when the reader sends a Tread message.

typedef	struct Message Message;
struct	Message {
	Ref;
	char	*msg;
};

typedef	struct Reader Reader;
struct	Reader {
	Reader	*next;
	Channel	*mq;
	Req	*req;
	char	*name;
};

typedef	struct Acl Acl;
struct	Acl {
	char	**acl;
	int	acllen;
};

typedef	struct Station Station;
struct	Station {
	Reader	*readers;
	Acl	*rallow;
	Acl	*rdisallow;
	Acl	*wallow;
	Acl	*wdisallow;
	Acl	*callow;
	Acl	*cdisallow;
};

static void	*ezmalloc(ulong);
static void	*estrdup(char *);

static void	consume(Reader *);
static void	produce(Message *, Reader *);

void	wfsopen(Req *);
void	wfsread(Req *);
void	wfswrite(Req *);

enum { POOLINCR = 20 };

Srv	serv = {
	.open =	wfsopen,
	.read =	wfsread,
	.write=	wfswrite
};
Tree	*tp;

static void *
ezmalloc(ulong size)
{
	void	*p;

	p = malloc(size);
	if (p == nil)
		sysfatal("malloc %lud bytes: %r\n", size);
	memset(p, 0, size);
	return(p);
}

static char *
estrdup(char *str)
{
	char	*p;

	p = strdup(str);
	if (p == nil)
		sysfatal("estrdup: malloc failed: %r\n");
	return(p);
}

static void
produce(Message *mp, Reader *readers)
{
	Reader	*rp;

	for (rp = readers; rp != nil; rp = rp->next)
		incref(mp);
	for (rp = readers; rp != nil; rp = rp->next)
		sendp(rp->mq, mp);
}

void
consume(void *reader)
{
	Reader	*rp;
	Message	*mp;
	Req	*req;
	long	len;

	rp = reader;
	for ( ; ; ) {
		req = rp->req;
		if (req == nil) {
			yield();
			continue;
		}
		mp = recvp(rp->mq);
		if (mp == nil)
			continue;
		len = min(strlen(mp->msg), req->ifcall.count);
		memcpy(req->ofcall.data, mp->msg, len);
		req->ofcall.count = len;
		if (decref(mp) == 0)
			free(mp);
		rp->req = nil;
		respond(req, nil);
	}
	threadexits(0);
}

Reader *
addreader(File *file, char *user)
{
	Station	*sp;
	Reader	*rp, *pp, *np;

	sp = file->aux;
	for (pp = nil, rp = sp->readers;
	    rp != nil && strcmp(rp->name, user) < 0;
	    pp = rp, rp = rp->next)
		;
	if (rp != nil && STREQ(rp->name, user))
		return(rp);
	np = ezmalloc(sizeof(*rp));
	np->name = estrdup(user);
	np->req = nil;
	np->mq = chancreate(sizeof(Message *), 128);
	np->next = rp;
	if (pp == nil) {
		//  At the beginning or empty.
		sp->readers = np;
	} else {
		pp->next = np;
	}
	proccreate(consume, np, 8192);
	return(np);
}

/*
 *  Add ACL testing code.
 */
void
wfsopen(Req *req)
{
	if (!STREQ(req->fid->file->name, "clone") &&
	    !(req->fid->file->mode & DMDIR) &&
	    (req->ifcall.mode == OREAD) || req->ifcall.mode == ORDWR)
		addreader(req->fid->file, req->fid->uid);
	respond(req, nil);
}

void
wfsread(Req *req)
{
	File	*file;
	Fid	*fid;
	char	*uid;
	Station	*sp;
	Reader	*rp;

	uid = req->fid->uid;
	file = req->fid->file;
	if (file == nil)
		sysfatal("fid->file == nil?");
	if (STREQ(file->name, "clone")) {
		if (req->ifcall.offset > 0) {
			req->ofcall.count = 0;
			respond(req, nil);
			return;
		}
		file = createfile(tp->root, uid, uid, 0622, ezmalloc(sizeof(*sp)));
		if (file == nil) {
			respond(req, "user already exists");
			return;
		}
		strcpy(req->ofcall.data, uid);
		req->ofcall.count = strlen(uid);
		respond(req, nil);
		return;
	}
	sp = file->aux;
	if (sp != nil)
		for (rp = sp->readers; rp != nil; rp = rp->next)
			if (STREQ(uid, rp->name)) {
				if (rp->req != nil)
					respond(rp->req, nil);
				rp->req = req;
			}
}

void
wfswrite(Req *req)
{
	Station	*sp;
	Reader	*rp;
	Message	*mp;
	Fid	*fid;
	int	len;
	char	date[32];

	fid = req->fid;
	sp = fid->file->aux;
	if (sp->readers == nil) {
		respond(req, "no listeners");
		return;
	}
	snprint(date, sizeof date, "%lld", (vlong)time(nil));
	len = strlen(date) + 1 + strlen(fid->uid) + 1 + strlen(fid->file->name) + 1 + req->ifcall.count + 1;
	mp = ezmalloc(sizeof(*mp) + len);
	mp->msg = (char *)mp + sizeof(*mp);
	snprint(mp->msg, len, "%s:%s:%s:%.*s", date,
	    fid->uid, fid->file->name,
	    req->ifcall.count, req->ifcall.data);
	req->ofcall.count = req->ifcall.count;
	produce(mp, sp->readers);
	respond(req, nil);
}

void
threadmain(int, char *[])
{
	File	*f;
	char	*user;

	rfork(RFNOTEG);
	user = getuser();
	if (user == nil)
		user = "unknown";
	tp = alloctree(user, user, DMDIR | 0664, nil);
	serv.tree = tp;
	f = createfile(tp->root, "clone", user, 0666, nil);
	if (f == nil)
		sysfatal("fcreate clone");
	threadpostmountsrv(&serv, "writefs", "/mnt/write", MREPL);

	exits(0);
}

[-- Attachment #4: writelstn --]
[-- Type: application/octet-stream, Size: 235 bytes --]

#!/bin/rc
rfork n
mount /srv/writefs /mnt/write
cat /mnt/write/clone > /dev/null >[2=1]
exec awk -F: '{
	time = $1
	from = $2
	to = $3
	msg = $0
	sub(/^[^:]*:[^:]*:[^:]*:/, "", msg)
	print from ": " msg
}' /mnt/write/$user

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2003-10-09  4:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-10-09  4:58 [9fans] writefs Dan Cross

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).