1 /* $NetBSD: sys_mqueue.c,v 1.4 2007/11/11 23:22:24 matt Exp $ */ 2 3 /* 4 * Copyright (c) 2007, Mindaugas Rasiukevicius <rmind at NetBSD org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * Implementation of POSIX message queues. 30 * Defined in the Base Definitions volume of IEEE Std 1003.1-2001. 31 * 32 * Locking 33 * Global list of message queues and proc::p_mqueue_cnt counter are protected 34 * by mqlist_mtx lock. Concrete message queue and its members are protected 35 * by mqueue::mq_mtx. 36 * 37 * Lock order: 38 * mqlist_mtx 39 * -> mqueue::mq_mtx 40 * 41 * TODO: 42 * - Hashing or RB-tree for the global list. 43 * - Support for select(), poll() and perhaps kqueue(). 44 */ 45 46 #include <sys/cdefs.h> 47 __KERNEL_RCSID(0, "$NetBSD: sys_mqueue.c,v 1.4 2007/11/11 23:22:24 matt Exp $"); 48 49 #include <sys/param.h> 50 #include <sys/types.h> 51 52 #include <sys/condvar.h> 53 #include <sys/errno.h> 54 #include <sys/fcntl.h> 55 #include <sys/file.h> 56 #include <sys/filedesc.h> 57 #include <sys/kauth.h> 58 #include <sys/kernel.h> 59 #include <sys/kmem.h> 60 #include <sys/lwp.h> 61 #include <sys/mqueue.h> 62 #include <sys/mutex.h> 63 #include <sys/pool.h> 64 #include <sys/proc.h> 65 #include <sys/queue.h> 66 #include <sys/signal.h> 67 #include <sys/signalvar.h> 68 #include <sys/sysctl.h> 69 #include <sys/syscallargs.h> 70 #include <sys/systm.h> 71 #include <sys/unistd.h> 72 #include <sys/vnode.h> 73 74 /* System-wide limits. */ 75 static u_int mq_open_max = MQ_OPEN_MAX; 76 static u_int mq_prio_max = MQ_PRIO_MAX; 77 78 static u_int mq_max_msgsize = 16 * MQ_DEF_MSGSIZE; 79 static u_int mq_def_maxmsg = 32; 80 81 static kmutex_t mqlist_mtx; 82 static struct pool mqmsg_poll; 83 static LIST_HEAD(, mqueue) mqueue_head = 84 LIST_HEAD_INITIALIZER(mqueue_head); 85 86 static int mq_close_fop(struct file *, struct lwp *); 87 88 static const struct fileops mqops = { 89 fbadop_read, fbadop_write, fbadop_ioctl, fnullop_fcntl, fnullop_poll, 90 fbadop_stat, mq_close_fop, fnullop_kqfilter 91 }; 92 93 /* 94 * Initialize POSIX message queue subsystem. 95 */ 96 void 97 mqueue_sysinit(void) 98 { 99 100 pool_init(&mqmsg_poll, MQ_DEF_MSGSIZE, 0, 0, 0, 101 "mqmsg_poll", &pool_allocator_nointr, IPL_NONE); 102 mutex_init(&mqlist_mtx, MUTEX_DEFAULT, IPL_NONE); 103 } 104 105 /* 106 * Free the message. 107 */ 108 static void 109 mqueue_freemsg(struct mq_msg *msg, const size_t size) 110 { 111 112 if (size > MQ_DEF_MSGSIZE) 113 kmem_free(msg, size); 114 else 115 pool_put(&mqmsg_poll, msg); 116 } 117 118 /* 119 * Destroy the message queue. 120 */ 121 static void 122 mqueue_destroy(struct mqueue *mq) 123 { 124 struct mq_msg *msg; 125 126 while ((msg = TAILQ_FIRST(&mq->mq_head)) != NULL) { 127 TAILQ_REMOVE(&mq->mq_head, msg, msg_queue); 128 mqueue_freemsg(msg, sizeof(struct mq_msg) + msg->msg_len); 129 } 130 cv_destroy(&mq->mq_send_cv); 131 cv_destroy(&mq->mq_recv_cv); 132 mutex_destroy(&mq->mq_mtx); 133 kmem_free(mq, sizeof(struct mqueue)); 134 } 135 136 /* 137 * Lookup for file name in general list of message queues. 138 * => locks the message queue 139 */ 140 static void * 141 mqueue_lookup(char *name) 142 { 143 struct mqueue *mq; 144 KASSERT(mutex_owned(&mqlist_mtx)); 145 146 LIST_FOREACH(mq, &mqueue_head, mq_list) { 147 if (strncmp(mq->mq_name, name, MQ_NAMELEN) == 0) { 148 mutex_enter(&mq->mq_mtx); 149 return mq; 150 } 151 } 152 153 return NULL; 154 } 155 156 /* 157 * Check access against message queue. 158 */ 159 static inline int 160 mqueue_access(struct lwp *l, struct mqueue *mq, mode_t acc_mode) 161 { 162 163 KASSERT(mutex_owned(&mq->mq_mtx)); 164 return vaccess(VNON, mq->mq_mode, mq->mq_euid, mq->mq_egid, 165 acc_mode, l->l_cred); 166 } 167 168 /* 169 * Get the mqueue from the descriptor. 170 * => locks the message queue 171 * => increments the reference on file entry 172 */ 173 static int 174 mqueue_get(struct lwp *l, mqd_t mqd, mode_t acc_mode, struct file **fpr) 175 { 176 const struct proc *p = l->l_proc; 177 struct file *fp; 178 struct mqueue *mq; 179 180 /* Get the file and descriptor */ 181 fp = fd_getfile(p->p_fd, (int)mqd); 182 if (fp == NULL) 183 return EBADF; 184 185 /* Increment the reference of file entry, and lock the mqueue */ 186 FILE_USE(fp); 187 mq = fp->f_data; 188 *fpr = fp; 189 mutex_enter(&mq->mq_mtx); 190 191 /* Check the access mode and permission if needed */ 192 if (acc_mode == VNOVAL) 193 return 0; 194 if ((acc_mode & fp->f_flag) == 0 || mqueue_access(l, mq, acc_mode)) { 195 mutex_exit(&mq->mq_mtx); 196 FILE_UNUSE(fp, l); 197 return EPERM; 198 } 199 200 return 0; 201 } 202 203 /* 204 * Converter from struct timespec to the ticks. 205 * Used by mq_timedreceive(), mq_timedsend(). 206 */ 207 static int 208 abstimeout2timo(const void *uaddr, int *timo) 209 { 210 struct timespec ts; 211 int error; 212 213 error = copyin(uaddr, &ts, sizeof(struct timespec)); 214 if (error) 215 return error; 216 217 /* 218 * According to POSIX, validation check is needed only in case of 219 * blocking. Thus, set the invalid value right now, and fail latter. 220 */ 221 error = itimespecfix(&ts); 222 *timo = (error == 0) ? tstohz(&ts) : -1; 223 224 return 0; 225 } 226 227 static int 228 mq_close_fop(struct file *fp, struct lwp *l) 229 { 230 struct proc *p = l->l_proc; 231 struct mqueue *mq = fp->f_data; 232 bool destroy; 233 234 mutex_enter(&mqlist_mtx); 235 mutex_enter(&mq->mq_mtx); 236 237 /* Decrease the counters */ 238 p->p_mqueue_cnt--; 239 mq->mq_refcnt--; 240 241 /* Remove notification if registered for this process */ 242 if (mq->mq_notify_proc == p) 243 mq->mq_notify_proc = NULL; 244 245 /* 246 * If this is the last reference and mqueue is marked for unlink, 247 * remove and later destroy the message queue. 248 */ 249 if (mq->mq_refcnt == 0 && (mq->mq_attrib.mq_flags & MQ_UNLINK)) { 250 LIST_REMOVE(mq, mq_list); 251 destroy = true; 252 } else 253 destroy = false; 254 255 mutex_exit(&mq->mq_mtx); 256 mutex_exit(&mqlist_mtx); 257 258 if (destroy) 259 mqueue_destroy(mq); 260 261 return 0; 262 } 263 264 /* 265 * General mqueue system calls. 266 */ 267 268 int 269 sys_mq_open(struct lwp *l, void *v, register_t *retval) 270 { 271 struct sys_mq_open_args /* { 272 syscallarg(const char *) name; 273 syscallarg(int) oflag; 274 syscallarg(mode_t) mode; 275 syscallarg(struct mq_attr) attr; 276 } */ *uap = v; 277 struct proc *p = l->l_proc; 278 struct mqueue *mq, *mq_new = NULL; 279 struct file *fp; 280 char *name; 281 int mqd, error, oflag; 282 283 /* Check access mode flags */ 284 oflag = SCARG(uap, oflag); 285 if ((oflag & (O_RDWR | O_RDONLY | O_WRONLY)) == 0) 286 return EINVAL; 287 288 /* Get the name from the user-space */ 289 name = kmem_zalloc(MQ_NAMELEN, KM_SLEEP); 290 error = copyinstr(SCARG(uap, name), name, MQ_NAMELEN - 1, NULL); 291 if (error) { 292 kmem_free(name, MQ_NAMELEN); 293 return error; 294 } 295 296 if (oflag & O_CREAT) { 297 struct mq_attr attr; 298 299 /* Check the limit */ 300 if (p->p_mqueue_cnt == mq_open_max) { 301 kmem_free(name, MQ_NAMELEN); 302 return EMFILE; 303 } 304 305 /* Check for mqueue attributes */ 306 if (SCARG(uap, attr)) { 307 error = copyin(SCARG(uap, attr), &attr, 308 sizeof(struct mq_attr)); 309 if (error) { 310 kmem_free(name, MQ_NAMELEN); 311 return error; 312 } 313 if (attr.mq_maxmsg <= 0 || attr.mq_msgsize <= 0 || 314 attr.mq_msgsize > mq_max_msgsize) { 315 kmem_free(name, MQ_NAMELEN); 316 return EINVAL; 317 } 318 attr.mq_curmsgs = 0; 319 } else { 320 memset(&attr, 0, sizeof(struct mq_attr)); 321 attr.mq_maxmsg = mq_def_maxmsg; 322 attr.mq_msgsize = 323 MQ_DEF_MSGSIZE - sizeof(struct mq_msg); 324 } 325 326 /* 327 * Allocate new mqueue, initialize data structures, 328 * copy the name, attributes and set the flag. 329 */ 330 mq_new = kmem_zalloc(sizeof(struct mqueue), KM_SLEEP); 331 332 mutex_init(&mq_new->mq_mtx, MUTEX_DEFAULT, IPL_NONE); 333 cv_init(&mq_new->mq_send_cv, "mqsendcv"); 334 cv_init(&mq_new->mq_recv_cv, "mqrecvcv"); 335 TAILQ_INIT(&mq_new->mq_head); 336 337 strlcpy(mq_new->mq_name, name, MQ_NAMELEN); 338 memcpy(&mq_new->mq_attrib, &attr, sizeof(struct mq_attr)); 339 mq_new->mq_attrib.mq_flags = oflag; 340 341 /* Store mode and effective UID with GID */ 342 mq_new->mq_mode = SCARG(uap, mode); 343 mq_new->mq_euid = kauth_cred_geteuid(l->l_cred); 344 mq_new->mq_egid = kauth_cred_getegid(l->l_cred); 345 } 346 347 /* Allocate file structure and descriptor */ 348 error = falloc(l, &fp, &mqd); 349 if (error) { 350 if (mq_new) 351 mqueue_destroy(mq_new); 352 kmem_free(name, MQ_NAMELEN); 353 return error; 354 } 355 fp->f_type = DTYPE_MQUEUE; 356 fp->f_flag = (oflag & O_RDWR) ? (VREAD | VWRITE) : 357 ((oflag & O_RDONLY) ? VREAD : VWRITE); 358 fp->f_ops = &mqops; 359 360 /* Look up for mqueue with such name */ 361 mutex_enter(&mqlist_mtx); 362 mq = mqueue_lookup(name); 363 if (mq) { 364 KASSERT(mutex_owned(&mq->mq_mtx)); 365 /* Check if mqueue is not marked as unlinking */ 366 if (mq->mq_attrib.mq_flags & MQ_UNLINK) { 367 error = EACCES; 368 goto exit; 369 } 370 /* Fail if O_EXCL is set, and mqueue already exists */ 371 if ((oflag & O_CREAT) && (oflag & O_EXCL)) { 372 error = EEXIST; 373 goto exit; 374 } 375 /* Check the permission */ 376 if (mqueue_access(l, mq, fp->f_flag)) { 377 error = EACCES; 378 goto exit; 379 } 380 } else { 381 /* Fail if mqueue neither exists, nor we create it */ 382 if ((oflag & O_CREAT) == 0) { 383 mutex_exit(&mqlist_mtx); 384 KASSERT(mq_new == NULL); 385 FILE_UNUSE(fp, l); 386 ffree(fp); 387 fdremove(p->p_fd, mqd); 388 kmem_free(name, MQ_NAMELEN); 389 return ENOENT; 390 } 391 392 /* Check the limit */ 393 if (p->p_mqueue_cnt == mq_open_max) { 394 error = EMFILE; 395 goto exit; 396 } 397 398 /* Insert the queue to the list */ 399 mq = mq_new; 400 mutex_enter(&mq->mq_mtx); 401 LIST_INSERT_HEAD(&mqueue_head, mq, mq_list); 402 mq_new = NULL; 403 } 404 405 /* Increase the counters, and make descriptor ready */ 406 p->p_mqueue_cnt++; 407 mq->mq_refcnt++; 408 fp->f_data = mq; 409 FILE_SET_MATURE(fp); 410 exit: 411 mutex_exit(&mq->mq_mtx); 412 mutex_exit(&mqlist_mtx); 413 FILE_UNUSE(fp, l); 414 415 if (mq_new) 416 mqueue_destroy(mq_new); 417 if (error) { 418 ffree(fp); 419 fdremove(p->p_fd, mqd); 420 } else 421 *retval = mqd; 422 kmem_free(name, MQ_NAMELEN); 423 424 return error; 425 } 426 427 int 428 sys_mq_close(struct lwp *l, void *v, register_t *retval) 429 { 430 431 return sys_close(l, v, retval); 432 } 433 434 /* 435 * Primary mq_receive1() function. 436 */ 437 static int 438 mq_receive1(struct lwp *l, mqd_t mqdes, void *msg_ptr, size_t msg_len, 439 unsigned *msg_prio, int t, ssize_t *mlen) 440 { 441 struct file *fp = NULL; 442 struct mqueue *mq; 443 struct mq_msg *msg = NULL; 444 int error; 445 446 /* Get the message queue */ 447 error = mqueue_get(l, mqdes, VREAD, &fp); 448 if (error) 449 return error; 450 mq = fp->f_data; 451 452 /* Check the message size limits */ 453 if (msg_len < mq->mq_attrib.mq_msgsize) { 454 error = EMSGSIZE; 455 goto error; 456 } 457 458 /* Check if queue is empty */ 459 while (TAILQ_EMPTY(&mq->mq_head)) { 460 if (mq->mq_attrib.mq_flags & O_NONBLOCK) { 461 error = EAGAIN; 462 goto error; 463 } 464 if (t < 0) { 465 error = EINVAL; 466 goto error; 467 } 468 /* 469 * Block until someone sends the message. 470 * While doing this, notification should not be sent. 471 */ 472 mq->mq_attrib.mq_flags |= MQ_RECEIVE; 473 error = cv_timedwait_sig(&mq->mq_send_cv, &mq->mq_mtx, t); 474 mq->mq_attrib.mq_flags &= ~MQ_RECEIVE; 475 if (error || (mq->mq_attrib.mq_flags & MQ_UNLINK)) { 476 error = (error == EWOULDBLOCK) ? ETIMEDOUT : EINTR; 477 goto error; 478 } 479 } 480 481 /* Remove the message from the queue */ 482 msg = TAILQ_FIRST(&mq->mq_head); 483 KASSERT(msg != NULL); 484 TAILQ_REMOVE(&mq->mq_head, msg, msg_queue); 485 486 /* Decrement the counter and signal waiter, if any */ 487 mq->mq_attrib.mq_curmsgs--; 488 cv_signal(&mq->mq_recv_cv); 489 error: 490 mutex_exit(&mq->mq_mtx); 491 FILE_UNUSE(fp, l); 492 if (error) 493 return error; 494 495 /* 496 * Copy the data to the user-space. 497 * Note: According to POSIX, no message should be removed from the 498 * queue in case of fail - this would be violated. 499 */ 500 *mlen = msg->msg_len; 501 error = copyout(msg->msg_ptr, msg_ptr, msg->msg_len); 502 if (error == 0 && msg_prio) 503 error = copyout(&msg->msg_prio, msg_prio, sizeof(unsigned)); 504 mqueue_freemsg(msg, sizeof(struct mq_msg) + msg->msg_len); 505 506 return error; 507 } 508 509 int 510 sys_mq_receive(struct lwp *l, void *v, register_t *retval) 511 { 512 struct sys_mq_receive_args /* { 513 syscallarg(mqd_t) mqdes; 514 syscallarg(char *) msg_ptr; 515 syscallarg(size_t) msg_len; 516 syscallarg(unsigned *) msg_prio; 517 } */ *uap = v; 518 int error; 519 ssize_t mlen; 520 521 error = mq_receive1(l, SCARG(uap, mqdes), SCARG(uap, msg_ptr), 522 SCARG(uap, msg_len), SCARG(uap, msg_prio), 0, &mlen); 523 if (error == 0) 524 *retval = mlen; 525 526 return error; 527 } 528 529 int 530 sys_mq_timedreceive(struct lwp *l, void *v, register_t *retval) 531 { 532 struct sys_mq_timedreceive_args /* { 533 syscallarg(mqd_t) mqdes; 534 syscallarg(char *) msg_ptr; 535 syscallarg(size_t) msg_len; 536 syscallarg(unsigned *) msg_prio; 537 syscallarg(const struct timespec *) abs_timeout; 538 } */ *uap = v; 539 int error, t; 540 ssize_t mlen; 541 542 /* Get and convert time value */ 543 if (SCARG(uap, abs_timeout)) { 544 error = abstimeout2timo(SCARG(uap, abs_timeout), &t); 545 if (error) 546 return error; 547 } else 548 t = 0; 549 550 error = mq_receive1(l, SCARG(uap, mqdes), SCARG(uap, msg_ptr), 551 SCARG(uap, msg_len), SCARG(uap, msg_prio), t, &mlen); 552 if (error == 0) 553 *retval = mlen; 554 555 return error; 556 } 557 558 /* 559 * Primary mq_send1() function. 560 */ 561 static int 562 mq_send1(struct lwp *l, mqd_t mqdes, const char *msg_ptr, size_t msg_len, 563 unsigned msg_prio, int t) 564 { 565 struct file *fp = NULL; 566 struct mqueue *mq; 567 struct mq_msg *msg, *pos_msg; 568 struct proc *notify = NULL; 569 ksiginfo_t ksi; 570 size_t size; 571 int error; 572 573 /* Check the priority range */ 574 if (msg_prio >= mq_prio_max) 575 return EINVAL; 576 577 /* Allocate a new message */ 578 size = sizeof(struct mq_msg) + msg_len; 579 if (size > mq_max_msgsize) 580 return EMSGSIZE; 581 582 if (size > MQ_DEF_MSGSIZE) 583 msg = kmem_alloc(size, KM_SLEEP); 584 else 585 msg = pool_get(&mqmsg_poll, PR_WAITOK); 586 587 /* Get the data from user-space */ 588 error = copyin(msg_ptr, msg->msg_ptr, msg_len); 589 if (error) { 590 mqueue_freemsg(msg, size); 591 return error; 592 } 593 msg->msg_len = msg_len; 594 msg->msg_prio = msg_prio; 595 596 /* Get the mqueue */ 597 error = mqueue_get(l, mqdes, VWRITE, &fp); 598 if (error) { 599 mqueue_freemsg(msg, size); 600 return error; 601 } 602 mq = fp->f_data; 603 604 /* Check the message size limit */ 605 if (msg_len <= 0 || msg_len > mq->mq_attrib.mq_msgsize) { 606 error = EMSGSIZE; 607 goto error; 608 } 609 610 /* Check if queue is full */ 611 while (mq->mq_attrib.mq_curmsgs >= mq->mq_attrib.mq_maxmsg) { 612 if (mq->mq_attrib.mq_flags & O_NONBLOCK) { 613 error = EAGAIN; 614 goto error; 615 } 616 if (t < 0) { 617 error = EINVAL; 618 goto error; 619 } 620 /* Block until queue becomes available */ 621 error = cv_timedwait_sig(&mq->mq_recv_cv, &mq->mq_mtx, t); 622 if (error || (mq->mq_attrib.mq_flags & MQ_UNLINK)) { 623 error = (error == EWOULDBLOCK) ? ETIMEDOUT : error; 624 goto error; 625 } 626 } 627 KASSERT(mq->mq_attrib.mq_curmsgs < mq->mq_attrib.mq_maxmsg); 628 629 /* Insert message into the queue, according to the priority */ 630 TAILQ_FOREACH(pos_msg, &mq->mq_head, msg_queue) 631 if (msg->msg_prio > pos_msg->msg_prio) 632 break; 633 if (pos_msg == NULL) 634 TAILQ_INSERT_TAIL(&mq->mq_head, msg, msg_queue); 635 else 636 TAILQ_INSERT_BEFORE(pos_msg, msg, msg_queue); 637 638 /* Check for the notify */ 639 if (mq->mq_attrib.mq_curmsgs == 0 && mq->mq_notify_proc && 640 (mq->mq_attrib.mq_flags & MQ_RECEIVE) == 0) { 641 /* Initialize the signal */ 642 KSI_INIT(&ksi); 643 ksi.ksi_signo = mq->mq_sig_notify.sigev_signo; 644 ksi.ksi_code = SI_MESGQ; 645 ksi.ksi_value = mq->mq_sig_notify.sigev_value; 646 /* Unregister the process */ 647 notify = mq->mq_notify_proc; 648 mq->mq_notify_proc = NULL; 649 } 650 651 /* Increment the counter and signal waiter, if any */ 652 mq->mq_attrib.mq_curmsgs++; 653 cv_signal(&mq->mq_send_cv); 654 error: 655 mutex_exit(&mq->mq_mtx); 656 FILE_UNUSE(fp, l); 657 658 if (error) { 659 mqueue_freemsg(msg, size); 660 } else if (notify) { 661 /* Send the notify, if needed */ 662 mutex_enter(&proclist_mutex); 663 kpsignal(notify, &ksi, NULL); 664 mutex_exit(&proclist_mutex); 665 } 666 667 return error; 668 } 669 670 int 671 sys_mq_send(struct lwp *l, void *v, register_t *retval) 672 { 673 struct sys_mq_send_args /* { 674 syscallarg(mqd_t) mqdes; 675 syscallarg(const char *) msg_ptr; 676 syscallarg(size_t) msg_len; 677 syscallarg(unsigned) msg_prio; 678 } */ *uap = v; 679 680 return mq_send1(l, SCARG(uap, mqdes), SCARG(uap, msg_ptr), 681 SCARG(uap, msg_len), SCARG(uap, msg_prio), 0); 682 } 683 684 int 685 sys_mq_timedsend(struct lwp *l, void *v, register_t *retval) 686 { 687 struct sys_mq_timedsend_args /* { 688 syscallarg(mqd_t) mqdes; 689 syscallarg(const char *) msg_ptr; 690 syscallarg(size_t) msg_len; 691 syscallarg(unsigned) msg_prio; 692 syscallarg(const struct timespec *) abs_timeout; 693 } */ *uap = v; 694 int t; 695 696 /* Get and convert time value */ 697 if (SCARG(uap, abs_timeout)) { 698 int error = abstimeout2timo(SCARG(uap, abs_timeout), &t); 699 if (error) 700 return error; 701 } else 702 t = 0; 703 704 return mq_send1(l, SCARG(uap, mqdes), SCARG(uap, msg_ptr), 705 SCARG(uap, msg_len), SCARG(uap, msg_prio), t); 706 } 707 708 int 709 sys_mq_notify(struct lwp *l, void *v, register_t *retval) 710 { 711 struct sys_mq_notify_args /* { 712 syscallarg(mqd_t) mqdes; 713 syscallarg(const struct sigevent *) notification; 714 } */ *uap = v; 715 struct file *fp = NULL; 716 struct mqueue *mq; 717 struct sigevent sig; 718 int error; 719 720 if (SCARG(uap, notification)) { 721 /* Get the signal from user-space */ 722 error = copyin(SCARG(uap, notification), &sig, 723 sizeof(struct sigevent)); 724 if (error) 725 return error; 726 } 727 728 error = mqueue_get(l, SCARG(uap, mqdes), VNOVAL, &fp); 729 if (error) 730 return error; 731 mq = fp->f_data; 732 733 if (SCARG(uap, notification)) { 734 /* Register notification: set the signal and target process */ 735 if (mq->mq_notify_proc == NULL) { 736 memcpy(&mq->mq_sig_notify, &sig, 737 sizeof(struct sigevent)); 738 mq->mq_notify_proc = l->l_proc; 739 } else { 740 /* Fail if someone else already registered */ 741 error = EBUSY; 742 } 743 } else { 744 /* Unregister the notification */ 745 mq->mq_notify_proc = NULL; 746 } 747 mutex_exit(&mq->mq_mtx); 748 FILE_UNUSE(fp, l); 749 750 return error; 751 } 752 753 int 754 sys_mq_getattr(struct lwp *l, void *v, register_t *retval) 755 { 756 struct sys_mq_getattr_args /* { 757 syscallarg(mqd_t) mqdes; 758 syscallarg(struct mq_attr *) mqstat; 759 } */ *uap = v; 760 struct file *fp = NULL; 761 struct mqueue *mq; 762 struct mq_attr attr; 763 int error; 764 765 /* Get the message queue */ 766 error = mqueue_get(l, SCARG(uap, mqdes), VNOVAL, &fp); 767 if (error) 768 return error; 769 mq = fp->f_data; 770 memcpy(&attr, &mq->mq_attrib, sizeof(struct mq_attr)); 771 mutex_exit(&mq->mq_mtx); 772 FILE_UNUSE(fp, l); 773 774 return copyout(&attr, SCARG(uap, mqstat), sizeof(struct mq_attr)); 775 } 776 777 int 778 sys_mq_setattr(struct lwp *l, void *v, register_t *retval) 779 { 780 struct sys_mq_setattr_args /* { 781 syscallarg(mqd_t) mqdes; 782 syscallarg(const struct mq_attr *) mqstat; 783 syscallarg(struct mq_attr *) omqstat; 784 } */ *uap = v; 785 struct file *fp = NULL; 786 struct mqueue *mq; 787 struct mq_attr attr; 788 int error, nonblock; 789 790 error = copyin(SCARG(uap, mqstat), &attr, sizeof(struct mq_attr)); 791 if (error) 792 return error; 793 nonblock = (attr.mq_flags & O_NONBLOCK); 794 795 /* Get the message queue */ 796 error = mqueue_get(l, SCARG(uap, mqdes), VNOVAL, &fp); 797 if (error) 798 return error; 799 mq = fp->f_data; 800 801 /* Copy the old attributes, if needed */ 802 if (SCARG(uap, omqstat)) 803 memcpy(&attr, &mq->mq_attrib, sizeof(struct mq_attr)); 804 805 /* Ignore everything, except O_NONBLOCK */ 806 if (nonblock) 807 mq->mq_attrib.mq_flags |= O_NONBLOCK; 808 else 809 mq->mq_attrib.mq_flags &= ~O_NONBLOCK; 810 811 mutex_exit(&mq->mq_mtx); 812 FILE_UNUSE(fp, l); 813 814 /* 815 * Copy the data to the user-space. 816 * Note: According to POSIX, the new attributes should not be set in 817 * case of fail - this would be violated. 818 */ 819 if (SCARG(uap, omqstat)) 820 error = copyout(&attr, SCARG(uap, omqstat), 821 sizeof(struct mq_attr)); 822 823 return error; 824 } 825 826 int 827 sys_mq_unlink(struct lwp *l, void *v, register_t *retval) 828 { 829 struct sys_mq_unlink_args /* { 830 syscallarg(const char *) name; 831 } */ *uap = v; 832 struct mqueue *mq; 833 char *name; 834 int error, refcnt = 0; 835 836 /* Get the name from the user-space */ 837 name = kmem_zalloc(MQ_NAMELEN, KM_SLEEP); 838 error = copyinstr(SCARG(uap, name), name, MQ_NAMELEN - 1, NULL); 839 if (error) { 840 kmem_free(name, MQ_NAMELEN); 841 return error; 842 } 843 844 /* Lookup for this file */ 845 mutex_enter(&mqlist_mtx); 846 mq = mqueue_lookup(name); 847 if (mq == NULL) { 848 error = ENOENT; 849 goto error; 850 } 851 852 /* Check the permissions */ 853 if (mqueue_access(l, mq, VWRITE)) { 854 mutex_exit(&mq->mq_mtx); 855 error = EACCES; 856 goto error; 857 } 858 859 /* Mark message queue as unlinking, before leaving the window */ 860 mq->mq_attrib.mq_flags |= MQ_UNLINK; 861 862 /* Wake up all waiters, if there are such */ 863 cv_broadcast(&mq->mq_send_cv); 864 cv_broadcast(&mq->mq_recv_cv); 865 866 refcnt = mq->mq_refcnt; 867 if (refcnt == 0) 868 LIST_REMOVE(mq, mq_list); 869 870 mutex_exit(&mq->mq_mtx); 871 error: 872 mutex_exit(&mqlist_mtx); 873 874 /* 875 * If there are no references - destroy the message 876 * queue, otherwise, the last mq_close() will do that. 877 */ 878 if (error == 0 && refcnt == 0) 879 mqueue_destroy(mq); 880 881 kmem_free(name, MQ_NAMELEN); 882 return error; 883 } 884 885 /* 886 * SysCtl. 887 */ 888 889 SYSCTL_SETUP(sysctl_mqueue_setup, "sysctl mqueue setup") 890 { 891 const struct sysctlnode *node = NULL; 892 893 sysctl_createv(clog, 0, NULL, NULL, 894 CTLFLAG_PERMANENT, 895 CTLTYPE_NODE, "kern", NULL, 896 NULL, 0, NULL, 0, 897 CTL_KERN, CTL_EOL); 898 sysctl_createv(clog, 0, NULL, NULL, 899 CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE, 900 CTLTYPE_INT, "posix_msg", 901 SYSCTL_DESCR("Version of IEEE Std 1003.1 and its " 902 "Message Passing option to which the " 903 "system attempts to conform"), 904 NULL, _POSIX_MESSAGE_PASSING, NULL, 0, 905 CTL_KERN, CTL_CREATE, CTL_EOL); 906 sysctl_createv(clog, 0, NULL, &node, 907 CTLFLAG_PERMANENT, 908 CTLTYPE_NODE, "mqueue", 909 SYSCTL_DESCR("Message queue options"), 910 NULL, 0, NULL, 0, 911 CTL_KERN, CTL_CREATE, CTL_EOL); 912 913 if (node == NULL) 914 return; 915 916 sysctl_createv(clog, 0, &node, NULL, 917 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 918 CTLTYPE_INT, "mq_open_max", 919 SYSCTL_DESCR("Maximal number of message queue descriptors " 920 "that process could open"), 921 NULL, 0, &mq_open_max, 0, 922 CTL_CREATE, CTL_EOL); 923 sysctl_createv(clog, 0, &node, NULL, 924 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 925 CTLTYPE_INT, "mq_prio_max", 926 SYSCTL_DESCR("Maximal priority of the message"), 927 NULL, 0, &mq_prio_max, 0, 928 CTL_CREATE, CTL_EOL); 929 sysctl_createv(clog, 0, &node, NULL, 930 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 931 CTLTYPE_INT, "mq_max_msgsize", 932 SYSCTL_DESCR("Maximal allowed size of the message"), 933 NULL, 0, &mq_max_msgsize, 0, 934 CTL_CREATE, CTL_EOL); 935 sysctl_createv(clog, 0, &node, NULL, 936 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 937 CTLTYPE_INT, "mq_def_maxmsg", 938 SYSCTL_DESCR("Default maximal message count"), 939 NULL, 0, &mq_def_maxmsg, 0, 940 CTL_CREATE, CTL_EOL); 941 } 942 943 /* 944 * Debugging. 945 */ 946 #if defined(DDB) 947 948 void 949 mqueue_print_list(void (*pr)(const char *, ...)) 950 { 951 struct mqueue *mq; 952 953 (*pr)("Global list of the message queues:\n"); 954 (*pr)("%20s %10s %8s %8s %3s %4s %4s %4s\n", 955 "Name", "Ptr", "Mode", "Flags", "Ref", 956 "MaxMsg", "MsgSze", "CurMsg"); 957 LIST_FOREACH(mq, &mqueue_head, mq_list) { 958 (*pr)("%20s %10p %8x %8x %3u %6lu %6lu %6lu\n", 959 mq->mq_name, mq, mq->mq_mode, 960 mq->mq_attrib.mq_flags, mq->mq_refcnt, 961 mq->mq_attrib.mq_maxmsg, mq->mq_attrib.mq_msgsize, 962 mq->mq_attrib.mq_curmsgs); 963 } 964 } 965 966 #endif /* defined(DDB) */ 967