9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: Dan Cross <cross@sdgm.net>
To: 9fans@cse.psu.edu
Subject: [9fans] writefs.
Date: Thu,  9 Oct 2003 00:58:36 -0400	[thread overview]
Message-ID: <3DC2CA21-FA15-11D7-AF9F-000A95EA6DE8@sdgm.net> (raw)

[-- 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

                 reply	other threads:[~2003-10-09  4:58 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3DC2CA21-FA15-11D7-AF9F-000A95EA6DE8@sdgm.net \
    --to=cross@sdgm.net \
    --cc=9fans@cse.psu.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).