xref: /dflybsd-src/lib/libc/sysvipc/msg.c (revision ff86f40163b90743b832c47e55fc6ca83aa45121)
182657471SMarkus Pfeiffer /* $FreeBSD: src/sys/kern/sysv_msg.c,v 1.23.2.5 2002/12/31 08:54:53 maxim Exp $ */
282657471SMarkus Pfeiffer 
382657471SMarkus Pfeiffer /*
482657471SMarkus Pfeiffer  * Implementation of SVID messages
582657471SMarkus Pfeiffer  *
682657471SMarkus Pfeiffer  * Author:  Daniel Boulet
782657471SMarkus Pfeiffer  *
882657471SMarkus Pfeiffer  * Copyright 1993 Daniel Boulet and RTMX Inc.
982657471SMarkus Pfeiffer  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
1082657471SMarkus Pfeiffer  *
1182657471SMarkus Pfeiffer  * This system call was implemented by Daniel Boulet under contract from RTMX.
1282657471SMarkus Pfeiffer  *
1382657471SMarkus Pfeiffer  * Redistribution and use in source forms, with and without modification,
1482657471SMarkus Pfeiffer  * are permitted provided that this entire comment appears intact.
1582657471SMarkus Pfeiffer  *
1682657471SMarkus Pfeiffer  * Redistribution in binary form may occur without any restrictions.
1782657471SMarkus Pfeiffer  * Obviously, it would be nice if you gave credit where credit is due
1882657471SMarkus Pfeiffer  * but requiring it would be too onerous.
1982657471SMarkus Pfeiffer  *
2082657471SMarkus Pfeiffer  * This software is provided ``AS IS'' without any warranties of any kind.
2182657471SMarkus Pfeiffer  */
2282657471SMarkus Pfeiffer 
2382657471SMarkus Pfeiffer #include "namespace.h"
2482657471SMarkus Pfeiffer #include <stdio.h>
2582657471SMarkus Pfeiffer #include <stdlib.h>
2682657471SMarkus Pfeiffer #include <errno.h>
2782657471SMarkus Pfeiffer #include <err.h>
2882657471SMarkus Pfeiffer #include <pthread.h>
2982657471SMarkus Pfeiffer #include <string.h>
3082657471SMarkus Pfeiffer #include <stdarg.h>
3182657471SMarkus Pfeiffer #include <sys/param.h>
3282657471SMarkus Pfeiffer #include <sys/queue.h>
3382657471SMarkus Pfeiffer #include <sys/mman.h>
3482657471SMarkus Pfeiffer #include "un-namespace.h"
3582657471SMarkus Pfeiffer 
3682657471SMarkus Pfeiffer #include "sysvipc_lock.h"
3782657471SMarkus Pfeiffer #include "sysvipc_ipc.h"
3882657471SMarkus Pfeiffer #include "sysvipc_hash.h"
3982657471SMarkus Pfeiffer #include "sysvipc_msg.h"
4082657471SMarkus Pfeiffer #include "sysvipc_shm.h"
4182657471SMarkus Pfeiffer 
4282657471SMarkus Pfeiffer #define SYSV_MUTEX_LOCK(x)	if (__isthreaded) _pthread_mutex_lock(x)
4382657471SMarkus Pfeiffer #define SYSV_MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
4482657471SMarkus Pfeiffer #define SYSV_MUTEX_DESTROY(x)	if (__isthreaded) _pthread_mutex_destroy(x)
4582657471SMarkus Pfeiffer 
4682657471SMarkus Pfeiffer extern struct hashtable *shmaddrs;
4782657471SMarkus Pfeiffer extern struct hashtable *shmres;
4882657471SMarkus Pfeiffer extern pthread_mutex_t lock_resources;
4982657471SMarkus Pfeiffer 
5082657471SMarkus Pfeiffer struct msginfo msginfo = {
5182657471SMarkus Pfeiffer                 MSGMAX,         /* max chars in a message */
5282657471SMarkus Pfeiffer                 MSGMNI,         /* # of message queue identifiers */
5382657471SMarkus Pfeiffer                 MSGMNB,         /* max chars in a queue */
5482657471SMarkus Pfeiffer                 MSGTQL,         /* max messages in system */
5582657471SMarkus Pfeiffer                 MSGSSZ,         /* size of a message segment (must be small power of 2 greater than 4) */
5682657471SMarkus Pfeiffer                 MSGSEG          /* number of message segments */
5782657471SMarkus Pfeiffer };
5882657471SMarkus Pfeiffer 
5982657471SMarkus Pfeiffer static int
put_shmdata(int id)60*ff86f401SSascha Wildner put_shmdata(int id)
61*ff86f401SSascha Wildner {
6282657471SMarkus Pfeiffer 	struct shm_data *data;
6382657471SMarkus Pfeiffer 	int ret = -1;
6482657471SMarkus Pfeiffer 
6582657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
6682657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, id);
6782657471SMarkus Pfeiffer 	if (!data) {
6882657471SMarkus Pfeiffer 		sysv_print_err("something wrong put_shmdata\n");
6982657471SMarkus Pfeiffer 		goto done; /* It should not reach here. */
7082657471SMarkus Pfeiffer 	}
7182657471SMarkus Pfeiffer 
7282657471SMarkus Pfeiffer 	data->used--;
7382657471SMarkus Pfeiffer 	if (data->used == 0 && data->removed) {
7482657471SMarkus Pfeiffer 		sysv_print("really remove the sem\n");
7582657471SMarkus Pfeiffer 		SYSV_MUTEX_UNLOCK(&lock_resources);
7682657471SMarkus Pfeiffer 		/* OBS: Even if the shmctl fails (the thread doesn't
7782657471SMarkus Pfeiffer 		 * have IPC_M permissions), all structures associated
7882657471SMarkus Pfeiffer 		 * with it will be removed in the current process.*/
7982657471SMarkus Pfeiffer 		shmdt(data->internal);
8082657471SMarkus Pfeiffer 		if (data->removed == SEG_ALREADY_REMOVED)
8182657471SMarkus Pfeiffer 			return 1; /* The queue was removed
8282657471SMarkus Pfeiffer 			by another process so there is nothing else
8382657471SMarkus Pfeiffer 			we must do. */
8482657471SMarkus Pfeiffer 		/* Else inform the daemon that the segment is removed. */
8582657471SMarkus Pfeiffer 		return (sysvipc_shmctl(id, IPC_RMID, NULL));
8682657471SMarkus Pfeiffer 	}
8782657471SMarkus Pfeiffer 
8882657471SMarkus Pfeiffer 	ret = 0;
8982657471SMarkus Pfeiffer done:
9082657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
9182657471SMarkus Pfeiffer 	return (ret);
9282657471SMarkus Pfeiffer }
9382657471SMarkus Pfeiffer 
9482657471SMarkus Pfeiffer static struct msqid_pool*
get_msqpptr(int msqid,int to_remove,int shm_access)95*ff86f401SSascha Wildner get_msqpptr(int msqid, int to_remove, int shm_access)
96*ff86f401SSascha Wildner {
9782657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr;
9882657471SMarkus Pfeiffer 
9982657471SMarkus Pfeiffer 	struct shm_data *shmdata =
10082657471SMarkus Pfeiffer 		get_shmdata(msqid, to_remove, shm_access);
10182657471SMarkus Pfeiffer 	if (!shmdata) {
10282657471SMarkus Pfeiffer 		/* Error is set in get_shmdata. */
10382657471SMarkus Pfeiffer 		return NULL;
10482657471SMarkus Pfeiffer 	}
10582657471SMarkus Pfeiffer 
10682657471SMarkus Pfeiffer 	msqpptr = (struct msqid_pool *)shmdata->internal;
10782657471SMarkus Pfeiffer 	if (!msqpptr) {
10882657471SMarkus Pfeiffer 		put_shmdata(msqid);
10982657471SMarkus Pfeiffer 		errno = EINVAL;
11082657471SMarkus Pfeiffer 		return NULL;
11182657471SMarkus Pfeiffer 	}
11282657471SMarkus Pfeiffer 
11382657471SMarkus Pfeiffer 	return msqpptr;
11482657471SMarkus Pfeiffer }
11582657471SMarkus Pfeiffer 
11682657471SMarkus Pfeiffer static int
msqp_exist(int msqid,struct msqid_pool * msqpptr)117*ff86f401SSascha Wildner msqp_exist(int msqid, struct msqid_pool *msqpptr)
118*ff86f401SSascha Wildner {
11982657471SMarkus Pfeiffer 	/* Was it removed? */
12082657471SMarkus Pfeiffer 	if (msqpptr->gen == -1 ||
12182657471SMarkus Pfeiffer 			msqpptr->ds.msg_perm.seq != IPCID_TO_SEQ(msqid))
12282657471SMarkus Pfeiffer 		return 0;
12382657471SMarkus Pfeiffer 
12482657471SMarkus Pfeiffer 	return 1;
12582657471SMarkus Pfeiffer }
12682657471SMarkus Pfeiffer 
12782657471SMarkus Pfeiffer static int
try_rwlock_rdlock(int msqid,struct msqid_pool * msqpptr)128*ff86f401SSascha Wildner try_rwlock_rdlock(int msqid, struct msqid_pool *msqpptr)
129*ff86f401SSascha Wildner {
13082657471SMarkus Pfeiffer 	sysv_print("try get rd lock\n");
13182657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
13282657471SMarkus Pfeiffer 	sysv_rwlock_rdlock(&msqpptr->rwlock);
13382657471SMarkus Pfeiffer #else
13482657471SMarkus Pfeiffer 	sysv_mutex_lock(&msqpptr->mutex);
13582657471SMarkus Pfeiffer #endif
13682657471SMarkus Pfeiffer 	sysv_print("get rd lock\n");
13782657471SMarkus Pfeiffer 	if (!msqp_exist(msqid, msqpptr)) {
13882657471SMarkus Pfeiffer 		errno = EINVAL;
13982657471SMarkus Pfeiffer 		sysv_print("error rd lock\n");
14082657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
14182657471SMarkus Pfeiffer 		sysv_rwlock_unlock(&msqpptr->rwlock);
14282657471SMarkus Pfeiffer #else
14382657471SMarkus Pfeiffer 		sysv_mutex_unlock(&msqpptr->mutex);
14482657471SMarkus Pfeiffer #endif
14582657471SMarkus Pfeiffer 		return -1;
14682657471SMarkus Pfeiffer 	}
14782657471SMarkus Pfeiffer 	sysv_print("end rd lock\n");
14882657471SMarkus Pfeiffer 	return 0;
14982657471SMarkus Pfeiffer }
15082657471SMarkus Pfeiffer 
15182657471SMarkus Pfeiffer static int
try_rwlock_wrlock(int msqid,struct msqid_pool * msqpptr)152*ff86f401SSascha Wildner try_rwlock_wrlock(int msqid, struct msqid_pool *msqpptr)
153*ff86f401SSascha Wildner {
15482657471SMarkus Pfeiffer 	sysv_print("try get wr lock\n");
15582657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
15682657471SMarkus Pfeiffer 	sysv_rwlock_wrlock(&msqpptr->rwlock);
15782657471SMarkus Pfeiffer #else
15882657471SMarkus Pfeiffer 	sysv_mutex_lock(&msqpptr->mutex);
15982657471SMarkus Pfeiffer #endif
16082657471SMarkus Pfeiffer 	sysv_print("get wr lock\n");
16182657471SMarkus Pfeiffer 	if (!msqp_exist(msqid, msqpptr)) {
16282657471SMarkus Pfeiffer 		sysv_print("error rw lock\n");
16382657471SMarkus Pfeiffer 		errno = EINVAL;
16482657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
16582657471SMarkus Pfeiffer 		sysv_rwlock_unlock(&msqpptr->rwlock);
16682657471SMarkus Pfeiffer #else
16782657471SMarkus Pfeiffer 		sysv_mutex_unlock(&msqpptr->mutex);
16882657471SMarkus Pfeiffer #endif
16982657471SMarkus Pfeiffer 		return -1;
17082657471SMarkus Pfeiffer 	}
17182657471SMarkus Pfeiffer 	sysv_print("end rw lock\n");
17282657471SMarkus Pfeiffer 	return 0;
17382657471SMarkus Pfeiffer }
17482657471SMarkus Pfeiffer 
17582657471SMarkus Pfeiffer static int
rwlock_unlock(int msqid,struct msqid_pool * msqpptr)176*ff86f401SSascha Wildner rwlock_unlock(int msqid, struct msqid_pool *msqpptr)
177*ff86f401SSascha Wildner {
17882657471SMarkus Pfeiffer 	if (!msqp_exist(msqid, msqpptr)) {
17982657471SMarkus Pfeiffer 		errno = EINVAL;
18082657471SMarkus Pfeiffer 		return -1;
18182657471SMarkus Pfeiffer 	}
18282657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
18382657471SMarkus Pfeiffer 	sysv_rwlock_unlock(&msqpptr->rwlock);
18482657471SMarkus Pfeiffer #else
18582657471SMarkus Pfeiffer 	sysv_mutex_unlock(&msqpptr->mutex);
18682657471SMarkus Pfeiffer #endif
18782657471SMarkus Pfeiffer 	sysv_print("unlock rw lock\n");
18882657471SMarkus Pfeiffer 	return 0;
18982657471SMarkus Pfeiffer }
19082657471SMarkus Pfeiffer 
19182657471SMarkus Pfeiffer static void
msg_freehdr(struct msqid_pool * msqpptr,struct msg * msghdr)19282657471SMarkus Pfeiffer msg_freehdr(struct msqid_pool *msqpptr, struct msg *msghdr)
19382657471SMarkus Pfeiffer {
19482657471SMarkus Pfeiffer 	while (msghdr->msg_ts > 0) {
19582657471SMarkus Pfeiffer 		short next;
19682657471SMarkus Pfeiffer 		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) {
19782657471SMarkus Pfeiffer 			sysv_print_err("msghdr->msg_spot out of range");
19882657471SMarkus Pfeiffer 			exit(-1);
19982657471SMarkus Pfeiffer 		}
20082657471SMarkus Pfeiffer 		next = msqpptr->msgmaps[msghdr->msg_spot].next;
20182657471SMarkus Pfeiffer 		msqpptr->msgmaps[msghdr->msg_spot].next =
20282657471SMarkus Pfeiffer 			msqpptr->free_msgmaps;
20382657471SMarkus Pfeiffer 		msqpptr->free_msgmaps = msghdr->msg_spot;
20482657471SMarkus Pfeiffer 		msqpptr->nfree_msgmaps++;
20582657471SMarkus Pfeiffer 		msghdr->msg_spot = next;
20682657471SMarkus Pfeiffer 		if (msghdr->msg_ts >= msginfo.msgssz)
20782657471SMarkus Pfeiffer 			msghdr->msg_ts -= msginfo.msgssz;
20882657471SMarkus Pfeiffer 		else
20982657471SMarkus Pfeiffer 			msghdr->msg_ts = 0;
21082657471SMarkus Pfeiffer 	}
21182657471SMarkus Pfeiffer 	if (msghdr->msg_spot != -1) {
21282657471SMarkus Pfeiffer 		sysv_print_err("msghdr->msg_spot != -1");
21382657471SMarkus Pfeiffer 		exit(-1);
21482657471SMarkus Pfeiffer 	}
21582657471SMarkus Pfeiffer 	msghdr->msg_next = msqpptr->free_msghdrs;
21682657471SMarkus Pfeiffer 	msqpptr->free_msghdrs = (msghdr - &msqpptr->msghdrs[0]) /
21782657471SMarkus Pfeiffer 		sizeof(struct msg);
21882657471SMarkus Pfeiffer }
21982657471SMarkus Pfeiffer 
22082657471SMarkus Pfeiffer int
sysvipc_msgget(key_t key,int msgflg)221*ff86f401SSascha Wildner sysvipc_msgget(key_t key, int msgflg)
222*ff86f401SSascha Wildner {
22382657471SMarkus Pfeiffer 	int msqid;
22482657471SMarkus Pfeiffer 	void *shmaddr;
22582657471SMarkus Pfeiffer 	size_t size = sizeof(struct msqid_pool);
22682657471SMarkus Pfeiffer 
22782657471SMarkus Pfeiffer 	msqid = _shmget(key, size, msgflg, MSGGET);
22882657471SMarkus Pfeiffer 	if (msqid == -1)
22982657471SMarkus Pfeiffer 		goto done;
23082657471SMarkus Pfeiffer 
23182657471SMarkus Pfeiffer 	/* If the msg is in process of being removed there are two cases:
23282657471SMarkus Pfeiffer 	 * - the daemon knows that and it will handle this situation.
23382657471SMarkus Pfeiffer 	 * - one of the threads from this address space remove it and the daemon
23482657471SMarkus Pfeiffer 	 *   wasn't announced yet; in this scenario, the msg is marked
23582657471SMarkus Pfeiffer 	 *   using "removed" field of shm_data and future calls will return
23682657471SMarkus Pfeiffer 	 *   EIDRM error.
23782657471SMarkus Pfeiffer 	 */
23882657471SMarkus Pfeiffer 
23982657471SMarkus Pfeiffer #if 0
24082657471SMarkus Pfeiffer 	/* Set access type. */
24182657471SMarkus Pfeiffer 	shm_access = semflg & (IPC_W | IPC_R);
24282657471SMarkus Pfeiffer 	if(set_shmdata_access(semid, shm_access) != 0) {
24382657471SMarkus Pfeiffer 		/* errno already set. */
24482657471SMarkus Pfeiffer 		goto done;
24582657471SMarkus Pfeiffer 	}
24682657471SMarkus Pfeiffer #endif
24782657471SMarkus Pfeiffer 
24882657471SMarkus Pfeiffer 	shmaddr = sysvipc_shmat(msqid, NULL, 0);
24982657471SMarkus Pfeiffer 	if (!shmaddr) {
25082657471SMarkus Pfeiffer 		msqid = -1;
25182657471SMarkus Pfeiffer 		sysvipc_shmctl(msqid, IPC_RMID, NULL);
25282657471SMarkus Pfeiffer 		goto done;
25382657471SMarkus Pfeiffer 	}
25482657471SMarkus Pfeiffer 	sysv_print("shmaddr = %lx\n", (unsigned long)shmaddr);
25582657471SMarkus Pfeiffer 
25682657471SMarkus Pfeiffer done:
25782657471SMarkus Pfeiffer 	return msqid;
25882657471SMarkus Pfeiffer }
25982657471SMarkus Pfeiffer 
26082657471SMarkus Pfeiffer int
sysvipc_msgctl(int msqid,int cmd,struct msqid_ds * buf)261*ff86f401SSascha Wildner sysvipc_msgctl(int msqid, int cmd, struct msqid_ds *buf)
262*ff86f401SSascha Wildner {
26382657471SMarkus Pfeiffer 	int error;
26482657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr = NULL;
26582657471SMarkus Pfeiffer 	struct shmid_ds shmds;
26682657471SMarkus Pfeiffer 	int shm_access = 0;
26782657471SMarkus Pfeiffer 
26882657471SMarkus Pfeiffer 	error = 0;
26982657471SMarkus Pfeiffer 
27082657471SMarkus Pfeiffer 	switch (cmd) {
27182657471SMarkus Pfeiffer 		case IPC_SET: /* Originally was IPC_M but this is checked
27282657471SMarkus Pfeiffer 				 by daemon. */
27382657471SMarkus Pfeiffer 			shm_access = IPC_W;
27482657471SMarkus Pfeiffer 			break;
27582657471SMarkus Pfeiffer 		case IPC_STAT:
27682657471SMarkus Pfeiffer 			shm_access = IPC_R;
27782657471SMarkus Pfeiffer 			break;
27882657471SMarkus Pfeiffer 		default:
27982657471SMarkus Pfeiffer 			break;
28082657471SMarkus Pfeiffer 	}
28182657471SMarkus Pfeiffer 
28282657471SMarkus Pfeiffer 	msqpptr = get_msqpptr(msqid, cmd==IPC_RMID, shm_access);
28382657471SMarkus Pfeiffer 	if (!msqpptr) {
28482657471SMarkus Pfeiffer 		errno = EINVAL;
28582657471SMarkus Pfeiffer 		return -1;
28682657471SMarkus Pfeiffer 	}
28782657471SMarkus Pfeiffer 
28882657471SMarkus Pfeiffer 	switch (cmd) {
28982657471SMarkus Pfeiffer 	case IPC_RMID:
29082657471SMarkus Pfeiffer 		/* Mark that the segment is removed. This is done in
29182657471SMarkus Pfeiffer 		 * get_msqpptr call in order to announce other processes.
29282657471SMarkus Pfeiffer 		 * It will be actually removed after put_shmdata call and
29382657471SMarkus Pfeiffer 		 * not other thread from this address space use shm_data
29482657471SMarkus Pfeiffer 		 * structure.
29582657471SMarkus Pfeiffer 		 */
29682657471SMarkus Pfeiffer 		break;
29782657471SMarkus Pfeiffer 	case IPC_SET:
29882657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(msqid, msqpptr);
29982657471SMarkus Pfeiffer 		if (error)
30082657471SMarkus Pfeiffer 			break;
30182657471SMarkus Pfeiffer 		if (buf->msg_qbytes == 0) {
30282657471SMarkus Pfeiffer 			sysv_print_err("can't reduce msg_qbytes to 0\n");
30382657471SMarkus Pfeiffer 			errno = EINVAL;		/* non-standard errno! */
30482657471SMarkus Pfeiffer 			rwlock_unlock(msqid, msqpptr);
30582657471SMarkus Pfeiffer 			break;
30682657471SMarkus Pfeiffer 		}
30782657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
30882657471SMarkus Pfeiffer 
30982657471SMarkus Pfeiffer 		memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char));
31082657471SMarkus Pfeiffer 		memcpy(&shmds.shm_perm, &buf->msg_perm,
31182657471SMarkus Pfeiffer 				sizeof(struct ipc_perm));
31282657471SMarkus Pfeiffer 		error = sysvipc_shmctl(msqid, cmd, &shmds);
31382657471SMarkus Pfeiffer 		if (error)
31482657471SMarkus Pfeiffer 			break;
31582657471SMarkus Pfeiffer 
31682657471SMarkus Pfeiffer 		/* There is no need to check if we have right to modify the
31782657471SMarkus Pfeiffer 		 * size because we have right to change other fileds. */
31882657471SMarkus Pfeiffer 
31982657471SMarkus Pfeiffer 		if (round_page(buf->msg_qbytes) !=
32082657471SMarkus Pfeiffer 				round_page(msqpptr->ds.msg_qbytes)) {
32182657471SMarkus Pfeiffer 				sysv_print("change msg size\n");
32282657471SMarkus Pfeiffer 				/* TODO same as in semundo_adjust only
32382657471SMarkus Pfeiffer 				 * that there is no way to inform other
32482657471SMarkus Pfeiffer 				 * processes about the change. */
32582657471SMarkus Pfeiffer 		}
32682657471SMarkus Pfeiffer 
32782657471SMarkus Pfeiffer 		error = try_rwlock_wrlock(msqid, msqpptr);
32882657471SMarkus Pfeiffer 		if (error)
32982657471SMarkus Pfeiffer 			break;
33082657471SMarkus Pfeiffer 		msqpptr->ds.msg_qbytes = buf->msg_qbytes;
33182657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
33282657471SMarkus Pfeiffer 		/* OBS: didn't update ctime and mode as in kernel implementation
33382657471SMarkus Pfeiffer 		 * it is done. Those fields are already updated for shmid_ds
33482657471SMarkus Pfeiffer 		 * struct when calling shmctl
33582657471SMarkus Pfeiffer 		 */
33682657471SMarkus Pfeiffer 		break;
33782657471SMarkus Pfeiffer 
33882657471SMarkus Pfeiffer 	case IPC_STAT:
33982657471SMarkus Pfeiffer 		error = sysvipc_shmctl(msqid, cmd, &shmds);
34082657471SMarkus Pfeiffer 		if (error)
34182657471SMarkus Pfeiffer 			break;
34282657471SMarkus Pfeiffer 
34382657471SMarkus Pfeiffer 		memcpy(&buf->msg_perm, &shmds.shm_perm,
34482657471SMarkus Pfeiffer 				sizeof(struct ipc_perm));
34582657471SMarkus Pfeiffer 		buf->msg_ctime = shmds.shm_ctime;
34682657471SMarkus Pfeiffer 
34782657471SMarkus Pfeiffer 		/* Read fields that are not kept in shmds. */
34882657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(msqid, msqpptr);
34982657471SMarkus Pfeiffer 		if (error)
35082657471SMarkus Pfeiffer 			break;
35182657471SMarkus Pfeiffer 		buf->msg_first = (struct msg *)(u_long)
35282657471SMarkus Pfeiffer 			msqpptr->ds.first.msg_first_index;
35382657471SMarkus Pfeiffer 		buf->msg_last = (struct msg *)(u_long)
35482657471SMarkus Pfeiffer 			msqpptr->ds.last.msg_last_index;
35582657471SMarkus Pfeiffer 		buf->msg_cbytes = msqpptr->ds.msg_cbytes;
35682657471SMarkus Pfeiffer 		buf->msg_qnum = msqpptr->ds.msg_qnum;
35782657471SMarkus Pfeiffer 		buf->msg_qbytes = msqpptr->ds.msg_qbytes;
35882657471SMarkus Pfeiffer 		buf->msg_lspid = msqpptr->ds.msg_lspid;
35982657471SMarkus Pfeiffer 		buf->msg_lrpid = msqpptr->ds.msg_lrpid;
36082657471SMarkus Pfeiffer 		buf->msg_stime = msqpptr->ds.msg_stime;
36182657471SMarkus Pfeiffer 		buf->msg_rtime = msqpptr->ds.msg_rtime;
36282657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
36382657471SMarkus Pfeiffer 		break;
36482657471SMarkus Pfeiffer 	default:
36582657471SMarkus Pfeiffer 		sysv_print_err("invalid command %d\n", cmd);
36682657471SMarkus Pfeiffer 		errno = EINVAL;
36782657471SMarkus Pfeiffer 		break;
36882657471SMarkus Pfeiffer 	}
36982657471SMarkus Pfeiffer 
37082657471SMarkus Pfeiffer 	put_shmdata(msqid);
37182657471SMarkus Pfeiffer 
37282657471SMarkus Pfeiffer 	return(error);
37382657471SMarkus Pfeiffer }
37482657471SMarkus Pfeiffer 
37582657471SMarkus Pfeiffer int
sysvipc_msgsnd(int msqid,const void * msgp,size_t msgsz,int msgflg)376d217426cSSascha Wildner sysvipc_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
37782657471SMarkus Pfeiffer {
37882657471SMarkus Pfeiffer 	int segs_needed, error;
37982657471SMarkus Pfeiffer 	struct msg *msghdr;
38082657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr, *auxmsqpptr;
38182657471SMarkus Pfeiffer 	struct msqid_ds_internal *msqptr;
38282657471SMarkus Pfeiffer 	short next;
38382657471SMarkus Pfeiffer 	int val_to_sleep;
38482657471SMarkus Pfeiffer 	char *auxmsgp = (char *)msgp;
38582657471SMarkus Pfeiffer 	int _index;
38682657471SMarkus Pfeiffer 
38782657471SMarkus Pfeiffer 	sysv_print("call to msgsnd(%d, %ld, %d)\n", msqid, msgsz, msgflg);
38882657471SMarkus Pfeiffer 
38982657471SMarkus Pfeiffer 	/*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
39082657471SMarkus Pfeiffer 		return (ENOSYS);
39182657471SMarkus Pfeiffer */
39282657471SMarkus Pfeiffer 	if (!msgp) {
39382657471SMarkus Pfeiffer 		errno = EINVAL;
39482657471SMarkus Pfeiffer 		return -1;
39582657471SMarkus Pfeiffer 	}
39682657471SMarkus Pfeiffer 
39782657471SMarkus Pfeiffer 	msqpptr = get_msqpptr(msqid, 0, IPC_W);
39882657471SMarkus Pfeiffer 	if (!msqpptr) {
39982657471SMarkus Pfeiffer 		errno = EINVAL;
40082657471SMarkus Pfeiffer 		return -1;
40182657471SMarkus Pfeiffer 	}
40282657471SMarkus Pfeiffer 	error = -1;
40382657471SMarkus Pfeiffer 
40482657471SMarkus Pfeiffer 	if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
40582657471SMarkus Pfeiffer 		errno = EIDRM;
40682657471SMarkus Pfeiffer 		goto done;
40782657471SMarkus Pfeiffer 	}
40882657471SMarkus Pfeiffer 
40982657471SMarkus Pfeiffer 	msqptr = &msqpptr->ds;
41082657471SMarkus Pfeiffer 
41182657471SMarkus Pfeiffer 	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
41282657471SMarkus Pfeiffer 	sysv_print("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
41382657471SMarkus Pfeiffer 	    segs_needed);
41482657471SMarkus Pfeiffer 	for (;;) {
41582657471SMarkus Pfeiffer 		int need_more_resources = 0;
41682657471SMarkus Pfeiffer 
41782657471SMarkus Pfeiffer 		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
41882657471SMarkus Pfeiffer 			sysv_print("msgsz + msg_cbytes > msg_qbytes\n");
41982657471SMarkus Pfeiffer 			need_more_resources = 1;
42082657471SMarkus Pfeiffer 		}
42182657471SMarkus Pfeiffer 
42282657471SMarkus Pfeiffer 		if (segs_needed > msqpptr->nfree_msgmaps) {
42382657471SMarkus Pfeiffer 			sysv_print("segs_needed > nfree_msgmaps (= %d)\n",
42482657471SMarkus Pfeiffer 					msqpptr->nfree_msgmaps);
42582657471SMarkus Pfeiffer 			need_more_resources = 1;
42682657471SMarkus Pfeiffer 		}
42782657471SMarkus Pfeiffer 
42882657471SMarkus Pfeiffer 		if (msqpptr->free_msghdrs == -1) {
42982657471SMarkus Pfeiffer 			sysv_print("no more msghdrs\n");
43082657471SMarkus Pfeiffer 			need_more_resources = 1;
43182657471SMarkus Pfeiffer 		}
43282657471SMarkus Pfeiffer 
43382657471SMarkus Pfeiffer 		if (need_more_resources) {
43482657471SMarkus Pfeiffer 			if ((msgflg & IPC_NOWAIT) != 0) {
43582657471SMarkus Pfeiffer 				sysv_print_err("need more resources but caller doesn't want to wait\n");
43682657471SMarkus Pfeiffer 				errno = EAGAIN;
43782657471SMarkus Pfeiffer 				goto done;
43882657471SMarkus Pfeiffer 			}
43982657471SMarkus Pfeiffer 
44082657471SMarkus Pfeiffer 			sysv_print("goodnight\n");
44182657471SMarkus Pfeiffer 			val_to_sleep = msqpptr->gen;
44282657471SMarkus Pfeiffer 			rwlock_unlock(msqid, msqpptr);
44382657471SMarkus Pfeiffer 			put_shmdata(msqid);
44482657471SMarkus Pfeiffer 
44582657471SMarkus Pfeiffer 			if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) {
44682657471SMarkus Pfeiffer 				sysv_print_err("msgsnd:  interrupted system call\n");
44782657471SMarkus Pfeiffer 				errno = EINTR;
44882657471SMarkus Pfeiffer 				goto done;
44982657471SMarkus Pfeiffer 			}
45082657471SMarkus Pfeiffer 
45182657471SMarkus Pfeiffer 			/* Check if another thread didn't remove the msg queue. */
45282657471SMarkus Pfeiffer 			auxmsqpptr = get_msqpptr(msqid, 0, IPC_W);
45382657471SMarkus Pfeiffer 			if (!auxmsqpptr) {
45482657471SMarkus Pfeiffer 				errno = EIDRM;
45582657471SMarkus Pfeiffer 				return -1;
45682657471SMarkus Pfeiffer 			}
45782657471SMarkus Pfeiffer 
45882657471SMarkus Pfeiffer 			if (auxmsqpptr != msqpptr) {
45982657471SMarkus Pfeiffer 				errno = EIDRM;
46082657471SMarkus Pfeiffer 				goto done;
46182657471SMarkus Pfeiffer 			}
46282657471SMarkus Pfeiffer 
46382657471SMarkus Pfeiffer 			/* Check if another process didn't remove the queue. */
46482657471SMarkus Pfeiffer 			if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
46582657471SMarkus Pfeiffer 				errno = EIDRM;
46682657471SMarkus Pfeiffer 				goto done;
46782657471SMarkus Pfeiffer 			}
46882657471SMarkus Pfeiffer 
46982657471SMarkus Pfeiffer 			if (msqptr != &msqpptr->ds) {
47082657471SMarkus Pfeiffer 				sysv_print("msqptr != &msqpptr->ds");
47182657471SMarkus Pfeiffer 				exit(-1);
47282657471SMarkus Pfeiffer 			}
47382657471SMarkus Pfeiffer 
47482657471SMarkus Pfeiffer 		} else {
47582657471SMarkus Pfeiffer 			sysv_print("got all the resources that we need\n");
47682657471SMarkus Pfeiffer 			break;
47782657471SMarkus Pfeiffer 		}
47882657471SMarkus Pfeiffer 	}
47982657471SMarkus Pfeiffer 
48082657471SMarkus Pfeiffer 	/*
48182657471SMarkus Pfeiffer 	 * We have the resources that we need.
48282657471SMarkus Pfeiffer 	 * Make sure!
48382657471SMarkus Pfeiffer 	 */
48482657471SMarkus Pfeiffer #if 0
48582657471SMarkus Pfeiffer 	if (segs_needed > nfree_msgmaps) {
48682657471SMarkus Pfeiffer 		sysv_print_err("segs_needed > nfree_msgmaps");
48782657471SMarkus Pfeiffer 		exit(-1);
48882657471SMarkus Pfeiffer 	}
48982657471SMarkus Pfeiffer #endif
49082657471SMarkus Pfeiffer 	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
49182657471SMarkus Pfeiffer 		sysv_print_err("msgsz + msg_cbytes > msg_qbytes");
49282657471SMarkus Pfeiffer 		exit(-1);
49382657471SMarkus Pfeiffer 	}
49482657471SMarkus Pfeiffer 
49582657471SMarkus Pfeiffer 	/*
49682657471SMarkus Pfeiffer 	 * Allocate a message header
49782657471SMarkus Pfeiffer 	 */
49882657471SMarkus Pfeiffer 	msghdr = &msqpptr->msghdrs[msqpptr->free_msghdrs];
49982657471SMarkus Pfeiffer 	msqpptr->free_msghdrs = msghdr->msg_next;
50082657471SMarkus Pfeiffer 	msghdr->msg_spot = -1;
50182657471SMarkus Pfeiffer 	msghdr->msg_ts = msgsz;
50282657471SMarkus Pfeiffer 
50382657471SMarkus Pfeiffer 	/*
50482657471SMarkus Pfeiffer 	 * Allocate space for the message
50582657471SMarkus Pfeiffer 	 */
50682657471SMarkus Pfeiffer 	while (segs_needed > 0) {
50782657471SMarkus Pfeiffer 		next = msqpptr->free_msgmaps;
50882657471SMarkus Pfeiffer 		if (next < 0 || next > msginfo.msgseg) {
50982657471SMarkus Pfeiffer 			sysv_print_err("out of range free_msgmaps %d #1\n", next);
51082657471SMarkus Pfeiffer 			exit(-1);
51182657471SMarkus Pfeiffer 		}
51282657471SMarkus Pfeiffer 
51382657471SMarkus Pfeiffer 		msqpptr->free_msgmaps = msqpptr->msgmaps[next].next;
51482657471SMarkus Pfeiffer 		msqpptr->nfree_msgmaps--;
51582657471SMarkus Pfeiffer 		msqpptr->msgmaps[next].next = msghdr->msg_spot;
51682657471SMarkus Pfeiffer 		msghdr->msg_spot = next;
51782657471SMarkus Pfeiffer 		segs_needed--;
51882657471SMarkus Pfeiffer 	}
51982657471SMarkus Pfeiffer 
52082657471SMarkus Pfeiffer 	/*
52182657471SMarkus Pfeiffer 	 * Copy in the message type
52282657471SMarkus Pfeiffer 	 */
52382657471SMarkus Pfeiffer 	memcpy(&msghdr->msg_type, auxmsgp, sizeof(msghdr->msg_type));
52482657471SMarkus Pfeiffer 	auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type);
52582657471SMarkus Pfeiffer 
52682657471SMarkus Pfeiffer 	/*
52782657471SMarkus Pfeiffer 	 * Validate the message type
52882657471SMarkus Pfeiffer 	 */
52982657471SMarkus Pfeiffer 	sysv_print("msg_type = %ld\n", msghdr->msg_type);
53082657471SMarkus Pfeiffer 
53182657471SMarkus Pfeiffer 	if (msghdr->msg_type < 1) {
53282657471SMarkus Pfeiffer 		msg_freehdr(msqpptr, msghdr);
53382657471SMarkus Pfeiffer 		umtx_wakeup((int *)&msqpptr->gen, 0);
53482657471SMarkus Pfeiffer 		sysv_print_err("mtype (%ld) < 1\n", msghdr->msg_type);
53582657471SMarkus Pfeiffer 		errno = EINVAL;
53682657471SMarkus Pfeiffer 		goto done;
53782657471SMarkus Pfeiffer 	}
53882657471SMarkus Pfeiffer 
53982657471SMarkus Pfeiffer 	/*
54082657471SMarkus Pfeiffer 	 * Copy in the message body
54182657471SMarkus Pfeiffer 	 */
54282657471SMarkus Pfeiffer 	next = msghdr->msg_spot;
54382657471SMarkus Pfeiffer 	while (msgsz > 0) {
54482657471SMarkus Pfeiffer 		size_t tlen;
54582657471SMarkus Pfeiffer 		if (msgsz > (size_t)msginfo.msgssz)
54682657471SMarkus Pfeiffer 			tlen = msginfo.msgssz;
54782657471SMarkus Pfeiffer 		else
54882657471SMarkus Pfeiffer 			tlen = msgsz;
54982657471SMarkus Pfeiffer 		if (next < 0 || next > msginfo.msgseg) {
55082657471SMarkus Pfeiffer 			sysv_print_err("out of range free_msgmaps %d #2\n", next);
55182657471SMarkus Pfeiffer 			exit(-1);
55282657471SMarkus Pfeiffer 		}
55382657471SMarkus Pfeiffer 
55482657471SMarkus Pfeiffer 		memcpy(&msqpptr->msgpool[next * msginfo.msgssz], auxmsgp, tlen);
55582657471SMarkus Pfeiffer 		msgsz -= tlen;
55682657471SMarkus Pfeiffer 		auxmsgp = (char *)auxmsgp + tlen;
55782657471SMarkus Pfeiffer 		next = msqpptr->msgmaps[next].next;
55882657471SMarkus Pfeiffer 	}
55982657471SMarkus Pfeiffer 
56082657471SMarkus Pfeiffer 	/*
56182657471SMarkus Pfeiffer 	 * Put the message into the queue
56282657471SMarkus Pfeiffer 	 */
56382657471SMarkus Pfeiffer 	_index = (msghdr - &msqpptr->msghdrs[0]) /
56482657471SMarkus Pfeiffer 		sizeof(struct msg);
56582657471SMarkus Pfeiffer 	sysv_print("index_msghdr = %d\n", _index);
56682657471SMarkus Pfeiffer 	if (msqptr->first.msg_first_index == -1) {
56782657471SMarkus Pfeiffer 		msqptr->first.msg_first_index = _index;
56882657471SMarkus Pfeiffer 		msqptr->last.msg_last_index = _index;
56982657471SMarkus Pfeiffer 	} else {
57082657471SMarkus Pfeiffer 		msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = _index;
57182657471SMarkus Pfeiffer 		msqptr->last.msg_last_index = _index;
57282657471SMarkus Pfeiffer 	}
57382657471SMarkus Pfeiffer 	msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = -1;
57482657471SMarkus Pfeiffer 
57582657471SMarkus Pfeiffer 	msqptr->msg_cbytes += msghdr->msg_ts;
57682657471SMarkus Pfeiffer 	msqptr->msg_qnum++;
57782657471SMarkus Pfeiffer 	msqptr->msg_lspid = getpid();
57882657471SMarkus Pfeiffer 	msqptr->msg_stime = time(NULL);
57982657471SMarkus Pfeiffer 
58082657471SMarkus Pfeiffer 	umtx_wakeup((int *)&msqpptr->gen, 0);
58182657471SMarkus Pfeiffer 	error = 0;
58282657471SMarkus Pfeiffer 
58382657471SMarkus Pfeiffer done:
58482657471SMarkus Pfeiffer 	rwlock_unlock(msqid, msqpptr);
58582657471SMarkus Pfeiffer 	put_shmdata(msqid);
58682657471SMarkus Pfeiffer 	return(error);
58782657471SMarkus Pfeiffer }
58882657471SMarkus Pfeiffer 
58982657471SMarkus Pfeiffer int
sysvipc_msgrcv(int msqid,void * msgp,size_t msgsz,long mtype,int msgflg)59082657471SMarkus Pfeiffer sysvipc_msgrcv(int msqid, void *msgp, size_t msgsz, long mtype, int msgflg)
59182657471SMarkus Pfeiffer {
59282657471SMarkus Pfeiffer 	size_t len;
59382657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr, *auxmsqpptr;
59482657471SMarkus Pfeiffer 	struct msqid_ds_internal *msqptr;
59582657471SMarkus Pfeiffer 	struct msg *msghdr;
59682657471SMarkus Pfeiffer 	short msghdr_index;
59782657471SMarkus Pfeiffer 	int error;
59882657471SMarkus Pfeiffer 	short next;
59982657471SMarkus Pfeiffer 	int val_to_sleep;
60082657471SMarkus Pfeiffer 	char *auxmsgp = (char *)msgp;
60182657471SMarkus Pfeiffer 
60282657471SMarkus Pfeiffer 	sysv_print("call to msgrcv(%d, %ld, %ld, %d)\n", msqid, msgsz, mtype, msgflg);
60382657471SMarkus Pfeiffer /*
60482657471SMarkus Pfeiffer 	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
60582657471SMarkus Pfeiffer 		return (ENOSYS);
60682657471SMarkus Pfeiffer */
60782657471SMarkus Pfeiffer 
60882657471SMarkus Pfeiffer 	if (!msgp) {
60982657471SMarkus Pfeiffer 		errno = EINVAL;
61082657471SMarkus Pfeiffer 		return -1;
61182657471SMarkus Pfeiffer 	}
61282657471SMarkus Pfeiffer 
61382657471SMarkus Pfeiffer 	msqpptr = get_msqpptr(msqid, 0, IPC_R);
61482657471SMarkus Pfeiffer 	if (!msqpptr) {
61582657471SMarkus Pfeiffer 		errno = EINVAL;
61682657471SMarkus Pfeiffer 		return -1;
61782657471SMarkus Pfeiffer 	}
61882657471SMarkus Pfeiffer 
61982657471SMarkus Pfeiffer 	error = -1;
62082657471SMarkus Pfeiffer 
62182657471SMarkus Pfeiffer 	if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
62282657471SMarkus Pfeiffer 		errno = EIDRM;
62382657471SMarkus Pfeiffer 		goto done;
62482657471SMarkus Pfeiffer 	}
62582657471SMarkus Pfeiffer 
62682657471SMarkus Pfeiffer 	msqptr = &msqpptr->ds;
62782657471SMarkus Pfeiffer 
62882657471SMarkus Pfeiffer 	msghdr_index = -1;
62982657471SMarkus Pfeiffer 	while (msghdr_index == -1) {
63082657471SMarkus Pfeiffer 		if (mtype == 0) {
63182657471SMarkus Pfeiffer 			msghdr_index = msqptr->first.msg_first_index;
63282657471SMarkus Pfeiffer 			msghdr = &msqpptr->msghdrs[msghdr_index];
63382657471SMarkus Pfeiffer 			if (msghdr_index != -1) {
63482657471SMarkus Pfeiffer 				if (msgsz < msghdr->msg_ts &&
63582657471SMarkus Pfeiffer 				    (msgflg & MSG_NOERROR) == 0) {
63682657471SMarkus Pfeiffer 						sysv_print_err("first message on the queue is too big"
63740d436c0SSascha Wildner 							"(want %zu, got %d)\n",
63882657471SMarkus Pfeiffer 							msgsz, msghdr->msg_ts);
63982657471SMarkus Pfeiffer 					errno = E2BIG;
64082657471SMarkus Pfeiffer 					goto done;
64182657471SMarkus Pfeiffer 				}
64282657471SMarkus Pfeiffer 				if (msqptr->first.msg_first_index == msqptr->last.msg_last_index) {
64382657471SMarkus Pfeiffer 					msqptr->first.msg_first_index = -1;
64482657471SMarkus Pfeiffer 					msqptr->last.msg_last_index = -1;
64582657471SMarkus Pfeiffer 				} else {
64682657471SMarkus Pfeiffer 					msqptr->first.msg_first_index = msghdr->msg_next;
64782657471SMarkus Pfeiffer 					if (msqptr->first.msg_first_index == -1) {
64882657471SMarkus Pfeiffer 						sysv_print_err("first.msg_first_index/last screwed up #1");
64982657471SMarkus Pfeiffer 						exit(-1);
65082657471SMarkus Pfeiffer 					}
65182657471SMarkus Pfeiffer 				}
65282657471SMarkus Pfeiffer 			}
65382657471SMarkus Pfeiffer 		} else {
65482657471SMarkus Pfeiffer 			short previous;
65582657471SMarkus Pfeiffer 			short prev;
65682657471SMarkus Pfeiffer 			previous = -1;
65782657471SMarkus Pfeiffer 			prev = msqptr->first.msg_first_index;
65882657471SMarkus Pfeiffer 			while ((msghdr_index = prev) != -1) {
65982657471SMarkus Pfeiffer 				msghdr = &msqpptr->msghdrs[msghdr_index];
66082657471SMarkus Pfeiffer 				/*
66182657471SMarkus Pfeiffer 				 * Is this message's type an exact match or is
66282657471SMarkus Pfeiffer 				 * this message's type less than or equal to
66382657471SMarkus Pfeiffer 				 * the absolute value of a negative mtype?
66482657471SMarkus Pfeiffer 				 * Note that the second half of this test can
66582657471SMarkus Pfeiffer 				 * NEVER be true if mtype is positive since
66682657471SMarkus Pfeiffer 				 * msg_type is always positive!
66782657471SMarkus Pfeiffer 				 */
66882657471SMarkus Pfeiffer 				if (mtype == msghdr->msg_type ||
66982657471SMarkus Pfeiffer 				    msghdr->msg_type <= -mtype) {
67040d436c0SSascha Wildner 					sysv_print("found message type %ld, requested %ld\n",
67182657471SMarkus Pfeiffer 					    msghdr->msg_type, mtype);
67282657471SMarkus Pfeiffer 					if (msgsz < msghdr->msg_ts &&
67382657471SMarkus Pfeiffer 					    (msgflg & MSG_NOERROR) == 0) {
67482657471SMarkus Pfeiffer 						sysv_print_err("requested message on the queue"
67540d436c0SSascha Wildner 							" is too big (want %zu, got %d)\n",
67682657471SMarkus Pfeiffer 						    msgsz, msghdr->msg_ts);
67782657471SMarkus Pfeiffer 						errno = E2BIG;
67882657471SMarkus Pfeiffer 						goto done;
67982657471SMarkus Pfeiffer 					}
68082657471SMarkus Pfeiffer 					prev = msghdr->msg_next;
68182657471SMarkus Pfeiffer 					if (msghdr_index == msqptr->last.msg_last_index) {
68282657471SMarkus Pfeiffer 						if (previous == -1) {
68382657471SMarkus Pfeiffer 							msqptr->first.msg_first_index = -1;
68482657471SMarkus Pfeiffer 							msqptr->last.msg_last_index = -1;
68582657471SMarkus Pfeiffer 						} else {
68682657471SMarkus Pfeiffer 							msqptr->last.msg_last_index = previous;
68782657471SMarkus Pfeiffer 						}
68882657471SMarkus Pfeiffer 					}
68982657471SMarkus Pfeiffer 					break;
69082657471SMarkus Pfeiffer 				}
69182657471SMarkus Pfeiffer 				previous = msghdr_index;
69282657471SMarkus Pfeiffer 				prev = msghdr->msg_next;
69382657471SMarkus Pfeiffer 			}
69482657471SMarkus Pfeiffer 		}
69582657471SMarkus Pfeiffer 
69682657471SMarkus Pfeiffer 		/*
69782657471SMarkus Pfeiffer 		 * We've either extracted the msghdr for the appropriate
69882657471SMarkus Pfeiffer 		 * message or there isn't one.
69982657471SMarkus Pfeiffer 		 * If there is one then bail out of this loop.
70082657471SMarkus Pfeiffer 		 */
70182657471SMarkus Pfeiffer 		if (msghdr_index != -1)
70282657471SMarkus Pfeiffer 			break;
70382657471SMarkus Pfeiffer 
70482657471SMarkus Pfeiffer 		/*
70582657471SMarkus Pfeiffer 		 * No message found.  Does the user want to wait?
70682657471SMarkus Pfeiffer 		 */
70782657471SMarkus Pfeiffer 		if ((msgflg & IPC_NOWAIT) != 0) {
70840d436c0SSascha Wildner 			sysv_print_err("no appropriate message found (mtype=%ld)\n",
70982657471SMarkus Pfeiffer 			    mtype);
71082657471SMarkus Pfeiffer 			errno = ENOMSG;
71182657471SMarkus Pfeiffer 			goto done;
71282657471SMarkus Pfeiffer 		}
71382657471SMarkus Pfeiffer 
71482657471SMarkus Pfeiffer 		/*
71582657471SMarkus Pfeiffer 		 * Wait for something to happen
71682657471SMarkus Pfeiffer 		 */
71782657471SMarkus Pfeiffer 		sysv_print("goodnight\n");
71882657471SMarkus Pfeiffer 		val_to_sleep = msqpptr->gen;
71982657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
72082657471SMarkus Pfeiffer 		put_shmdata(msqid);
72182657471SMarkus Pfeiffer 
72282657471SMarkus Pfeiffer 		/* We don't sleep more than SYSV_TIMEOUT because we could
72382657471SMarkus Pfeiffer 		 * go to sleep after another process calls wakeup and remain
72482657471SMarkus Pfeiffer 		 * blocked.
72582657471SMarkus Pfeiffer 		 */
72682657471SMarkus Pfeiffer 		if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) {
72782657471SMarkus Pfeiffer 			sysv_print_err("msgrcv:  interrupted system call\n");
72882657471SMarkus Pfeiffer 			errno = EINTR;
72982657471SMarkus Pfeiffer 			goto done;
73082657471SMarkus Pfeiffer 		}
73182657471SMarkus Pfeiffer 		sysv_print("msgrcv:  good morning (error=%d)\n", errno);
73282657471SMarkus Pfeiffer 
73382657471SMarkus Pfeiffer 		/* Check if another thread didn't remove the msg queue. */
73482657471SMarkus Pfeiffer 		auxmsqpptr = get_msqpptr(msqid, 0, IPC_R);
73582657471SMarkus Pfeiffer 		if (!auxmsqpptr) {
73682657471SMarkus Pfeiffer 			errno = EIDRM;
73782657471SMarkus Pfeiffer 			return -1;
73882657471SMarkus Pfeiffer 		}
73982657471SMarkus Pfeiffer 
74082657471SMarkus Pfeiffer 		if (auxmsqpptr != msqpptr) {
74182657471SMarkus Pfeiffer 			errno = EIDRM;
74282657471SMarkus Pfeiffer 			goto done;
74382657471SMarkus Pfeiffer 		}
74482657471SMarkus Pfeiffer 
74582657471SMarkus Pfeiffer 		/* Check if another process didn't remove the msg queue. */
74682657471SMarkus Pfeiffer 		if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
74782657471SMarkus Pfeiffer 			errno = EIDRM;
74882657471SMarkus Pfeiffer 			goto done;
74982657471SMarkus Pfeiffer 		}
75082657471SMarkus Pfeiffer 
75182657471SMarkus Pfeiffer 		if (msqptr != &msqpptr->ds) {
75282657471SMarkus Pfeiffer 			sysv_print_err("msqptr != &msqpptr->ds");
75382657471SMarkus Pfeiffer 			exit(-1);
75482657471SMarkus Pfeiffer 		}
75582657471SMarkus Pfeiffer 	}
75682657471SMarkus Pfeiffer 
75782657471SMarkus Pfeiffer 	/*
75882657471SMarkus Pfeiffer 	 * Return the message to the user.
75982657471SMarkus Pfeiffer 	 */
76082657471SMarkus Pfeiffer 	msqptr->msg_cbytes -= msghdr->msg_ts;
76182657471SMarkus Pfeiffer 	msqptr->msg_qnum--;
76282657471SMarkus Pfeiffer 	msqptr->msg_lrpid = getpid();
76382657471SMarkus Pfeiffer 	msqptr->msg_rtime = time(NULL);
76482657471SMarkus Pfeiffer 
76582657471SMarkus Pfeiffer 	/*
76682657471SMarkus Pfeiffer 	 * Make msgsz the actual amount that we'll be returning.
76782657471SMarkus Pfeiffer 	 * Note that this effectively truncates the message if it is too long
76882657471SMarkus Pfeiffer 	 * (since msgsz is never increased).
76982657471SMarkus Pfeiffer 	 */
77040d436c0SSascha Wildner 	sysv_print("found a message, msgsz=%zu, msg_ts=%d\n", msgsz,
77182657471SMarkus Pfeiffer 	    msghdr->msg_ts);
77282657471SMarkus Pfeiffer 	if (msgsz > msghdr->msg_ts)
77382657471SMarkus Pfeiffer 		msgsz = msghdr->msg_ts;
77482657471SMarkus Pfeiffer 
77582657471SMarkus Pfeiffer 	/*
77682657471SMarkus Pfeiffer 	 * Return the type to the user.
77782657471SMarkus Pfeiffer 	 */
77882657471SMarkus Pfeiffer 	memcpy(auxmsgp, (caddr_t)&(msghdr->msg_type), sizeof(msghdr->msg_type));
77982657471SMarkus Pfeiffer 	auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type);
78082657471SMarkus Pfeiffer 
78182657471SMarkus Pfeiffer 	/*
78282657471SMarkus Pfeiffer 	 * Return the segments to the user
78382657471SMarkus Pfeiffer 	 */
78482657471SMarkus Pfeiffer 	next = msghdr->msg_spot;
78582657471SMarkus Pfeiffer 	for (len = 0; len < msgsz; len += msginfo.msgssz) {
78682657471SMarkus Pfeiffer 		size_t tlen;
78782657471SMarkus Pfeiffer 
78882657471SMarkus Pfeiffer 		if (msgsz - len > (size_t)msginfo.msgssz)
78982657471SMarkus Pfeiffer 			tlen = msginfo.msgssz;
79082657471SMarkus Pfeiffer 		else
79182657471SMarkus Pfeiffer 			tlen = msgsz - len;
79282657471SMarkus Pfeiffer 		if (next < 0 || next > msginfo.msgseg) {
79382657471SMarkus Pfeiffer 			sysv_print_err("out of range free_msgmaps %d #3\n", next);
79482657471SMarkus Pfeiffer 			exit(-1);
79582657471SMarkus Pfeiffer 		}
79682657471SMarkus Pfeiffer 
79782657471SMarkus Pfeiffer 		memcpy(auxmsgp, (caddr_t)&msqpptr->msgpool[next * msginfo.msgssz], tlen);
79882657471SMarkus Pfeiffer 		auxmsgp = (char *)auxmsgp + tlen;
79982657471SMarkus Pfeiffer 		next = msqpptr->msgmaps[next].next;
80082657471SMarkus Pfeiffer 	}
80182657471SMarkus Pfeiffer 
80282657471SMarkus Pfeiffer 	/*
80382657471SMarkus Pfeiffer 	 * Done, return the actual number of bytes copied out.
80482657471SMarkus Pfeiffer 	 */
80582657471SMarkus Pfeiffer 	msg_freehdr(msqpptr, msghdr);
80682657471SMarkus Pfeiffer 	umtx_wakeup((int *)&msqpptr->gen, 0);
80782657471SMarkus Pfeiffer 	error = msgsz;
80882657471SMarkus Pfeiffer done:
80982657471SMarkus Pfeiffer 	rwlock_unlock(msqid, msqpptr);
81082657471SMarkus Pfeiffer 	put_shmdata(msqid);
81182657471SMarkus Pfeiffer 	return(error);
81282657471SMarkus Pfeiffer }
813