xref: /plan9-contrib/sys/src/cmd/fossil/9proc.c (revision 81cf874224a4620dc66ce2d894718f8bb735f81d)
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);
8234e04225SDavid du Colombier 		vtRendezAlloc(con->wrendez);
8334e04225SDavid du Colombier 		vtLockFree(con->wlock);
8434e04225SDavid du Colombier 		vtRendezAlloc(con->mrendez);
8534e04225SDavid du Colombier 		vtLockFree(con->mlock);
8634e04225SDavid du Colombier 		vtRendezAlloc(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 
19634e04225SDavid du Colombier 	/*
19734e04225SDavid du Colombier 	 * Look for the message to be flushed in the
19834e04225SDavid du Colombier 	 * queue of all messages still on this connection.
19934e04225SDavid du Colombier 	 */
20034e04225SDavid du Colombier 	vtLock(con->mlock);
20134e04225SDavid du Colombier 	for(old = con->mhead; old != nil; old = old->mnext)
20234e04225SDavid du Colombier 		if(old->t.tag == m->t.oldtag)
20334e04225SDavid du Colombier 			break;
20434e04225SDavid du Colombier 	if(old == nil){
20534e04225SDavid du Colombier 		if(Dflag)
20634e04225SDavid du Colombier 			fprint(2, "msgFlush: cannot find %d\n", m->t.oldtag);
20734e04225SDavid du Colombier 		vtUnlock(con->mlock);
20834e04225SDavid du Colombier 		return;
20934e04225SDavid du Colombier 	}
21034e04225SDavid du Colombier 
21134e04225SDavid du Colombier 	/*
21234e04225SDavid du Colombier 	 * Found it.
21334e04225SDavid du Colombier 	 *
21434e04225SDavid du Colombier 	 * Easy case is no 9P processing done yet,
21534e04225SDavid du Colombier 	 * message is on the read queue.
21634e04225SDavid du Colombier 	 * Mark the message as flushed and let the read
21734e04225SDavid du Colombier 	 * process throw it away after after pulling
21834e04225SDavid du Colombier 	 * it off the read queue.
21934e04225SDavid du Colombier 	 */
22034e04225SDavid du Colombier 	if(old->state == MsgR){
22134e04225SDavid du Colombier 		old->state = MsgF;
22234e04225SDavid du Colombier 		if(Dflag)
22334e04225SDavid du Colombier 			fprint(2, "msgFlush: change %d from MsgR to MsgF\n", m->t.oldtag);
22434e04225SDavid du Colombier 		vtUnlock(con->mlock);
22534e04225SDavid du Colombier 		return;
22634e04225SDavid du Colombier 	}
22734e04225SDavid du Colombier 
22834e04225SDavid du Colombier 	/*
22934e04225SDavid du Colombier 	 * Flushing flushes.
23034e04225SDavid du Colombier 	 * Since they don't affect the server state, flushes
23134e04225SDavid du Colombier 	 * can be deleted when in Msg9 or MsgW state.
23234e04225SDavid du Colombier 	 */
23334e04225SDavid du Colombier 	if(old->t.type == Tflush){
23434e04225SDavid du Colombier 		/*
23534e04225SDavid du Colombier 		 * For Msg9 state, the old message may
23634e04225SDavid du Colombier 		 * or may not be on the write queue.
23734e04225SDavid du Colombier 		 * Mark the message as flushed and let
23834e04225SDavid du Colombier 		 * the write process throw it away after
23934e04225SDavid du Colombier 		 * after pulling it off the write queue.
24034e04225SDavid du Colombier 		 */
24134e04225SDavid du Colombier 		if(old->state == Msg9){
24234e04225SDavid du Colombier 			old->state = MsgF;
24334e04225SDavid du Colombier 			if(Dflag)
24434e04225SDavid du Colombier 				fprint(2, "msgFlush: change %d from Msg9 to MsgF\n", m->t.oldtag);
24534e04225SDavid du Colombier 			vtUnlock(con->mlock);
24634e04225SDavid du Colombier 			return;
24734e04225SDavid du Colombier 		}
24834e04225SDavid du Colombier 		assert(old->state == MsgW);
24934e04225SDavid du Colombier 
25034e04225SDavid du Colombier 		/*
25134e04225SDavid du Colombier 		 * A flush in MsgW state implies it is waiting
25234e04225SDavid du Colombier 		 * for its corresponding old message to be written,
25334e04225SDavid du Colombier 		 * so it can be deleted right here, right now...
25434e04225SDavid du Colombier 		 * right here, right now... right here, right now...
25534e04225SDavid du Colombier 		 * right about now... the funk soul brother.
25634e04225SDavid du Colombier 		 */
25734e04225SDavid du Colombier 		if(Dflag)
25834e04225SDavid du Colombier 			fprint(2, "msgFlush: delete pending flush %F\n", &old->t);
25934e04225SDavid du Colombier 		msgUnlinkUnlockAndFree(old);
26034e04225SDavid du Colombier 		return;
26134e04225SDavid du Colombier 	}
26234e04225SDavid du Colombier 
26334e04225SDavid du Colombier 	/*
26434e04225SDavid du Colombier 	 * Must wait for the old message to be written.
26534e04225SDavid du Colombier 	 * Add m to old's flush queue.
26634e04225SDavid du Colombier 	 * Old is the head of its own flush queue.
26734e04225SDavid du Colombier 	 */
26834e04225SDavid du Colombier 	m->fprev = old;
26934e04225SDavid du Colombier 	m->fnext = old->fnext;
27034e04225SDavid du Colombier 	if(m->fnext)
27134e04225SDavid du Colombier 		m->fnext->fprev = m;
27234e04225SDavid du Colombier 	old->fnext = m;
27334e04225SDavid du Colombier 	if(Dflag)
27434e04225SDavid du Colombier 		fprint(2, "msgFlush: add %d to %d queue\n", m->t.tag, old->t.tag);
27534e04225SDavid du Colombier 	vtUnlock(con->mlock);
2765e96a66cSDavid du Colombier }
2775e96a66cSDavid du Colombier 
2785e96a66cSDavid du Colombier static void
2795e96a66cSDavid du Colombier msgProc(void*)
2805e96a66cSDavid du Colombier {
2815e96a66cSDavid du Colombier 	Msg *m;
2825e96a66cSDavid du Colombier 	char *e;
2835e96a66cSDavid du Colombier 	Con *con;
2845e96a66cSDavid du Colombier 
28534e04225SDavid du Colombier 	vtThreadSetName("msgProc");
2865e96a66cSDavid du Colombier 
28734e04225SDavid du Colombier 	for(;;){
28834e04225SDavid du Colombier 		/*
28934e04225SDavid du Colombier 		 * If surplus to requirements, exit.
29034e04225SDavid du Colombier 		 * If not, wait for and pull a message off
29134e04225SDavid du Colombier 		 * the read queue.
29234e04225SDavid du Colombier 		 */
29334e04225SDavid du Colombier 		vtLock(mbox.rlock);
29434e04225SDavid du Colombier 		if(mbox.nproc > mbox.maxproc){
29534e04225SDavid du Colombier 			mbox.nproc--;
29634e04225SDavid du Colombier 			vtUnlock(mbox.rlock);
29734e04225SDavid du Colombier 			break;
29834e04225SDavid du Colombier 		}
29934e04225SDavid du Colombier 		while(mbox.rhead == nil)
30034e04225SDavid du Colombier 			vtSleep(mbox.rrendez);
30134e04225SDavid du Colombier 		m = mbox.rhead;
30234e04225SDavid du Colombier 		mbox.rhead = m->rwnext;
30334e04225SDavid du Colombier 		m->rwnext = nil;
30434e04225SDavid du Colombier 		vtUnlock(mbox.rlock);
3055e96a66cSDavid du Colombier 
3065e96a66cSDavid du Colombier 		con = m->con;
30734e04225SDavid du Colombier 		e = nil;
3085e96a66cSDavid du Colombier 
30934e04225SDavid du Colombier 		/*
31034e04225SDavid du Colombier 		 * If the message has been flushed before any
31134e04225SDavid du Colombier 		 * 9P processing has started, just throw it away.
31234e04225SDavid du Colombier 		 */
31334e04225SDavid du Colombier 		vtLock(con->mlock);
31434e04225SDavid du Colombier 		if(m->state == MsgF){
31534e04225SDavid du Colombier 			msgUnlinkUnlockAndFree(m);
31634e04225SDavid du Colombier 			continue;
31734e04225SDavid du Colombier 		}
31834e04225SDavid du Colombier 		m->state = Msg9;
31934e04225SDavid du Colombier 		vtUnlock(con->mlock);
32034e04225SDavid du Colombier 
32134e04225SDavid du Colombier 		/*
32234e04225SDavid du Colombier 		 * explain this
32334e04225SDavid du Colombier 		 */
32434e04225SDavid du Colombier 		vtLock(con->lock);
3255e96a66cSDavid du Colombier 		if(m->t.type == Tversion){
3265e96a66cSDavid du Colombier 			con->version = m;
32734e04225SDavid du Colombier 			con->state = ConDown;
32834e04225SDavid du Colombier 			while(con->mhead != m)
32934e04225SDavid du Colombier 				vtSleep(con->rendez);
33034e04225SDavid du Colombier 			assert(con->state == ConDown);
3315e96a66cSDavid du Colombier 			if(con->version == m){
3325e96a66cSDavid du Colombier 				con->version = nil;
33334e04225SDavid du Colombier 				con->state = ConInit;
3345e96a66cSDavid du Colombier 			}
3355e96a66cSDavid du Colombier 			else
3365e96a66cSDavid du Colombier 				e = "Tversion aborted";
3375e96a66cSDavid du Colombier 		}
33834e04225SDavid du Colombier 		else if(con->state != ConUp)
3395e96a66cSDavid du Colombier 			e = "connection not ready";
3405e96a66cSDavid du Colombier 		vtUnlock(con->lock);
3415e96a66cSDavid du Colombier 
3425e96a66cSDavid du Colombier 		/*
3435e96a66cSDavid du Colombier 		 * Dispatch if not error already.
3445e96a66cSDavid du Colombier 		 */
3455e96a66cSDavid du Colombier 		m->r.tag = m->t.tag;
3465e96a66cSDavid du Colombier 		if(e == nil && !(*rFcall[m->t.type])(m))
3475e96a66cSDavid du Colombier 			e = vtGetError();
3485e96a66cSDavid du Colombier 		if(e != nil){
3495e96a66cSDavid du Colombier 			m->r.type = Rerror;
3505e96a66cSDavid du Colombier 			m->r.ename = e;
3515e96a66cSDavid du Colombier 		}
3525e96a66cSDavid du Colombier 		else
3535e96a66cSDavid du Colombier 			m->r.type = m->t.type+1;
3545e96a66cSDavid du Colombier 
35534e04225SDavid du Colombier 
3565e96a66cSDavid du Colombier 		/*
35734e04225SDavid du Colombier 		 * Put the message (with reply) on the
35834e04225SDavid du Colombier 		 * write queue and wakeup the write process.
3595e96a66cSDavid du Colombier 		 */
36034e04225SDavid du Colombier 		vtLock(con->wlock);
36134e04225SDavid du Colombier 		if(con->whead == nil)
36234e04225SDavid du Colombier 			con->whead = m;
3635e96a66cSDavid du Colombier 		else
36434e04225SDavid du Colombier 			con->wtail->rwnext = m;
36534e04225SDavid du Colombier 		con->wtail = m;
36634e04225SDavid du Colombier 		vtWakeup(con->wrendez);
36734e04225SDavid du Colombier 		vtUnlock(con->wlock);
3685e96a66cSDavid du Colombier 	}
3695e96a66cSDavid du Colombier }
3705e96a66cSDavid du Colombier 
3715e96a66cSDavid du Colombier static void
37234e04225SDavid du Colombier msgRead(void* v)
3735e96a66cSDavid du Colombier {
3745e96a66cSDavid du Colombier 	Msg *m;
3755e96a66cSDavid du Colombier 	Con *con;
3765e96a66cSDavid du Colombier 	int eof, fd, n;
3775e96a66cSDavid du Colombier 
37834e04225SDavid du Colombier 	vtThreadSetName("msgRead");
3795e96a66cSDavid du Colombier 
3805e96a66cSDavid du Colombier 	con = v;
3815e96a66cSDavid du Colombier 	fd = con->fd;
3825e96a66cSDavid du Colombier 	eof = 0;
3835e96a66cSDavid du Colombier 
3845e96a66cSDavid du Colombier 	while(!eof){
38534e04225SDavid du Colombier 		m = msgAlloc(con);
3865e96a66cSDavid du Colombier 
3875e96a66cSDavid du Colombier 		while((n = read9pmsg(fd, m->data, con->msize)) == 0)
3885e96a66cSDavid du Colombier 			;
3895e96a66cSDavid du Colombier 		if(n < 0){
3905e96a66cSDavid du Colombier 			m->t.type = Tversion;
3915e96a66cSDavid du Colombier 			m->t.fid = NOFID;
3925e96a66cSDavid du Colombier 			m->t.tag = NOTAG;
3935e96a66cSDavid du Colombier 			m->t.msize = con->msize;
3945e96a66cSDavid du Colombier 			m->t.version = "9PEoF";
3955e96a66cSDavid du Colombier 			eof = 1;
3965e96a66cSDavid du Colombier 		}
3975e96a66cSDavid du Colombier 		else if(convM2S(m->data, n, &m->t) != n){
3985e96a66cSDavid du Colombier 			if(Dflag)
39934e04225SDavid du Colombier 				fprint(2, "msgRead: convM2S error: %s\n",
4005e96a66cSDavid du Colombier 					con->name);
4015e96a66cSDavid du Colombier 			msgFree(m);
4025e96a66cSDavid du Colombier 			continue;
4035e96a66cSDavid du Colombier 		}
4045e96a66cSDavid du Colombier 		if(Dflag)
40534e04225SDavid du Colombier 			fprint(2, "msgRead: t %F\n", &m->t);
4065e96a66cSDavid du Colombier 
40734e04225SDavid du Colombier 		vtLock(con->mlock);
40834e04225SDavid du Colombier 		if(con->mtail != nil){
40934e04225SDavid du Colombier 			m->mprev = con->mtail;
41034e04225SDavid du Colombier 			con->mtail->mnext = m;
41134e04225SDavid du Colombier 		}
41234e04225SDavid du Colombier 		else{
41334e04225SDavid du Colombier 			con->mhead = m;
41434e04225SDavid du Colombier 			m->mprev = nil;
41534e04225SDavid du Colombier 		}
41634e04225SDavid du Colombier 		con->mtail = m;
41734e04225SDavid du Colombier 		vtUnlock(con->mlock);
41834e04225SDavid du Colombier 
41934e04225SDavid du Colombier 		vtLock(mbox.rlock);
42034e04225SDavid du Colombier 		if(mbox.rhead == nil){
42134e04225SDavid du Colombier 			mbox.rhead = m;
42234e04225SDavid du Colombier 			if(!vtWakeup(mbox.rrendez)){
42334e04225SDavid du Colombier 				if(mbox.nproc < mbox.maxproc){
4245e96a66cSDavid du Colombier 					if(vtThread(msgProc, nil) > 0)
4255e96a66cSDavid du Colombier 						mbox.nproc++;
4265e96a66cSDavid du Colombier 				}
42734e04225SDavid du Colombier 				else
42834e04225SDavid du Colombier 					mbox.nprocstarve++;
42934e04225SDavid du Colombier 			}
43034e04225SDavid du Colombier 			/*
43134e04225SDavid du Colombier 			 * don't need this surely?
43234e04225SDavid du Colombier 			vtWakeup(mbox.rrendez);
43334e04225SDavid du Colombier 			 */
4345e96a66cSDavid du Colombier 		}
4355e96a66cSDavid du Colombier 		else
43634e04225SDavid du Colombier 			mbox.rtail->rwnext = m;
43734e04225SDavid du Colombier 		mbox.rtail = m;
43834e04225SDavid du Colombier 		vtUnlock(mbox.rlock);
4395e96a66cSDavid du Colombier 	}
44034e04225SDavid du Colombier }
44134e04225SDavid du Colombier 
44234e04225SDavid du Colombier static int
44334e04225SDavid du Colombier _msgWrite(Msg* m)
44434e04225SDavid du Colombier {
44534e04225SDavid du Colombier 	Con *con;
44634e04225SDavid du Colombier 	int eof, n;
44734e04225SDavid du Colombier 
44834e04225SDavid du Colombier 	con = m->con;
44934e04225SDavid du Colombier 
45034e04225SDavid du Colombier 	/*
45134e04225SDavid du Colombier 	 * An Rflush with a .fprev implies it is on a flush queue waiting for
45234e04225SDavid du Colombier 	 * its corresponding 'oldtag' message to go out first, so punt
45334e04225SDavid du Colombier 	 * until the 'oldtag' message goes out (see below).
45434e04225SDavid du Colombier 	 */
45534e04225SDavid du Colombier 	if(m->r.type == Rflush && m->fprev != nil){
45634e04225SDavid du Colombier 		fprint(2, "msgWrite: delay r %F\n", &m->r);
45734e04225SDavid du Colombier 		return 0;
45834e04225SDavid du Colombier 	}
45934e04225SDavid du Colombier 
46034e04225SDavid du Colombier 	msgMunlink(m);
46134e04225SDavid du Colombier 	vtUnlock(con->mlock);
46234e04225SDavid du Colombier 
46334e04225SDavid du Colombier 	/*
46434e04225SDavid du Colombier 	 * TODO: optimise this copy away somehow for
46534e04225SDavid du Colombier 	 * read, stat, etc.
46634e04225SDavid du Colombier 	 */
46734e04225SDavid du Colombier 	assert(n = convS2M(&m->r, con->data, con->msize));
46834e04225SDavid du Colombier 	if(write(con->fd, con->data, n) != n)
46934e04225SDavid du Colombier 		eof = 1;
47034e04225SDavid du Colombier 	else
47134e04225SDavid du Colombier 		eof = 0;
47234e04225SDavid du Colombier 
47334e04225SDavid du Colombier 	if(Dflag)
47434e04225SDavid du Colombier 		fprint(2, "msgWrite: r %F\n", &m->r);
47534e04225SDavid du Colombier 
47634e04225SDavid du Colombier 	/*
47734e04225SDavid du Colombier 	 * Just wrote a reply. If it has any flushes waiting
47834e04225SDavid du Colombier 	 * for it to have gone out, recurse down the list writing
47934e04225SDavid du Colombier 	 * them out too.
48034e04225SDavid du Colombier 	 */
48134e04225SDavid du Colombier 	vtLock(con->mlock);
48234e04225SDavid du Colombier 	if(m->fnext != nil){
48334e04225SDavid du Colombier 		m->fnext->fprev = nil;
48434e04225SDavid du Colombier 		eof += _msgWrite(m->fnext);
48534e04225SDavid du Colombier 		m->fnext = nil;
48634e04225SDavid du Colombier 	}
48734e04225SDavid du Colombier 	msgFree(m);
48834e04225SDavid du Colombier 
48934e04225SDavid du Colombier 	return eof;
49034e04225SDavid du Colombier }
49134e04225SDavid du Colombier 
49234e04225SDavid du Colombier static void
49334e04225SDavid du Colombier msgWrite(void* v)
49434e04225SDavid du Colombier {
49534e04225SDavid du Colombier 	Msg *m;
49634e04225SDavid du Colombier 	int eof;
49734e04225SDavid du Colombier 	Con *con;
49834e04225SDavid du Colombier 
49934e04225SDavid du Colombier 	vtThreadSetName("msgWrite");
50034e04225SDavid du Colombier 
50134e04225SDavid du Colombier 	con = v;
50234e04225SDavid du Colombier 	if(vtThread(msgRead, con) < 0){
50334e04225SDavid du Colombier 		conFree(con);
50434e04225SDavid du Colombier 		return;
50534e04225SDavid du Colombier 	}
50634e04225SDavid du Colombier 
50734e04225SDavid du Colombier 	for(;;){
50834e04225SDavid du Colombier 		/*
50934e04225SDavid du Colombier 		 * Wait for and pull a message off the write queue.
51034e04225SDavid du Colombier 		 */
51134e04225SDavid du Colombier 		vtLock(con->wlock);
51234e04225SDavid du Colombier 		while(con->whead == nil)
51334e04225SDavid du Colombier 			vtSleep(con->wrendez);
51434e04225SDavid du Colombier 		m = con->whead;
51534e04225SDavid du Colombier 		con->whead = m->rwnext;
51634e04225SDavid du Colombier 		m->rwnext = nil;
51734e04225SDavid du Colombier 		vtUnlock(con->wlock);
51834e04225SDavid du Colombier 
51934e04225SDavid du Colombier 		/*
52034e04225SDavid du Colombier 		 * Throw the message away if it's a flushed flush,
52134e04225SDavid du Colombier 		 * otherwise change its state and try to write it out.
52234e04225SDavid du Colombier 		 */
52334e04225SDavid du Colombier 		vtLock(con->mlock);
52434e04225SDavid du Colombier 		if(m->state == MsgF){
52534e04225SDavid du Colombier 			assert(m->t.type == Tflush);
52634e04225SDavid du Colombier 			msgUnlinkUnlockAndFree(m);
52734e04225SDavid du Colombier 			continue;
52834e04225SDavid du Colombier 		}
52934e04225SDavid du Colombier 		m->state = MsgW;
53034e04225SDavid du Colombier 		eof = _msgWrite(m);
53134e04225SDavid du Colombier 		vtUnlock(con->mlock);
53234e04225SDavid du Colombier 
53334e04225SDavid du Colombier 		vtLock(con->lock);
53434e04225SDavid du Colombier 		if(eof && con->fd >= 0){
53534e04225SDavid du Colombier 			close(con->fd);
53634e04225SDavid du Colombier 			con->fd = -1;
53734e04225SDavid du Colombier 		}
53834e04225SDavid du Colombier 		if(con->state == ConDown)
53934e04225SDavid du Colombier 			vtWakeup(con->rendez);
54034e04225SDavid du Colombier 		if(con->state == ConMoribund && con->mhead == nil){
54134e04225SDavid du Colombier 			vtUnlock(con->lock);
54234e04225SDavid du Colombier 			conFree(con);
54334e04225SDavid du Colombier 			break;
54434e04225SDavid du Colombier 		}
54534e04225SDavid du Colombier 		vtUnlock(con->lock);
54634e04225SDavid du Colombier 	}
5475e96a66cSDavid du Colombier }
5485e96a66cSDavid du Colombier 
5495e96a66cSDavid du Colombier Con*
5505e96a66cSDavid du Colombier conAlloc(int fd, char* name)
5515e96a66cSDavid du Colombier {
5525e96a66cSDavid du Colombier 	Con *con;
5535e96a66cSDavid du Colombier 
55434e04225SDavid du Colombier 	vtLock(cbox.alock);
55534e04225SDavid du Colombier 	while(cbox.ahead == nil){
55634e04225SDavid du Colombier 		if(cbox.ncon >= cbox.maxcon){
55734e04225SDavid du Colombier 			cbox.nconstarve++;
55834e04225SDavid du Colombier 			vtSleep(cbox.arendez);
55934e04225SDavid du Colombier 			continue;
5605e96a66cSDavid du Colombier 		}
5615e96a66cSDavid du Colombier 		con = vtMemAllocZ(sizeof(Con));
5625e96a66cSDavid du Colombier 		con->lock = vtLockAlloc();
56334e04225SDavid du Colombier 		con->rendez = vtRendezAlloc(con->lock);
5645e96a66cSDavid du Colombier 		con->data = vtMemAlloc(cbox.msize);
5655e96a66cSDavid du Colombier 		con->msize = cbox.msize;
56634e04225SDavid du Colombier 		con->alock = vtLockAlloc();
56734e04225SDavid du Colombier 		con->mlock = vtLockAlloc();
56834e04225SDavid du Colombier 		con->mrendez = vtRendezAlloc(con->mlock);
56934e04225SDavid du Colombier 		con->wlock = vtLockAlloc();
57034e04225SDavid du Colombier 		con->wrendez = vtRendezAlloc(con->wlock);
5715e96a66cSDavid du Colombier 		con->fidlock = vtLockAlloc();
57234e04225SDavid du Colombier 
57334e04225SDavid du Colombier 		cbox.ncon++;
57434e04225SDavid du Colombier 		cbox.ahead = con;
57534e04225SDavid du Colombier 		break;
5765e96a66cSDavid du Colombier 	}
57734e04225SDavid du Colombier 	con = cbox.ahead;
57834e04225SDavid du Colombier 	cbox.ahead = con->anext;
57934e04225SDavid du Colombier 	con->anext = nil;
58034e04225SDavid du Colombier 
58134e04225SDavid du Colombier 	if(cbox.ctail != nil){
58234e04225SDavid du Colombier 		con->cprev = cbox.ctail;
58334e04225SDavid du Colombier 		cbox.ctail->cnext = con;
58434e04225SDavid du Colombier 	}
58534e04225SDavid du Colombier 	else{
58634e04225SDavid du Colombier 		cbox.chead = con;
58734e04225SDavid du Colombier 		con->cprev = nil;
58834e04225SDavid du Colombier 	}
58934e04225SDavid du Colombier 	cbox.ctail = con;
59034e04225SDavid du Colombier 
5915e96a66cSDavid du Colombier 	assert(con->mhead == nil);
59234e04225SDavid du Colombier 	assert(con->whead == nil);
5935e96a66cSDavid du Colombier 	assert(con->fhead == nil);
5945e96a66cSDavid du Colombier 	assert(con->nfid == 0);
5955e96a66cSDavid du Colombier 
59634e04225SDavid du Colombier 	con->state = ConNew;
5975e96a66cSDavid du Colombier 	con->fd = fd;
5985e96a66cSDavid du Colombier 	if(con->name != nil){
5995e96a66cSDavid du Colombier 		vtMemFree(con->name);
6005e96a66cSDavid du Colombier 		con->name = nil;
6015e96a66cSDavid du Colombier 	}
6025e96a66cSDavid du Colombier 	if(name != nil)
6035e96a66cSDavid du Colombier 		con->name = vtStrDup(name);
60434e04225SDavid du Colombier 	else
60534e04225SDavid du Colombier 		con->name = vtStrDup("unknown");
6065e96a66cSDavid du Colombier 	con->aok = 0;
60734e04225SDavid du Colombier 	vtUnlock(cbox.alock);
6085e96a66cSDavid du Colombier 
60934e04225SDavid du Colombier 	if(vtThread(msgWrite, con) < 0){
6105e96a66cSDavid du Colombier 		conFree(con);
6115e96a66cSDavid du Colombier 		return nil;
6125e96a66cSDavid du Colombier 	}
6135e96a66cSDavid du Colombier 
6145e96a66cSDavid du Colombier 	return con;
6155e96a66cSDavid du Colombier }
6165e96a66cSDavid du Colombier 
6175e96a66cSDavid du Colombier static int
6185e96a66cSDavid du Colombier cmdMsg(int argc, char* argv[])
6195e96a66cSDavid du Colombier {
6205e96a66cSDavid du Colombier 	char *p;
6215e96a66cSDavid du Colombier 	char *usage = "usage: msg [-m nmsg] [-p nproc]";
62234e04225SDavid du Colombier 	int maxmsg, nmsg, nmsgstarve, maxproc, nproc, nprocstarve;
6235e96a66cSDavid du Colombier 
6245e96a66cSDavid du Colombier 	maxmsg = maxproc = 0;
6255e96a66cSDavid du Colombier 
6265e96a66cSDavid du Colombier 	ARGBEGIN{
6275e96a66cSDavid du Colombier 	default:
6285e96a66cSDavid du Colombier 		return cliError(usage);
6295e96a66cSDavid du Colombier 	case 'm':
6305e96a66cSDavid du Colombier 		p = ARGF();
6315e96a66cSDavid du Colombier 		if(p == nil)
6325e96a66cSDavid du Colombier 			return cliError(usage);
6335e96a66cSDavid du Colombier 		maxmsg = strtol(argv[0], &p, 0);
6345e96a66cSDavid du Colombier 		if(maxmsg <= 0 || p == argv[0] || *p != '\0')
6355e96a66cSDavid du Colombier 			return cliError(usage);
6365e96a66cSDavid du Colombier 		break;
6375e96a66cSDavid du Colombier 	case 'p':
6385e96a66cSDavid du Colombier 		p = ARGF();
6395e96a66cSDavid du Colombier 		if(p == nil)
6405e96a66cSDavid du Colombier 			return cliError(usage);
6415e96a66cSDavid du Colombier 		maxproc = strtol(argv[0], &p, 0);
6425e96a66cSDavid du Colombier 		if(maxproc <= 0 || p == argv[0] || *p != '\0')
6435e96a66cSDavid du Colombier 			return cliError(usage);
6445e96a66cSDavid du Colombier 		break;
6455e96a66cSDavid du Colombier 	}ARGEND
6465e96a66cSDavid du Colombier 	if(argc)
6475e96a66cSDavid du Colombier 		return cliError(usage);
6485e96a66cSDavid du Colombier 
64934e04225SDavid du Colombier 	vtLock(mbox.alock);
6505e96a66cSDavid du Colombier 	if(maxmsg)
6515e96a66cSDavid du Colombier 		mbox.maxmsg = maxmsg;
6525e96a66cSDavid du Colombier 	maxmsg = mbox.maxmsg;
65334e04225SDavid du Colombier 	nmsg = mbox.nmsg;
65434e04225SDavid du Colombier 	nmsgstarve = mbox.nmsgstarve;
65534e04225SDavid du Colombier 	vtUnlock(mbox.alock);
65634e04225SDavid du Colombier 
65734e04225SDavid du Colombier 	vtLock(mbox.rlock);
6585e96a66cSDavid du Colombier 	if(maxproc)
6595e96a66cSDavid du Colombier 		mbox.maxproc = maxproc;
6605e96a66cSDavid du Colombier 	maxproc = mbox.maxproc;
66134e04225SDavid du Colombier 	nproc = mbox.nproc;
66234e04225SDavid du Colombier 	nprocstarve = mbox.nprocstarve;
66334e04225SDavid du Colombier 	vtUnlock(mbox.rlock);
6645e96a66cSDavid du Colombier 
6655e96a66cSDavid du Colombier 	consPrint("\tmsg -m %d -p %d\n", maxmsg, maxproc);
66634e04225SDavid du Colombier 	consPrint("\tnmsg %d nmsgstarve %d nproc %d nprocstarve %d\n",
66734e04225SDavid du Colombier 		nmsg, nmsgstarve, nproc, nprocstarve);
6685e96a66cSDavid du Colombier 
6695e96a66cSDavid du Colombier 	return 1;
6705e96a66cSDavid du Colombier }
6715e96a66cSDavid du Colombier 
672*81cf8742SDavid du Colombier static int
673*81cf8742SDavid du Colombier scmp(Fid *a, Fid *b)
674*81cf8742SDavid du Colombier {
675*81cf8742SDavid du Colombier 	if(a == 0)
676*81cf8742SDavid du Colombier 		return 1;
677*81cf8742SDavid du Colombier 	if(b == 0)
678*81cf8742SDavid du Colombier 		return -1;
679*81cf8742SDavid du Colombier 	return strcmp(a->uname, b->uname);
680*81cf8742SDavid du Colombier }
681*81cf8742SDavid du Colombier 
682*81cf8742SDavid du Colombier static Fid*
683*81cf8742SDavid du Colombier fidMerge(Fid *a, Fid *b)
684*81cf8742SDavid du Colombier {
685*81cf8742SDavid du Colombier 	Fid *s, **l;
686*81cf8742SDavid du Colombier 
687*81cf8742SDavid du Colombier 	l = &s;
688*81cf8742SDavid du Colombier 	while(a || b){
689*81cf8742SDavid du Colombier 		if(scmp(a, b) < 0){
690*81cf8742SDavid du Colombier 			*l = a;
691*81cf8742SDavid du Colombier 			l = &a->sort;
692*81cf8742SDavid du Colombier 			a = a->sort;
693*81cf8742SDavid du Colombier 		}else{
694*81cf8742SDavid du Colombier 			*l = b;
695*81cf8742SDavid du Colombier 			l = &b->sort;
696*81cf8742SDavid du Colombier 			b = b->sort;
697*81cf8742SDavid du Colombier 		}
698*81cf8742SDavid du Colombier 	}
699*81cf8742SDavid du Colombier 	*l = 0;
700*81cf8742SDavid du Colombier 	return s;
701*81cf8742SDavid du Colombier }
702*81cf8742SDavid du Colombier 
703*81cf8742SDavid du Colombier static Fid*
704*81cf8742SDavid du Colombier fidMergeSort(Fid *f)
705*81cf8742SDavid du Colombier {
706*81cf8742SDavid du Colombier 	int delay;
707*81cf8742SDavid du Colombier 	Fid *a, *b;
708*81cf8742SDavid du Colombier 
709*81cf8742SDavid du Colombier 	if(f == nil)
710*81cf8742SDavid du Colombier 		return nil;
711*81cf8742SDavid du Colombier 	if(f->sort == nil)
712*81cf8742SDavid du Colombier 		return f;
713*81cf8742SDavid du Colombier 
714*81cf8742SDavid du Colombier 	a = b = f;
715*81cf8742SDavid du Colombier 	delay = 1;
716*81cf8742SDavid du Colombier 	while(a && b){
717*81cf8742SDavid du Colombier 		if(delay)	/* easy way to handle 2-element list */
718*81cf8742SDavid du Colombier 			delay = 0;
719*81cf8742SDavid du Colombier 		else
720*81cf8742SDavid du Colombier 			a = a->sort;
721*81cf8742SDavid du Colombier 		if(b = b->sort)
722*81cf8742SDavid du Colombier 			b = b->sort;
723*81cf8742SDavid du Colombier 	}
724*81cf8742SDavid du Colombier 
725*81cf8742SDavid du Colombier 	b = a->sort;
726*81cf8742SDavid du Colombier 	a->sort = nil;
727*81cf8742SDavid du Colombier 
728*81cf8742SDavid du Colombier 	a = fidMergeSort(f);
729*81cf8742SDavid du Colombier 	b = fidMergeSort(b);
730*81cf8742SDavid du Colombier 
731*81cf8742SDavid du Colombier 	return fidMerge(a, b);
732*81cf8742SDavid du Colombier }
733*81cf8742SDavid du Colombier 
734*81cf8742SDavid du Colombier 
735*81cf8742SDavid du Colombier static int
736*81cf8742SDavid du Colombier cmdWho(int argc, char* argv[])
737*81cf8742SDavid du Colombier {
738*81cf8742SDavid du Colombier 	char *usage = "usage: who";
739*81cf8742SDavid du Colombier 	int i;
740*81cf8742SDavid du Colombier 	Con *con;
741*81cf8742SDavid du Colombier 	Fid *fid, *last;
742*81cf8742SDavid du Colombier 
743*81cf8742SDavid du Colombier 	ARGBEGIN{
744*81cf8742SDavid du Colombier 	default:
745*81cf8742SDavid du Colombier 		return cliError(usage);
746*81cf8742SDavid du Colombier 	}ARGEND
747*81cf8742SDavid du Colombier 
748*81cf8742SDavid du Colombier 	if(argc > 0)
749*81cf8742SDavid du Colombier 		return cliError(usage);
750*81cf8742SDavid du Colombier 
751*81cf8742SDavid du Colombier 	vtRLock(cbox.clock);
752*81cf8742SDavid du Colombier 	for(con=cbox.chead; con; con=con->cnext){
753*81cf8742SDavid du Colombier 		consPrint("\t%q:", con->name);
754*81cf8742SDavid du Colombier 		vtLock(con->fidlock);
755*81cf8742SDavid du Colombier 		last = nil;
756*81cf8742SDavid du Colombier 		for(i=0; i<NFidHash; i++)
757*81cf8742SDavid du Colombier 			for(fid=con->fidhash[i]; fid; fid=fid->hash)
758*81cf8742SDavid du Colombier 				if(fid->fidno != NOFID && fid->uname){
759*81cf8742SDavid du Colombier 					fid->sort = last;
760*81cf8742SDavid du Colombier 					last = fid;
761*81cf8742SDavid du Colombier 				}
762*81cf8742SDavid du Colombier 		fid = fidMergeSort(last);
763*81cf8742SDavid du Colombier 		last = nil;
764*81cf8742SDavid du Colombier 		for(; fid; last=fid, fid=fid->sort)
765*81cf8742SDavid du Colombier 			if(last==nil || strcmp(fid->uname, last->uname) != 0)
766*81cf8742SDavid du Colombier 				consPrint(" %q", fid->uname);
767*81cf8742SDavid du Colombier 		vtUnlock(con->fidlock);
768*81cf8742SDavid du Colombier 		consPrint("\n");
769*81cf8742SDavid du Colombier 	}
770*81cf8742SDavid du Colombier 	vtRUnlock(cbox.clock);
771*81cf8742SDavid du Colombier 	return 1;
772*81cf8742SDavid du Colombier }
773*81cf8742SDavid du Colombier 
7745e96a66cSDavid du Colombier void
77534e04225SDavid du Colombier msgInit(void)
7765e96a66cSDavid du Colombier {
77734e04225SDavid du Colombier 	mbox.alock = vtLockAlloc();
77834e04225SDavid du Colombier 	mbox.arendez = vtRendezAlloc(mbox.alock);
77934e04225SDavid du Colombier 
78034e04225SDavid du Colombier 	mbox.rlock = vtLockAlloc();
78134e04225SDavid du Colombier 	mbox.rrendez = vtRendezAlloc(mbox.rlock);
7825e96a66cSDavid du Colombier 
7835e96a66cSDavid du Colombier 	mbox.maxmsg = NMsgInit;
7845e96a66cSDavid du Colombier 	mbox.maxproc = NMsgProcInit;
7855e96a66cSDavid du Colombier 	mbox.msize = NMsizeInit;
7865e96a66cSDavid du Colombier 
7875e96a66cSDavid du Colombier 	cliAddCmd("msg", cmdMsg);
78834e04225SDavid du Colombier }
7895e96a66cSDavid du Colombier 
79034e04225SDavid du Colombier static int
79134e04225SDavid du Colombier cmdCon(int argc, char* argv[])
79234e04225SDavid du Colombier {
79334e04225SDavid du Colombier 	char *p;
79434e04225SDavid du Colombier 	Con *con;
79534e04225SDavid du Colombier 	char *usage = "usage: con [-m ncon]";
79634e04225SDavid du Colombier 	int maxcon, ncon, nconstarve;
79734e04225SDavid du Colombier 
79834e04225SDavid du Colombier 	maxcon = 0;
79934e04225SDavid du Colombier 
80034e04225SDavid du Colombier 	ARGBEGIN{
80134e04225SDavid du Colombier 	default:
80234e04225SDavid du Colombier 		return cliError(usage);
80334e04225SDavid du Colombier 	case 'm':
80434e04225SDavid du Colombier 		p = ARGF();
80534e04225SDavid du Colombier 		if(p == nil)
80634e04225SDavid du Colombier 			return cliError(usage);
80734e04225SDavid du Colombier 		maxcon = strtol(argv[0], &p, 0);
80834e04225SDavid du Colombier 		if(maxcon <= 0 || p == argv[0] || *p != '\0')
80934e04225SDavid du Colombier 			return cliError(usage);
81034e04225SDavid du Colombier 		break;
81134e04225SDavid du Colombier 	}ARGEND
81234e04225SDavid du Colombier 	if(argc)
81334e04225SDavid du Colombier 		return cliError(usage);
81434e04225SDavid du Colombier 
81534e04225SDavid du Colombier 	vtLock(cbox.clock);
81634e04225SDavid du Colombier 	if(maxcon)
81734e04225SDavid du Colombier 		cbox.maxcon = maxcon;
81834e04225SDavid du Colombier 	maxcon = cbox.maxcon;
81934e04225SDavid du Colombier 	ncon = cbox.ncon;
82034e04225SDavid du Colombier 	nconstarve = cbox.nconstarve;
82134e04225SDavid du Colombier 	vtUnlock(cbox.clock);
82234e04225SDavid du Colombier 
82334e04225SDavid du Colombier 	consPrint("\tcon -m %d\n", maxcon);
82434e04225SDavid du Colombier 	consPrint("\tncon %d nconstarve %d\n", ncon, nconstarve);
82534e04225SDavid du Colombier 
82634e04225SDavid du Colombier 	vtRLock(cbox.clock);
82734e04225SDavid du Colombier 	for(con = cbox.chead; con != nil; con = con->cnext){
82834e04225SDavid du Colombier 		consPrint("\t%s\n", con->name);
82934e04225SDavid du Colombier 	}
83034e04225SDavid du Colombier 	vtRUnlock(cbox.clock);
83134e04225SDavid du Colombier 
83234e04225SDavid du Colombier 	return 1;
83334e04225SDavid du Colombier }
83434e04225SDavid du Colombier 
83534e04225SDavid du Colombier void
83634e04225SDavid du Colombier conInit(void)
83734e04225SDavid du Colombier {
83834e04225SDavid du Colombier 	cbox.alock = vtLockAlloc();
83934e04225SDavid du Colombier 	cbox.arendez = vtRendezAlloc(cbox.alock);
84034e04225SDavid du Colombier 
84134e04225SDavid du Colombier 	cbox.clock = vtLockAlloc();
84234e04225SDavid du Colombier 
84334e04225SDavid du Colombier 	cbox.maxcon = NConInit;
8445e96a66cSDavid du Colombier 	cbox.msize = NMsizeInit;
84534e04225SDavid du Colombier 
84634e04225SDavid du Colombier 	cliAddCmd("con", cmdCon);
847*81cf8742SDavid du Colombier 	cliAddCmd("who", cmdWho);
8485e96a66cSDavid du Colombier }
849