/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* * SCCSID: @(#)msg.c 3.2 10/8/87 */ /* * Inter-Process Communication Message Facility. */ #include <sys/types.h> #include <sys/param.h> #include <sys/dir.h> #include <signal.h> #include <sys/user.h> #include <sys/seg.h> #include <sys/proc.h> #include <sys/buf.h> #include <errno.h> #include <sys/map.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/systm.h> 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 */ struct msg *msgfp; /* ptr to head of free header list */ unsigned msgbase; /* base address of message buffer */ long msg; extern time_t time; /* system idea of date */ struct msqid_ds *ipcget(), *msgconv(); /* Convert bytes to msg segments. */ #define btoq(X) ((X + msginfo.msgssz - 1) / msginfo.msgssz) /* Choose appropriate message copy routine. */ #define MOVE msgpimove /* * msgconv - Convert a user supplied message queue id into a ptr to a * msqid_ds structure. */ static struct msqid_ds * msgconv(id) register int id; { register struct msqid_ds *qp; /* ptr to associated q slot */ qp = &msgque[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. */ static 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, 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(&qp->msg_qnum); if(qp->msg_perm.mode & MSG_WWAIT) wakeup(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(uap->buf, &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(qp, 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. */ static 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(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(&msgfp); msgfp = mp; } /* * msgget - Msgget system call. */ static 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 = ipcget(uap->key, uap->msgflg, msgque, msginfo.msgmni, sizeof(*qp), &s)) == NULL) return; if(s) { /* This is a new queue. Finish initialization. */ qp->msg_first = qp->msg_last = NULL; qp->msg_qnum = 0; qp->msg_qbytes = msginfo.msgmnb; qp->msg_lspid = qp->msg_lrpid = 0; qp->msg_stime = 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 */ register int bs; /* message buffer size */ long limit; /* Maximum message buffer size */ limit = (long)64*1024 - 63; /* Allocate physical memory for message buffer. */ if((long)msginfo.msgseg * msginfo.msgssz >= limit) { printf("\nMessage buffer greater than 64K, Unallocated\n"); msginfo.msgseg = 0; bs = 0; } else if((msgbase = malloc(coremap, bs=(int)btoc((long)msginfo.msgseg * msginfo.msgssz))) == 0) { printf("Can't allocate message buffer.\n"); msginfo.msgseg = 0; } msg = ctob((long)(unsigned)msgbase); MAPINIT(msgmap, msginfo.msgmap); mfree(msgmap, msginfo.msgseg, 1); for(i = 0, mp = msgfp = msgh;++i < msginfo.msgtql;mp++) mp->msg_next = mp + 1; return(bs); } /* * msgpimove - PDP 11 pimove interface for possibly large copies. */ static msgpimove(base, count, mode) long base; /* base (spot * msginfo.msgssz) in msgmap */ /* in bytes */ 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; u.u_base += tcount; count -= tcount; } } /* * msgrcv - Msgrcv system call. */ static 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 = 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; copyout(&smp->msg_type, 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(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; sleep(&qp->msg_qnum, PMSG); if(msgconv(uap->msqid) == NULL) { u.u_error = EIDRM; return; } goto findmsg; } /* * msgsnd - Msgsnd system call. */ static 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; } copyin(uap->msgp, &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; sleep(qp, PMSG ); /* if(sleep(qp, PMSG | PCATCH)) { u.u_error = EINTR; qp->msg_perm.mode &= ~MSG_WWAIT; wakeup(qp); return; } */ goto getres; } if(msgfp == NULL) { if(uap->msgflg & IPC_NOWAIT) { u.u_error = EAGAIN; return; } sleep(&msgfp, PMSG ); /*if(sleep(&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)++; */ msgmap[0].m_addr++; sleep(msgmap, PMSG ); /*if(sleep(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(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 = 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(&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])(); }