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