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