/* @(#)msg.c	1.3 *//***	Inter-Process Communication Message Facility.*/#include "sys/types.h"#include "sys/param.h"#include "sys/dir.h"#ifdef u3b#include "sys/istk.h"#endif#include "sys/signal.h"#include "sys/user.h"#include "sys/seg.h"#include "sys/proc.h"#include "sys/buf.h"#include "sys/errno.h"#include "sys/map.h"#include "sys/ipc.h"#include "sys/msg.h"#include "sys/systm.h"#ifdef vax#include "sys/page.h"#endif#ifdef	u3b#include "sys/macro.h"#else#include "sys/sysmacros.h"#endifextern struct map	msgmap[];	/* msg allocation map */extern struct msqid_ds	msgque[];	/* msg queue headers */extern struct msg	msgh[];		/* message headers */extern struct msginfo	msginfo;	/* message parameters */extern char		msgspace[];	/* space for message buffers */struct msg		*msgfp;	/* ptr to head of free header list */paddr_t			msg;		/* base address of message buffer */extern time_t		time;		/* system idea of date */struct ipc_perm		*ipcget();struct msqid_ds		*msgconv();/* Convert bytes to msg segments. */#define	btoq(X)	((X + msginfo.msgssz - 1) / msginfo.msgssz)/* Choose appropriate message copy routine. */#ifdef pdp11#define MOVE	msgpimove#else#define	MOVE	iomove#endif/***	msgconv - Convert a user supplied message queue id into a ptr to a**		msqid_ds structure.*/struct msqid_ds *msgconv(id)register int	id;{	register struct msqid_ds	*qp;	/* ptr to associated q slot */	qp = &msgque[(short)(id % msginfo.msgmni)];	if((qp->msg_perm.mode & IPC_ALLOC) == 0 ||		id / msginfo.msgmni != qp->msg_perm.seq) {		u.u_error = EINVAL;		return(NULL);	}	return(qp);}/***	msgctl - Msgctl system call.*/msgctl(){	register struct a {		int		msgid,				cmd;		struct msqid_ds	*buf;	}		*uap = (struct a *)u.u_ap;	struct msqid_ds			ds;	/* queue work area */	register struct msqid_ds	*qp;	/* ptr to associated q */	if((qp = msgconv(uap->msgid)) == NULL)		return;	u.u_rval1 = 0;	switch(uap->cmd) {	case IPC_RMID:		if(u.u_uid != qp->msg_perm.uid && u.u_uid != qp->msg_perm.cuid			&& !suser())			return;		while(qp->msg_first)			msgfree(qp, (struct msg *)NULL, qp->msg_first);		qp->msg_cbytes = 0;		if(uap->msgid + msginfo.msgmni < 0)			qp->msg_perm.seq = 0;		else			qp->msg_perm.seq++;		if(qp->msg_perm.mode & MSG_RWAIT)			wakeup((caddr_t)&qp->msg_qnum);		if(qp->msg_perm.mode & MSG_WWAIT)			wakeup((caddr_t)qp);		qp->msg_perm.mode = 0;		return;	case IPC_SET:		if(u.u_uid != qp->msg_perm.uid && u.u_uid != qp->msg_perm.cuid			 && !suser())			return;		if(copyin((caddr_t)uap->buf, (caddr_t)&ds, sizeof(ds))) {			u.u_error = EFAULT;			return;		}		if(ds.msg_qbytes > qp->msg_qbytes && !suser())			return;		qp->msg_perm.uid = ds.msg_perm.uid;		qp->msg_perm.gid = ds.msg_perm.gid;		qp->msg_perm.mode = (qp->msg_perm.mode & ~0777) |			(ds.msg_perm.mode & 0777);		qp->msg_qbytes = ds.msg_qbytes;		qp->msg_ctime = time;		return;	case IPC_STAT:		if(ipcaccess(&qp->msg_perm, MSG_R))			return;		if(copyout((caddr_t)qp, (caddr_t)uap->buf, sizeof(*qp))) {			u.u_error = EFAULT;			return;		}		return;	default:		u.u_error = EINVAL;		return;	}}/***	msgfree - Free up space and message header, relink pointers on q,**	and wakeup anyone waiting for resources.*/msgfree(qp, pmp, mp)register struct msqid_ds	*qp;	/* ptr to q of mesg being freed */register struct msg		*mp,	/* ptr to msg being freed */				*pmp;	/* ptr to mp's predecessor */{	/* Unlink message from the q. */	if(pmp == NULL)		qp->msg_first = mp->msg_next;	else		pmp->msg_next = mp->msg_next;	if(mp->msg_next == NULL)		qp->msg_last = pmp;	qp->msg_qnum--;	if(qp->msg_perm.mode & MSG_WWAIT) {		qp->msg_perm.mode &= ~MSG_WWAIT;		wakeup((caddr_t)qp);	}	/* Free up message text. */	if(mp->msg_ts)		mfree(msgmap, btoq(mp->msg_ts), mp->msg_spot + 1);	/* Free up header */	mp->msg_next = msgfp;	if(msgfp == NULL)		wakeup((caddr_t)&msgfp);	msgfp = mp;}/***	msgget - Msgget system call.*/msgget(){	register struct a {		key_t	key;		int	msgflg;	}	*uap = (struct a *)u.u_ap;	register struct msqid_ds	*qp;	/* ptr to associated q */	int				s;	/* ipcget status return */	if((qp = (struct msqid_ds *)		ipcget(uap->key, uap->msgflg, (struct ipc_perm *)msgque,		msginfo.msgmni, sizeof(*qp), &s)) == NULL)		return;	if(s) {		/* This is a new queue.  Finish initialization. */		qp->msg_first = NULL; qp->msg_last = NULL;		qp->msg_qnum = 0;		qp->msg_qbytes = msginfo.msgmnb;		qp->msg_lspid = 0; qp->msg_lrpid = 0;		qp->msg_stime = 0; qp->msg_rtime = 0;		qp->msg_ctime = time;	}	u.u_rval1 = qp->msg_perm.seq * msginfo.msgmni + (qp - msgque);}/***	msginit - Called by main(main.c) to initialize message queues.*/msginit(){	register int		i;	/* loop control */	register struct msg	*mp;	/* ptr to msg begin linked */#ifndef mc68000	register int		bs;	/* message buffer size */#endif	/* Allocate physical memory for message buffer. */#ifndef mc68000	bs = (long)msginfo.msgseg * msginfo.msgssz;#endif#ifdef mc68000	msg = (paddr_t) msgspace;#endif#ifdef pdp11	if((msg = (paddr_t)ctob((long)(unsigned)malloc(coremap,		bs=(int)btoc((long)msginfo.msgseg * msginfo.msgssz)))) == 0) {#endif#ifdef vax	if((msg = (paddr_t)sptalloc(bs=btoc(msginfo.msgseg * msginfo.msgssz),		PG_V | PG_KW, 0)) == NULL) {#endif#ifdef u3b	if((msg = (paddr_t)kseg(RW, btop(msginfo.msgseg * msginfo.msgssz))) ==		NULL) {#endif#ifndef mc68000		printf("Can't allocate message buffer.\n");		msginfo.msgseg = 0;	}#endif	mapinit(msgmap, msginfo.msgmap);	mfree(msgmap, (int)msginfo.msgseg, 1);	for(i = 0, mp = msgfp = msgh;++i < msginfo.msgtql;mp++)		mp->msg_next = mp + 1;#ifdef vax	maxmem -= bs;#endif#ifdef	mc68000	return;#endif#ifdef	pdp11	return(bs);#endif}#ifdef pdp11/***	msgpimove - PDP 11 pimove interface for possibly large copies.*/msgpimove(base, count, mode)paddr_t			base;	/* base address */register unsigned	count;	/* byte count */int			mode;	/* transfer mode */{	register unsigned	tcount;	/* current transfer count */	while(u.u_error == 0 && count) {		tcount = count > 8064 ? 8064 : count;		pimove(base, tcount, mode);		base += tcount;		count -= tcount;	}}#endif/***	msgrcv - Msgrcv system call.*/msgrcv(){	register struct a {		int		msqid;		struct msgbuf	*msgp;		int		msgsz;		long		msgtyp;		int		msgflg;	}	*uap = (struct a *)u.u_ap;	register struct msg		*mp,	/* ptr to msg on q */					*pmp,	/* ptr to mp's predecessor */					*smp,	/* ptr to best msg on q */					*spmp;	/* ptr to smp's predecessor */	register struct msqid_ds	*qp;	/* ptr to associated q */	int				sz;	/* transfer byte count */	if((qp = msgconv(uap->msqid)) == NULL)		return;	if(ipcaccess(&qp->msg_perm, MSG_R))		return;	if(uap->msgsz < 0) {		u.u_error = EINVAL;		return;	}	smp = NULL; spmp = NULL;findmsg:	pmp = NULL;	mp = qp->msg_first;	if(uap->msgtyp == 0)		smp = mp;	else		for(;mp;pmp = mp, mp = mp->msg_next) {			if(uap->msgtyp > 0) {				if(uap->msgtyp != mp->msg_type)					continue;				smp = mp;				spmp = pmp;				break;			}			if(mp->msg_type <= -uap->msgtyp) {				if(smp && smp->msg_type <= mp->msg_type)					continue;				smp = mp;				spmp = pmp;			}		}	if(smp) {		if(uap->msgsz < smp->msg_ts)			if(!(uap->msgflg & MSG_NOERROR)) {				u.u_error = E2BIG;				return;			} else				sz = uap->msgsz;		else			sz = smp->msg_ts;		(void) copyout((caddr_t)&smp->msg_type, (caddr_t)uap->msgp,			sizeof(smp->msg_type));		if(u.u_error)			return;		if(sz) {			u.u_base = (caddr_t)uap->msgp + sizeof(smp->msg_type);			u.u_segflg = 0;			MOVE((caddr_t)(msg + msginfo.msgssz * smp->msg_spot),				sz, B_READ);			if(u.u_error)				return;		}		u.u_rval1 = sz;		qp->msg_cbytes -= smp->msg_ts;		qp->msg_lrpid = u.u_procp->p_pid;		qp->msg_rtime = time;		curpri = PMSG;		msgfree(qp, spmp, smp);		return;	}	if(uap->msgflg & IPC_NOWAIT) {		u.u_error = ENOMSG;		return;	}	qp->msg_perm.mode |= MSG_RWAIT;	if(sleep((caddr_t)&qp->msg_qnum, PMSG | PCATCH)) {		u.u_error = EINTR;		return;	}	if(msgconv(uap->msqid) == NULL) {		u.u_error = EIDRM;		return;	}	goto findmsg;}/***	msgsnd - Msgsnd system call.*/msgsnd(){	register struct a {		int		msqid;		struct msgbuf	*msgp;		int		msgsz;		int		msgflg;	}	*uap = (struct a *)u.u_ap;	register struct msqid_ds	*qp;	/* ptr to associated q */	register struct msg		*mp;	/* ptr to allocated msg hdr */	register int			cnt,	/* byte count */					spot;	/* msg pool allocation spot */	long				type;	/* msg type */	if((qp = msgconv(uap->msqid)) == NULL)		return;	if(ipcaccess(&qp->msg_perm, MSG_W))		return;	if((cnt = uap->msgsz) < 0 || cnt > msginfo.msgmax) {		u.u_error = EINVAL;		return;	}	(void) copyin((caddr_t)uap->msgp, (caddr_t)&type, sizeof(type));	if(u.u_error)		return;	if(type < 1) {		u.u_error = EINVAL;		return;	}getres:	/* Be sure that q has not been removed. */	if(msgconv(uap->msqid) == NULL) {		u.u_error = EIDRM;		return;	}	/* Allocate space on q, message header, & buffer space. */	if(cnt + qp->msg_cbytes > qp->msg_qbytes) {		if(uap->msgflg & IPC_NOWAIT) {			u.u_error = EAGAIN;			return;		}		qp->msg_perm.mode |= MSG_WWAIT;		if(sleep((caddr_t)qp, PMSG | PCATCH)) {			u.u_error = EINTR;			qp->msg_perm.mode &= ~MSG_WWAIT;			wakeup((caddr_t)qp);			return;		}		goto getres;	}	if(msgfp == NULL) {		if(uap->msgflg & IPC_NOWAIT) {			u.u_error = EAGAIN;			return;		}		if(sleep((caddr_t)&msgfp, PMSG | PCATCH)) {			u.u_error = EINTR;			return;		}		goto getres;	}	if(cnt && (spot = malloc(msgmap, btoq(cnt))) == NULL) {		if(uap->msgflg & IPC_NOWAIT) {			u.u_error = EAGAIN;			return;		}		mapwant(msgmap)++;		if(sleep((caddr_t)msgmap, PMSG | PCATCH)) {			u.u_error = EINTR;			return;		}		goto getres;	}	/* Everything is available, copy in text and put msg on q. */	if(cnt) {		u.u_base = (caddr_t)uap->msgp + sizeof(type);		u.u_segflg = 0;		MOVE((caddr_t)(msg + msginfo.msgssz * --spot), cnt, B_WRITE);		if(u.u_error) {			mfree(msgmap, btoq(cnt), spot + 1);			return;		}	}	qp->msg_qnum++;	qp->msg_cbytes += cnt;	qp->msg_lspid = u.u_procp->p_pid;	qp->msg_stime = time;	mp = msgfp;	msgfp = mp->msg_next;	mp->msg_next = NULL;	mp->msg_type = type;	mp->msg_ts = cnt;	mp->msg_spot = cnt ? spot : -1;	if(qp->msg_last == NULL) {		qp->msg_first = mp; qp->msg_last = mp;	} else {		qp->msg_last->msg_next = mp;		qp->msg_last = mp;	}	if(qp->msg_perm.mode & MSG_RWAIT) {		qp->msg_perm.mode &= ~MSG_RWAIT;		curpri = PMSG;		wakeup((caddr_t)&qp->msg_qnum);	}	u.u_rval1 = 0;}/***	msgsys - System entry point for msgctl, msgget, msgrcv, and msgsnd**		system calls.*/msgsys(){	int		msgctl(),			msgget(),			msgrcv(),			msgsnd();	static int	(*calls[])() = { msgget, msgctl, msgrcv, msgsnd };	register struct a {		unsigned	id;	/* function code id */		int		*ap;	/* arg pointer for recvmsg */	}		*uap = (struct a *)u.u_ap;	if(uap->id > 3) {		u.u_error = EINVAL;		return;	}	u.u_ap = &u.u_arg[1];	(*calls[uap->id])();}