1 /* $FreeBSD: src/sys/kern/sysv_msg.c,v 1.23.2.5 2002/12/31 08:54:53 maxim Exp $ */
4 * Implementation of SVID messages
6 * Author: Daniel Boulet
8 * Copyright 1993 Daniel Boulet and RTMX Inc.
9 * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
11 * This system call was implemented by Daniel Boulet under contract from RTMX.
13 * Redistribution and use in source forms, with and without modification,
14 * are permitted provided that this entire comment appears intact.
16 * Redistribution in binary form may occur without any restrictions.
17 * Obviously, it would be nice if you gave credit where credit is due
18 * but requiring it would be too onerous.
20 * This software is provided ``AS IS'' without any warranties of any kind.
23 #include "namespace.h"
31 #include <sys/param.h>
32 #include <sys/queue.h>
34 #include "un-namespace.h"
36 #include "sysvipc_lock.h"
37 #include "sysvipc_ipc.h"
38 #include "sysvipc_hash.h"
39 #include "sysvipc_msg.h"
40 #include "sysvipc_shm.h"
42 #define SYSV_MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x)
43 #define SYSV_MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x)
44 #define SYSV_MUTEX_DESTROY(x) if (__isthreaded) _pthread_mutex_destroy(x)
46 extern struct hashtable *shmaddrs;
47 extern struct hashtable *shmres;
48 extern pthread_mutex_t lock_resources;
50 struct msginfo msginfo = {
51 MSGMAX, /* max chars in a message */
52 MSGMNI, /* # of message queue identifiers */
53 MSGMNB, /* max chars in a queue */
54 MSGTQL, /* max messages in system */
55 MSGSSZ, /* size of a message segment (must be small power of 2 greater than 4) */
56 MSGSEG /* number of message segments */
61 struct shm_data *data;
64 SYSV_MUTEX_LOCK(&lock_resources);
65 data = _hash_lookup(shmres, id);
67 sysv_print_err("something wrong put_shmdata\n");
68 goto done; /* It should not reach here. */
72 if (data->used == 0 && data->removed) {
73 sysv_print("really remove the sem\n");
74 SYSV_MUTEX_UNLOCK(&lock_resources);
75 /* OBS: Even if the shmctl fails (the thread doesn't
76 * have IPC_M permissions), all structures associated
77 * with it will be removed in the current process.*/
78 shmdt(data->internal);
79 if (data->removed == SEG_ALREADY_REMOVED)
80 return 1; /* The queue was removed
81 by another process so there is nothing else
83 /* Else inform the daemon that the segment is removed. */
84 return (sysvipc_shmctl(id, IPC_RMID, NULL));
89 SYSV_MUTEX_UNLOCK(&lock_resources);
93 static struct msqid_pool*
94 get_msqpptr(int msqid, int to_remove, int shm_access) {
95 struct msqid_pool *msqpptr;
97 struct shm_data *shmdata =
98 get_shmdata(msqid, to_remove, shm_access);
100 /* Error is set in get_shmdata. */
104 msqpptr = (struct msqid_pool *)shmdata->internal;
115 msqp_exist(int msqid, struct msqid_pool *msqpptr) {
116 /* Was it removed? */
117 if (msqpptr->gen == -1 ||
118 msqpptr->ds.msg_perm.seq != IPCID_TO_SEQ(msqid))
125 try_rwlock_rdlock(int msqid, struct msqid_pool *msqpptr) {
126 sysv_print("try get rd lock\n");
128 sysv_rwlock_rdlock(&msqpptr->rwlock);
130 sysv_mutex_lock(&msqpptr->mutex);
132 sysv_print("get rd lock\n");
133 if (!msqp_exist(msqid, msqpptr)) {
135 sysv_print("error rd lock\n");
137 sysv_rwlock_unlock(&msqpptr->rwlock);
139 sysv_mutex_unlock(&msqpptr->mutex);
143 sysv_print("end rd lock\n");
148 try_rwlock_wrlock(int msqid, struct msqid_pool *msqpptr) {
149 sysv_print("try get wr lock\n");
151 sysv_rwlock_wrlock(&msqpptr->rwlock);
153 sysv_mutex_lock(&msqpptr->mutex);
155 sysv_print("get wr lock\n");
156 if (!msqp_exist(msqid, msqpptr)) {
157 sysv_print("error rw lock\n");
160 sysv_rwlock_unlock(&msqpptr->rwlock);
162 sysv_mutex_unlock(&msqpptr->mutex);
166 sysv_print("end rw lock\n");
171 rwlock_unlock(int msqid, struct msqid_pool *msqpptr) {
172 if (!msqp_exist(msqid, msqpptr)) {
177 sysv_rwlock_unlock(&msqpptr->rwlock);
179 sysv_mutex_unlock(&msqpptr->mutex);
181 sysv_print("unlock rw lock\n");
186 msg_freehdr(struct msqid_pool *msqpptr, struct msg *msghdr)
188 while (msghdr->msg_ts > 0) {
190 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) {
191 sysv_print_err("msghdr->msg_spot out of range");
194 next = msqpptr->msgmaps[msghdr->msg_spot].next;
195 msqpptr->msgmaps[msghdr->msg_spot].next =
196 msqpptr->free_msgmaps;
197 msqpptr->free_msgmaps = msghdr->msg_spot;
198 msqpptr->nfree_msgmaps++;
199 msghdr->msg_spot = next;
200 if (msghdr->msg_ts >= msginfo.msgssz)
201 msghdr->msg_ts -= msginfo.msgssz;
205 if (msghdr->msg_spot != -1) {
206 sysv_print_err("msghdr->msg_spot != -1");
209 msghdr->msg_next = msqpptr->free_msghdrs;
210 msqpptr->free_msghdrs = (msghdr - &msqpptr->msghdrs[0]) /
215 sysvipc_msgget(key_t key, int msgflg) {
218 size_t size = sizeof(struct msqid_pool);
220 msqid = _shmget(key, size, msgflg, MSGGET);
224 /* If the msg is in process of being removed there are two cases:
225 * - the daemon knows that and it will handle this situation.
226 * - one of the threads from this address space remove it and the daemon
227 * wasn't announced yet; in this scenario, the msg is marked
228 * using "removed" field of shm_data and future calls will return
233 /* Set access type. */
234 shm_access = semflg & (IPC_W | IPC_R);
235 if(set_shmdata_access(semid, shm_access) != 0) {
236 /* errno already set. */
241 shmaddr = sysvipc_shmat(msqid, NULL, 0);
244 sysvipc_shmctl(msqid, IPC_RMID, NULL);
247 sysv_print("shmaddr = %lx\n", (unsigned long)shmaddr);
254 sysvipc_msgctl(int msqid, int cmd, struct msqid_ds *buf) {
256 struct msqid_pool *msqpptr = NULL;
257 struct shmid_ds shmds;
263 case IPC_SET: /* Originally was IPC_M but this is checked
274 msqpptr = get_msqpptr(msqid, cmd==IPC_RMID, shm_access);
282 /* Mark that the segment is removed. This is done in
283 * get_msqpptr call in order to announce other processes.
284 * It will be actually removed after put_shmdata call and
285 * not other thread from this address space use shm_data
290 error = try_rwlock_rdlock(msqid, msqpptr);
293 if (buf->msg_qbytes == 0) {
294 sysv_print_err("can't reduce msg_qbytes to 0\n");
295 errno = EINVAL; /* non-standard errno! */
296 rwlock_unlock(msqid, msqpptr);
299 rwlock_unlock(msqid, msqpptr);
301 memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char));
302 memcpy(&shmds.shm_perm, &buf->msg_perm,
303 sizeof(struct ipc_perm));
304 error = sysvipc_shmctl(msqid, cmd, &shmds);
308 /* There is no need to check if we have right to modify the
309 * size because we have right to change other fileds. */
311 if (round_page(buf->msg_qbytes) !=
312 round_page(msqpptr->ds.msg_qbytes)) {
313 sysv_print("change msg size\n");
314 /* TODO same as in semundo_adjust only
315 * that there is no way to inform other
316 * processes about the change. */
319 error = try_rwlock_wrlock(msqid, msqpptr);
322 msqpptr->ds.msg_qbytes = buf->msg_qbytes;
323 rwlock_unlock(msqid, msqpptr);
324 /* OBS: didn't update ctime and mode as in kernel implementation
325 * it is done. Those fields are already updated for shmid_ds
326 * struct when calling shmctl
331 error = sysvipc_shmctl(msqid, cmd, &shmds);
335 memcpy(&buf->msg_perm, &shmds.shm_perm,
336 sizeof(struct ipc_perm));
337 buf->msg_ctime = shmds.shm_ctime;
339 /* Read fields that are not kept in shmds. */
340 error = try_rwlock_rdlock(msqid, msqpptr);
343 buf->msg_first = (struct msg *)(u_long)
344 msqpptr->ds.first.msg_first_index;
345 buf->msg_last = (struct msg *)(u_long)
346 msqpptr->ds.last.msg_last_index;
347 buf->msg_cbytes = msqpptr->ds.msg_cbytes;
348 buf->msg_qnum = msqpptr->ds.msg_qnum;
349 buf->msg_qbytes = msqpptr->ds.msg_qbytes;
350 buf->msg_lspid = msqpptr->ds.msg_lspid;
351 buf->msg_lrpid = msqpptr->ds.msg_lrpid;
352 buf->msg_stime = msqpptr->ds.msg_stime;
353 buf->msg_rtime = msqpptr->ds.msg_rtime;
354 rwlock_unlock(msqid, msqpptr);
357 sysv_print_err("invalid command %d\n", cmd);
368 sysvipc_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
370 int segs_needed, error;
372 struct msqid_pool *msqpptr, *auxmsqpptr;
373 struct msqid_ds_internal *msqptr;
376 char *auxmsgp = (char *)msgp;
379 sysv_print("call to msgsnd(%d, %ld, %d)\n", msqid, msgsz, msgflg);
381 /*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
389 msqpptr = get_msqpptr(msqid, 0, IPC_W);
396 if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
401 msqptr = &msqpptr->ds;
403 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
404 sysv_print("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
407 int need_more_resources = 0;
409 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
410 sysv_print("msgsz + msg_cbytes > msg_qbytes\n");
411 need_more_resources = 1;
414 if (segs_needed > msqpptr->nfree_msgmaps) {
415 sysv_print("segs_needed > nfree_msgmaps (= %d)\n",
416 msqpptr->nfree_msgmaps);
417 need_more_resources = 1;
420 if (msqpptr->free_msghdrs == -1) {
421 sysv_print("no more msghdrs\n");
422 need_more_resources = 1;
425 if (need_more_resources) {
426 if ((msgflg & IPC_NOWAIT) != 0) {
427 sysv_print_err("need more resources but caller doesn't want to wait\n");
432 sysv_print("goodnight\n");
433 val_to_sleep = msqpptr->gen;
434 rwlock_unlock(msqid, msqpptr);
437 if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) {
438 sysv_print_err("msgsnd: interrupted system call\n");
443 /* Check if another thread didn't remove the msg queue. */
444 auxmsqpptr = get_msqpptr(msqid, 0, IPC_W);
450 if (auxmsqpptr != msqpptr) {
455 /* Check if another process didn't remove the queue. */
456 if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
461 if (msqptr != &msqpptr->ds) {
462 sysv_print("msqptr != &msqpptr->ds");
467 sysv_print("got all the resources that we need\n");
473 * We have the resources that we need.
477 if (segs_needed > nfree_msgmaps) {
478 sysv_print_err("segs_needed > nfree_msgmaps");
482 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
483 sysv_print_err("msgsz + msg_cbytes > msg_qbytes");
488 * Allocate a message header
490 msghdr = &msqpptr->msghdrs[msqpptr->free_msghdrs];
491 msqpptr->free_msghdrs = msghdr->msg_next;
492 msghdr->msg_spot = -1;
493 msghdr->msg_ts = msgsz;
496 * Allocate space for the message
498 while (segs_needed > 0) {
499 next = msqpptr->free_msgmaps;
500 if (next < 0 || next > msginfo.msgseg) {
501 sysv_print_err("out of range free_msgmaps %d #1\n", next);
505 msqpptr->free_msgmaps = msqpptr->msgmaps[next].next;
506 msqpptr->nfree_msgmaps--;
507 msqpptr->msgmaps[next].next = msghdr->msg_spot;
508 msghdr->msg_spot = next;
513 * Copy in the message type
515 memcpy(&msghdr->msg_type, auxmsgp, sizeof(msghdr->msg_type));
516 auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type);
519 * Validate the message type
521 sysv_print("msg_type = %ld\n", msghdr->msg_type);
523 if (msghdr->msg_type < 1) {
524 msg_freehdr(msqpptr, msghdr);
525 umtx_wakeup((int *)&msqpptr->gen, 0);
526 sysv_print_err("mtype (%ld) < 1\n", msghdr->msg_type);
532 * Copy in the message body
534 next = msghdr->msg_spot;
537 if (msgsz > (size_t)msginfo.msgssz)
538 tlen = msginfo.msgssz;
541 if (next < 0 || next > msginfo.msgseg) {
542 sysv_print_err("out of range free_msgmaps %d #2\n", next);
546 memcpy(&msqpptr->msgpool[next * msginfo.msgssz], auxmsgp, tlen);
548 auxmsgp = (char *)auxmsgp + tlen;
549 next = msqpptr->msgmaps[next].next;
553 * Put the message into the queue
555 _index = (msghdr - &msqpptr->msghdrs[0]) /
557 sysv_print("index_msghdr = %d\n", _index);
558 if (msqptr->first.msg_first_index == -1) {
559 msqptr->first.msg_first_index = _index;
560 msqptr->last.msg_last_index = _index;
562 msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = _index;
563 msqptr->last.msg_last_index = _index;
565 msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = -1;
567 msqptr->msg_cbytes += msghdr->msg_ts;
569 msqptr->msg_lspid = getpid();
570 msqptr->msg_stime = time(NULL);
572 umtx_wakeup((int *)&msqpptr->gen, 0);
576 rwlock_unlock(msqid, msqpptr);
582 sysvipc_msgrcv(int msqid, void *msgp, size_t msgsz, long mtype, int msgflg)
585 struct msqid_pool *msqpptr, *auxmsqpptr;
586 struct msqid_ds_internal *msqptr;
592 char *auxmsgp = (char *)msgp;
594 sysv_print("call to msgrcv(%d, %ld, %ld, %d)\n", msqid, msgsz, mtype, msgflg);
596 if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
605 msqpptr = get_msqpptr(msqid, 0, IPC_R);
613 if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
618 msqptr = &msqpptr->ds;
621 while (msghdr_index == -1) {
623 msghdr_index = msqptr->first.msg_first_index;
624 msghdr = &msqpptr->msghdrs[msghdr_index];
625 if (msghdr_index != -1) {
626 if (msgsz < msghdr->msg_ts &&
627 (msgflg & MSG_NOERROR) == 0) {
628 sysv_print_err("first message on the queue is too big"
629 "(want %d, got %d)\n",
630 msgsz, msghdr->msg_ts);
634 if (msqptr->first.msg_first_index == msqptr->last.msg_last_index) {
635 msqptr->first.msg_first_index = -1;
636 msqptr->last.msg_last_index = -1;
638 msqptr->first.msg_first_index = msghdr->msg_next;
639 if (msqptr->first.msg_first_index == -1) {
640 sysv_print_err("first.msg_first_index/last screwed up #1");
649 prev = msqptr->first.msg_first_index;
650 while ((msghdr_index = prev) != -1) {
651 msghdr = &msqpptr->msghdrs[msghdr_index];
653 * Is this message's type an exact match or is
654 * this message's type less than or equal to
655 * the absolute value of a negative mtype?
656 * Note that the second half of this test can
657 * NEVER be true if mtype is positive since
658 * msg_type is always positive!
660 if (mtype == msghdr->msg_type ||
661 msghdr->msg_type <= -mtype) {
662 sysv_print("found message type %d, requested %d\n",
663 msghdr->msg_type, mtype);
664 if (msgsz < msghdr->msg_ts &&
665 (msgflg & MSG_NOERROR) == 0) {
666 sysv_print_err("requested message on the queue"
667 " is too big (want %d, got %d)\n",
668 msgsz, msghdr->msg_ts);
672 prev = msghdr->msg_next;
673 if (msghdr_index == msqptr->last.msg_last_index) {
674 if (previous == -1) {
675 msqptr->first.msg_first_index = -1;
676 msqptr->last.msg_last_index = -1;
678 msqptr->last.msg_last_index = previous;
683 previous = msghdr_index;
684 prev = msghdr->msg_next;
689 * We've either extracted the msghdr for the appropriate
690 * message or there isn't one.
691 * If there is one then bail out of this loop.
693 if (msghdr_index != -1)
697 * No message found. Does the user want to wait?
699 if ((msgflg & IPC_NOWAIT) != 0) {
700 sysv_print_err("no appropriate message found (mtype=%d)\n",
707 * Wait for something to happen
709 sysv_print("goodnight\n");
710 val_to_sleep = msqpptr->gen;
711 rwlock_unlock(msqid, msqpptr);
714 /* We don't sleep more than SYSV_TIMEOUT because we could
715 * go to sleep after another process calls wakeup and remain
718 if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) {
719 sysv_print_err("msgrcv: interrupted system call\n");
723 sysv_print("msgrcv: good morning (error=%d)\n", errno);
725 /* Check if another thread didn't remove the msg queue. */
726 auxmsqpptr = get_msqpptr(msqid, 0, IPC_R);
732 if (auxmsqpptr != msqpptr) {
737 /* Check if another process didn't remove the msg queue. */
738 if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
743 if (msqptr != &msqpptr->ds) {
744 sysv_print_err("msqptr != &msqpptr->ds");
750 * Return the message to the user.
752 msqptr->msg_cbytes -= msghdr->msg_ts;
754 msqptr->msg_lrpid = getpid();
755 msqptr->msg_rtime = time(NULL);
758 * Make msgsz the actual amount that we'll be returning.
759 * Note that this effectively truncates the message if it is too long
760 * (since msgsz is never increased).
762 sysv_print("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
764 if (msgsz > msghdr->msg_ts)
765 msgsz = msghdr->msg_ts;
768 * Return the type to the user.
770 memcpy(auxmsgp, (caddr_t)&(msghdr->msg_type), sizeof(msghdr->msg_type));
771 auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type);
774 * Return the segments to the user
776 next = msghdr->msg_spot;
777 for (len = 0; len < msgsz; len += msginfo.msgssz) {
780 if (msgsz - len > (size_t)msginfo.msgssz)
781 tlen = msginfo.msgssz;
784 if (next < 0 || next > msginfo.msgseg) {
785 sysv_print_err("out of range free_msgmaps %d #3\n", next);
789 memcpy(auxmsgp, (caddr_t)&msqpptr->msgpool[next * msginfo.msgssz], tlen);
790 auxmsgp = (char *)auxmsgp + tlen;
791 next = msqpptr->msgmaps[next].next;
795 * Done, return the actual number of bytes copied out.
797 msg_freehdr(msqpptr, msghdr);
798 umtx_wakeup((int *)&msqpptr->gen, 0);
801 rwlock_unlock(msqid, msqpptr);