/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ #pragma ident "@(#)msg.c 1.32 95/08/08 SMI" /* from S5R4 1.15 */ /* * Inter-Process Communication Message Facility. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * ATTENTION: All ye who enter here * As an optimization, all global data items are declared in space.c * When this module get unloaded, the data stays around. The data * is only allocated on first load. * * XXX Unloading disabled - there's more to leaving state * lying around in the system than not freeing global data. */ /* * The following variables msginfo_* are there so that the * elements of the data structure msginfo can be tuned * (if necessary) using the /etc/system file syntax for * tuning of integer data types. */ int msginfo_msgmap = 100; /* # of entries in msg map */ int msginfo_msgmax = 2048; /* max message size */ int msginfo_msgmnb = 4096; /* max # bytes on queue */ int msginfo_msgmni = 50; /* # of message queue identifiers */ int msginfo_msgssz = 8; /* msg segment size (s/b word size multiple) */ int msginfo_msgtql = 40; /* # of system message headers */ ushort msginfo_msgseg = 1024; /* # of msg segments (MUST BE < 32768) */ static kmutex_t msg_lock; /* protects msg mechanism data structures */ static kcondvar_t msgfp_cv; /* define macro to round up to nearest int boundary "from machdep.c" */ #define INTRND(X) roundup((X), sizeof (int)) #include #include struct msgsysa; static int msgsys(struct msgsysa *uap, rval_t *rvp); static struct sysent ipcmsg_sysent = { 6, SE_NOUNLOAD, msgsys }; /* * Module linkage information for the kernel. */ static struct modlsys modlsys = { &mod_syscallops, "System V message facility", &ipcmsg_sysent }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlsys, NULL }; char _depends_on[] = "misc/ipc"; /* ipcaccess, ipcget */ int _init(void) { int retval; register int i; /* loop control */ register struct msg *mp; /* ptr to msg being linked */ u_longlong_t mavail; /* * msginfo_* are inited in param.c to default values * These values can be tuned if need be using the * integer tuning capabilities in the /etc/system file. */ msginfo.msgmap = msginfo_msgmap; msginfo.msgmax = msginfo_msgmax; msginfo.msgmnb = msginfo_msgmnb; msginfo.msgmni = msginfo_msgmni; msginfo.msgssz = msginfo_msgssz; msginfo.msgtql = msginfo_msgtql; msginfo.msgseg = msginfo_msgseg; /* * Don't use more than 25% of the available kernel memory */ mavail = kmem_maxavail() / 4; if ((INTRND((u_longlong_t)msginfo.msgseg * (u_longlong_t)msginfo.msgssz) * sizeof (char) + (u_longlong_t)msginfo.msgmap * sizeof (struct map) + (u_longlong_t)msginfo.msgmni * sizeof (struct msqid_ds) + INTRND((u_longlong_t)msginfo.msgmni * sizeof (struct msglock)) + (u_longlong_t)msginfo.msgtql * sizeof (struct msg)) > mavail) { cmn_err(CE_WARN, "msgsys: can't load module, too much memory requested"); return (ENOMEM); } mutex_init(&msg_lock, "Sys V msg queue", MUTEX_DEFAULT, NULL); cv_init(&msgfp_cv, "msgfp cv", CV_DEFAULT, NULL); ASSERT(msg == NULL); ASSERT(msgmap == NULL); ASSERT(msgh == NULL); ASSERT(msgque == NULL); ASSERT(msglock == NULL); msg = kmem_zalloc(INTRND(msginfo.msgseg * msginfo.msgssz) * sizeof (char), KM_SLEEP); msgmap = kmem_zalloc(msginfo.msgmap * sizeof (struct map), KM_SLEEP); msgh = kmem_zalloc(msginfo.msgtql * sizeof (struct msg), KM_SLEEP); msgque = kmem_zalloc(msginfo.msgmni * sizeof (struct msqid_ds), KM_SLEEP); msglock = kmem_zalloc(INTRND(msginfo.msgmni * sizeof (struct msglock)), KM_SLEEP); mapinit(msgmap, (long)msginfo.msgseg, (ulong)1, "msgmap", msginfo.msgmap); for (i = 0, mp = msgfp = msgh; ++i < msginfo.msgtql; mp++) mp->msg_next = mp + 1; if ((retval = mod_install(&modlinkage)) == 0) return (0); kmem_free(msg, INTRND(msginfo.msgseg * msginfo.msgssz) * sizeof (char)); kmem_free(msgmap, msginfo.msgmap * sizeof (struct map)); kmem_free(msgh, msginfo.msgtql * sizeof (struct msg)); kmem_free(msgque, msginfo.msgmni * sizeof (struct msqid_ds)); kmem_free(msglock, INTRND(msginfo.msgmni * sizeof (struct msglock))); msg = NULL; msgmap = NULL; msgh = NULL; msgque = NULL; msglock = NULL; cv_destroy(&msgfp_cv); mutex_destroy(&msg_lock); return (retval); } /* * See comments in shm.c. */ int _fini(void) { return (EBUSY); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* HACK :: msgbuf structure renamed to avoid kernel compilation conflicts */ #define msgbuf ipcmsgbuf /* Convert bytes to msg segments. */ #define btoq(X) (((long)(X) + msginfo.msgssz - 1) / msginfo.msgssz) /* * Argument vectors for the various flavors of msgsys(). */ #define MSGGET 0 #define MSGCTL 1 #define MSGRCV 2 #define MSGSND 3 struct msgsysa { int opcode; }; struct msgctla { int opcode; int msgid; int cmd; struct msqid_ds *buf; }; struct msggeta { int opcode; key_t key; int msgflg; }; struct msgrcva { int opcode; int msqid; struct msgbuf *msgp; int msgsz; long msgtyp; int msgflg; }; struct msgsnda { int opcode; int msqid; struct msgbuf *msgp; int msgsz; int msgflg; }; /* * msgfree - Free up space and message header, relink pointers on q, * and wakeup anyone waiting for resources. */ static void msgfree( register struct msqid_ds *qp, /* ptr to q of mesg being freed */ register struct msg *pmp, /* ptr to mp's predecessor */ register struct msg *mp) /* ptr to msg being freed */ { ASSERT(MUTEX_HELD(&msg_lock)); /* 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; cv_broadcast(&qp->msg_cv); } /* Free up message text. */ if (mp->msg_ts) { rmfree(msgmap, btoq(mp->msg_ts), (ulong)(mp->msg_spot + 1)); } /* Free up header */ mp->msg_next = msgfp; if (msgfp == NULL) cv_broadcast(&msgfp_cv); msgfp = mp; } /* * msgconv - Convert a user supplied message queue id into a ptr to a * msqid_ds structure. */ static int msgconv(register int id, struct msqid_ds **qpp) { register struct msqid_ds *qp; /* ptr to associated q slot */ register struct msglock *lockp; /* ptr to lock. */ ASSERT(MUTEX_HELD(&msg_lock)); if (id < 0) return (EINVAL); qp = &msgque[id % msginfo.msgmni]; lockp = MSGLOCK(qp); while (lockp->msglock_lock) { if (!cv_wait_sig(&lockp->msglock_cv, &msg_lock)) return (EINTR); } lockp->msglock_lock = 1; if ((qp->msg_perm.mode & IPC_ALLOC) == 0 || (id / msginfo.msgmni) != qp->msg_perm.seq) { lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (EINVAL); } *qpp = qp; return (0); } /* * msgctl - Msgctl system call. */ static int msgctl(register struct msgctla *uap, rval_t *rvp) { struct o_msqid_ds ods; /* SVR3 queue work area */ struct msqid_ds ds; /* SVR4 queue work area */ struct msqid_ds *qp; /* ptr to associated q */ register struct msglock *lockp; register int error; register struct cred *cr; ASSERT(MUTEX_HELD(&msg_lock)); if (error = msgconv(uap->msgid, &qp)) { /* get msqid_ds for this msgid */ return (error); } lockp = MSGLOCK(qp); cr = CRED(); rvp->r_val1 = 0; switch (uap->cmd) { case IPC_O_RMID: case IPC_RMID: /* * IPC_RMID is for use with expanded msqid_ds struct - * msqid_ds not currently used with this command */ if (cr->cr_uid != qp->msg_perm.uid && cr->cr_uid != qp->msg_perm.cuid && !suser(cr)) { error = EPERM; break; } 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) cv_broadcast(&qp->msg_qnum_cv); if (qp->msg_perm.mode & MSG_WWAIT) cv_broadcast(&qp->msg_cv); qp->msg_perm.mode = 0; break; case IPC_O_SET: if (cr->cr_uid != qp->msg_perm.uid && cr->cr_uid != qp->msg_perm.cuid && !suser(cr)) { error = EPERM; break; } if (copyin((caddr_t)uap->buf, (caddr_t)&ods, sizeof (ods))) { error = EFAULT; break; } if (ods.msg_qbytes > qp->msg_qbytes && !suser(cr)) { error = EPERM; break; } if (ods.msg_perm.uid > MAXUID || ods.msg_perm.gid > MAXUID) { error = EINVAL; break; } qp->msg_perm.uid = ods.msg_perm.uid; qp->msg_perm.gid = ods.msg_perm.gid; qp->msg_perm.mode = (qp->msg_perm.mode & ~0777) | (ods.msg_perm.mode & 0777); qp->msg_qbytes = ods.msg_qbytes; qp->msg_ctime = hrestime.tv_sec; break; case IPC_SET: if (cr->cr_uid != qp->msg_perm.uid && cr->cr_uid != qp->msg_perm.cuid && !suser(cr)) { error = EPERM; break; } if (copyin((caddr_t)uap->buf, (caddr_t)&ds, sizeof (ds))) { error = EFAULT; break; } if (ds.msg_qbytes > qp->msg_qbytes && !suser(cr)) { error = EPERM; break; } if (ds.msg_perm.uid < (uid_t)0 || ds.msg_perm.uid > MAXUID || ds.msg_perm.gid < (gid_t)0 || ds.msg_perm.gid > MAXUID) { error = EINVAL; break; } 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 = hrestime.tv_sec; break; case IPC_O_STAT: if (error = ipcaccess(&qp->msg_perm, MSG_R, cr)) break; /* * copy expanded msqid_ds struct to SVR3 msqid_ds structure - * support for non-eft applications. * Check whether SVR4 values are too large to store into an SVR3 * msqid_ds structure. */ if ((unsigned)qp->msg_perm.uid > USHRT_MAX || (unsigned)qp->msg_perm.gid > USHRT_MAX || (unsigned)qp->msg_perm.cuid > USHRT_MAX || (unsigned)qp->msg_perm.cgid > USHRT_MAX || (unsigned)qp->msg_perm.seq > USHRT_MAX || (unsigned)qp->msg_cbytes > USHRT_MAX || (unsigned)qp->msg_qnum > USHRT_MAX || (unsigned)qp->msg_qbytes > USHRT_MAX || (unsigned)qp->msg_lspid > SHRT_MAX || (unsigned)qp->msg_lrpid > SHRT_MAX) { error = EOVERFLOW; break; } ods.msg_perm.uid = (o_uid_t)qp->msg_perm.uid; ods.msg_perm.gid = (o_gid_t)qp->msg_perm.gid; ods.msg_perm.cuid = (o_uid_t)qp->msg_perm.cuid; ods.msg_perm.cgid = (o_gid_t)qp->msg_perm.cgid; ods.msg_perm.mode = (o_mode_t)qp->msg_perm.mode; ods.msg_perm.seq = (ushort)qp->msg_perm.seq; ods.msg_perm.key = qp->msg_perm.key; ods.msg_first = NULL; /* kernel addr */ ods.msg_last = NULL; ods.msg_cbytes = (ushort)qp->msg_cbytes; ods.msg_qnum = (ushort)qp->msg_qnum; ods.msg_qbytes = (ushort)qp->msg_qbytes; ods.msg_lspid = (o_pid_t)qp->msg_lspid; ods.msg_lrpid = (o_pid_t)qp->msg_lrpid; ods.msg_stime = qp->msg_stime; ods.msg_rtime = qp->msg_rtime; ods.msg_ctime = qp->msg_ctime; if (copyout((caddr_t)&ods, (caddr_t)uap->buf, sizeof (ods))) { error = EFAULT; break; } break; case IPC_STAT: if (error = ipcaccess(&qp->msg_perm, MSG_R, cr)) break; if (copyout((caddr_t)qp, (caddr_t)uap->buf, sizeof (*qp))) { error = EFAULT; break; } break; default: error = EINVAL; break; } lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (error); } /* * msgget - Msgget system call. */ static int msgget(register struct msggeta *uap, rval_t *rvp) { struct msqid_ds *qp; /* ptr to associated q */ int s; /* ipcget status return */ register int error; ASSERT(MUTEX_HELD(&msg_lock)); if (error = ipcget(uap->key, uap->msgflg, (struct ipc_perm *)msgque, msginfo.msgmni, sizeof (*qp), &s, (struct ipc_perm **)&qp)) return (error); 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 = hrestime.tv_sec; /* initialize reserve area */ { int i; for (i = 0; i < 4; i++) qp->msg_perm.pad[i] = 0; qp->msg_pad1 = 0; qp->msg_pad2 = 0; qp->msg_pad3 = 0; for (i = 0; i < 3; i++) qp->msg_pad4[i] = 0; } } rvp->r_val1 = qp->msg_perm.seq * msginfo.msgmni + (qp - msgque); return (0); } /* * msgrcv - Msgrcv system call. */ static int msgrcv(register struct msgrcva *uap, rval_t *rvp) { register struct msg *mp; /* ptr to msg on q */ register struct msg *pmp; /* ptr to mp's predecessor */ register struct msg *smp; /* ptr to best msg on q */ register struct msg *spmp; /* ptr to smp's predecessor */ struct msqid_ds *qp; /* ptr to associated q */ register struct msglock *lockp; struct msqid_ds *qp1; int sz; /* transfer byte count */ int error; ASSERT(MUTEX_HELD(&msg_lock)); CPU_STAT_ADD(CPU, cpu_sysinfo.msg, 1); /* bump msg send/rcv count */ if (error = msgconv(uap->msqid, &qp)) return (error); lockp = MSGLOCK(qp); if (error = ipcaccess(&qp->msg_perm, MSG_R, CRED())) goto msgrcv_out; if (uap->msgsz < 0) { error = EINVAL; goto msgrcv_out; } smp = spmp = NULL; lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); findmsg: if (msgconv(uap->msqid, &qp1) != 0) return (EIDRM); if (qp1 != qp) { lockp = MSGLOCK(qp1); lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (EIDRM); } 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 ((unsigned)uap->msgsz < smp->msg_ts) if (!(uap->msgflg & MSG_NOERROR)) { error = E2BIG; goto msgrcv_out; } else sz = uap->msgsz; else sz = smp->msg_ts; if (copyout((caddr_t)&smp->msg_type, (caddr_t)uap->msgp, sizeof (smp->msg_type))) { error = EFAULT; goto msgrcv_out; } if (sz && copyout((caddr_t)(msg + msginfo.msgssz * smp->msg_spot), (caddr_t)uap->msgp + sizeof (smp->msg_type), sz)) { error = EFAULT; goto msgrcv_out; } rvp->r_val1 = sz; qp->msg_cbytes -= smp->msg_ts; qp->msg_lrpid = ttoproc(curthread)->p_pid; qp->msg_rtime = hrestime.tv_sec; msgfree(qp, spmp, smp); goto msgrcv_out; } if (uap->msgflg & IPC_NOWAIT) { error = ENOMSG; goto msgrcv_out; } qp->msg_perm.mode |= MSG_RWAIT; lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); if (!cv_wait_sig(&qp->msg_qnum_cv, &msg_lock)) return (EINTR); goto findmsg; msgrcv_out: lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (error); } /* * msgsnd - Msgsnd system call. */ static int msgsnd(register struct msgsnda *uap, rval_t *rvp) { struct msqid_ds *qp; /* ptr to associated q */ register struct msg *mp; /* ptr to allocated msg hdr */ register int cnt; /* byte count */ register ulong spot; /* msg pool allocation spot */ register struct msglock *lockp; struct msqid_ds *qp1; long type; /* msg type */ int error; ASSERT(MUTEX_HELD(&msg_lock)); CPU_STAT_ADD(CPU, cpu_sysinfo.msg, 1); /* bump msg send/rcv count */ if (error = msgconv(uap->msqid, &qp)) return (error); lockp = MSGLOCK(qp); if (error = ipcaccess(&qp->msg_perm, MSG_W, CRED())) goto msgsnd_out; if ((cnt = uap->msgsz) < 0 || cnt > msginfo.msgmax) { error = EINVAL; goto msgsnd_out; } if (copyin((caddr_t)uap->msgp, (caddr_t)&type, sizeof (type))) { error = EFAULT; goto msgsnd_out; } if (type < 1) { error = EINVAL; goto msgsnd_out; } lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); getres: /* Be sure that q has not been removed. */ if (msgconv(uap->msqid, &qp1) != 0) return (EIDRM); if (qp1 != qp) { lockp = MSGLOCK(qp1); lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (EIDRM); } /* Allocate space on q, message header, & buffer space. */ if (cnt + qp->msg_cbytes > (uint)qp->msg_qbytes) { if (uap->msgflg & IPC_NOWAIT) { error = EAGAIN; goto msgsnd_out; } qp->msg_perm.mode |= MSG_WWAIT; lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); if (!cv_wait_sig(&qp->msg_cv, &msg_lock)) { if (error = msgconv(uap->msqid, &qp1)) { return (error); } error = EINTR; if (qp1 != qp) { lockp = MSGLOCK(qp1); lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (error); } qp->msg_perm.mode &= ~MSG_WWAIT; cv_broadcast(&qp->msg_cv); goto msgsnd_out; } goto getres; } if (msgfp == NULL) { if (uap->msgflg & IPC_NOWAIT) { error = EAGAIN; goto msgsnd_out; } lockp->msglock_lock = 0; if (!cv_wait_sig(&msgfp_cv, &msg_lock)) { if (error = msgconv(uap->msqid, &qp1)) return (error); error = EINTR; if (qp1 != qp) { lockp = MSGLOCK(qp1); lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (error); } goto msgsnd_out; } goto getres; } mp = msgfp; msgfp = mp->msg_next; mutex_enter(&maplock(msgmap)); if (cnt && (spot = rmalloc_locked(msgmap, btoq(cnt))) == NULL) { if (uap->msgflg & IPC_NOWAIT) { error = EAGAIN; mutex_exit(&maplock(msgmap)); goto msgsnd_out1; } mapwant(msgmap)++; mp->msg_next = msgfp; if (msgfp == NULL) cv_broadcast(&msgfp_cv); msgfp = mp; lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); mutex_exit(&msg_lock); if (!cv_wait_sig(&map_cv(msgmap), &maplock(msgmap))) { mutex_exit(&maplock(msgmap)); mutex_enter(&msg_lock); if (error = msgconv(uap->msqid, &qp1)) return (error); error = EINTR; if (qp1 != qp) { lockp = MSGLOCK(qp1); lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (error); } goto msgsnd_out; } else { mutex_exit(&maplock(msgmap)); mutex_enter(&msg_lock); } goto getres; } else mutex_exit(&maplock(msgmap)); /* Everything is available, copy in text and put msg on q. */ if (cnt && copyin((caddr_t)uap->msgp + sizeof (type), (caddr_t)(msg + msginfo.msgssz * --spot), cnt)) { error = EFAULT; rmfree(msgmap, btoq(cnt), spot + 1); goto msgsnd_out1; } qp->msg_qnum++; qp->msg_cbytes += cnt; qp->msg_lspid = curproc->p_pid; qp->msg_stime = hrestime.tv_sec; mp->msg_next = NULL; mp->msg_type = type; mp->msg_ts = (ushort)cnt; mp->msg_spot = (short)(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; cv_broadcast(&qp->msg_qnum_cv); } rvp->r_val1 = 0; goto msgsnd_out; msgsnd_out1: mp->msg_next = msgfp; if (msgfp == NULL) cv_broadcast(&msgfp_cv); msgfp = mp; msgsnd_out: lockp->msglock_lock = 0; cv_broadcast(&lockp->msglock_cv); return (error); } /* * msgsys - System entry point for msgctl, msgget, msgrcv, and msgsnd * system calls. */ static int msgsys(register struct msgsysa *uap, rval_t *rvp) { register int error; mutex_enter(&msg_lock); switch (uap->opcode) { case MSGGET: error = msgget((struct msggeta *)uap, rvp); break; case MSGCTL: error = msgctl((struct msgctla *)uap, rvp); break; case MSGRCV: error = msgrcv((struct msgrcva *)uap, rvp); break; case MSGSND: error = msgsnd((struct msgsnda *)uap, rvp); break; default: error = EINVAL; break; } mutex_exit(&msg_lock); return (error); }