xref: /plan9-contrib/sys/src/cmd/fossil/9proc.c (revision 39734e7ed1eb944f5e7b41936007d0d38b560d7f)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier 
35e96a66cSDavid du Colombier #include "9.h"
45e96a66cSDavid du Colombier #include "dat.h"
55e96a66cSDavid du Colombier #include "fns.h"
65e96a66cSDavid du Colombier 
75e96a66cSDavid du Colombier enum {
85e96a66cSDavid du Colombier 	NConInit	= 128,
95e96a66cSDavid du Colombier 	NMsgInit	= 20,
105e96a66cSDavid du Colombier 	NMsgProcInit	= 4,
115e96a66cSDavid du Colombier 	NMsizeInit	= 8192+IOHDRSZ,
125e96a66cSDavid du Colombier };
135e96a66cSDavid du Colombier 
145e96a66cSDavid du Colombier static struct {
1534e04225SDavid du Colombier 	VtLock*	alock;			/* alloc */
1634e04225SDavid du Colombier 	Msg*	ahead;
1734e04225SDavid du Colombier 	VtRendez* arendez;
185e96a66cSDavid du Colombier 
195e96a66cSDavid du Colombier 	int	maxmsg;
205e96a66cSDavid du Colombier 	int	nmsg;
2134e04225SDavid du Colombier 	int	nmsgstarve;
2234e04225SDavid du Colombier 
2334e04225SDavid du Colombier 	VtLock*	rlock;			/* read */
2434e04225SDavid du Colombier 	Msg*	rhead;
2534e04225SDavid du Colombier 	Msg*	rtail;
2634e04225SDavid du Colombier 	VtRendez* rrendez;
2734e04225SDavid du Colombier 
285e96a66cSDavid du Colombier 	int	maxproc;
295e96a66cSDavid du Colombier 	int	nproc;
3034e04225SDavid du Colombier 	int	nprocstarve;
315e96a66cSDavid du Colombier 
325e96a66cSDavid du Colombier 	u32int	msize;			/* immutable */
335e96a66cSDavid du Colombier } mbox;
345e96a66cSDavid du Colombier 
3534e04225SDavid du Colombier static struct {
3634e04225SDavid du Colombier 	VtLock*	alock;			/* alloc */
3734e04225SDavid du Colombier 	Con*	ahead;
3834e04225SDavid du Colombier 	VtRendez* arendez;
3934e04225SDavid du Colombier 
4034e04225SDavid du Colombier 	VtLock*	clock;
4134e04225SDavid du Colombier 	Con*	chead;
4234e04225SDavid du Colombier 	Con*	ctail;
4334e04225SDavid du Colombier 
4434e04225SDavid du Colombier 	int	maxcon;
4534e04225SDavid du Colombier 	int	ncon;
4634e04225SDavid du Colombier 	int	nconstarve;
4734e04225SDavid du Colombier 
4834e04225SDavid du Colombier 	u32int	msize;
4934e04225SDavid du Colombier } cbox;
505e96a66cSDavid du Colombier 
515e96a66cSDavid du Colombier static void
525e96a66cSDavid du Colombier conFree(Con* con)
535e96a66cSDavid du Colombier {
5434e04225SDavid du Colombier 	assert(con->version == nil);
5534e04225SDavid du Colombier 	assert(con->mhead == nil);
5634e04225SDavid du Colombier 	assert(con->whead == nil);
5734e04225SDavid du Colombier 	assert(con->nfid == 0);
5834e04225SDavid du Colombier 	assert(con->state == ConMoribund);
5934e04225SDavid du Colombier 
605e96a66cSDavid du Colombier 	if(con->fd >= 0){
615e96a66cSDavid du Colombier 		close(con->fd);
625e96a66cSDavid du Colombier 		con->fd = -1;
635e96a66cSDavid du Colombier 	}
6434e04225SDavid du Colombier 	con->state = ConDead;
655e96a66cSDavid du Colombier 
6634e04225SDavid du Colombier 	vtLock(cbox.alock);
6734e04225SDavid du Colombier 	if(con->cprev != nil)
6834e04225SDavid du Colombier 		con->cprev->cnext = con->cnext;
6934e04225SDavid du Colombier 	else
7034e04225SDavid du Colombier 		cbox.chead = con->cnext;
7134e04225SDavid du Colombier 	if(con->cnext != nil)
7234e04225SDavid du Colombier 		con->cnext->cprev = con->cprev;
7334e04225SDavid du Colombier 	else
7434e04225SDavid du Colombier 		cbox.ctail = con->cprev;
7534e04225SDavid du Colombier 	con->cprev = con->cnext = nil;
765e96a66cSDavid du Colombier 
7734e04225SDavid du Colombier 	if(cbox.ncon > cbox.maxcon){
7834e04225SDavid du Colombier 		if(con->name != nil)
7934e04225SDavid du Colombier 			vtMemFree(con->name);
8034e04225SDavid du Colombier 		vtLockFree(con->fidlock);
8134e04225SDavid du Colombier 		vtMemFree(con->data);
82c366c7d4SDavid du Colombier 		vtRendezFree(con->wrendez);
8334e04225SDavid du Colombier 		vtLockFree(con->wlock);
84c366c7d4SDavid du Colombier 		vtRendezFree(con->mrendez);
8534e04225SDavid du Colombier 		vtLockFree(con->mlock);
86c366c7d4SDavid du Colombier 		vtRendezFree(con->rendez);
8734e04225SDavid du Colombier 		vtLockFree(con->lock);
8834e04225SDavid du Colombier 		vtMemFree(con);
8934e04225SDavid du Colombier 		cbox.ncon--;
9034e04225SDavid du Colombier 		vtUnlock(cbox.alock);
9134e04225SDavid du Colombier 		return;
9234e04225SDavid du Colombier 	}
9334e04225SDavid du Colombier 	con->anext = cbox.ahead;
9434e04225SDavid du Colombier 	cbox.ahead = con;
9534e04225SDavid du Colombier 	if(con->anext == nil)
9634e04225SDavid du Colombier 		vtWakeup(cbox.arendez);
9734e04225SDavid du Colombier 	vtUnlock(cbox.alock);
9834e04225SDavid du Colombier }
9934e04225SDavid du Colombier 
10034e04225SDavid du Colombier static void
10134e04225SDavid du Colombier msgFree(Msg* m)
10234e04225SDavid du Colombier {
10334e04225SDavid du Colombier 	assert(m->rwnext == nil);
10434e04225SDavid du Colombier 	assert(m->fnext == nil && m->fprev == nil);
10534e04225SDavid du Colombier 
10634e04225SDavid du Colombier 	vtLock(mbox.alock);
10734e04225SDavid du Colombier 	if(mbox.nmsg > mbox.maxmsg){
10834e04225SDavid du Colombier 		vtMemFree(m->data);
10934e04225SDavid du Colombier 		vtMemFree(m);
11034e04225SDavid du Colombier 		mbox.nmsg--;
11134e04225SDavid du Colombier 		vtUnlock(mbox.alock);
11234e04225SDavid du Colombier 		return;
11334e04225SDavid du Colombier 	}
11434e04225SDavid du Colombier 	m->anext = mbox.ahead;
11534e04225SDavid du Colombier 	mbox.ahead = m;
11634e04225SDavid du Colombier 	if(m->anext == nil)
11734e04225SDavid du Colombier 		vtWakeup(mbox.arendez);
11834e04225SDavid du Colombier 	vtUnlock(mbox.alock);
11934e04225SDavid du Colombier }
12034e04225SDavid du Colombier 
12134e04225SDavid du Colombier static Msg*
12234e04225SDavid du Colombier msgAlloc(Con* con)
12334e04225SDavid du Colombier {
12434e04225SDavid du Colombier 	Msg *m;
12534e04225SDavid du Colombier 
12634e04225SDavid du Colombier 	vtLock(mbox.alock);
12734e04225SDavid du Colombier 	while(mbox.ahead == nil){
12834e04225SDavid du Colombier 		if(mbox.nmsg >= mbox.maxmsg){
12934e04225SDavid du Colombier 			mbox.nmsgstarve++;
13034e04225SDavid du Colombier 			vtSleep(mbox.arendez);
13134e04225SDavid du Colombier 			continue;
13234e04225SDavid du Colombier 		}
13334e04225SDavid du Colombier 		m = vtMemAllocZ(sizeof(Msg));
13434e04225SDavid du Colombier 		m->data = vtMemAlloc(mbox.msize);
13534e04225SDavid du Colombier 		m->msize = mbox.msize;
13634e04225SDavid du Colombier 		mbox.nmsg++;
13734e04225SDavid du Colombier 		mbox.ahead = m;
13834e04225SDavid du Colombier 		break;
13934e04225SDavid du Colombier 	}
14034e04225SDavid du Colombier 	m = mbox.ahead;
14134e04225SDavid du Colombier 	mbox.ahead = m->anext;
14234e04225SDavid du Colombier 	m->anext = nil;
14334e04225SDavid du Colombier 	vtUnlock(mbox.alock);
14434e04225SDavid du Colombier 
14534e04225SDavid du Colombier 	m->con = con;
14634e04225SDavid du Colombier 	m->state = MsgR;
14734e04225SDavid du Colombier 
14834e04225SDavid du Colombier 	return m;
14934e04225SDavid du Colombier }
15034e04225SDavid du Colombier 
15134e04225SDavid du Colombier static void
15234e04225SDavid du Colombier msgMunlink(Msg* m)
15334e04225SDavid du Colombier {
15434e04225SDavid du Colombier 	Con *con;
15534e04225SDavid du Colombier 
15634e04225SDavid du Colombier 	con = m->con;
15734e04225SDavid du Colombier 
15834e04225SDavid du Colombier 	if(m->mprev != nil)
15934e04225SDavid du Colombier 		m->mprev->mnext = m->mnext;
16034e04225SDavid du Colombier 	else
16134e04225SDavid du Colombier 		con->mhead = m->mnext;
16234e04225SDavid du Colombier 	if(m->mnext != nil)
16334e04225SDavid du Colombier 		m->mnext->mprev = m->mprev;
16434e04225SDavid du Colombier 	else
16534e04225SDavid du Colombier 		con->mtail = m->mprev;
16634e04225SDavid du Colombier 	m->mprev = m->mnext = nil;
16734e04225SDavid du Colombier }
16834e04225SDavid du Colombier 
16934e04225SDavid du Colombier static void
17034e04225SDavid du Colombier msgUnlinkUnlockAndFree(Msg* m)
17134e04225SDavid du Colombier {
17234e04225SDavid du Colombier 	/*
17334e04225SDavid du Colombier 	 * Unlink the message from the flush and message queues,
17434e04225SDavid du Colombier 	 * unlock the connection message lock and free the message.
17534e04225SDavid du Colombier 	 * Called with con->mlock locked.
17634e04225SDavid du Colombier 	 */
17734e04225SDavid du Colombier 	if(m->fprev != nil)
17834e04225SDavid du Colombier 		m->fprev->fnext = m->fnext;
17934e04225SDavid du Colombier 	if(m->fnext != nil)
18034e04225SDavid du Colombier 		m->fnext->fprev = m->fprev;
18134e04225SDavid du Colombier 	m->fprev = m->fnext = nil;
18234e04225SDavid du Colombier 
18334e04225SDavid du Colombier 	msgMunlink(m);
18434e04225SDavid du Colombier 	vtUnlock(m->con->mlock);
18534e04225SDavid du Colombier 	msgFree(m);
18634e04225SDavid du Colombier }
18734e04225SDavid du Colombier 
18834e04225SDavid du Colombier void
18934e04225SDavid du Colombier msgFlush(Msg* m)
19034e04225SDavid du Colombier {
19134e04225SDavid du Colombier 	Msg *old;
19234e04225SDavid du Colombier 	Con *con;
19334e04225SDavid du Colombier 
19434e04225SDavid du Colombier 	con = m->con;
19534e04225SDavid du Colombier 
196*39734e7eSDavid du Colombier fprint(2, "msgFlush %F\n", &m->t);
197*39734e7eSDavid du Colombier 
19834e04225SDavid du Colombier 	/*
19934e04225SDavid du Colombier 	 * Look for the message to be flushed in the
20034e04225SDavid du Colombier 	 * queue of all messages still on this connection.
20134e04225SDavid du Colombier 	 */
20234e04225SDavid du Colombier 	vtLock(con->mlock);
20334e04225SDavid du Colombier 	for(old = con->mhead; old != nil; old = old->mnext)
20434e04225SDavid du Colombier 		if(old->t.tag == m->t.oldtag)
20534e04225SDavid du Colombier 			break;
20634e04225SDavid du Colombier 	if(old == nil){
20734e04225SDavid du Colombier 		if(Dflag)
20834e04225SDavid du Colombier 			fprint(2, "msgFlush: cannot find %d\n", m->t.oldtag);
20934e04225SDavid du Colombier 		vtUnlock(con->mlock);
21034e04225SDavid du Colombier 		return;
21134e04225SDavid du Colombier 	}
21234e04225SDavid du Colombier 
213*39734e7eSDavid du Colombier fprint(2, "\tmsgFlush found %F\n", &old->t);
214*39734e7eSDavid du Colombier 
21534e04225SDavid du Colombier 	/*
21634e04225SDavid du Colombier 	 * Found it.
21734e04225SDavid du Colombier 	 *
21834e04225SDavid du Colombier 	 * Easy case is no 9P processing done yet,
21934e04225SDavid du Colombier 	 * message is on the read queue.
22034e04225SDavid du Colombier 	 * Mark the message as flushed and let the read
22134e04225SDavid du Colombier 	 * process throw it away after after pulling
22234e04225SDavid du Colombier 	 * it off the read queue.
22334e04225SDavid du Colombier 	 */
22434e04225SDavid du Colombier 	if(old->state == MsgR){
22534e04225SDavid du Colombier 		old->state = MsgF;
22634e04225SDavid du Colombier 		if(Dflag)
22734e04225SDavid du Colombier 			fprint(2, "msgFlush: change %d from MsgR to MsgF\n", m->t.oldtag);
22834e04225SDavid du Colombier 		vtUnlock(con->mlock);
22934e04225SDavid du Colombier 		return;
23034e04225SDavid du Colombier 	}
23134e04225SDavid du Colombier 
23234e04225SDavid du Colombier 	/*
23334e04225SDavid du Colombier 	 * Flushing flushes.
23434e04225SDavid du Colombier 	 * Since they don't affect the server state, flushes
23534e04225SDavid du Colombier 	 * can be deleted when in Msg9 or MsgW state.
23634e04225SDavid du Colombier 	 */
23734e04225SDavid du Colombier 	if(old->t.type == Tflush){
23834e04225SDavid du Colombier 		/*
23934e04225SDavid du Colombier 		 * For Msg9 state, the old message may
24034e04225SDavid du Colombier 		 * or may not be on the write queue.
24134e04225SDavid du Colombier 		 * Mark the message as flushed and let
24234e04225SDavid du Colombier 		 * the write process throw it away after
24334e04225SDavid du Colombier 		 * after pulling it off the write queue.
24434e04225SDavid du Colombier 		 */
24534e04225SDavid du Colombier 		if(old->state == Msg9){
24634e04225SDavid du Colombier 			old->state = MsgF;
24734e04225SDavid du Colombier 			if(Dflag)
24834e04225SDavid du Colombier 				fprint(2, "msgFlush: change %d from Msg9 to MsgF\n", m->t.oldtag);
24934e04225SDavid du Colombier 			vtUnlock(con->mlock);
25034e04225SDavid du Colombier 			return;
25134e04225SDavid du Colombier 		}
25234e04225SDavid du Colombier 		assert(old->state == MsgW);
25334e04225SDavid du Colombier 
25434e04225SDavid du Colombier 		/*
25534e04225SDavid du Colombier 		 * A flush in MsgW state implies it is waiting
25634e04225SDavid du Colombier 		 * for its corresponding old message to be written,
25734e04225SDavid du Colombier 		 * so it can be deleted right here, right now...
25834e04225SDavid du Colombier 		 * right here, right now... right here, right now...
25934e04225SDavid du Colombier 		 * right about now... the funk soul brother.
26034e04225SDavid du Colombier 		 */
26134e04225SDavid du Colombier 		if(Dflag)
26234e04225SDavid du Colombier 			fprint(2, "msgFlush: delete pending flush %F\n", &old->t);
26334e04225SDavid du Colombier 		msgUnlinkUnlockAndFree(old);
26434e04225SDavid du Colombier 		return;
26534e04225SDavid du Colombier 	}
26634e04225SDavid du Colombier 
26734e04225SDavid du Colombier 	/*
26834e04225SDavid du Colombier 	 * Must wait for the old message to be written.
26934e04225SDavid du Colombier 	 * Add m to old's flush queue.
27034e04225SDavid du Colombier 	 * Old is the head of its own flush queue.
27134e04225SDavid du Colombier 	 */
27234e04225SDavid du Colombier 	m->fprev = old;
27334e04225SDavid du Colombier 	m->fnext = old->fnext;
27434e04225SDavid du Colombier 	if(m->fnext)
27534e04225SDavid du Colombier 		m->fnext->fprev = m;
27634e04225SDavid du Colombier 	old->fnext = m;
27734e04225SDavid du Colombier 	if(Dflag)
27834e04225SDavid du Colombier 		fprint(2, "msgFlush: add %d to %d queue\n", m->t.tag, old->t.tag);
27934e04225SDavid du Colombier 	vtUnlock(con->mlock);
2805e96a66cSDavid du Colombier }
2815e96a66cSDavid du Colombier 
2825e96a66cSDavid du Colombier static void
2835e96a66cSDavid du Colombier msgProc(void*)
2845e96a66cSDavid du Colombier {
2855e96a66cSDavid du Colombier 	Msg *m;
2865e96a66cSDavid du Colombier 	char *e;
2875e96a66cSDavid du Colombier 	Con *con;
2885e96a66cSDavid du Colombier 
28934e04225SDavid du Colombier 	vtThreadSetName("msgProc");
2905e96a66cSDavid du Colombier 
29134e04225SDavid du Colombier 	for(;;){
29234e04225SDavid du Colombier 		/*
29334e04225SDavid du Colombier 		 * If surplus to requirements, exit.
29434e04225SDavid du Colombier 		 * If not, wait for and pull a message off
29534e04225SDavid du Colombier 		 * the read queue.
29634e04225SDavid du Colombier 		 */
29734e04225SDavid du Colombier 		vtLock(mbox.rlock);
29834e04225SDavid du Colombier 		if(mbox.nproc > mbox.maxproc){
29934e04225SDavid du Colombier 			mbox.nproc--;
30034e04225SDavid du Colombier 			vtUnlock(mbox.rlock);
30134e04225SDavid du Colombier 			break;
30234e04225SDavid du Colombier 		}
30334e04225SDavid du Colombier 		while(mbox.rhead == nil)
30434e04225SDavid du Colombier 			vtSleep(mbox.rrendez);
30534e04225SDavid du Colombier 		m = mbox.rhead;
30634e04225SDavid du Colombier 		mbox.rhead = m->rwnext;
30734e04225SDavid du Colombier 		m->rwnext = nil;
30834e04225SDavid du Colombier 		vtUnlock(mbox.rlock);
3095e96a66cSDavid du Colombier 
3105e96a66cSDavid du Colombier 		con = m->con;
31134e04225SDavid du Colombier 		e = nil;
3125e96a66cSDavid du Colombier 
31334e04225SDavid du Colombier 		/*
31434e04225SDavid du Colombier 		 * If the message has been flushed before any
31534e04225SDavid du Colombier 		 * 9P processing has started, just throw it away.
31634e04225SDavid du Colombier 		 */
31734e04225SDavid du Colombier 		vtLock(con->mlock);
31834e04225SDavid du Colombier 		if(m->state == MsgF){
31934e04225SDavid du Colombier 			msgUnlinkUnlockAndFree(m);
32034e04225SDavid du Colombier 			continue;
32134e04225SDavid du Colombier 		}
32234e04225SDavid du Colombier 		m->state = Msg9;
32334e04225SDavid du Colombier 		vtUnlock(con->mlock);
32434e04225SDavid du Colombier 
32534e04225SDavid du Colombier 		/*
32634e04225SDavid du Colombier 		 * explain this
32734e04225SDavid du Colombier 		 */
32834e04225SDavid du Colombier 		vtLock(con->lock);
3295e96a66cSDavid du Colombier 		if(m->t.type == Tversion){
3305e96a66cSDavid du Colombier 			con->version = m;
33134e04225SDavid du Colombier 			con->state = ConDown;
33234e04225SDavid du Colombier 			while(con->mhead != m)
33334e04225SDavid du Colombier 				vtSleep(con->rendez);
33434e04225SDavid du Colombier 			assert(con->state == ConDown);
3355e96a66cSDavid du Colombier 			if(con->version == m){
3365e96a66cSDavid du Colombier 				con->version = nil;
33734e04225SDavid du Colombier 				con->state = ConInit;
3385e96a66cSDavid du Colombier 			}
3395e96a66cSDavid du Colombier 			else
3405e96a66cSDavid du Colombier 				e = "Tversion aborted";
3415e96a66cSDavid du Colombier 		}
34234e04225SDavid du Colombier 		else if(con->state != ConUp)
3435e96a66cSDavid du Colombier 			e = "connection not ready";
3445e96a66cSDavid du Colombier 		vtUnlock(con->lock);
3455e96a66cSDavid du Colombier 
3465e96a66cSDavid du Colombier 		/*
3475e96a66cSDavid du Colombier 		 * Dispatch if not error already.
3485e96a66cSDavid du Colombier 		 */
3495e96a66cSDavid du Colombier 		m->r.tag = m->t.tag;
3505e96a66cSDavid du Colombier 		if(e == nil && !(*rFcall[m->t.type])(m))
3515e96a66cSDavid du Colombier 			e = vtGetError();
3525e96a66cSDavid du Colombier 		if(e != nil){
3535e96a66cSDavid du Colombier 			m->r.type = Rerror;
3545e96a66cSDavid du Colombier 			m->r.ename = e;
3555e96a66cSDavid du Colombier 		}
3565e96a66cSDavid du Colombier 		else
3575e96a66cSDavid du Colombier 			m->r.type = m->t.type+1;
3585e96a66cSDavid du Colombier 
35934e04225SDavid du Colombier 
3605e96a66cSDavid du Colombier 		/*
36134e04225SDavid du Colombier 		 * Put the message (with reply) on the
36234e04225SDavid du Colombier 		 * write queue and wakeup the write process.
3635e96a66cSDavid du Colombier 		 */
36434e04225SDavid du Colombier 		vtLock(con->wlock);
36534e04225SDavid du Colombier 		if(con->whead == nil)
36634e04225SDavid du Colombier 			con->whead = m;
3675e96a66cSDavid du Colombier 		else
36834e04225SDavid du Colombier 			con->wtail->rwnext = m;
36934e04225SDavid du Colombier 		con->wtail = m;
37034e04225SDavid du Colombier 		vtWakeup(con->wrendez);
37134e04225SDavid du Colombier 		vtUnlock(con->wlock);
3725e96a66cSDavid du Colombier 	}
3735e96a66cSDavid du Colombier }
3745e96a66cSDavid du Colombier 
3755e96a66cSDavid du Colombier static void
37634e04225SDavid du Colombier msgRead(void* v)
3775e96a66cSDavid du Colombier {
3785e96a66cSDavid du Colombier 	Msg *m;
3795e96a66cSDavid du Colombier 	Con *con;
3805e96a66cSDavid du Colombier 	int eof, fd, n;
3815e96a66cSDavid du Colombier 
38234e04225SDavid du Colombier 	vtThreadSetName("msgRead");
3835e96a66cSDavid du Colombier 
3845e96a66cSDavid du Colombier 	con = v;
3855e96a66cSDavid du Colombier 	fd = con->fd;
3865e96a66cSDavid du Colombier 	eof = 0;
3875e96a66cSDavid du Colombier 
3885e96a66cSDavid du Colombier 	while(!eof){
38934e04225SDavid du Colombier 		m = msgAlloc(con);
3905e96a66cSDavid du Colombier 
3915e96a66cSDavid du Colombier 		while((n = read9pmsg(fd, m->data, con->msize)) == 0)
3925e96a66cSDavid du Colombier 			;
3935e96a66cSDavid du Colombier 		if(n < 0){
3945e96a66cSDavid du Colombier 			m->t.type = Tversion;
3955e96a66cSDavid du Colombier 			m->t.fid = NOFID;
3965e96a66cSDavid du Colombier 			m->t.tag = NOTAG;
3975e96a66cSDavid du Colombier 			m->t.msize = con->msize;
3985e96a66cSDavid du Colombier 			m->t.version = "9PEoF";
3995e96a66cSDavid du Colombier 			eof = 1;
4005e96a66cSDavid du Colombier 		}
4015e96a66cSDavid du Colombier 		else if(convM2S(m->data, n, &m->t) != n){
4025e96a66cSDavid du Colombier 			if(Dflag)
40334e04225SDavid du Colombier 				fprint(2, "msgRead: convM2S error: %s\n",
4045e96a66cSDavid du Colombier 					con->name);
4055e96a66cSDavid du Colombier 			msgFree(m);
4065e96a66cSDavid du Colombier 			continue;
4075e96a66cSDavid du Colombier 		}
4085e96a66cSDavid du Colombier 		if(Dflag)
409*39734e7eSDavid du Colombier 			fprint(2, "msgRead %p: t %F\n", con, &m->t);
4105e96a66cSDavid du Colombier 
41134e04225SDavid du Colombier 		vtLock(con->mlock);
41234e04225SDavid du Colombier 		if(con->mtail != nil){
41334e04225SDavid du Colombier 			m->mprev = con->mtail;
41434e04225SDavid du Colombier 			con->mtail->mnext = m;
41534e04225SDavid du Colombier 		}
41634e04225SDavid du Colombier 		else{
41734e04225SDavid du Colombier 			con->mhead = m;
41834e04225SDavid du Colombier 			m->mprev = nil;
41934e04225SDavid du Colombier 		}
42034e04225SDavid du Colombier 		con->mtail = m;
42134e04225SDavid du Colombier 		vtUnlock(con->mlock);
42234e04225SDavid du Colombier 
42334e04225SDavid du Colombier 		vtLock(mbox.rlock);
42434e04225SDavid du Colombier 		if(mbox.rhead == nil){
42534e04225SDavid du Colombier 			mbox.rhead = m;
42634e04225SDavid du Colombier 			if(!vtWakeup(mbox.rrendez)){
42734e04225SDavid du Colombier 				if(mbox.nproc < mbox.maxproc){
4285e96a66cSDavid du Colombier 					if(vtThread(msgProc, nil) > 0)
4295e96a66cSDavid du Colombier 						mbox.nproc++;
4305e96a66cSDavid du Colombier 				}
43134e04225SDavid du Colombier 				else
43234e04225SDavid du Colombier 					mbox.nprocstarve++;
43334e04225SDavid du Colombier 			}
43434e04225SDavid du Colombier 			/*
43534e04225SDavid du Colombier 			 * don't need this surely?
43634e04225SDavid du Colombier 			vtWakeup(mbox.rrendez);
43734e04225SDavid du Colombier 			 */
4385e96a66cSDavid du Colombier 		}
4395e96a66cSDavid du Colombier 		else
44034e04225SDavid du Colombier 			mbox.rtail->rwnext = m;
44134e04225SDavid du Colombier 		mbox.rtail = m;
44234e04225SDavid du Colombier 		vtUnlock(mbox.rlock);
4435e96a66cSDavid du Colombier 	}
44434e04225SDavid du Colombier }
44534e04225SDavid du Colombier 
44634e04225SDavid du Colombier static int
44734e04225SDavid du Colombier _msgWrite(Msg* m)
44834e04225SDavid du Colombier {
44934e04225SDavid du Colombier 	Con *con;
45034e04225SDavid du Colombier 	int eof, n;
45134e04225SDavid du Colombier 
45234e04225SDavid du Colombier 	con = m->con;
45334e04225SDavid du Colombier 
45434e04225SDavid du Colombier 	/*
45534e04225SDavid du Colombier 	 * An Rflush with a .fprev implies it is on a flush queue waiting for
45634e04225SDavid du Colombier 	 * its corresponding 'oldtag' message to go out first, so punt
45734e04225SDavid du Colombier 	 * until the 'oldtag' message goes out (see below).
45834e04225SDavid du Colombier 	 */
45934e04225SDavid du Colombier 	if(m->r.type == Rflush && m->fprev != nil){
460*39734e7eSDavid du Colombier 		fprint(2, "msgWrite %p: delay r %F\n", con, &m->r);
46134e04225SDavid du Colombier 		return 0;
46234e04225SDavid du Colombier 	}
46334e04225SDavid du Colombier 
46434e04225SDavid du Colombier 	msgMunlink(m);
46534e04225SDavid du Colombier 	vtUnlock(con->mlock);
46634e04225SDavid du Colombier 
46734e04225SDavid du Colombier 	/*
46834e04225SDavid du Colombier 	 * TODO: optimise this copy away somehow for
46934e04225SDavid du Colombier 	 * read, stat, etc.
47034e04225SDavid du Colombier 	 */
47134e04225SDavid du Colombier 	assert(n = convS2M(&m->r, con->data, con->msize));
47234e04225SDavid du Colombier 	if(write(con->fd, con->data, n) != n)
47334e04225SDavid du Colombier 		eof = 1;
47434e04225SDavid du Colombier 	else
47534e04225SDavid du Colombier 		eof = 0;
47634e04225SDavid du Colombier 
47734e04225SDavid du Colombier 	if(Dflag)
478*39734e7eSDavid du Colombier 		fprint(2, "msgWrite %p: r %F\n", con, &m->r);
47934e04225SDavid du Colombier 
48034e04225SDavid du Colombier 	/*
48134e04225SDavid du Colombier 	 * Just wrote a reply. If it has any flushes waiting
48234e04225SDavid du Colombier 	 * for it to have gone out, recurse down the list writing
48334e04225SDavid du Colombier 	 * them out too.
48434e04225SDavid du Colombier 	 */
48534e04225SDavid du Colombier 	vtLock(con->mlock);
48634e04225SDavid du Colombier 	if(m->fnext != nil){
48734e04225SDavid du Colombier 		m->fnext->fprev = nil;
48834e04225SDavid du Colombier 		eof += _msgWrite(m->fnext);
48934e04225SDavid du Colombier 		m->fnext = nil;
49034e04225SDavid du Colombier 	}
49134e04225SDavid du Colombier 	msgFree(m);
49234e04225SDavid du Colombier 
49334e04225SDavid du Colombier 	return eof;
49434e04225SDavid du Colombier }
49534e04225SDavid du Colombier 
49634e04225SDavid du Colombier static void
49734e04225SDavid du Colombier msgWrite(void* v)
49834e04225SDavid du Colombier {
49934e04225SDavid du Colombier 	Msg *m;
50034e04225SDavid du Colombier 	int eof;
50134e04225SDavid du Colombier 	Con *con;
50234e04225SDavid du Colombier 
50334e04225SDavid du Colombier 	vtThreadSetName("msgWrite");
50434e04225SDavid du Colombier 
50534e04225SDavid du Colombier 	con = v;
50634e04225SDavid du Colombier 	if(vtThread(msgRead, con) < 0){
50734e04225SDavid du Colombier 		conFree(con);
50834e04225SDavid du Colombier 		return;
50934e04225SDavid du Colombier 	}
51034e04225SDavid du Colombier 
51134e04225SDavid du Colombier 	for(;;){
51234e04225SDavid du Colombier 		/*
51334e04225SDavid du Colombier 		 * Wait for and pull a message off the write queue.
51434e04225SDavid du Colombier 		 */
51534e04225SDavid du Colombier 		vtLock(con->wlock);
51634e04225SDavid du Colombier 		while(con->whead == nil)
51734e04225SDavid du Colombier 			vtSleep(con->wrendez);
51834e04225SDavid du Colombier 		m = con->whead;
51934e04225SDavid du Colombier 		con->whead = m->rwnext;
52034e04225SDavid du Colombier 		m->rwnext = nil;
52134e04225SDavid du Colombier 		vtUnlock(con->wlock);
52234e04225SDavid du Colombier 
52334e04225SDavid du Colombier 		/*
52434e04225SDavid du Colombier 		 * Throw the message away if it's a flushed flush,
52534e04225SDavid du Colombier 		 * otherwise change its state and try to write it out.
52634e04225SDavid du Colombier 		 */
52734e04225SDavid du Colombier 		vtLock(con->mlock);
52834e04225SDavid du Colombier 		if(m->state == MsgF){
52934e04225SDavid du Colombier 			assert(m->t.type == Tflush);
53034e04225SDavid du Colombier 			msgUnlinkUnlockAndFree(m);
53134e04225SDavid du Colombier 			continue;
53234e04225SDavid du Colombier 		}
53334e04225SDavid du Colombier 		m->state = MsgW;
53434e04225SDavid du Colombier 		eof = _msgWrite(m);
53534e04225SDavid du Colombier 		vtUnlock(con->mlock);
53634e04225SDavid du Colombier 
53734e04225SDavid du Colombier 		vtLock(con->lock);
53834e04225SDavid du Colombier 		if(eof && con->fd >= 0){
53934e04225SDavid du Colombier 			close(con->fd);
54034e04225SDavid du Colombier 			con->fd = -1;
54134e04225SDavid du Colombier 		}
54234e04225SDavid du Colombier 		if(con->state == ConDown)
54334e04225SDavid du Colombier 			vtWakeup(con->rendez);
54434e04225SDavid du Colombier 		if(con->state == ConMoribund && con->mhead == nil){
54534e04225SDavid du Colombier 			vtUnlock(con->lock);
54634e04225SDavid du Colombier 			conFree(con);
54734e04225SDavid du Colombier 			break;
54834e04225SDavid du Colombier 		}
54934e04225SDavid du Colombier 		vtUnlock(con->lock);
55034e04225SDavid du Colombier 	}
5515e96a66cSDavid du Colombier }
5525e96a66cSDavid du Colombier 
5535e96a66cSDavid du Colombier Con*
5545e96a66cSDavid du Colombier conAlloc(int fd, char* name)
5555e96a66cSDavid du Colombier {
5565e96a66cSDavid du Colombier 	Con *con;
5575e96a66cSDavid du Colombier 
55834e04225SDavid du Colombier 	vtLock(cbox.alock);
55934e04225SDavid du Colombier 	while(cbox.ahead == nil){
56034e04225SDavid du Colombier 		if(cbox.ncon >= cbox.maxcon){
56134e04225SDavid du Colombier 			cbox.nconstarve++;
56234e04225SDavid du Colombier 			vtSleep(cbox.arendez);
56334e04225SDavid du Colombier 			continue;
5645e96a66cSDavid du Colombier 		}
5655e96a66cSDavid du Colombier 		con = vtMemAllocZ(sizeof(Con));
5665e96a66cSDavid du Colombier 		con->lock = vtLockAlloc();
56734e04225SDavid du Colombier 		con->rendez = vtRendezAlloc(con->lock);
5685e96a66cSDavid du Colombier 		con->data = vtMemAlloc(cbox.msize);
5695e96a66cSDavid du Colombier 		con->msize = cbox.msize;
57034e04225SDavid du Colombier 		con->alock = vtLockAlloc();
57134e04225SDavid du Colombier 		con->mlock = vtLockAlloc();
57234e04225SDavid du Colombier 		con->mrendez = vtRendezAlloc(con->mlock);
57334e04225SDavid du Colombier 		con->wlock = vtLockAlloc();
57434e04225SDavid du Colombier 		con->wrendez = vtRendezAlloc(con->wlock);
5755e96a66cSDavid du Colombier 		con->fidlock = vtLockAlloc();
57634e04225SDavid du Colombier 
57734e04225SDavid du Colombier 		cbox.ncon++;
57834e04225SDavid du Colombier 		cbox.ahead = con;
57934e04225SDavid du Colombier 		break;
5805e96a66cSDavid du Colombier 	}
58134e04225SDavid du Colombier 	con = cbox.ahead;
58234e04225SDavid du Colombier 	cbox.ahead = con->anext;
58334e04225SDavid du Colombier 	con->anext = nil;
58434e04225SDavid du Colombier 
58534e04225SDavid du Colombier 	if(cbox.ctail != nil){
58634e04225SDavid du Colombier 		con->cprev = cbox.ctail;
58734e04225SDavid du Colombier 		cbox.ctail->cnext = con;
58834e04225SDavid du Colombier 	}
58934e04225SDavid du Colombier 	else{
59034e04225SDavid du Colombier 		cbox.chead = con;
59134e04225SDavid du Colombier 		con->cprev = nil;
59234e04225SDavid du Colombier 	}
59334e04225SDavid du Colombier 	cbox.ctail = con;
59434e04225SDavid du Colombier 
5955e96a66cSDavid du Colombier 	assert(con->mhead == nil);
59634e04225SDavid du Colombier 	assert(con->whead == nil);
5975e96a66cSDavid du Colombier 	assert(con->fhead == nil);
5985e96a66cSDavid du Colombier 	assert(con->nfid == 0);
5995e96a66cSDavid du Colombier 
60034e04225SDavid du Colombier 	con->state = ConNew;
6015e96a66cSDavid du Colombier 	con->fd = fd;
6025e96a66cSDavid du Colombier 	if(con->name != nil){
6035e96a66cSDavid du Colombier 		vtMemFree(con->name);
6045e96a66cSDavid du Colombier 		con->name = nil;
6055e96a66cSDavid du Colombier 	}
6065e96a66cSDavid du Colombier 	if(name != nil)
6075e96a66cSDavid du Colombier 		con->name = vtStrDup(name);
60834e04225SDavid du Colombier 	else
60934e04225SDavid du Colombier 		con->name = vtStrDup("unknown");
6105e96a66cSDavid du Colombier 	con->aok = 0;
61134e04225SDavid du Colombier 	vtUnlock(cbox.alock);
6125e96a66cSDavid du Colombier 
61334e04225SDavid du Colombier 	if(vtThread(msgWrite, con) < 0){
6145e96a66cSDavid du Colombier 		conFree(con);
6155e96a66cSDavid du Colombier 		return nil;
6165e96a66cSDavid du Colombier 	}
6175e96a66cSDavid du Colombier 
6185e96a66cSDavid du Colombier 	return con;
6195e96a66cSDavid du Colombier }
6205e96a66cSDavid du Colombier 
6215e96a66cSDavid du Colombier static int
6225e96a66cSDavid du Colombier cmdMsg(int argc, char* argv[])
6235e96a66cSDavid du Colombier {
6245e96a66cSDavid du Colombier 	char *p;
6255e96a66cSDavid du Colombier 	char *usage = "usage: msg [-m nmsg] [-p nproc]";
62634e04225SDavid du Colombier 	int maxmsg, nmsg, nmsgstarve, maxproc, nproc, nprocstarve;
6275e96a66cSDavid du Colombier 
6285e96a66cSDavid du Colombier 	maxmsg = maxproc = 0;
6295e96a66cSDavid du Colombier 
6305e96a66cSDavid du Colombier 	ARGBEGIN{
6315e96a66cSDavid du Colombier 	default:
6325e96a66cSDavid du Colombier 		return cliError(usage);
6335e96a66cSDavid du Colombier 	case 'm':
6345e96a66cSDavid du Colombier 		p = ARGF();
6355e96a66cSDavid du Colombier 		if(p == nil)
6365e96a66cSDavid du Colombier 			return cliError(usage);
6375e96a66cSDavid du Colombier 		maxmsg = strtol(argv[0], &p, 0);
6385e96a66cSDavid du Colombier 		if(maxmsg <= 0 || p == argv[0] || *p != '\0')
6395e96a66cSDavid du Colombier 			return cliError(usage);
6405e96a66cSDavid du Colombier 		break;
6415e96a66cSDavid du Colombier 	case 'p':
6425e96a66cSDavid du Colombier 		p = ARGF();
6435e96a66cSDavid du Colombier 		if(p == nil)
6445e96a66cSDavid du Colombier 			return cliError(usage);
6455e96a66cSDavid du Colombier 		maxproc = strtol(argv[0], &p, 0);
6465e96a66cSDavid du Colombier 		if(maxproc <= 0 || p == argv[0] || *p != '\0')
6475e96a66cSDavid du Colombier 			return cliError(usage);
6485e96a66cSDavid du Colombier 		break;
6495e96a66cSDavid du Colombier 	}ARGEND
6505e96a66cSDavid du Colombier 	if(argc)
6515e96a66cSDavid du Colombier 		return cliError(usage);
6525e96a66cSDavid du Colombier 
65334e04225SDavid du Colombier 	vtLock(mbox.alock);
6545e96a66cSDavid du Colombier 	if(maxmsg)
6555e96a66cSDavid du Colombier 		mbox.maxmsg = maxmsg;
6565e96a66cSDavid du Colombier 	maxmsg = mbox.maxmsg;
65734e04225SDavid du Colombier 	nmsg = mbox.nmsg;
65834e04225SDavid du Colombier 	nmsgstarve = mbox.nmsgstarve;
65934e04225SDavid du Colombier 	vtUnlock(mbox.alock);
66034e04225SDavid du Colombier 
66134e04225SDavid du Colombier 	vtLock(mbox.rlock);
6625e96a66cSDavid du Colombier 	if(maxproc)
6635e96a66cSDavid du Colombier 		mbox.maxproc = maxproc;
6645e96a66cSDavid du Colombier 	maxproc = mbox.maxproc;
66534e04225SDavid du Colombier 	nproc = mbox.nproc;
66634e04225SDavid du Colombier 	nprocstarve = mbox.nprocstarve;
66734e04225SDavid du Colombier 	vtUnlock(mbox.rlock);
6685e96a66cSDavid du Colombier 
6695e96a66cSDavid du Colombier 	consPrint("\tmsg -m %d -p %d\n", maxmsg, maxproc);
67034e04225SDavid du Colombier 	consPrint("\tnmsg %d nmsgstarve %d nproc %d nprocstarve %d\n",
67134e04225SDavid du Colombier 		nmsg, nmsgstarve, nproc, nprocstarve);
6725e96a66cSDavid du Colombier 
6735e96a66cSDavid du Colombier 	return 1;
6745e96a66cSDavid du Colombier }
6755e96a66cSDavid du Colombier 
67681cf8742SDavid du Colombier static int
67781cf8742SDavid du Colombier scmp(Fid *a, Fid *b)
67881cf8742SDavid du Colombier {
67981cf8742SDavid du Colombier 	if(a == 0)
68081cf8742SDavid du Colombier 		return 1;
68181cf8742SDavid du Colombier 	if(b == 0)
68281cf8742SDavid du Colombier 		return -1;
68381cf8742SDavid du Colombier 	return strcmp(a->uname, b->uname);
68481cf8742SDavid du Colombier }
68581cf8742SDavid du Colombier 
68681cf8742SDavid du Colombier static Fid*
68781cf8742SDavid du Colombier fidMerge(Fid *a, Fid *b)
68881cf8742SDavid du Colombier {
68981cf8742SDavid du Colombier 	Fid *s, **l;
69081cf8742SDavid du Colombier 
69181cf8742SDavid du Colombier 	l = &s;
69281cf8742SDavid du Colombier 	while(a || b){
69381cf8742SDavid du Colombier 		if(scmp(a, b) < 0){
69481cf8742SDavid du Colombier 			*l = a;
69581cf8742SDavid du Colombier 			l = &a->sort;
69681cf8742SDavid du Colombier 			a = a->sort;
69781cf8742SDavid du Colombier 		}else{
69881cf8742SDavid du Colombier 			*l = b;
69981cf8742SDavid du Colombier 			l = &b->sort;
70081cf8742SDavid du Colombier 			b = b->sort;
70181cf8742SDavid du Colombier 		}
70281cf8742SDavid du Colombier 	}
70381cf8742SDavid du Colombier 	*l = 0;
70481cf8742SDavid du Colombier 	return s;
70581cf8742SDavid du Colombier }
70681cf8742SDavid du Colombier 
70781cf8742SDavid du Colombier static Fid*
70881cf8742SDavid du Colombier fidMergeSort(Fid *f)
70981cf8742SDavid du Colombier {
71081cf8742SDavid du Colombier 	int delay;
71181cf8742SDavid du Colombier 	Fid *a, *b;
71281cf8742SDavid du Colombier 
71381cf8742SDavid du Colombier 	if(f == nil)
71481cf8742SDavid du Colombier 		return nil;
71581cf8742SDavid du Colombier 	if(f->sort == nil)
71681cf8742SDavid du Colombier 		return f;
71781cf8742SDavid du Colombier 
71881cf8742SDavid du Colombier 	a = b = f;
71981cf8742SDavid du Colombier 	delay = 1;
72081cf8742SDavid du Colombier 	while(a && b){
72181cf8742SDavid du Colombier 		if(delay)	/* easy way to handle 2-element list */
72281cf8742SDavid du Colombier 			delay = 0;
72381cf8742SDavid du Colombier 		else
72481cf8742SDavid du Colombier 			a = a->sort;
72581cf8742SDavid du Colombier 		if(b = b->sort)
72681cf8742SDavid du Colombier 			b = b->sort;
72781cf8742SDavid du Colombier 	}
72881cf8742SDavid du Colombier 
72981cf8742SDavid du Colombier 	b = a->sort;
73081cf8742SDavid du Colombier 	a->sort = nil;
73181cf8742SDavid du Colombier 
73281cf8742SDavid du Colombier 	a = fidMergeSort(f);
73381cf8742SDavid du Colombier 	b = fidMergeSort(b);
73481cf8742SDavid du Colombier 
73581cf8742SDavid du Colombier 	return fidMerge(a, b);
73681cf8742SDavid du Colombier }
73781cf8742SDavid du Colombier 
73881cf8742SDavid du Colombier static int
73981cf8742SDavid du Colombier cmdWho(int argc, char* argv[])
74081cf8742SDavid du Colombier {
74181cf8742SDavid du Colombier 	char *usage = "usage: who";
74281cf8742SDavid du Colombier 	int i;
74381cf8742SDavid du Colombier 	Con *con;
74481cf8742SDavid du Colombier 	Fid *fid, *last;
74581cf8742SDavid du Colombier 
74681cf8742SDavid du Colombier 	ARGBEGIN{
74781cf8742SDavid du Colombier 	default:
74881cf8742SDavid du Colombier 		return cliError(usage);
74981cf8742SDavid du Colombier 	}ARGEND
75081cf8742SDavid du Colombier 
75181cf8742SDavid du Colombier 	if(argc > 0)
75281cf8742SDavid du Colombier 		return cliError(usage);
75381cf8742SDavid du Colombier 
75481cf8742SDavid du Colombier 	vtRLock(cbox.clock);
75581cf8742SDavid du Colombier 	for(con=cbox.chead; con; con=con->cnext){
75681cf8742SDavid du Colombier 		consPrint("\t%q:", con->name);
75781cf8742SDavid du Colombier 		vtLock(con->fidlock);
75881cf8742SDavid du Colombier 		last = nil;
75981cf8742SDavid du Colombier 		for(i=0; i<NFidHash; i++)
76081cf8742SDavid du Colombier 			for(fid=con->fidhash[i]; fid; fid=fid->hash)
76181cf8742SDavid du Colombier 				if(fid->fidno != NOFID && fid->uname){
76281cf8742SDavid du Colombier 					fid->sort = last;
76381cf8742SDavid du Colombier 					last = fid;
76481cf8742SDavid du Colombier 				}
76581cf8742SDavid du Colombier 		fid = fidMergeSort(last);
76681cf8742SDavid du Colombier 		last = nil;
76781cf8742SDavid du Colombier 		for(; fid; last=fid, fid=fid->sort)
76881cf8742SDavid du Colombier 			if(last==nil || strcmp(fid->uname, last->uname) != 0)
76981cf8742SDavid du Colombier 				consPrint(" %q", fid->uname);
77081cf8742SDavid du Colombier 		vtUnlock(con->fidlock);
77181cf8742SDavid du Colombier 		consPrint("\n");
77281cf8742SDavid du Colombier 	}
77381cf8742SDavid du Colombier 	vtRUnlock(cbox.clock);
77481cf8742SDavid du Colombier 	return 1;
77581cf8742SDavid du Colombier }
77681cf8742SDavid du Colombier 
7775e96a66cSDavid du Colombier void
77834e04225SDavid du Colombier msgInit(void)
7795e96a66cSDavid du Colombier {
78034e04225SDavid du Colombier 	mbox.alock = vtLockAlloc();
78134e04225SDavid du Colombier 	mbox.arendez = vtRendezAlloc(mbox.alock);
78234e04225SDavid du Colombier 
78334e04225SDavid du Colombier 	mbox.rlock = vtLockAlloc();
78434e04225SDavid du Colombier 	mbox.rrendez = vtRendezAlloc(mbox.rlock);
7855e96a66cSDavid du Colombier 
7865e96a66cSDavid du Colombier 	mbox.maxmsg = NMsgInit;
7875e96a66cSDavid du Colombier 	mbox.maxproc = NMsgProcInit;
7885e96a66cSDavid du Colombier 	mbox.msize = NMsizeInit;
7895e96a66cSDavid du Colombier 
7905e96a66cSDavid du Colombier 	cliAddCmd("msg", cmdMsg);
79134e04225SDavid du Colombier }
7925e96a66cSDavid du Colombier 
79334e04225SDavid du Colombier static int
79434e04225SDavid du Colombier cmdCon(int argc, char* argv[])
79534e04225SDavid du Colombier {
79634e04225SDavid du Colombier 	char *p;
79734e04225SDavid du Colombier 	Con *con;
79834e04225SDavid du Colombier 	char *usage = "usage: con [-m ncon]";
79934e04225SDavid du Colombier 	int maxcon, ncon, nconstarve;
80034e04225SDavid du Colombier 
80134e04225SDavid du Colombier 	maxcon = 0;
80234e04225SDavid du Colombier 
80334e04225SDavid du Colombier 	ARGBEGIN{
80434e04225SDavid du Colombier 	default:
80534e04225SDavid du Colombier 		return cliError(usage);
80634e04225SDavid du Colombier 	case 'm':
80734e04225SDavid du Colombier 		p = ARGF();
80834e04225SDavid du Colombier 		if(p == nil)
80934e04225SDavid du Colombier 			return cliError(usage);
81034e04225SDavid du Colombier 		maxcon = strtol(argv[0], &p, 0);
81134e04225SDavid du Colombier 		if(maxcon <= 0 || p == argv[0] || *p != '\0')
81234e04225SDavid du Colombier 			return cliError(usage);
81334e04225SDavid du Colombier 		break;
81434e04225SDavid du Colombier 	}ARGEND
81534e04225SDavid du Colombier 	if(argc)
81634e04225SDavid du Colombier 		return cliError(usage);
81734e04225SDavid du Colombier 
81834e04225SDavid du Colombier 	vtLock(cbox.clock);
81934e04225SDavid du Colombier 	if(maxcon)
82034e04225SDavid du Colombier 		cbox.maxcon = maxcon;
82134e04225SDavid du Colombier 	maxcon = cbox.maxcon;
82234e04225SDavid du Colombier 	ncon = cbox.ncon;
82334e04225SDavid du Colombier 	nconstarve = cbox.nconstarve;
82434e04225SDavid du Colombier 	vtUnlock(cbox.clock);
82534e04225SDavid du Colombier 
82634e04225SDavid du Colombier 	consPrint("\tcon -m %d\n", maxcon);
82734e04225SDavid du Colombier 	consPrint("\tncon %d nconstarve %d\n", ncon, nconstarve);
82834e04225SDavid du Colombier 
82934e04225SDavid du Colombier 	vtRLock(cbox.clock);
83034e04225SDavid du Colombier 	for(con = cbox.chead; con != nil; con = con->cnext){
83134e04225SDavid du Colombier 		consPrint("\t%s\n", con->name);
83234e04225SDavid du Colombier 	}
83334e04225SDavid du Colombier 	vtRUnlock(cbox.clock);
83434e04225SDavid du Colombier 
83534e04225SDavid du Colombier 	return 1;
83634e04225SDavid du Colombier }
83734e04225SDavid du Colombier 
83834e04225SDavid du Colombier void
83934e04225SDavid du Colombier conInit(void)
84034e04225SDavid du Colombier {
84134e04225SDavid du Colombier 	cbox.alock = vtLockAlloc();
84234e04225SDavid du Colombier 	cbox.arendez = vtRendezAlloc(cbox.alock);
84334e04225SDavid du Colombier 
84434e04225SDavid du Colombier 	cbox.clock = vtLockAlloc();
84534e04225SDavid du Colombier 
84634e04225SDavid du Colombier 	cbox.maxcon = NConInit;
8475e96a66cSDavid du Colombier 	cbox.msize = NMsizeInit;
84834e04225SDavid du Colombier 
84934e04225SDavid du Colombier 	cliAddCmd("con", cmdCon);
85081cf8742SDavid du Colombier 	cliAddCmd("who", cmdWho);
8515e96a66cSDavid du Colombier }
852