sysv_msg.c revision 91703
1234353Sdim/* $FreeBSD: head/sys/kern/sysv_msg.c 91703 2002-03-05 18:57:36Z jhb $ */ 2193323Sed 3193323Sed/* 4193323Sed * Implementation of SVID messages 5193323Sed * 6193323Sed * Author: Daniel Boulet 7193323Sed * 8193323Sed * Copyright 1993 Daniel Boulet and RTMX Inc. 9193323Sed * 10193323Sed * This system call was implemented by Daniel Boulet under contract from RTMX. 11193323Sed * 12193323Sed * Redistribution and use in source forms, with and without modification, 13226633Sdim * are permitted provided that this entire comment appears intact. 14263508Sdim * 15263508Sdim * Redistribution in binary form may occur without any restrictions. 16263508Sdim * Obviously, it would be nice if you gave credit where credit is due 17263508Sdim * but requiring it would be too onerous. 18263508Sdim * 19263508Sdim * This software is provided ``AS IS'' without any warranties of any kind. 20263508Sdim */ 21263508Sdim 22263508Sdim#include "opt_sysvipc.h" 23226633Sdim 24193323Sed#include <sys/param.h> 25249423Sdim#include <sys/systm.h> 26249423Sdim#include <sys/sysproto.h> 27249423Sdim#include <sys/kernel.h> 28249423Sdim#include <sys/proc.h> 29193323Sed#include <sys/lock.h> 30249423Sdim#include <sys/mutex.h> 31249423Sdim#include <sys/msg.h> 32193323Sed#include <sys/syscall.h> 33193323Sed#include <sys/sysent.h> 34193323Sed#include <sys/sysctl.h> 35249423Sdim#include <sys/malloc.h> 36199511Srdivacky#include <sys/jail.h> 37249423Sdim 38199511Srdivackystatic MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues"); 39199511Srdivacky 40199511Srdivackystatic void msginit __P((void)); 41193323Sedstatic int msgunload __P((void)); 42249423Sdimstatic int sysvmsg_modload __P((struct module *, int, void *)); 43193323Sed 44226633Sdim#define MSG_DEBUG 45249423Sdim#undef MSG_DEBUG_OK 46249423Sdim 47226633Sdimstatic void msg_freehdr __P((struct msg *msghdr)); 48226633Sdim 49226633Sdim/* XXX casting to (sy_call_t *) is bogus, as usual. */ 50193323Sedstatic sy_call_t *msgcalls[] = { 51249423Sdim (sy_call_t *)msgctl, (sy_call_t *)msgget, 52193323Sed (sy_call_t *)msgsnd, (sy_call_t *)msgrcv 53193323Sed}; 54249423Sdim 55249423Sdimstruct msg { 56263508Sdim struct msg *msg_next; /* next msg in the chain */ 57234353Sdim long msg_type; /* type of this message */ 58193323Sed /* >0 -> type of this message */ 59193323Sed /* 0 -> free header */ 60249423Sdim u_short msg_ts; /* size of this message */ 61249423Sdim short msg_spot; /* location of start of msg in buffer */ 62263508Sdim}; 63263508Sdim 64226633Sdim 65226633Sdim#ifndef MSGSSZ 66263508Sdim#define MSGSSZ 8 /* Each segment must be 2^N long */ 67263508Sdim#endif 68263508Sdim#ifndef MSGSEG 69263508Sdim#define MSGSEG 2048 /* must be less than 32767 */ 70263508Sdim#endif 71263508Sdim#define MSGMAX (MSGSSZ*MSGSEG) 72249423Sdim#ifndef MSGMNB 73263508Sdim#define MSGMNB 2048 /* max # of bytes in a queue */ 74249423Sdim#endif 75249423Sdim#ifndef MSGMNI 76249423Sdim#define MSGMNI 40 77223017Sdim#endif 78223017Sdim#ifndef MSGTQL 79249423Sdim#define MSGTQL 40 80249423Sdim#endif 81249423Sdim 82193323Sed/* 83193323Sed * Based on the configuration parameters described in an SVR2 (yes, two) 84193323Sed * config(1m) man page. 85193323Sed * 86193323Sed * Each message is broken up and stored in segments that are msgssz bytes 87193323Sed * long. For efficiency reasons, this should be a power of two. Also, 88239462Sdim * it doesn't make sense if it is less than 8 or greater than about 256. 89243830Sdim * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of 90193323Sed * two between 8 and 1024 inclusive (and panic's if it isn't). 91193323Sed */ 92223017Sdimstruct msginfo msginfo = { 93193323Sed MSGMAX, /* max chars in a message */ 94193323Sed MSGMNI, /* # of message queue identifiers */ 95193323Sed MSGMNB, /* max chars in a queue */ 96193323Sed MSGTQL, /* max messages in system */ 97193323Sed MSGSSZ, /* size of a message segment */ 98193323Sed /* (must be small power of 2 greater than 4) */ 99193323Sed MSGSEG /* number of message segments */ 100193323Sed}; 101193323Sed 102193323Sed/* 103193323Sed * macros to convert between msqid_ds's and msqid's. 104193323Sed * (specific to this implementation) 105193323Sed */ 106193323Sed#define MSQID(ix,ds) ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000)) 107193323Sed#define MSQID_IX(id) ((id) & 0xffff) 108193323Sed#define MSQID_SEQ(id) (((id) >> 16) & 0xffff) 109193323Sed 110193323Sed/* 111193323Sed * The rest of this file is specific to this particular implementation. 112193323Sed */ 113193323Sed 114193323Sedstruct msgmap { 115193323Sed short next; /* next segment in buffer */ 116239462Sdim /* -1 -> available */ 117239462Sdim /* 0..(MSGSEG-1) -> index of next segment */ 118239462Sdim}; 119239462Sdim 120221345Sdim#define MSG_LOCKED 01000 /* Is this msqid_ds locked? */ 121226633Sdim 122239462Sdimstatic int nfree_msgmaps; /* # of free map entries */ 123243830Sdimstatic short free_msgmaps; /* head of linked list of free map entries */ 124234353Sdimstatic struct msg *free_msghdrs;/* list of free msg headers */ 125234353Sdimstatic char *msgpool; /* MSGMAX byte long msg buffer pool */ 126234353Sdimstatic struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 127234353Sdimstatic struct msg *msghdrs; /* MSGTQL msg headers */ 128234353Sdimstatic struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ 129234353Sdim 130234353Sdimstatic void 131234353Sdimmsginit() 132234353Sdim{ 133234353Sdim register int i; 134234353Sdim 135234353Sdim TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg); 136234353Sdim TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz); 137234353Sdim msginfo.msgmax = msginfo.msgseg * msginfo.msgssz; 138234353Sdim TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni); 139234353Sdim 140234353Sdim msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK); 141234353Sdim if (msgpool == NULL) 142234353Sdim panic("msgpool is NULL"); 143234353Sdim msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK); 144234353Sdim if (msgmaps == NULL) 145234353Sdim panic("msgmaps is NULL"); 146234353Sdim msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK); 147234353Sdim if (msghdrs == NULL) 148234353Sdim panic("msghdrs is NULL"); 149234353Sdim msqids = malloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK); 150239462Sdim if (msqids == NULL) 151239462Sdim panic("msqids is NULL"); 152239462Sdim 153239462Sdim /* 154226633Sdim * msginfo.msgssz should be a power of two for efficiency reasons. 155193323Sed * It is also pretty silly if msginfo.msgssz is less than 8 156263508Sdim * or greater than about 256 so ... 157263508Sdim */ 158221345Sdim 159263508Sdim i = 8; 160263508Sdim while (i < 1024 && i != msginfo.msgssz) 161263508Sdim i <<= 1; 162263508Sdim if (i != msginfo.msgssz) { 163193323Sed printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, 164193323Sed msginfo.msgssz); 165263508Sdim panic("msginfo.msgssz not a small power of 2"); 166263508Sdim } 167263508Sdim 168263508Sdim if (msginfo.msgseg > 32767) { 169193323Sed printf("msginfo.msgseg=%d\n", msginfo.msgseg); 170226633Sdim panic("msginfo.msgseg > 32767"); 171263508Sdim } 172263508Sdim 173263508Sdim if (msgmaps == NULL) 174226633Sdim panic("msgmaps is NULL"); 175263508Sdim 176263508Sdim for (i = 0; i < msginfo.msgseg; i++) { 177263508Sdim if (i > 0) 178263508Sdim msgmaps[i-1].next = i; 179263508Sdim msgmaps[i].next = -1; /* implies entry is available */ 180263508Sdim } 181193323Sed free_msgmaps = 0; 182263508Sdim nfree_msgmaps = msginfo.msgseg; 183263508Sdim 184263508Sdim if (msghdrs == NULL) 185263508Sdim panic("msghdrs is NULL"); 186263508Sdim 187263508Sdim for (i = 0; i < msginfo.msgtql; i++) { 188263508Sdim msghdrs[i].msg_type = 0; 189263508Sdim if (i > 0) 190193323Sed msghdrs[i-1].msg_next = &msghdrs[i]; 191226633Sdim msghdrs[i].msg_next = NULL; 192263508Sdim } 193263508Sdim free_msghdrs = &msghdrs[0]; 194226633Sdim 195226633Sdim if (msqids == NULL) 196263508Sdim panic("msqids is NULL"); 197263508Sdim 198263508Sdim for (i = 0; i < msginfo.msgmni; i++) { 199223017Sdim msqids[i].msg_qbytes = 0; /* implies entry is available */ 200263508Sdim msqids[i].msg_perm.seq = 0; /* reset to a known value */ 201263508Sdim msqids[i].msg_perm.mode = 0; 202263508Sdim } 203239462Sdim} 204263508Sdim 205263508Sdimstatic int 206263508Sdimmsgunload() 207263508Sdim{ 208243830Sdim struct msqid_ds *msqptr; 209243830Sdim int msqid; 210243830Sdim 211223017Sdim for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 212249423Sdim /* 213243830Sdim * Look for an unallocated and unlocked msqid_ds. 214243830Sdim * msqid_ds's can be locked by msgsnd or msgrcv while 215263508Sdim * they are copying the message in/out. We can't 216263508Sdim * re-use the entry until they release it. 217263508Sdim */ 218243830Sdim msqptr = &msqids[msqid]; 219263508Sdim if (msqptr->msg_qbytes != 0 || 220249423Sdim (msqptr->msg_perm.mode & MSG_LOCKED) != 0) 221251662Sdim break; 222251662Sdim } 223251662Sdim if (msqid != msginfo.msgmni) 224251662Sdim return (EBUSY); 225251662Sdim 226251662Sdim free(msgpool, M_MSG); 227251662Sdim free(msgmaps, M_MSG); 228251662Sdim free(msghdrs, M_MSG); 229251662Sdim free(msqids, M_MSG); 230251662Sdim return (0); 231251662Sdim} 232251662Sdim 233251662Sdim 234251662Sdimstatic int 235251662Sdimsysvmsg_modload(struct module *module, int cmd, void *arg) 236251662Sdim{ 237251662Sdim int error = 0; 238263508Sdim 239263508Sdim switch (cmd) { 240263508Sdim case MOD_LOAD: 241263508Sdim msginit(); 242263508Sdim break; 243263508Sdim case MOD_UNLOAD: 244263508Sdim error = msgunload(); 245263508Sdim break; 246263508Sdim case MOD_SHUTDOWN: 247263508Sdim break; 248193323Sed default: 249193323Sed error = EINVAL; 250193323Sed break; 251193323Sed } 252193323Sed return (error); 253193323Sed} 254263508Sdim 255243830Sdimstatic moduledata_t sysvmsg_mod = { 256239462Sdim "sysvmsg", 257239462Sdim &sysvmsg_modload, 258193323Sed NULL 259224145Sdim}; 260193323Sed 261239462SdimSYSCALL_MODULE_HELPER(msgsys); 262193323SedSYSCALL_MODULE_HELPER(msgctl); 263193323SedSYSCALL_MODULE_HELPER(msgget); 264239462SdimSYSCALL_MODULE_HELPER(msgsnd); 265239462SdimSYSCALL_MODULE_HELPER(msgrcv); 266193323Sed 267239462SdimDECLARE_MODULE(sysvmsg, sysvmsg_mod, 268193323Sed SI_SUB_SYSV_MSG, SI_ORDER_FIRST); 269263508SdimMODULE_VERSION(sysvmsg, 1); 270263508Sdim 271243830Sdim/* 272263508Sdim * Entry point for all MSG calls 273239462Sdim * 274239462Sdim * MPSAFE 275226633Sdim */ 276226633Sdimint 277226633Sdimmsgsys(td, uap) 278239462Sdim struct thread *td; 279226633Sdim /* XXX actually varargs. */ 280226633Sdim struct msgsys_args /* { 281239462Sdim u_int which; 282239462Sdim int a2; 283226633Sdim int a3; 284239462Sdim int a4; 285226633Sdim int a5; 286239462Sdim int a6; 287239462Sdim } */ *uap; 288239462Sdim{ 289239462Sdim int error; 290239462Sdim 291239462Sdim if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 292263508Sdim return (ENOSYS); 293263508Sdim if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) 294263508Sdim return (EINVAL); 295263508Sdim mtx_lock(&Giant); 296263508Sdim error = (*msgcalls[uap->which])(td, &uap->a2); 297263508Sdim mtx_unlock(&Giant); 298263508Sdim return (error); 299249423Sdim} 300239462Sdim 301249423Sdimstatic void 302239462Sdimmsg_freehdr(msghdr) 303193323Sed struct msg *msghdr; 304193323Sed{ 305221345Sdim while (msghdr->msg_ts > 0) { 306193323Sed short next; 307193323Sed if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 308193323Sed panic("msghdr->msg_spot out of range"); 309193323Sed next = msgmaps[msghdr->msg_spot].next; 310224145Sdim msgmaps[msghdr->msg_spot].next = free_msgmaps; 311193323Sed free_msgmaps = msghdr->msg_spot; 312263508Sdim nfree_msgmaps++; 313263508Sdim msghdr->msg_spot = next; 314263508Sdim if (msghdr->msg_ts >= msginfo.msgssz) 315224145Sdim msghdr->msg_ts -= msginfo.msgssz; 316193323Sed else 317239462Sdim msghdr->msg_ts = 0; 318193323Sed } 319239462Sdim if (msghdr->msg_spot != -1) 320239462Sdim panic("msghdr->msg_spot != -1"); 321239462Sdim msghdr->msg_next = free_msghdrs; 322239462Sdim free_msghdrs = msghdr; 323239462Sdim} 324193323Sed 325239462Sdim#ifndef _SYS_SYSPROTO_H_ 326193323Sedstruct msgctl_args { 327239462Sdim int msqid; 328226633Sdim int cmd; 329263508Sdim struct msqid_ds *buf; 330263508Sdim}; 331263508Sdim#endif 332193323Sed 333263508Sdim/* 334263508Sdim * MPSAFE 335263508Sdim */ 336263508Sdimint 337263508Sdimmsgctl(td, uap) 338263508Sdim struct thread *td; 339263508Sdim register struct msgctl_args *uap; 340263508Sdim{ 341263508Sdim int msqid = uap->msqid; 342263508Sdim int cmd = uap->cmd; 343263508Sdim struct msqid_ds *user_msqptr = uap->buf; 344263508Sdim int rval, error; 345263508Sdim struct msqid_ds msqbuf; 346263508Sdim register struct msqid_ds *msqptr; 347263508Sdim 348263508Sdim#ifdef MSG_DEBUG_OK 349193323Sed printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr); 350263508Sdim#endif 351263508Sdim if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 352263508Sdim return (ENOSYS); 353263508Sdim 354263508Sdim mtx_lock(&Giant); 355263508Sdim msqid = IPCID_TO_IX(msqid); 356193323Sed 357223017Sdim if (msqid < 0 || msqid >= msginfo.msgmni) { 358249423Sdim#ifdef MSG_DEBUG_OK 359234353Sdim printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 360243830Sdim msginfo.msgmni); 361263508Sdim#endif 362249423Sdim error = EINVAL; 363249423Sdim goto done2; 364249423Sdim } 365263508Sdim 366249423Sdim msqptr = &msqids[msqid]; 367249423Sdim 368249423Sdim if (msqptr->msg_qbytes == 0) { 369263508Sdim#ifdef MSG_DEBUG_OK 370249423Sdim printf("no such msqid\n"); 371249423Sdim#endif 372249423Sdim error = EINVAL; 373251662Sdim goto done2; 374251662Sdim } 375263508Sdim if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 376263508Sdim#ifdef MSG_DEBUG_OK 377263508Sdim printf("wrong sequence number\n"); 378263508Sdim#endif 379251662Sdim error = EINVAL; 380263508Sdim goto done2; 381263508Sdim } 382263508Sdim 383249423Sdim error = 0; 384263508Sdim rval = 0; 385263508Sdim 386263508Sdim switch (cmd) { 387263508Sdim 388249423Sdim case IPC_RMID: 389263508Sdim { 390263508Sdim struct msg *msghdr; 391263508Sdim if ((error = ipcperm(td, &msqptr->msg_perm, IPC_M))) 392249423Sdim goto done2; 393249423Sdim /* Free the message headers */ 394263508Sdim msghdr = msqptr->msg_first; 395263508Sdim while (msghdr != NULL) { 396263508Sdim struct msg *msghdr_tmp; 397263508Sdim 398263508Sdim /* Free the segments of each message */ 399263508Sdim msqptr->msg_cbytes -= msghdr->msg_ts; 400263508Sdim msqptr->msg_qnum--; 401263508Sdim msghdr_tmp = msghdr; 402263508Sdim msghdr = msghdr->msg_next; 403263508Sdim msg_freehdr(msghdr_tmp); 404263508Sdim } 405263508Sdim 406263508Sdim if (msqptr->msg_cbytes != 0) 407263508Sdim panic("msg_cbytes is screwed up"); 408263508Sdim if (msqptr->msg_qnum != 0) 409263508Sdim panic("msg_qnum is screwed up"); 410249423Sdim 411249423Sdim msqptr->msg_qbytes = 0; /* Mark it as free */ 412249423Sdim 413249423Sdim wakeup((caddr_t)msqptr); 414263508Sdim } 415263508Sdim 416263508Sdim break; 417249423Sdim 418249423Sdim case IPC_SET: 419263508Sdim if ((error = ipcperm(td, &msqptr->msg_perm, IPC_M))) 420263508Sdim goto done2; 421263508Sdim if ((error = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) 422249423Sdim goto done2; 423249423Sdim if (msqbuf.msg_qbytes > msqptr->msg_qbytes) { 424263508Sdim error = suser_td(td); 425263508Sdim if (error) 426263508Sdim goto done2; 427263508Sdim } 428263508Sdim if (msqbuf.msg_qbytes > msginfo.msgmnb) { 429263508Sdim#ifdef MSG_DEBUG_OK 430263508Sdim printf("can't increase msg_qbytes beyond %d (truncating)\n", 431263508Sdim msginfo.msgmnb); 432263508Sdim#endif 433263508Sdim msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 434263508Sdim } 435263508Sdim if (msqbuf.msg_qbytes == 0) { 436263508Sdim#ifdef MSG_DEBUG_OK 437263508Sdim printf("can't reduce msg_qbytes to 0\n"); 438263508Sdim#endif 439263508Sdim error = EINVAL; /* non-standard errno! */ 440263508Sdim goto done2; 441263508Sdim } 442263508Sdim msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 443263508Sdim msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 444263508Sdim msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 445263508Sdim (msqbuf.msg_perm.mode & 0777); 446263508Sdim msqptr->msg_qbytes = msqbuf.msg_qbytes; 447263508Sdim msqptr->msg_ctime = time_second; 448263508Sdim break; 449263508Sdim 450263508Sdim case IPC_STAT: 451263508Sdim if ((error = ipcperm(td, &msqptr->msg_perm, IPC_R))) { 452263508Sdim#ifdef MSG_DEBUG_OK 453263508Sdim printf("requester doesn't have read access\n"); 454263508Sdim#endif 455263508Sdim goto done2; 456263508Sdim } 457263508Sdim error = copyout((caddr_t)msqptr, user_msqptr, 458263508Sdim sizeof(struct msqid_ds)); 459263508Sdim break; 460263508Sdim 461263508Sdim default: 462263508Sdim#ifdef MSG_DEBUG_OK 463263508Sdim printf("invalid command %d\n", cmd); 464263508Sdim#endif 465263508Sdim error = EINVAL; 466263508Sdim goto done2; 467263508Sdim } 468263508Sdim 469263508Sdim if (error == 0) 470263508Sdim td->td_retval[0] = rval; 471263508Sdimdone2: 472263508Sdim mtx_unlock(&Giant); 473263508Sdim return(error); 474263508Sdim} 475263508Sdim 476263508Sdim#ifndef _SYS_SYSPROTO_H_ 477249423Sdimstruct msgget_args { 478249423Sdim key_t key; 479249423Sdim int msgflg; 480263508Sdim}; 481249423Sdim#endif 482249423Sdim 483249423Sdim/* 484249423Sdim * MPSAFE 485263508Sdim */ 486263508Sdimint 487263508Sdimmsgget(td, uap) 488249423Sdim struct thread *td; 489249423Sdim register struct msgget_args *uap; 490263508Sdim{ 491249423Sdim int msqid, error = 0; 492249423Sdim int key = uap->key; 493249423Sdim int msgflg = uap->msgflg; 494263508Sdim struct ucred *cred = td->td_ucred; 495263508Sdim register struct msqid_ds *msqptr = NULL; 496249423Sdim 497263508Sdim#ifdef MSG_DEBUG_OK 498263508Sdim printf("msgget(0x%x, 0%o)\n", key, msgflg); 499263508Sdim#endif 500263508Sdim 501263508Sdim if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 502263508Sdim return (ENOSYS); 503263508Sdim 504263508Sdim mtx_lock(&Giant); 505263508Sdim if (key != IPC_PRIVATE) { 506263508Sdim for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 507263508Sdim msqptr = &msqids[msqid]; 508263508Sdim if (msqptr->msg_qbytes != 0 && 509263508Sdim msqptr->msg_perm.key == key) 510263508Sdim break; 511263508Sdim } 512263508Sdim if (msqid < msginfo.msgmni) { 513263508Sdim#ifdef MSG_DEBUG_OK 514263508Sdim printf("found public key\n"); 515263508Sdim#endif 516263508Sdim if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 517263508Sdim#ifdef MSG_DEBUG_OK 518263508Sdim printf("not exclusive\n"); 519263508Sdim#endif 520263508Sdim error = EEXIST; 521263508Sdim goto done2; 522263508Sdim } 523263508Sdim if ((error = ipcperm(td, &msqptr->msg_perm, msgflg & 0700 ))) { 524263508Sdim#ifdef MSG_DEBUG_OK 525263508Sdim printf("requester doesn't have 0%o access\n", 526263508Sdim msgflg & 0700); 527263508Sdim#endif 528263508Sdim goto done2; 529263508Sdim } 530263508Sdim goto found; 531263508Sdim } 532263508Sdim } 533263508Sdim 534263508Sdim#ifdef MSG_DEBUG_OK 535263508Sdim printf("need to allocate the msqid_ds\n"); 536263508Sdim#endif 537263508Sdim if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 538263508Sdim for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 539263508Sdim /* 540263508Sdim * Look for an unallocated and unlocked msqid_ds. 541263508Sdim * msqid_ds's can be locked by msgsnd or msgrcv while 542263508Sdim * they are copying the message in/out. We can't 543263508Sdim * re-use the entry until they release it. 544263508Sdim */ 545263508Sdim msqptr = &msqids[msqid]; 546263508Sdim if (msqptr->msg_qbytes == 0 && 547263508Sdim (msqptr->msg_perm.mode & MSG_LOCKED) == 0) 548263508Sdim break; 549263508Sdim } 550 if (msqid == msginfo.msgmni) { 551#ifdef MSG_DEBUG_OK 552 printf("no more msqid_ds's available\n"); 553#endif 554 error = ENOSPC; 555 goto done2; 556 } 557#ifdef MSG_DEBUG_OK 558 printf("msqid %d is available\n", msqid); 559#endif 560 msqptr->msg_perm.key = key; 561 msqptr->msg_perm.cuid = cred->cr_uid; 562 msqptr->msg_perm.uid = cred->cr_uid; 563 msqptr->msg_perm.cgid = cred->cr_gid; 564 msqptr->msg_perm.gid = cred->cr_gid; 565 msqptr->msg_perm.mode = (msgflg & 0777); 566 /* Make sure that the returned msqid is unique */ 567 msqptr->msg_perm.seq++; 568 msqptr->msg_first = NULL; 569 msqptr->msg_last = NULL; 570 msqptr->msg_cbytes = 0; 571 msqptr->msg_qnum = 0; 572 msqptr->msg_qbytes = msginfo.msgmnb; 573 msqptr->msg_lspid = 0; 574 msqptr->msg_lrpid = 0; 575 msqptr->msg_stime = 0; 576 msqptr->msg_rtime = 0; 577 msqptr->msg_ctime = time_second; 578 } else { 579#ifdef MSG_DEBUG_OK 580 printf("didn't find it and wasn't asked to create it\n"); 581#endif 582 error = ENOENT; 583 goto done2; 584 } 585 586found: 587 /* Construct the unique msqid */ 588 td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 589done2: 590 mtx_unlock(&Giant); 591 return (error); 592} 593 594#ifndef _SYS_SYSPROTO_H_ 595struct msgsnd_args { 596 int msqid; 597 void *msgp; 598 size_t msgsz; 599 int msgflg; 600}; 601#endif 602 603/* 604 * MPSAFE 605 */ 606int 607msgsnd(td, uap) 608 struct thread *td; 609 register struct msgsnd_args *uap; 610{ 611 int msqid = uap->msqid; 612 void *user_msgp = uap->msgp; 613 size_t msgsz = uap->msgsz; 614 int msgflg = uap->msgflg; 615 int segs_needed, error = 0; 616 register struct msqid_ds *msqptr; 617 register struct msg *msghdr; 618 short next; 619 620#ifdef MSG_DEBUG_OK 621 printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, 622 msgflg); 623#endif 624 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 625 return (ENOSYS); 626 627 mtx_lock(&Giant); 628 msqid = IPCID_TO_IX(msqid); 629 630 if (msqid < 0 || msqid >= msginfo.msgmni) { 631#ifdef MSG_DEBUG_OK 632 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 633 msginfo.msgmni); 634#endif 635 error = EINVAL; 636 goto done2; 637 } 638 639 msqptr = &msqids[msqid]; 640 if (msqptr->msg_qbytes == 0) { 641#ifdef MSG_DEBUG_OK 642 printf("no such message queue id\n"); 643#endif 644 error = EINVAL; 645 goto done2; 646 } 647 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 648#ifdef MSG_DEBUG_OK 649 printf("wrong sequence number\n"); 650#endif 651 error = EINVAL; 652 goto done2; 653 } 654 655 if ((error = ipcperm(td, &msqptr->msg_perm, IPC_W))) { 656#ifdef MSG_DEBUG_OK 657 printf("requester doesn't have write access\n"); 658#endif 659 goto done2; 660 } 661 662 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 663#ifdef MSG_DEBUG_OK 664 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 665 segs_needed); 666#endif 667 for (;;) { 668 int need_more_resources = 0; 669 670 /* 671 * check msgsz 672 * (inside this loop in case msg_qbytes changes while we sleep) 673 */ 674 675 if (msgsz > msqptr->msg_qbytes) { 676#ifdef MSG_DEBUG_OK 677 printf("msgsz > msqptr->msg_qbytes\n"); 678#endif 679 error = EINVAL; 680 goto done2; 681 } 682 683 if (msqptr->msg_perm.mode & MSG_LOCKED) { 684#ifdef MSG_DEBUG_OK 685 printf("msqid is locked\n"); 686#endif 687 need_more_resources = 1; 688 } 689 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 690#ifdef MSG_DEBUG_OK 691 printf("msgsz + msg_cbytes > msg_qbytes\n"); 692#endif 693 need_more_resources = 1; 694 } 695 if (segs_needed > nfree_msgmaps) { 696#ifdef MSG_DEBUG_OK 697 printf("segs_needed > nfree_msgmaps\n"); 698#endif 699 need_more_resources = 1; 700 } 701 if (free_msghdrs == NULL) { 702#ifdef MSG_DEBUG_OK 703 printf("no more msghdrs\n"); 704#endif 705 need_more_resources = 1; 706 } 707 708 if (need_more_resources) { 709 int we_own_it; 710 711 if ((msgflg & IPC_NOWAIT) != 0) { 712#ifdef MSG_DEBUG_OK 713 printf("need more resources but caller doesn't want to wait\n"); 714#endif 715 error = EAGAIN; 716 goto done2; 717 } 718 719 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 720#ifdef MSG_DEBUG_OK 721 printf("we don't own the msqid_ds\n"); 722#endif 723 we_own_it = 0; 724 } else { 725 /* Force later arrivals to wait for our 726 request */ 727#ifdef MSG_DEBUG_OK 728 printf("we own the msqid_ds\n"); 729#endif 730 msqptr->msg_perm.mode |= MSG_LOCKED; 731 we_own_it = 1; 732 } 733#ifdef MSG_DEBUG_OK 734 printf("goodnight\n"); 735#endif 736 error = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, 737 "msgwait", 0); 738#ifdef MSG_DEBUG_OK 739 printf("good morning, error=%d\n", error); 740#endif 741 if (we_own_it) 742 msqptr->msg_perm.mode &= ~MSG_LOCKED; 743 if (error != 0) { 744#ifdef MSG_DEBUG_OK 745 printf("msgsnd: interrupted system call\n"); 746#endif 747 error = EINTR; 748 goto done2; 749 } 750 751 /* 752 * Make sure that the msq queue still exists 753 */ 754 755 if (msqptr->msg_qbytes == 0) { 756#ifdef MSG_DEBUG_OK 757 printf("msqid deleted\n"); 758#endif 759 error = EIDRM; 760 goto done2; 761 } 762 763 } else { 764#ifdef MSG_DEBUG_OK 765 printf("got all the resources that we need\n"); 766#endif 767 break; 768 } 769 } 770 771 /* 772 * We have the resources that we need. 773 * Make sure! 774 */ 775 776 if (msqptr->msg_perm.mode & MSG_LOCKED) 777 panic("msg_perm.mode & MSG_LOCKED"); 778 if (segs_needed > nfree_msgmaps) 779 panic("segs_needed > nfree_msgmaps"); 780 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) 781 panic("msgsz + msg_cbytes > msg_qbytes"); 782 if (free_msghdrs == NULL) 783 panic("no more msghdrs"); 784 785 /* 786 * Re-lock the msqid_ds in case we page-fault when copying in the 787 * message 788 */ 789 790 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) 791 panic("msqid_ds is already locked"); 792 msqptr->msg_perm.mode |= MSG_LOCKED; 793 794 /* 795 * Allocate a message header 796 */ 797 798 msghdr = free_msghdrs; 799 free_msghdrs = msghdr->msg_next; 800 msghdr->msg_spot = -1; 801 msghdr->msg_ts = msgsz; 802 803 /* 804 * Allocate space for the message 805 */ 806 807 while (segs_needed > 0) { 808 if (nfree_msgmaps <= 0) 809 panic("not enough msgmaps"); 810 if (free_msgmaps == -1) 811 panic("nil free_msgmaps"); 812 next = free_msgmaps; 813 if (next <= -1) 814 panic("next too low #1"); 815 if (next >= msginfo.msgseg) 816 panic("next out of range #1"); 817#ifdef MSG_DEBUG_OK 818 printf("allocating segment %d to message\n", next); 819#endif 820 free_msgmaps = msgmaps[next].next; 821 nfree_msgmaps--; 822 msgmaps[next].next = msghdr->msg_spot; 823 msghdr->msg_spot = next; 824 segs_needed--; 825 } 826 827 /* 828 * Copy in the message type 829 */ 830 831 if ((error = copyin(user_msgp, &msghdr->msg_type, 832 sizeof(msghdr->msg_type))) != 0) { 833#ifdef MSG_DEBUG_OK 834 printf("error %d copying the message type\n", error); 835#endif 836 msg_freehdr(msghdr); 837 msqptr->msg_perm.mode &= ~MSG_LOCKED; 838 wakeup((caddr_t)msqptr); 839 goto done2; 840 } 841 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 842 843 /* 844 * Validate the message type 845 */ 846 847 if (msghdr->msg_type < 1) { 848 msg_freehdr(msghdr); 849 msqptr->msg_perm.mode &= ~MSG_LOCKED; 850 wakeup((caddr_t)msqptr); 851#ifdef MSG_DEBUG_OK 852 printf("mtype (%d) < 1\n", msghdr->msg_type); 853#endif 854 error = EINVAL; 855 goto done2; 856 } 857 858 /* 859 * Copy in the message body 860 */ 861 862 next = msghdr->msg_spot; 863 while (msgsz > 0) { 864 size_t tlen; 865 if (msgsz > msginfo.msgssz) 866 tlen = msginfo.msgssz; 867 else 868 tlen = msgsz; 869 if (next <= -1) 870 panic("next too low #2"); 871 if (next >= msginfo.msgseg) 872 panic("next out of range #2"); 873 if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 874 tlen)) != 0) { 875#ifdef MSG_DEBUG_OK 876 printf("error %d copying in message segment\n", error); 877#endif 878 msg_freehdr(msghdr); 879 msqptr->msg_perm.mode &= ~MSG_LOCKED; 880 wakeup((caddr_t)msqptr); 881 goto done2; 882 } 883 msgsz -= tlen; 884 user_msgp = (char *)user_msgp + tlen; 885 next = msgmaps[next].next; 886 } 887 if (next != -1) 888 panic("didn't use all the msg segments"); 889 890 /* 891 * We've got the message. Unlock the msqid_ds. 892 */ 893 894 msqptr->msg_perm.mode &= ~MSG_LOCKED; 895 896 /* 897 * Make sure that the msqid_ds is still allocated. 898 */ 899 900 if (msqptr->msg_qbytes == 0) { 901 msg_freehdr(msghdr); 902 wakeup((caddr_t)msqptr); 903 error = EIDRM; 904 goto done2; 905 } 906 907 /* 908 * Put the message into the queue 909 */ 910 911 if (msqptr->msg_first == NULL) { 912 msqptr->msg_first = msghdr; 913 msqptr->msg_last = msghdr; 914 } else { 915 msqptr->msg_last->msg_next = msghdr; 916 msqptr->msg_last = msghdr; 917 } 918 msqptr->msg_last->msg_next = NULL; 919 920 msqptr->msg_cbytes += msghdr->msg_ts; 921 msqptr->msg_qnum++; 922 msqptr->msg_lspid = td->td_proc->p_pid; 923 msqptr->msg_stime = time_second; 924 925 wakeup((caddr_t)msqptr); 926 td->td_retval[0] = 0; 927done2: 928 mtx_unlock(&Giant); 929 return (error); 930} 931 932#ifndef _SYS_SYSPROTO_H_ 933struct msgrcv_args { 934 int msqid; 935 void *msgp; 936 size_t msgsz; 937 long msgtyp; 938 int msgflg; 939}; 940#endif 941 942/* 943 * MPSAFE 944 */ 945int 946msgrcv(td, uap) 947 struct thread *td; 948 register struct msgrcv_args *uap; 949{ 950 int msqid = uap->msqid; 951 void *user_msgp = uap->msgp; 952 size_t msgsz = uap->msgsz; 953 long msgtyp = uap->msgtyp; 954 int msgflg = uap->msgflg; 955 size_t len; 956 register struct msqid_ds *msqptr; 957 register struct msg *msghdr; 958 int error = 0; 959 short next; 960 961#ifdef MSG_DEBUG_OK 962 printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, 963 msgsz, msgtyp, msgflg); 964#endif 965 966 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 967 return (ENOSYS); 968 969 mtx_lock(&Giant); 970 msqid = IPCID_TO_IX(msqid); 971 972 if (msqid < 0 || msqid >= msginfo.msgmni) { 973#ifdef MSG_DEBUG_OK 974 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 975 msginfo.msgmni); 976#endif 977 error = EINVAL; 978 goto done2; 979 } 980 981 msqptr = &msqids[msqid]; 982 if (msqptr->msg_qbytes == 0) { 983#ifdef MSG_DEBUG_OK 984 printf("no such message queue id\n"); 985#endif 986 error = EINVAL; 987 goto done2; 988 } 989 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 990#ifdef MSG_DEBUG_OK 991 printf("wrong sequence number\n"); 992#endif 993 error = EINVAL; 994 goto done2; 995 } 996 997 if ((error = ipcperm(td, &msqptr->msg_perm, IPC_R))) { 998#ifdef MSG_DEBUG_OK 999 printf("requester doesn't have read access\n"); 1000#endif 1001 goto done2; 1002 } 1003 1004 msghdr = NULL; 1005 while (msghdr == NULL) { 1006 if (msgtyp == 0) { 1007 msghdr = msqptr->msg_first; 1008 if (msghdr != NULL) { 1009 if (msgsz < msghdr->msg_ts && 1010 (msgflg & MSG_NOERROR) == 0) { 1011#ifdef MSG_DEBUG_OK 1012 printf("first message on the queue is too big (want %d, got %d)\n", 1013 msgsz, msghdr->msg_ts); 1014#endif 1015 error = E2BIG; 1016 goto done2; 1017 } 1018 if (msqptr->msg_first == msqptr->msg_last) { 1019 msqptr->msg_first = NULL; 1020 msqptr->msg_last = NULL; 1021 } else { 1022 msqptr->msg_first = msghdr->msg_next; 1023 if (msqptr->msg_first == NULL) 1024 panic("msg_first/last screwed up #1"); 1025 } 1026 } 1027 } else { 1028 struct msg *previous; 1029 struct msg **prev; 1030 1031 previous = NULL; 1032 prev = &(msqptr->msg_first); 1033 while ((msghdr = *prev) != NULL) { 1034 /* 1035 * Is this message's type an exact match or is 1036 * this message's type less than or equal to 1037 * the absolute value of a negative msgtyp? 1038 * Note that the second half of this test can 1039 * NEVER be true if msgtyp is positive since 1040 * msg_type is always positive! 1041 */ 1042 1043 if (msgtyp == msghdr->msg_type || 1044 msghdr->msg_type <= -msgtyp) { 1045#ifdef MSG_DEBUG_OK 1046 printf("found message type %d, requested %d\n", 1047 msghdr->msg_type, msgtyp); 1048#endif 1049 if (msgsz < msghdr->msg_ts && 1050 (msgflg & MSG_NOERROR) == 0) { 1051#ifdef MSG_DEBUG_OK 1052 printf("requested message on the queue is too big (want %d, got %d)\n", 1053 msgsz, msghdr->msg_ts); 1054#endif 1055 error = E2BIG; 1056 goto done2; 1057 } 1058 *prev = msghdr->msg_next; 1059 if (msghdr == msqptr->msg_last) { 1060 if (previous == NULL) { 1061 if (prev != 1062 &msqptr->msg_first) 1063 panic("msg_first/last screwed up #2"); 1064 msqptr->msg_first = 1065 NULL; 1066 msqptr->msg_last = 1067 NULL; 1068 } else { 1069 if (prev == 1070 &msqptr->msg_first) 1071 panic("msg_first/last screwed up #3"); 1072 msqptr->msg_last = 1073 previous; 1074 } 1075 } 1076 break; 1077 } 1078 previous = msghdr; 1079 prev = &(msghdr->msg_next); 1080 } 1081 } 1082 1083 /* 1084 * We've either extracted the msghdr for the appropriate 1085 * message or there isn't one. 1086 * If there is one then bail out of this loop. 1087 */ 1088 1089 if (msghdr != NULL) 1090 break; 1091 1092 /* 1093 * Hmph! No message found. Does the user want to wait? 1094 */ 1095 1096 if ((msgflg & IPC_NOWAIT) != 0) { 1097#ifdef MSG_DEBUG_OK 1098 printf("no appropriate message found (msgtyp=%d)\n", 1099 msgtyp); 1100#endif 1101 /* The SVID says to return ENOMSG. */ 1102 error = ENOMSG; 1103 goto done2; 1104 } 1105 1106 /* 1107 * Wait for something to happen 1108 */ 1109 1110#ifdef MSG_DEBUG_OK 1111 printf("msgrcv: goodnight\n"); 1112#endif 1113 error = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 1114 0); 1115#ifdef MSG_DEBUG_OK 1116 printf("msgrcv: good morning (error=%d)\n", error); 1117#endif 1118 1119 if (error != 0) { 1120#ifdef MSG_DEBUG_OK 1121 printf("msgsnd: interrupted system call\n"); 1122#endif 1123 error = EINTR; 1124 goto done2; 1125 } 1126 1127 /* 1128 * Make sure that the msq queue still exists 1129 */ 1130 1131 if (msqptr->msg_qbytes == 0 || 1132 msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 1133#ifdef MSG_DEBUG_OK 1134 printf("msqid deleted\n"); 1135#endif 1136 error = EIDRM; 1137 goto done2; 1138 } 1139 } 1140 1141 /* 1142 * Return the message to the user. 1143 * 1144 * First, do the bookkeeping (before we risk being interrupted). 1145 */ 1146 1147 msqptr->msg_cbytes -= msghdr->msg_ts; 1148 msqptr->msg_qnum--; 1149 msqptr->msg_lrpid = td->td_proc->p_pid; 1150 msqptr->msg_rtime = time_second; 1151 1152 /* 1153 * Make msgsz the actual amount that we'll be returning. 1154 * Note that this effectively truncates the message if it is too long 1155 * (since msgsz is never increased). 1156 */ 1157 1158#ifdef MSG_DEBUG_OK 1159 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 1160 msghdr->msg_ts); 1161#endif 1162 if (msgsz > msghdr->msg_ts) 1163 msgsz = msghdr->msg_ts; 1164 1165 /* 1166 * Return the type to the user. 1167 */ 1168 1169 error = copyout((caddr_t)&(msghdr->msg_type), user_msgp, 1170 sizeof(msghdr->msg_type)); 1171 if (error != 0) { 1172#ifdef MSG_DEBUG_OK 1173 printf("error (%d) copying out message type\n", error); 1174#endif 1175 msg_freehdr(msghdr); 1176 wakeup((caddr_t)msqptr); 1177 goto done2; 1178 } 1179 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 1180 1181 /* 1182 * Return the segments to the user 1183 */ 1184 1185 next = msghdr->msg_spot; 1186 for (len = 0; len < msgsz; len += msginfo.msgssz) { 1187 size_t tlen; 1188 1189 if (msgsz - len > msginfo.msgssz) 1190 tlen = msginfo.msgssz; 1191 else 1192 tlen = msgsz - len; 1193 if (next <= -1) 1194 panic("next too low #3"); 1195 if (next >= msginfo.msgseg) 1196 panic("next out of range #3"); 1197 error = copyout((caddr_t)&msgpool[next * msginfo.msgssz], 1198 user_msgp, tlen); 1199 if (error != 0) { 1200#ifdef MSG_DEBUG_OK 1201 printf("error (%d) copying out message segment\n", 1202 error); 1203#endif 1204 msg_freehdr(msghdr); 1205 wakeup((caddr_t)msqptr); 1206 goto done2; 1207 } 1208 user_msgp = (char *)user_msgp + tlen; 1209 next = msgmaps[next].next; 1210 } 1211 1212 /* 1213 * Done, return the actual number of bytes copied out. 1214 */ 1215 1216 msg_freehdr(msghdr); 1217 wakeup((caddr_t)msqptr); 1218 td->td_retval[0] = msgsz; 1219done2: 1220 mtx_unlock(&Giant); 1221 return (error); 1222} 1223 1224static int 1225sysctl_msqids(SYSCTL_HANDLER_ARGS) 1226{ 1227 1228 return (SYSCTL_OUT(req, msqids, 1229 sizeof(struct msqid_ds) * msginfo.msgmni)); 1230} 1231 1232SYSCTL_DECL(_kern_ipc); 1233SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, ""); 1234SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RD, &msginfo.msgmni, 0, ""); 1235SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RD, &msginfo.msgmnb, 0, ""); 1236SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RD, &msginfo.msgtql, 0, ""); 1237SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RD, &msginfo.msgssz, 0, ""); 1238SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RD, &msginfo.msgseg, 0, "") 1239SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD, 1240 NULL, 0, sysctl_msqids, "", "Message queue IDs"); 1241