sysv_msg.c revision 139804
1/*- 2 * Implementation of SVID messages 3 * 4 * Author: Daniel Boulet 5 * 6 * Copyright 1993 Daniel Boulet and RTMX Inc. 7 * 8 * This system call was implemented by Daniel Boulet under contract from RTMX. 9 * 10 * Redistribution and use in source forms, with and without modification, 11 * are permitted provided that this entire comment appears intact. 12 * 13 * Redistribution in binary form may occur without any restrictions. 14 * Obviously, it would be nice if you gave credit where credit is due 15 * but requiring it would be too onerous. 16 * 17 * This software is provided ``AS IS'' without any warranties of any kind. 18 */ 19 20#include <sys/cdefs.h> 21__FBSDID("$FreeBSD: head/sys/kern/sysv_msg.c 139804 2005-01-06 23:35:40Z imp $"); 22 23#include "opt_sysvipc.h" 24 25#include <sys/param.h> 26#include <sys/systm.h> 27#include <sys/sysproto.h> 28#include <sys/kernel.h> 29#include <sys/proc.h> 30#include <sys/lock.h> 31#include <sys/mutex.h> 32#include <sys/module.h> 33#include <sys/msg.h> 34#include <sys/syscall.h> 35#include <sys/sysent.h> 36#include <sys/sysctl.h> 37#include <sys/malloc.h> 38#include <sys/jail.h> 39 40static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues"); 41 42static void msginit(void); 43static int msgunload(void); 44static int sysvmsg_modload(struct module *, int, void *); 45 46#ifdef MSG_DEBUG 47#define DPRINTF(a) printf a 48#else 49#define DPRINTF(a) 50#endif 51 52static void msg_freehdr(struct msg *msghdr); 53 54/* XXX casting to (sy_call_t *) is bogus, as usual. */ 55static sy_call_t *msgcalls[] = { 56 (sy_call_t *)msgctl, (sy_call_t *)msgget, 57 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv 58}; 59 60#ifndef MSGSSZ 61#define MSGSSZ 8 /* Each segment must be 2^N long */ 62#endif 63#ifndef MSGSEG 64#define MSGSEG 2048 /* must be less than 32767 */ 65#endif 66#define MSGMAX (MSGSSZ*MSGSEG) 67#ifndef MSGMNB 68#define MSGMNB 2048 /* max # of bytes in a queue */ 69#endif 70#ifndef MSGMNI 71#define MSGMNI 40 72#endif 73#ifndef MSGTQL 74#define MSGTQL 40 75#endif 76 77/* 78 * Based on the configuration parameters described in an SVR2 (yes, two) 79 * config(1m) man page. 80 * 81 * Each message is broken up and stored in segments that are msgssz bytes 82 * long. For efficiency reasons, this should be a power of two. Also, 83 * it doesn't make sense if it is less than 8 or greater than about 256. 84 * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of 85 * two between 8 and 1024 inclusive (and panic's if it isn't). 86 */ 87struct msginfo msginfo = { 88 MSGMAX, /* max chars in a message */ 89 MSGMNI, /* # of message queue identifiers */ 90 MSGMNB, /* max chars in a queue */ 91 MSGTQL, /* max messages in system */ 92 MSGSSZ, /* size of a message segment */ 93 /* (must be small power of 2 greater than 4) */ 94 MSGSEG /* number of message segments */ 95}; 96 97/* 98 * macros to convert between msqid_ds's and msqid's. 99 * (specific to this implementation) 100 */ 101#define MSQID(ix,ds) ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000)) 102#define MSQID_IX(id) ((id) & 0xffff) 103#define MSQID_SEQ(id) (((id) >> 16) & 0xffff) 104 105/* 106 * The rest of this file is specific to this particular implementation. 107 */ 108 109struct msgmap { 110 short next; /* next segment in buffer */ 111 /* -1 -> available */ 112 /* 0..(MSGSEG-1) -> index of next segment */ 113}; 114 115#define MSG_LOCKED 01000 /* Is this msqid_ds locked? */ 116 117static int nfree_msgmaps; /* # of free map entries */ 118static short free_msgmaps; /* head of linked list of free map entries */ 119static struct msg *free_msghdrs;/* list of free msg headers */ 120static char *msgpool; /* MSGMAX byte long msg buffer pool */ 121static struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 122static struct msg *msghdrs; /* MSGTQL msg headers */ 123static struct msqid_kernel *msqids; /* MSGMNI msqid_kernel struct's */ 124static struct mtx msq_mtx; /* global mutex for message queues. */ 125 126static void 127msginit() 128{ 129 register int i; 130 131 TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg); 132 TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz); 133 msginfo.msgmax = msginfo.msgseg * msginfo.msgssz; 134 TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni); 135 TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb); 136 TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql); 137 138 msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK); 139 if (msgpool == NULL) 140 panic("msgpool is NULL"); 141 msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK); 142 if (msgmaps == NULL) 143 panic("msgmaps is NULL"); 144 msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK); 145 if (msghdrs == NULL) 146 panic("msghdrs is NULL"); 147 msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG, 148 M_WAITOK); 149 if (msqids == NULL) 150 panic("msqids is NULL"); 151 152 /* 153 * msginfo.msgssz should be a power of two for efficiency reasons. 154 * It is also pretty silly if msginfo.msgssz is less than 8 155 * or greater than about 256 so ... 156 */ 157 158 i = 8; 159 while (i < 1024 && i != msginfo.msgssz) 160 i <<= 1; 161 if (i != msginfo.msgssz) { 162 DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, 163 msginfo.msgssz)); 164 panic("msginfo.msgssz not a small power of 2"); 165 } 166 167 if (msginfo.msgseg > 32767) { 168 DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg)); 169 panic("msginfo.msgseg > 32767"); 170 } 171 172 if (msgmaps == NULL) 173 panic("msgmaps is NULL"); 174 175 for (i = 0; i < msginfo.msgseg; i++) { 176 if (i > 0) 177 msgmaps[i-1].next = i; 178 msgmaps[i].next = -1; /* implies entry is available */ 179 } 180 free_msgmaps = 0; 181 nfree_msgmaps = msginfo.msgseg; 182 183 if (msghdrs == NULL) 184 panic("msghdrs is NULL"); 185 186 for (i = 0; i < msginfo.msgtql; i++) { 187 msghdrs[i].msg_type = 0; 188 if (i > 0) 189 msghdrs[i-1].msg_next = &msghdrs[i]; 190 msghdrs[i].msg_next = NULL; 191 } 192 free_msghdrs = &msghdrs[0]; 193 194 if (msqids == NULL) 195 panic("msqids is NULL"); 196 197 for (i = 0; i < msginfo.msgmni; i++) { 198 msqids[i].u.msg_qbytes = 0; /* implies entry is available */ 199 msqids[i].u.msg_perm.seq = 0; /* reset to a known value */ 200 msqids[i].u.msg_perm.mode = 0; 201 } 202 mtx_init(&msq_mtx, "msq", NULL, MTX_DEF); 203} 204 205static int 206msgunload() 207{ 208 struct msqid_kernel *msqkptr; 209 int msqid; 210 211 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 212 /* 213 * Look for an unallocated and unlocked msqid_ds. 214 * msqid_ds's can be locked by msgsnd or msgrcv while 215 * they are copying the message in/out. We can't 216 * re-use the entry until they release it. 217 */ 218 msqkptr = &msqids[msqid]; 219 if (msqkptr->u.msg_qbytes != 0 || 220 (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) 221 break; 222 } 223 if (msqid != msginfo.msgmni) 224 return (EBUSY); 225 226 free(msgpool, M_MSG); 227 free(msgmaps, M_MSG); 228 free(msghdrs, M_MSG); 229 free(msqids, M_MSG); 230 mtx_destroy(&msq_mtx); 231 return (0); 232} 233 234 235static int 236sysvmsg_modload(struct module *module, int cmd, void *arg) 237{ 238 int error = 0; 239 240 switch (cmd) { 241 case MOD_LOAD: 242 msginit(); 243 break; 244 case MOD_UNLOAD: 245 error = msgunload(); 246 break; 247 case MOD_SHUTDOWN: 248 break; 249 default: 250 error = EINVAL; 251 break; 252 } 253 return (error); 254} 255 256static moduledata_t sysvmsg_mod = { 257 "sysvmsg", 258 &sysvmsg_modload, 259 NULL 260}; 261 262SYSCALL_MODULE_HELPER(msgsys); 263SYSCALL_MODULE_HELPER(msgctl); 264SYSCALL_MODULE_HELPER(msgget); 265SYSCALL_MODULE_HELPER(msgsnd); 266SYSCALL_MODULE_HELPER(msgrcv); 267 268DECLARE_MODULE(sysvmsg, sysvmsg_mod, 269 SI_SUB_SYSV_MSG, SI_ORDER_FIRST); 270MODULE_VERSION(sysvmsg, 1); 271 272/* 273 * Entry point for all MSG calls 274 * 275 * MPSAFE 276 */ 277int 278msgsys(td, uap) 279 struct thread *td; 280 /* XXX actually varargs. */ 281 struct msgsys_args /* { 282 int which; 283 int a2; 284 int a3; 285 int a4; 286 int a5; 287 int a6; 288 } */ *uap; 289{ 290 int error; 291 292 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 293 return (ENOSYS); 294 if (uap->which < 0 || 295 uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) 296 return (EINVAL); 297 error = (*msgcalls[uap->which])(td, &uap->a2); 298 return (error); 299} 300 301static void 302msg_freehdr(msghdr) 303 struct msg *msghdr; 304{ 305 while (msghdr->msg_ts > 0) { 306 short next; 307 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 308 panic("msghdr->msg_spot out of range"); 309 next = msgmaps[msghdr->msg_spot].next; 310 msgmaps[msghdr->msg_spot].next = free_msgmaps; 311 free_msgmaps = msghdr->msg_spot; 312 nfree_msgmaps++; 313 msghdr->msg_spot = next; 314 if (msghdr->msg_ts >= msginfo.msgssz) 315 msghdr->msg_ts -= msginfo.msgssz; 316 else 317 msghdr->msg_ts = 0; 318 } 319 if (msghdr->msg_spot != -1) 320 panic("msghdr->msg_spot != -1"); 321 msghdr->msg_next = free_msghdrs; 322 free_msghdrs = msghdr; 323} 324 325#ifndef _SYS_SYSPROTO_H_ 326struct msgctl_args { 327 int msqid; 328 int cmd; 329 struct msqid_ds *buf; 330}; 331#endif 332 333/* 334 * MPSAFE 335 */ 336int 337msgctl(td, uap) 338 struct thread *td; 339 register struct msgctl_args *uap; 340{ 341 int msqid = uap->msqid; 342 int cmd = uap->cmd; 343 struct msqid_ds *user_msqptr = uap->buf; 344 int rval, error; 345 struct msqid_ds msqbuf; 346 register struct msqid_kernel *msqkptr; 347 348 DPRINTF(("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr)); 349 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 350 return (ENOSYS); 351 352 msqid = IPCID_TO_IX(msqid); 353 354 if (msqid < 0 || msqid >= msginfo.msgmni) { 355 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 356 msginfo.msgmni)); 357 return (EINVAL); 358 } 359 if (cmd == IPC_SET && 360 (error = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) 361 return (error); 362 363 msqkptr = &msqids[msqid]; 364 365 mtx_lock(&msq_mtx); 366 if (msqkptr->u.msg_qbytes == 0) { 367 DPRINTF(("no such msqid\n")); 368 error = EINVAL; 369 goto done2; 370 } 371 if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 372 DPRINTF(("wrong sequence number\n")); 373 error = EINVAL; 374 goto done2; 375 } 376 377 error = 0; 378 rval = 0; 379 380 switch (cmd) { 381 382 case IPC_RMID: 383 { 384 struct msg *msghdr; 385 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M))) 386 goto done2; 387 388 /* Free the message headers */ 389 msghdr = msqkptr->u.msg_first; 390 while (msghdr != NULL) { 391 struct msg *msghdr_tmp; 392 393 /* Free the segments of each message */ 394 msqkptr->u.msg_cbytes -= msghdr->msg_ts; 395 msqkptr->u.msg_qnum--; 396 msghdr_tmp = msghdr; 397 msghdr = msghdr->msg_next; 398 msg_freehdr(msghdr_tmp); 399 } 400 401 if (msqkptr->u.msg_cbytes != 0) 402 panic("msg_cbytes is screwed up"); 403 if (msqkptr->u.msg_qnum != 0) 404 panic("msg_qnum is screwed up"); 405 406 msqkptr->u.msg_qbytes = 0; /* Mark it as free */ 407 408 wakeup(msqkptr); 409 } 410 411 break; 412 413 case IPC_SET: 414 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M))) 415 goto done2; 416 if (msqbuf.msg_qbytes > msqkptr->u.msg_qbytes) { 417 error = suser(td); 418 if (error) 419 goto done2; 420 } 421 if (msqbuf.msg_qbytes > msginfo.msgmnb) { 422 DPRINTF(("can't increase msg_qbytes beyond %d" 423 "(truncating)\n", msginfo.msgmnb)); 424 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 425 } 426 if (msqbuf.msg_qbytes == 0) { 427 DPRINTF(("can't reduce msg_qbytes to 0\n")); 428 error = EINVAL; /* non-standard errno! */ 429 goto done2; 430 } 431 msqkptr->u.msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 432 msqkptr->u.msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 433 msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) | 434 (msqbuf.msg_perm.mode & 0777); 435 msqkptr->u.msg_qbytes = msqbuf.msg_qbytes; 436 msqkptr->u.msg_ctime = time_second; 437 break; 438 439 case IPC_STAT: 440 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) { 441 DPRINTF(("requester doesn't have read access\n")); 442 goto done2; 443 } 444 break; 445 446 default: 447 DPRINTF(("invalid command %d\n", cmd)); 448 error = EINVAL; 449 goto done2; 450 } 451 452 if (error == 0) 453 td->td_retval[0] = rval; 454done2: 455 mtx_unlock(&msq_mtx); 456 if (cmd == IPC_STAT && error == 0) 457 error = copyout(&(msqkptr->u), user_msqptr, sizeof(struct msqid_ds)); 458 return(error); 459} 460 461#ifndef _SYS_SYSPROTO_H_ 462struct msgget_args { 463 key_t key; 464 int msgflg; 465}; 466#endif 467 468/* 469 * MPSAFE 470 */ 471int 472msgget(td, uap) 473 struct thread *td; 474 register struct msgget_args *uap; 475{ 476 int msqid, error = 0; 477 int key = uap->key; 478 int msgflg = uap->msgflg; 479 struct ucred *cred = td->td_ucred; 480 register struct msqid_kernel *msqkptr = NULL; 481 482 DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg)); 483 484 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 485 return (ENOSYS); 486 487 mtx_lock(&msq_mtx); 488 if (key != IPC_PRIVATE) { 489 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 490 msqkptr = &msqids[msqid]; 491 if (msqkptr->u.msg_qbytes != 0 && 492 msqkptr->u.msg_perm.key == key) 493 break; 494 } 495 if (msqid < msginfo.msgmni) { 496 DPRINTF(("found public key\n")); 497 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 498 DPRINTF(("not exclusive\n")); 499 error = EEXIST; 500 goto done2; 501 } 502 if ((error = ipcperm(td, &msqkptr->u.msg_perm, 503 msgflg & 0700))) { 504 DPRINTF(("requester doesn't have 0%o access\n", 505 msgflg & 0700)); 506 goto done2; 507 } 508 goto found; 509 } 510 } 511 512 DPRINTF(("need to allocate the msqid_ds\n")); 513 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 514 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 515 /* 516 * Look for an unallocated and unlocked msqid_ds. 517 * msqid_ds's can be locked by msgsnd or msgrcv while 518 * they are copying the message in/out. We can't 519 * re-use the entry until they release it. 520 */ 521 msqkptr = &msqids[msqid]; 522 if (msqkptr->u.msg_qbytes == 0 && 523 (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0) 524 break; 525 } 526 if (msqid == msginfo.msgmni) { 527 DPRINTF(("no more msqid_ds's available\n")); 528 error = ENOSPC; 529 goto done2; 530 } 531 DPRINTF(("msqid %d is available\n", msqid)); 532 msqkptr->u.msg_perm.key = key; 533 msqkptr->u.msg_perm.cuid = cred->cr_uid; 534 msqkptr->u.msg_perm.uid = cred->cr_uid; 535 msqkptr->u.msg_perm.cgid = cred->cr_gid; 536 msqkptr->u.msg_perm.gid = cred->cr_gid; 537 msqkptr->u.msg_perm.mode = (msgflg & 0777); 538 /* Make sure that the returned msqid is unique */ 539 msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff; 540 msqkptr->u.msg_first = NULL; 541 msqkptr->u.msg_last = NULL; 542 msqkptr->u.msg_cbytes = 0; 543 msqkptr->u.msg_qnum = 0; 544 msqkptr->u.msg_qbytes = msginfo.msgmnb; 545 msqkptr->u.msg_lspid = 0; 546 msqkptr->u.msg_lrpid = 0; 547 msqkptr->u.msg_stime = 0; 548 msqkptr->u.msg_rtime = 0; 549 msqkptr->u.msg_ctime = time_second; 550 } else { 551 DPRINTF(("didn't find it and wasn't asked to create it\n")); 552 error = ENOENT; 553 goto done2; 554 } 555 556found: 557 /* Construct the unique msqid */ 558 td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm); 559done2: 560 mtx_unlock(&msq_mtx); 561 return (error); 562} 563 564#ifndef _SYS_SYSPROTO_H_ 565struct msgsnd_args { 566 int msqid; 567 const void *msgp; 568 size_t msgsz; 569 int msgflg; 570}; 571#endif 572 573/* 574 * MPSAFE 575 */ 576int 577msgsnd(td, uap) 578 struct thread *td; 579 register struct msgsnd_args *uap; 580{ 581 int msqid = uap->msqid; 582 const void *user_msgp = uap->msgp; 583 size_t msgsz = uap->msgsz; 584 int msgflg = uap->msgflg; 585 int segs_needed, error = 0; 586 register struct msqid_kernel *msqkptr; 587 register struct msg *msghdr; 588 short next; 589 590 DPRINTF(("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, 591 msgflg)); 592 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 593 return (ENOSYS); 594 595 mtx_lock(&msq_mtx); 596 msqid = IPCID_TO_IX(msqid); 597 598 if (msqid < 0 || msqid >= msginfo.msgmni) { 599 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 600 msginfo.msgmni)); 601 error = EINVAL; 602 goto done2; 603 } 604 605 msqkptr = &msqids[msqid]; 606 if (msqkptr->u.msg_qbytes == 0) { 607 DPRINTF(("no such message queue id\n")); 608 error = EINVAL; 609 goto done2; 610 } 611 if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 612 DPRINTF(("wrong sequence number\n")); 613 error = EINVAL; 614 goto done2; 615 } 616 617 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) { 618 DPRINTF(("requester doesn't have write access\n")); 619 goto done2; 620 } 621 622 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 623 DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 624 segs_needed)); 625 for (;;) { 626 int need_more_resources = 0; 627 628 /* 629 * check msgsz 630 * (inside this loop in case msg_qbytes changes while we sleep) 631 */ 632 633 if (msgsz > msqkptr->u.msg_qbytes) { 634 DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n")); 635 error = EINVAL; 636 goto done2; 637 } 638 639 if (msqkptr->u.msg_perm.mode & MSG_LOCKED) { 640 DPRINTF(("msqid is locked\n")); 641 need_more_resources = 1; 642 } 643 if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) { 644 DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); 645 need_more_resources = 1; 646 } 647 if (segs_needed > nfree_msgmaps) { 648 DPRINTF(("segs_needed > nfree_msgmaps\n")); 649 need_more_resources = 1; 650 } 651 if (free_msghdrs == NULL) { 652 DPRINTF(("no more msghdrs\n")); 653 need_more_resources = 1; 654 } 655 656 if (need_more_resources) { 657 int we_own_it; 658 659 if ((msgflg & IPC_NOWAIT) != 0) { 660 DPRINTF(("need more resources but caller " 661 "doesn't want to wait\n")); 662 error = EAGAIN; 663 goto done2; 664 } 665 666 if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) { 667 DPRINTF(("we don't own the msqid_ds\n")); 668 we_own_it = 0; 669 } else { 670 /* Force later arrivals to wait for our 671 request */ 672 DPRINTF(("we own the msqid_ds\n")); 673 msqkptr->u.msg_perm.mode |= MSG_LOCKED; 674 we_own_it = 1; 675 } 676 DPRINTF(("goodnight\n")); 677 error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH, 678 "msgwait", 0); 679 DPRINTF(("good morning, error=%d\n", error)); 680 if (we_own_it) 681 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; 682 if (error != 0) { 683 DPRINTF(("msgsnd: interrupted system call\n")); 684 error = EINTR; 685 goto done2; 686 } 687 688 /* 689 * Make sure that the msq queue still exists 690 */ 691 692 if (msqkptr->u.msg_qbytes == 0) { 693 DPRINTF(("msqid deleted\n")); 694 error = EIDRM; 695 goto done2; 696 } 697 698 } else { 699 DPRINTF(("got all the resources that we need\n")); 700 break; 701 } 702 } 703 704 /* 705 * We have the resources that we need. 706 * Make sure! 707 */ 708 709 if (msqkptr->u.msg_perm.mode & MSG_LOCKED) 710 panic("msg_perm.mode & MSG_LOCKED"); 711 if (segs_needed > nfree_msgmaps) 712 panic("segs_needed > nfree_msgmaps"); 713 if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) 714 panic("msgsz + msg_cbytes > msg_qbytes"); 715 if (free_msghdrs == NULL) 716 panic("no more msghdrs"); 717 718 /* 719 * Re-lock the msqid_ds in case we page-fault when copying in the 720 * message 721 */ 722 723 if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) 724 panic("msqid_ds is already locked"); 725 msqkptr->u.msg_perm.mode |= MSG_LOCKED; 726 727 /* 728 * Allocate a message header 729 */ 730 731 msghdr = free_msghdrs; 732 free_msghdrs = msghdr->msg_next; 733 msghdr->msg_spot = -1; 734 msghdr->msg_ts = msgsz; 735 736 /* 737 * Allocate space for the message 738 */ 739 740 while (segs_needed > 0) { 741 if (nfree_msgmaps <= 0) 742 panic("not enough msgmaps"); 743 if (free_msgmaps == -1) 744 panic("nil free_msgmaps"); 745 next = free_msgmaps; 746 if (next <= -1) 747 panic("next too low #1"); 748 if (next >= msginfo.msgseg) 749 panic("next out of range #1"); 750 DPRINTF(("allocating segment %d to message\n", next)); 751 free_msgmaps = msgmaps[next].next; 752 nfree_msgmaps--; 753 msgmaps[next].next = msghdr->msg_spot; 754 msghdr->msg_spot = next; 755 segs_needed--; 756 } 757 758 /* 759 * Copy in the message type 760 */ 761 762 mtx_unlock(&msq_mtx); 763 if ((error = copyin(user_msgp, &msghdr->msg_type, 764 sizeof(msghdr->msg_type))) != 0) { 765 mtx_lock(&msq_mtx); 766 DPRINTF(("error %d copying the message type\n", error)); 767 msg_freehdr(msghdr); 768 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; 769 wakeup(msqkptr); 770 goto done2; 771 } 772 mtx_lock(&msq_mtx); 773 user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type); 774 775 /* 776 * Validate the message type 777 */ 778 779 if (msghdr->msg_type < 1) { 780 msg_freehdr(msghdr); 781 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; 782 wakeup(msqkptr); 783 DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type)); 784 error = EINVAL; 785 goto done2; 786 } 787 788 /* 789 * Copy in the message body 790 */ 791 792 next = msghdr->msg_spot; 793 while (msgsz > 0) { 794 size_t tlen; 795 if (msgsz > msginfo.msgssz) 796 tlen = msginfo.msgssz; 797 else 798 tlen = msgsz; 799 if (next <= -1) 800 panic("next too low #2"); 801 if (next >= msginfo.msgseg) 802 panic("next out of range #2"); 803 mtx_unlock(&msq_mtx); 804 if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 805 tlen)) != 0) { 806 mtx_lock(&msq_mtx); 807 DPRINTF(("error %d copying in message segment\n", 808 error)); 809 msg_freehdr(msghdr); 810 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; 811 wakeup(msqkptr); 812 goto done2; 813 } 814 mtx_lock(&msq_mtx); 815 msgsz -= tlen; 816 user_msgp = (const char *)user_msgp + tlen; 817 next = msgmaps[next].next; 818 } 819 if (next != -1) 820 panic("didn't use all the msg segments"); 821 822 /* 823 * We've got the message. Unlock the msqid_ds. 824 */ 825 826 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED; 827 828 /* 829 * Make sure that the msqid_ds is still allocated. 830 */ 831 832 if (msqkptr->u.msg_qbytes == 0) { 833 msg_freehdr(msghdr); 834 wakeup(msqkptr); 835 error = EIDRM; 836 goto done2; 837 } 838 839 /* 840 * Put the message into the queue 841 */ 842 if (msqkptr->u.msg_first == NULL) { 843 msqkptr->u.msg_first = msghdr; 844 msqkptr->u.msg_last = msghdr; 845 } else { 846 msqkptr->u.msg_last->msg_next = msghdr; 847 msqkptr->u.msg_last = msghdr; 848 } 849 msqkptr->u.msg_last->msg_next = NULL; 850 851 msqkptr->u.msg_cbytes += msghdr->msg_ts; 852 msqkptr->u.msg_qnum++; 853 msqkptr->u.msg_lspid = td->td_proc->p_pid; 854 msqkptr->u.msg_stime = time_second; 855 856 wakeup(msqkptr); 857 td->td_retval[0] = 0; 858done2: 859 mtx_unlock(&msq_mtx); 860 return (error); 861} 862 863#ifndef _SYS_SYSPROTO_H_ 864struct msgrcv_args { 865 int msqid; 866 void *msgp; 867 size_t msgsz; 868 long msgtyp; 869 int msgflg; 870}; 871#endif 872 873/* 874 * MPSAFE 875 */ 876int 877msgrcv(td, uap) 878 struct thread *td; 879 register struct msgrcv_args *uap; 880{ 881 int msqid = uap->msqid; 882 void *user_msgp = uap->msgp; 883 size_t msgsz = uap->msgsz; 884 long msgtyp = uap->msgtyp; 885 int msgflg = uap->msgflg; 886 size_t len; 887 register struct msqid_kernel *msqkptr; 888 register struct msg *msghdr; 889 int error = 0; 890 short next; 891 892 DPRINTF(("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, 893 msgsz, msgtyp, msgflg)); 894 895 if (!jail_sysvipc_allowed && jailed(td->td_ucred)) 896 return (ENOSYS); 897 898 msqid = IPCID_TO_IX(msqid); 899 900 if (msqid < 0 || msqid >= msginfo.msgmni) { 901 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 902 msginfo.msgmni)); 903 return (EINVAL); 904 } 905 906 msqkptr = &msqids[msqid]; 907 mtx_lock(&msq_mtx); 908 if (msqkptr->u.msg_qbytes == 0) { 909 DPRINTF(("no such message queue id\n")); 910 error = EINVAL; 911 goto done2; 912 } 913 if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 914 DPRINTF(("wrong sequence number\n")); 915 error = EINVAL; 916 goto done2; 917 } 918 919 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) { 920 DPRINTF(("requester doesn't have read access\n")); 921 goto done2; 922 } 923 924 msghdr = NULL; 925 while (msghdr == NULL) { 926 if (msgtyp == 0) { 927 msghdr = msqkptr->u.msg_first; 928 if (msghdr != NULL) { 929 if (msgsz < msghdr->msg_ts && 930 (msgflg & MSG_NOERROR) == 0) { 931 DPRINTF(("first message on the queue " 932 "is too big (want %d, got %d)\n", 933 msgsz, msghdr->msg_ts)); 934 error = E2BIG; 935 goto done2; 936 } 937 if (msqkptr->u.msg_first == msqkptr->u.msg_last) { 938 msqkptr->u.msg_first = NULL; 939 msqkptr->u.msg_last = NULL; 940 } else { 941 msqkptr->u.msg_first = msghdr->msg_next; 942 if (msqkptr->u.msg_first == NULL) 943 panic("msg_first/last screwed up #1"); 944 } 945 } 946 } else { 947 struct msg *previous; 948 struct msg **prev; 949 950 previous = NULL; 951 prev = &(msqkptr->u.msg_first); 952 while ((msghdr = *prev) != NULL) { 953 /* 954 * Is this message's type an exact match or is 955 * this message's type less than or equal to 956 * the absolute value of a negative msgtyp? 957 * Note that the second half of this test can 958 * NEVER be true if msgtyp is positive since 959 * msg_type is always positive! 960 */ 961 962 if (msgtyp == msghdr->msg_type || 963 msghdr->msg_type <= -msgtyp) { 964 DPRINTF(("found message type %d, " 965 "requested %d\n", 966 msghdr->msg_type, msgtyp)); 967 if (msgsz < msghdr->msg_ts && 968 (msgflg & MSG_NOERROR) == 0) { 969 DPRINTF(("requested message " 970 "on the queue is too big " 971 "(want %d, got %d)\n", 972 msgsz, msghdr->msg_ts)); 973 error = E2BIG; 974 goto done2; 975 } 976 *prev = msghdr->msg_next; 977 if (msghdr == msqkptr->u.msg_last) { 978 if (previous == NULL) { 979 if (prev != 980 &msqkptr->u.msg_first) 981 panic("msg_first/last screwed up #2"); 982 msqkptr->u.msg_first = 983 NULL; 984 msqkptr->u.msg_last = 985 NULL; 986 } else { 987 if (prev == 988 &msqkptr->u.msg_first) 989 panic("msg_first/last screwed up #3"); 990 msqkptr->u.msg_last = 991 previous; 992 } 993 } 994 break; 995 } 996 previous = msghdr; 997 prev = &(msghdr->msg_next); 998 } 999 } 1000 1001 /* 1002 * We've either extracted the msghdr for the appropriate 1003 * message or there isn't one. 1004 * If there is one then bail out of this loop. 1005 */ 1006 1007 if (msghdr != NULL) 1008 break; 1009 1010 /* 1011 * Hmph! No message found. Does the user want to wait? 1012 */ 1013 1014 if ((msgflg & IPC_NOWAIT) != 0) { 1015 DPRINTF(("no appropriate message found (msgtyp=%d)\n", 1016 msgtyp)); 1017 /* The SVID says to return ENOMSG. */ 1018 error = ENOMSG; 1019 goto done2; 1020 } 1021 1022 /* 1023 * Wait for something to happen 1024 */ 1025 1026 DPRINTF(("msgrcv: goodnight\n")); 1027 error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH, 1028 "msgwait", 0); 1029 DPRINTF(("msgrcv: good morning (error=%d)\n", error)); 1030 1031 if (error != 0) { 1032 DPRINTF(("msgsnd: interrupted system call\n")); 1033 error = EINTR; 1034 goto done2; 1035 } 1036 1037 /* 1038 * Make sure that the msq queue still exists 1039 */ 1040 1041 if (msqkptr->u.msg_qbytes == 0 || 1042 msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 1043 DPRINTF(("msqid deleted\n")); 1044 error = EIDRM; 1045 goto done2; 1046 } 1047 } 1048 1049 /* 1050 * Return the message to the user. 1051 * 1052 * First, do the bookkeeping (before we risk being interrupted). 1053 */ 1054 1055 msqkptr->u.msg_cbytes -= msghdr->msg_ts; 1056 msqkptr->u.msg_qnum--; 1057 msqkptr->u.msg_lrpid = td->td_proc->p_pid; 1058 msqkptr->u.msg_rtime = time_second; 1059 1060 /* 1061 * Make msgsz the actual amount that we'll be returning. 1062 * Note that this effectively truncates the message if it is too long 1063 * (since msgsz is never increased). 1064 */ 1065 1066 DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 1067 msghdr->msg_ts)); 1068 if (msgsz > msghdr->msg_ts) 1069 msgsz = msghdr->msg_ts; 1070 1071 /* 1072 * Return the type to the user. 1073 */ 1074 1075 mtx_unlock(&msq_mtx); 1076 error = copyout(&(msghdr->msg_type), user_msgp, 1077 sizeof(msghdr->msg_type)); 1078 mtx_lock(&msq_mtx); 1079 if (error != 0) { 1080 DPRINTF(("error (%d) copying out message type\n", error)); 1081 msg_freehdr(msghdr); 1082 wakeup(msqkptr); 1083 goto done2; 1084 } 1085 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 1086 1087 /* 1088 * Return the segments to the user 1089 */ 1090 1091 next = msghdr->msg_spot; 1092 for (len = 0; len < msgsz; len += msginfo.msgssz) { 1093 size_t tlen; 1094 1095 if (msgsz - len > msginfo.msgssz) 1096 tlen = msginfo.msgssz; 1097 else 1098 tlen = msgsz - len; 1099 if (next <= -1) 1100 panic("next too low #3"); 1101 if (next >= msginfo.msgseg) 1102 panic("next out of range #3"); 1103 mtx_unlock(&msq_mtx); 1104 error = copyout(&msgpool[next * msginfo.msgssz], 1105 user_msgp, tlen); 1106 mtx_lock(&msq_mtx); 1107 if (error != 0) { 1108 DPRINTF(("error (%d) copying out message segment\n", 1109 error)); 1110 msg_freehdr(msghdr); 1111 wakeup(msqkptr); 1112 goto done2; 1113 } 1114 user_msgp = (char *)user_msgp + tlen; 1115 next = msgmaps[next].next; 1116 } 1117 1118 /* 1119 * Done, return the actual number of bytes copied out. 1120 */ 1121 1122 msg_freehdr(msghdr); 1123 wakeup(msqkptr); 1124 td->td_retval[0] = msgsz; 1125done2: 1126 mtx_unlock(&msq_mtx); 1127 return (error); 1128} 1129 1130static int 1131sysctl_msqids(SYSCTL_HANDLER_ARGS) 1132{ 1133 1134 return (SYSCTL_OUT(req, msqids, 1135 sizeof(struct msqid_kernel) * msginfo.msgmni)); 1136} 1137 1138SYSCTL_DECL(_kern_ipc); 1139SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, ""); 1140SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0, ""); 1141SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0, ""); 1142SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0, ""); 1143SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0, ""); 1144SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0, ""); 1145SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD, 1146 NULL, 0, sysctl_msqids, "", "Message queue IDs"); 1147