xref: /dflybsd-src/lib/libc/sysvipc/msg.c (revision 8265747159d6085c462f06fcb5dcee70b773f66b)
1*82657471SMarkus Pfeiffer /* $FreeBSD: src/sys/kern/sysv_msg.c,v 1.23.2.5 2002/12/31 08:54:53 maxim Exp $ */
2*82657471SMarkus Pfeiffer 
3*82657471SMarkus Pfeiffer /*
4*82657471SMarkus Pfeiffer  * Implementation of SVID messages
5*82657471SMarkus Pfeiffer  *
6*82657471SMarkus Pfeiffer  * Author:  Daniel Boulet
7*82657471SMarkus Pfeiffer  *
8*82657471SMarkus Pfeiffer  * Copyright 1993 Daniel Boulet and RTMX Inc.
9*82657471SMarkus Pfeiffer  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
10*82657471SMarkus Pfeiffer  *
11*82657471SMarkus Pfeiffer  * This system call was implemented by Daniel Boulet under contract from RTMX.
12*82657471SMarkus Pfeiffer  *
13*82657471SMarkus Pfeiffer  * Redistribution and use in source forms, with and without modification,
14*82657471SMarkus Pfeiffer  * are permitted provided that this entire comment appears intact.
15*82657471SMarkus Pfeiffer  *
16*82657471SMarkus Pfeiffer  * Redistribution in binary form may occur without any restrictions.
17*82657471SMarkus Pfeiffer  * Obviously, it would be nice if you gave credit where credit is due
18*82657471SMarkus Pfeiffer  * but requiring it would be too onerous.
19*82657471SMarkus Pfeiffer  *
20*82657471SMarkus Pfeiffer  * This software is provided ``AS IS'' without any warranties of any kind.
21*82657471SMarkus Pfeiffer  */
22*82657471SMarkus Pfeiffer 
23*82657471SMarkus Pfeiffer #include "namespace.h"
24*82657471SMarkus Pfeiffer #include <stdio.h>
25*82657471SMarkus Pfeiffer #include <stdlib.h>
26*82657471SMarkus Pfeiffer #include <errno.h>
27*82657471SMarkus Pfeiffer #include <err.h>
28*82657471SMarkus Pfeiffer #include <pthread.h>
29*82657471SMarkus Pfeiffer #include <string.h>
30*82657471SMarkus Pfeiffer #include <stdarg.h>
31*82657471SMarkus Pfeiffer #include <sys/param.h>
32*82657471SMarkus Pfeiffer #include <sys/queue.h>
33*82657471SMarkus Pfeiffer #include <sys/mman.h>
34*82657471SMarkus Pfeiffer #include "un-namespace.h"
35*82657471SMarkus Pfeiffer 
36*82657471SMarkus Pfeiffer #include "sysvipc_lock.h"
37*82657471SMarkus Pfeiffer #include "sysvipc_ipc.h"
38*82657471SMarkus Pfeiffer #include "sysvipc_hash.h"
39*82657471SMarkus Pfeiffer #include "sysvipc_msg.h"
40*82657471SMarkus Pfeiffer #include "sysvipc_shm.h"
41*82657471SMarkus Pfeiffer 
42*82657471SMarkus Pfeiffer #define SYSV_MUTEX_LOCK(x)		if (__isthreaded) _pthread_mutex_lock(x)
43*82657471SMarkus Pfeiffer #define SYSV_MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
44*82657471SMarkus Pfeiffer #define SYSV_MUTEX_DESTROY(x)	if (__isthreaded) _pthread_mutex_destroy(x)
45*82657471SMarkus Pfeiffer 
46*82657471SMarkus Pfeiffer extern struct hashtable *shmaddrs;
47*82657471SMarkus Pfeiffer extern struct hashtable *shmres;
48*82657471SMarkus Pfeiffer extern pthread_mutex_t lock_resources;
49*82657471SMarkus Pfeiffer 
50*82657471SMarkus Pfeiffer struct msginfo msginfo = {
51*82657471SMarkus Pfeiffer                 MSGMAX,         /* max chars in a message */
52*82657471SMarkus Pfeiffer                 MSGMNI,         /* # of message queue identifiers */
53*82657471SMarkus Pfeiffer                 MSGMNB,         /* max chars in a queue */
54*82657471SMarkus Pfeiffer                 MSGTQL,         /* max messages in system */
55*82657471SMarkus Pfeiffer                 MSGSSZ,         /* size of a message segment (must be small power of 2 greater than 4) */
56*82657471SMarkus Pfeiffer                 MSGSEG          /* number of message segments */
57*82657471SMarkus Pfeiffer };
58*82657471SMarkus Pfeiffer 
59*82657471SMarkus Pfeiffer static int
60*82657471SMarkus Pfeiffer put_shmdata(int id) {
61*82657471SMarkus Pfeiffer 	struct shm_data *data;
62*82657471SMarkus Pfeiffer 	int ret = -1;
63*82657471SMarkus Pfeiffer 
64*82657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
65*82657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, id);
66*82657471SMarkus Pfeiffer 	if (!data) {
67*82657471SMarkus Pfeiffer 		sysv_print_err("something wrong put_shmdata\n");
68*82657471SMarkus Pfeiffer 		goto done; /* It should not reach here. */
69*82657471SMarkus Pfeiffer 	}
70*82657471SMarkus Pfeiffer 
71*82657471SMarkus Pfeiffer 	data->used--;
72*82657471SMarkus Pfeiffer 	if (data->used == 0 && data->removed) {
73*82657471SMarkus Pfeiffer 		sysv_print("really remove the sem\n");
74*82657471SMarkus Pfeiffer 		SYSV_MUTEX_UNLOCK(&lock_resources);
75*82657471SMarkus Pfeiffer 		/* OBS: Even if the shmctl fails (the thread doesn't
76*82657471SMarkus Pfeiffer 		 * have IPC_M permissions), all structures associated
77*82657471SMarkus Pfeiffer 		 * with it will be removed in the current process.*/
78*82657471SMarkus Pfeiffer 		shmdt(data->internal);
79*82657471SMarkus Pfeiffer 		if (data->removed == SEG_ALREADY_REMOVED)
80*82657471SMarkus Pfeiffer 			return 1; /* The queue was removed
81*82657471SMarkus Pfeiffer 			by another process so there is nothing else
82*82657471SMarkus Pfeiffer 			we must do. */
83*82657471SMarkus Pfeiffer 		/* Else inform the daemon that the segment is removed. */
84*82657471SMarkus Pfeiffer 		return (sysvipc_shmctl(id, IPC_RMID, NULL));
85*82657471SMarkus Pfeiffer 	}
86*82657471SMarkus Pfeiffer 
87*82657471SMarkus Pfeiffer 	ret = 0;
88*82657471SMarkus Pfeiffer done:
89*82657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
90*82657471SMarkus Pfeiffer 	return (ret);
91*82657471SMarkus Pfeiffer }
92*82657471SMarkus Pfeiffer 
93*82657471SMarkus Pfeiffer static struct msqid_pool*
94*82657471SMarkus Pfeiffer get_msqpptr(int msqid, int to_remove, int shm_access) {
95*82657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr;
96*82657471SMarkus Pfeiffer 
97*82657471SMarkus Pfeiffer 	struct shm_data *shmdata =
98*82657471SMarkus Pfeiffer 		get_shmdata(msqid, to_remove, shm_access);
99*82657471SMarkus Pfeiffer 	if (!shmdata) {
100*82657471SMarkus Pfeiffer 		/* Error is set in get_shmdata. */
101*82657471SMarkus Pfeiffer 		return NULL;
102*82657471SMarkus Pfeiffer 	}
103*82657471SMarkus Pfeiffer 
104*82657471SMarkus Pfeiffer 	msqpptr = (struct msqid_pool *)shmdata->internal;
105*82657471SMarkus Pfeiffer 	if (!msqpptr) {
106*82657471SMarkus Pfeiffer 		put_shmdata(msqid);
107*82657471SMarkus Pfeiffer 		errno = EINVAL;
108*82657471SMarkus Pfeiffer 		return NULL;
109*82657471SMarkus Pfeiffer 	}
110*82657471SMarkus Pfeiffer 
111*82657471SMarkus Pfeiffer 	return msqpptr;
112*82657471SMarkus Pfeiffer }
113*82657471SMarkus Pfeiffer 
114*82657471SMarkus Pfeiffer static int
115*82657471SMarkus Pfeiffer msqp_exist(int msqid, struct msqid_pool *msqpptr) {
116*82657471SMarkus Pfeiffer 	/* Was it removed? */
117*82657471SMarkus Pfeiffer 	if (msqpptr->gen == -1 ||
118*82657471SMarkus Pfeiffer 			msqpptr->ds.msg_perm.seq != IPCID_TO_SEQ(msqid))
119*82657471SMarkus Pfeiffer 		return 0;
120*82657471SMarkus Pfeiffer 
121*82657471SMarkus Pfeiffer 	return 1;
122*82657471SMarkus Pfeiffer }
123*82657471SMarkus Pfeiffer 
124*82657471SMarkus Pfeiffer static int
125*82657471SMarkus Pfeiffer try_rwlock_rdlock(int msqid, struct msqid_pool *msqpptr) {
126*82657471SMarkus Pfeiffer 	sysv_print("try get rd lock\n");
127*82657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
128*82657471SMarkus Pfeiffer 	sysv_rwlock_rdlock(&msqpptr->rwlock);
129*82657471SMarkus Pfeiffer #else
130*82657471SMarkus Pfeiffer 	sysv_mutex_lock(&msqpptr->mutex);
131*82657471SMarkus Pfeiffer #endif
132*82657471SMarkus Pfeiffer 	sysv_print("get rd lock\n");
133*82657471SMarkus Pfeiffer 	if (!msqp_exist(msqid, msqpptr)) {
134*82657471SMarkus Pfeiffer 		errno = EINVAL;
135*82657471SMarkus Pfeiffer 		sysv_print("error rd lock\n");
136*82657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
137*82657471SMarkus Pfeiffer 		sysv_rwlock_unlock(&msqpptr->rwlock);
138*82657471SMarkus Pfeiffer #else
139*82657471SMarkus Pfeiffer 		sysv_mutex_unlock(&msqpptr->mutex);
140*82657471SMarkus Pfeiffer #endif
141*82657471SMarkus Pfeiffer 		return -1;
142*82657471SMarkus Pfeiffer 	}
143*82657471SMarkus Pfeiffer 	sysv_print("end rd lock\n");
144*82657471SMarkus Pfeiffer 	return 0;
145*82657471SMarkus Pfeiffer }
146*82657471SMarkus Pfeiffer 
147*82657471SMarkus Pfeiffer static int
148*82657471SMarkus Pfeiffer try_rwlock_wrlock(int msqid, struct msqid_pool *msqpptr) {
149*82657471SMarkus Pfeiffer 	sysv_print("try get wr lock\n");
150*82657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
151*82657471SMarkus Pfeiffer 	sysv_rwlock_wrlock(&msqpptr->rwlock);
152*82657471SMarkus Pfeiffer #else
153*82657471SMarkus Pfeiffer 	sysv_mutex_lock(&msqpptr->mutex);
154*82657471SMarkus Pfeiffer #endif
155*82657471SMarkus Pfeiffer 	sysv_print("get wr lock\n");
156*82657471SMarkus Pfeiffer 	if (!msqp_exist(msqid, msqpptr)) {
157*82657471SMarkus Pfeiffer 		sysv_print("error rw lock\n");
158*82657471SMarkus Pfeiffer 		errno = EINVAL;
159*82657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
160*82657471SMarkus Pfeiffer 		sysv_rwlock_unlock(&msqpptr->rwlock);
161*82657471SMarkus Pfeiffer #else
162*82657471SMarkus Pfeiffer 		sysv_mutex_unlock(&msqpptr->mutex);
163*82657471SMarkus Pfeiffer #endif
164*82657471SMarkus Pfeiffer 		return -1;
165*82657471SMarkus Pfeiffer 	}
166*82657471SMarkus Pfeiffer 	sysv_print("end rw lock\n");
167*82657471SMarkus Pfeiffer 	return 0;
168*82657471SMarkus Pfeiffer }
169*82657471SMarkus Pfeiffer 
170*82657471SMarkus Pfeiffer static int
171*82657471SMarkus Pfeiffer rwlock_unlock(int msqid, struct msqid_pool *msqpptr) {
172*82657471SMarkus Pfeiffer 	if (!msqp_exist(msqid, msqpptr)) {
173*82657471SMarkus Pfeiffer 		errno = EINVAL;
174*82657471SMarkus Pfeiffer 		return -1;
175*82657471SMarkus Pfeiffer 	}
176*82657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
177*82657471SMarkus Pfeiffer 	sysv_rwlock_unlock(&msqpptr->rwlock);
178*82657471SMarkus Pfeiffer #else
179*82657471SMarkus Pfeiffer 	sysv_mutex_unlock(&msqpptr->mutex);
180*82657471SMarkus Pfeiffer #endif
181*82657471SMarkus Pfeiffer 	sysv_print("unlock rw lock\n");
182*82657471SMarkus Pfeiffer 	return 0;
183*82657471SMarkus Pfeiffer }
184*82657471SMarkus Pfeiffer 
185*82657471SMarkus Pfeiffer static void
186*82657471SMarkus Pfeiffer msg_freehdr(struct msqid_pool *msqpptr, struct msg *msghdr)
187*82657471SMarkus Pfeiffer {
188*82657471SMarkus Pfeiffer 	while (msghdr->msg_ts > 0) {
189*82657471SMarkus Pfeiffer 		short next;
190*82657471SMarkus Pfeiffer 		if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) {
191*82657471SMarkus Pfeiffer 			sysv_print_err("msghdr->msg_spot out of range");
192*82657471SMarkus Pfeiffer 			exit(-1);
193*82657471SMarkus Pfeiffer 		}
194*82657471SMarkus Pfeiffer 		next = msqpptr->msgmaps[msghdr->msg_spot].next;
195*82657471SMarkus Pfeiffer 		msqpptr->msgmaps[msghdr->msg_spot].next =
196*82657471SMarkus Pfeiffer 			msqpptr->free_msgmaps;
197*82657471SMarkus Pfeiffer 		msqpptr->free_msgmaps = msghdr->msg_spot;
198*82657471SMarkus Pfeiffer 		msqpptr->nfree_msgmaps++;
199*82657471SMarkus Pfeiffer 		msghdr->msg_spot = next;
200*82657471SMarkus Pfeiffer 		if (msghdr->msg_ts >= msginfo.msgssz)
201*82657471SMarkus Pfeiffer 			msghdr->msg_ts -= msginfo.msgssz;
202*82657471SMarkus Pfeiffer 		else
203*82657471SMarkus Pfeiffer 			msghdr->msg_ts = 0;
204*82657471SMarkus Pfeiffer 	}
205*82657471SMarkus Pfeiffer 	if (msghdr->msg_spot != -1) {
206*82657471SMarkus Pfeiffer 		sysv_print_err("msghdr->msg_spot != -1");
207*82657471SMarkus Pfeiffer 		exit(-1);
208*82657471SMarkus Pfeiffer 	}
209*82657471SMarkus Pfeiffer 	msghdr->msg_next = msqpptr->free_msghdrs;
210*82657471SMarkus Pfeiffer 	msqpptr->free_msghdrs = (msghdr - &msqpptr->msghdrs[0]) /
211*82657471SMarkus Pfeiffer 		sizeof(struct msg);
212*82657471SMarkus Pfeiffer }
213*82657471SMarkus Pfeiffer 
214*82657471SMarkus Pfeiffer int
215*82657471SMarkus Pfeiffer sysvipc_msgget(key_t key, int msgflg) {
216*82657471SMarkus Pfeiffer 	int msqid;
217*82657471SMarkus Pfeiffer 	void *shmaddr;
218*82657471SMarkus Pfeiffer 	size_t size = sizeof(struct msqid_pool);
219*82657471SMarkus Pfeiffer 
220*82657471SMarkus Pfeiffer 	msqid = _shmget(key, size, msgflg, MSGGET);
221*82657471SMarkus Pfeiffer 	if (msqid == -1)
222*82657471SMarkus Pfeiffer 		goto done;
223*82657471SMarkus Pfeiffer 
224*82657471SMarkus Pfeiffer 	/* If the msg is in process of being removed there are two cases:
225*82657471SMarkus Pfeiffer 	 * - the daemon knows that and it will handle this situation.
226*82657471SMarkus Pfeiffer 	 * - one of the threads from this address space remove it and the daemon
227*82657471SMarkus Pfeiffer 	 *   wasn't announced yet; in this scenario, the msg is marked
228*82657471SMarkus Pfeiffer 	 *   using "removed" field of shm_data and future calls will return
229*82657471SMarkus Pfeiffer 	 *   EIDRM error.
230*82657471SMarkus Pfeiffer 	 */
231*82657471SMarkus Pfeiffer 
232*82657471SMarkus Pfeiffer #if 0
233*82657471SMarkus Pfeiffer 	/* Set access type. */
234*82657471SMarkus Pfeiffer 	shm_access = semflg & (IPC_W | IPC_R);
235*82657471SMarkus Pfeiffer 	if(set_shmdata_access(semid, shm_access) != 0) {
236*82657471SMarkus Pfeiffer 		/* errno already set. */
237*82657471SMarkus Pfeiffer 		goto done;
238*82657471SMarkus Pfeiffer 	}
239*82657471SMarkus Pfeiffer #endif
240*82657471SMarkus Pfeiffer 
241*82657471SMarkus Pfeiffer 	shmaddr = sysvipc_shmat(msqid, NULL, 0);
242*82657471SMarkus Pfeiffer 	if (!shmaddr) {
243*82657471SMarkus Pfeiffer 		msqid = -1;
244*82657471SMarkus Pfeiffer 		sysvipc_shmctl(msqid, IPC_RMID, NULL);
245*82657471SMarkus Pfeiffer 		goto done;
246*82657471SMarkus Pfeiffer 	}
247*82657471SMarkus Pfeiffer 	sysv_print("shmaddr = %lx\n", (unsigned long)shmaddr);
248*82657471SMarkus Pfeiffer 
249*82657471SMarkus Pfeiffer done:
250*82657471SMarkus Pfeiffer 	return msqid;
251*82657471SMarkus Pfeiffer }
252*82657471SMarkus Pfeiffer 
253*82657471SMarkus Pfeiffer int
254*82657471SMarkus Pfeiffer sysvipc_msgctl(int msqid, int cmd, struct msqid_ds *buf) {
255*82657471SMarkus Pfeiffer 	int error;
256*82657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr = NULL;
257*82657471SMarkus Pfeiffer 	struct shmid_ds shmds;
258*82657471SMarkus Pfeiffer 	int shm_access = 0;
259*82657471SMarkus Pfeiffer 
260*82657471SMarkus Pfeiffer 	error = 0;
261*82657471SMarkus Pfeiffer 
262*82657471SMarkus Pfeiffer 	switch (cmd) {
263*82657471SMarkus Pfeiffer 		case IPC_SET: /* Originally was IPC_M but this is checked
264*82657471SMarkus Pfeiffer 				 by daemon. */
265*82657471SMarkus Pfeiffer 			shm_access = IPC_W;
266*82657471SMarkus Pfeiffer 			break;
267*82657471SMarkus Pfeiffer 		case IPC_STAT:
268*82657471SMarkus Pfeiffer 			shm_access = IPC_R;
269*82657471SMarkus Pfeiffer 			break;
270*82657471SMarkus Pfeiffer 		default:
271*82657471SMarkus Pfeiffer 			break;
272*82657471SMarkus Pfeiffer 	}
273*82657471SMarkus Pfeiffer 
274*82657471SMarkus Pfeiffer 	msqpptr = get_msqpptr(msqid, cmd==IPC_RMID, shm_access);
275*82657471SMarkus Pfeiffer 	if (!msqpptr) {
276*82657471SMarkus Pfeiffer 		errno = EINVAL;
277*82657471SMarkus Pfeiffer 		return -1;
278*82657471SMarkus Pfeiffer 	}
279*82657471SMarkus Pfeiffer 
280*82657471SMarkus Pfeiffer 	switch (cmd) {
281*82657471SMarkus Pfeiffer 	case IPC_RMID:
282*82657471SMarkus Pfeiffer 		/* Mark that the segment is removed. This is done in
283*82657471SMarkus Pfeiffer 		 * get_msqpptr call in order to announce other processes.
284*82657471SMarkus Pfeiffer 		 * It will be actually removed after put_shmdata call and
285*82657471SMarkus Pfeiffer 		 * not other thread from this address space use shm_data
286*82657471SMarkus Pfeiffer 		 * structure.
287*82657471SMarkus Pfeiffer 		 */
288*82657471SMarkus Pfeiffer 		break;
289*82657471SMarkus Pfeiffer 	case IPC_SET:
290*82657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(msqid, msqpptr);
291*82657471SMarkus Pfeiffer 		if (error)
292*82657471SMarkus Pfeiffer 			break;
293*82657471SMarkus Pfeiffer 		if (buf->msg_qbytes == 0) {
294*82657471SMarkus Pfeiffer 			sysv_print_err("can't reduce msg_qbytes to 0\n");
295*82657471SMarkus Pfeiffer 			errno = EINVAL;		/* non-standard errno! */
296*82657471SMarkus Pfeiffer 			rwlock_unlock(msqid, msqpptr);
297*82657471SMarkus Pfeiffer 			break;
298*82657471SMarkus Pfeiffer 		}
299*82657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
300*82657471SMarkus Pfeiffer 
301*82657471SMarkus Pfeiffer 		memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char));
302*82657471SMarkus Pfeiffer 		memcpy(&shmds.shm_perm, &buf->msg_perm,
303*82657471SMarkus Pfeiffer 				sizeof(struct ipc_perm));
304*82657471SMarkus Pfeiffer 		error = sysvipc_shmctl(msqid, cmd, &shmds);
305*82657471SMarkus Pfeiffer 		if (error)
306*82657471SMarkus Pfeiffer 			break;
307*82657471SMarkus Pfeiffer 
308*82657471SMarkus Pfeiffer 		/* There is no need to check if we have right to modify the
309*82657471SMarkus Pfeiffer 		 * size because we have right to change other fileds. */
310*82657471SMarkus Pfeiffer 
311*82657471SMarkus Pfeiffer 		if (round_page(buf->msg_qbytes) !=
312*82657471SMarkus Pfeiffer 				round_page(msqpptr->ds.msg_qbytes)) {
313*82657471SMarkus Pfeiffer 				sysv_print("change msg size\n");
314*82657471SMarkus Pfeiffer 				/* TODO same as in semundo_adjust only
315*82657471SMarkus Pfeiffer 				 * that there is no way to inform other
316*82657471SMarkus Pfeiffer 				 * processes about the change. */
317*82657471SMarkus Pfeiffer 		}
318*82657471SMarkus Pfeiffer 
319*82657471SMarkus Pfeiffer 		error = try_rwlock_wrlock(msqid, msqpptr);
320*82657471SMarkus Pfeiffer 		if (error)
321*82657471SMarkus Pfeiffer 			break;
322*82657471SMarkus Pfeiffer 		msqpptr->ds.msg_qbytes = buf->msg_qbytes;
323*82657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
324*82657471SMarkus Pfeiffer 		/* OBS: didn't update ctime and mode as in kernel implementation
325*82657471SMarkus Pfeiffer 		 * it is done. Those fields are already updated for shmid_ds
326*82657471SMarkus Pfeiffer 		 * struct when calling shmctl
327*82657471SMarkus Pfeiffer 		 */
328*82657471SMarkus Pfeiffer 		break;
329*82657471SMarkus Pfeiffer 
330*82657471SMarkus Pfeiffer 	case IPC_STAT:
331*82657471SMarkus Pfeiffer 		error = sysvipc_shmctl(msqid, cmd, &shmds);
332*82657471SMarkus Pfeiffer 		if (error)
333*82657471SMarkus Pfeiffer 			break;
334*82657471SMarkus Pfeiffer 
335*82657471SMarkus Pfeiffer 		memcpy(&buf->msg_perm, &shmds.shm_perm,
336*82657471SMarkus Pfeiffer 				sizeof(struct ipc_perm));
337*82657471SMarkus Pfeiffer 		buf->msg_ctime = shmds.shm_ctime;
338*82657471SMarkus Pfeiffer 
339*82657471SMarkus Pfeiffer 		/* Read fields that are not kept in shmds. */
340*82657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(msqid, msqpptr);
341*82657471SMarkus Pfeiffer 		if (error)
342*82657471SMarkus Pfeiffer 			break;
343*82657471SMarkus Pfeiffer 		buf->msg_first = (struct msg *)(u_long)
344*82657471SMarkus Pfeiffer 			msqpptr->ds.first.msg_first_index;
345*82657471SMarkus Pfeiffer 		buf->msg_last = (struct msg *)(u_long)
346*82657471SMarkus Pfeiffer 			msqpptr->ds.last.msg_last_index;
347*82657471SMarkus Pfeiffer 		buf->msg_cbytes = msqpptr->ds.msg_cbytes;
348*82657471SMarkus Pfeiffer 		buf->msg_qnum = msqpptr->ds.msg_qnum;
349*82657471SMarkus Pfeiffer 		buf->msg_qbytes = msqpptr->ds.msg_qbytes;
350*82657471SMarkus Pfeiffer 		buf->msg_lspid = msqpptr->ds.msg_lspid;
351*82657471SMarkus Pfeiffer 		buf->msg_lrpid = msqpptr->ds.msg_lrpid;
352*82657471SMarkus Pfeiffer 		buf->msg_stime = msqpptr->ds.msg_stime;
353*82657471SMarkus Pfeiffer 		buf->msg_rtime = msqpptr->ds.msg_rtime;
354*82657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
355*82657471SMarkus Pfeiffer 		break;
356*82657471SMarkus Pfeiffer 	default:
357*82657471SMarkus Pfeiffer 		sysv_print_err("invalid command %d\n", cmd);
358*82657471SMarkus Pfeiffer 		errno = EINVAL;
359*82657471SMarkus Pfeiffer 		break;
360*82657471SMarkus Pfeiffer 	}
361*82657471SMarkus Pfeiffer 
362*82657471SMarkus Pfeiffer 	put_shmdata(msqid);
363*82657471SMarkus Pfeiffer 
364*82657471SMarkus Pfeiffer 	return(error);
365*82657471SMarkus Pfeiffer }
366*82657471SMarkus Pfeiffer 
367*82657471SMarkus Pfeiffer int
368*82657471SMarkus Pfeiffer sysvipc_msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg)
369*82657471SMarkus Pfeiffer {
370*82657471SMarkus Pfeiffer 	int segs_needed, error;
371*82657471SMarkus Pfeiffer 	struct msg *msghdr;
372*82657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr, *auxmsqpptr;
373*82657471SMarkus Pfeiffer 	struct msqid_ds_internal *msqptr;
374*82657471SMarkus Pfeiffer 	short next;
375*82657471SMarkus Pfeiffer 	int val_to_sleep;
376*82657471SMarkus Pfeiffer 	char *auxmsgp = (char *)msgp;
377*82657471SMarkus Pfeiffer 	int _index;
378*82657471SMarkus Pfeiffer 
379*82657471SMarkus Pfeiffer 	sysv_print("call to msgsnd(%d, %ld, %d)\n", msqid, msgsz, msgflg);
380*82657471SMarkus Pfeiffer 
381*82657471SMarkus Pfeiffer 	/*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
382*82657471SMarkus Pfeiffer 		return (ENOSYS);
383*82657471SMarkus Pfeiffer */
384*82657471SMarkus Pfeiffer 	if (!msgp) {
385*82657471SMarkus Pfeiffer 		errno = EINVAL;
386*82657471SMarkus Pfeiffer 		return -1;
387*82657471SMarkus Pfeiffer 	}
388*82657471SMarkus Pfeiffer 
389*82657471SMarkus Pfeiffer 	msqpptr = get_msqpptr(msqid, 0, IPC_W);
390*82657471SMarkus Pfeiffer 	if (!msqpptr) {
391*82657471SMarkus Pfeiffer 		errno = EINVAL;
392*82657471SMarkus Pfeiffer 		return -1;
393*82657471SMarkus Pfeiffer 	}
394*82657471SMarkus Pfeiffer 	error = -1;
395*82657471SMarkus Pfeiffer 
396*82657471SMarkus Pfeiffer 	if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
397*82657471SMarkus Pfeiffer 		errno = EIDRM;
398*82657471SMarkus Pfeiffer 		goto done;
399*82657471SMarkus Pfeiffer 	}
400*82657471SMarkus Pfeiffer 
401*82657471SMarkus Pfeiffer 	msqptr = &msqpptr->ds;
402*82657471SMarkus Pfeiffer 
403*82657471SMarkus Pfeiffer 	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
404*82657471SMarkus Pfeiffer 	sysv_print("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
405*82657471SMarkus Pfeiffer 	    segs_needed);
406*82657471SMarkus Pfeiffer 	for (;;) {
407*82657471SMarkus Pfeiffer 		int need_more_resources = 0;
408*82657471SMarkus Pfeiffer 
409*82657471SMarkus Pfeiffer 		if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
410*82657471SMarkus Pfeiffer 			sysv_print("msgsz + msg_cbytes > msg_qbytes\n");
411*82657471SMarkus Pfeiffer 			need_more_resources = 1;
412*82657471SMarkus Pfeiffer 		}
413*82657471SMarkus Pfeiffer 
414*82657471SMarkus Pfeiffer 		if (segs_needed > msqpptr->nfree_msgmaps) {
415*82657471SMarkus Pfeiffer 			sysv_print("segs_needed > nfree_msgmaps (= %d)\n",
416*82657471SMarkus Pfeiffer 					msqpptr->nfree_msgmaps);
417*82657471SMarkus Pfeiffer 			need_more_resources = 1;
418*82657471SMarkus Pfeiffer 		}
419*82657471SMarkus Pfeiffer 
420*82657471SMarkus Pfeiffer 		if (msqpptr->free_msghdrs == -1) {
421*82657471SMarkus Pfeiffer 			sysv_print("no more msghdrs\n");
422*82657471SMarkus Pfeiffer 			need_more_resources = 1;
423*82657471SMarkus Pfeiffer 		}
424*82657471SMarkus Pfeiffer 
425*82657471SMarkus Pfeiffer 		if (need_more_resources) {
426*82657471SMarkus Pfeiffer 			if ((msgflg & IPC_NOWAIT) != 0) {
427*82657471SMarkus Pfeiffer 				sysv_print_err("need more resources but caller doesn't want to wait\n");
428*82657471SMarkus Pfeiffer 				errno = EAGAIN;
429*82657471SMarkus Pfeiffer 				goto done;
430*82657471SMarkus Pfeiffer 			}
431*82657471SMarkus Pfeiffer 
432*82657471SMarkus Pfeiffer 			sysv_print("goodnight\n");
433*82657471SMarkus Pfeiffer 			val_to_sleep = msqpptr->gen;
434*82657471SMarkus Pfeiffer 			rwlock_unlock(msqid, msqpptr);
435*82657471SMarkus Pfeiffer 			put_shmdata(msqid);
436*82657471SMarkus Pfeiffer 
437*82657471SMarkus Pfeiffer 			if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) {
438*82657471SMarkus Pfeiffer 				sysv_print_err("msgsnd:  interrupted system call\n");
439*82657471SMarkus Pfeiffer 				errno = EINTR;
440*82657471SMarkus Pfeiffer 				goto done;
441*82657471SMarkus Pfeiffer 			}
442*82657471SMarkus Pfeiffer 
443*82657471SMarkus Pfeiffer 			/* Check if another thread didn't remove the msg queue. */
444*82657471SMarkus Pfeiffer 			auxmsqpptr = get_msqpptr(msqid, 0, IPC_W);
445*82657471SMarkus Pfeiffer 			if (!auxmsqpptr) {
446*82657471SMarkus Pfeiffer 				errno = EIDRM;
447*82657471SMarkus Pfeiffer 				return -1;
448*82657471SMarkus Pfeiffer 			}
449*82657471SMarkus Pfeiffer 
450*82657471SMarkus Pfeiffer 			if (auxmsqpptr != msqpptr) {
451*82657471SMarkus Pfeiffer 				errno = EIDRM;
452*82657471SMarkus Pfeiffer 				goto done;
453*82657471SMarkus Pfeiffer 			}
454*82657471SMarkus Pfeiffer 
455*82657471SMarkus Pfeiffer 			/* Check if another process didn't remove the queue. */
456*82657471SMarkus Pfeiffer 			if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
457*82657471SMarkus Pfeiffer 				errno = EIDRM;
458*82657471SMarkus Pfeiffer 				goto done;
459*82657471SMarkus Pfeiffer 			}
460*82657471SMarkus Pfeiffer 
461*82657471SMarkus Pfeiffer 			if (msqptr != &msqpptr->ds) {
462*82657471SMarkus Pfeiffer 				sysv_print("msqptr != &msqpptr->ds");
463*82657471SMarkus Pfeiffer 				exit(-1);
464*82657471SMarkus Pfeiffer 			}
465*82657471SMarkus Pfeiffer 
466*82657471SMarkus Pfeiffer 		} else {
467*82657471SMarkus Pfeiffer 			sysv_print("got all the resources that we need\n");
468*82657471SMarkus Pfeiffer 			break;
469*82657471SMarkus Pfeiffer 		}
470*82657471SMarkus Pfeiffer 	}
471*82657471SMarkus Pfeiffer 
472*82657471SMarkus Pfeiffer 	/*
473*82657471SMarkus Pfeiffer 	 * We have the resources that we need.
474*82657471SMarkus Pfeiffer 	 * Make sure!
475*82657471SMarkus Pfeiffer 	 */
476*82657471SMarkus Pfeiffer #if 0
477*82657471SMarkus Pfeiffer 	if (segs_needed > nfree_msgmaps) {
478*82657471SMarkus Pfeiffer 		sysv_print_err("segs_needed > nfree_msgmaps");
479*82657471SMarkus Pfeiffer 		exit(-1);
480*82657471SMarkus Pfeiffer 	}
481*82657471SMarkus Pfeiffer #endif
482*82657471SMarkus Pfeiffer 	if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
483*82657471SMarkus Pfeiffer 		sysv_print_err("msgsz + msg_cbytes > msg_qbytes");
484*82657471SMarkus Pfeiffer 		exit(-1);
485*82657471SMarkus Pfeiffer 	}
486*82657471SMarkus Pfeiffer 
487*82657471SMarkus Pfeiffer 	/*
488*82657471SMarkus Pfeiffer 	 * Allocate a message header
489*82657471SMarkus Pfeiffer 	 */
490*82657471SMarkus Pfeiffer 	msghdr = &msqpptr->msghdrs[msqpptr->free_msghdrs];
491*82657471SMarkus Pfeiffer 	msqpptr->free_msghdrs = msghdr->msg_next;
492*82657471SMarkus Pfeiffer 	msghdr->msg_spot = -1;
493*82657471SMarkus Pfeiffer 	msghdr->msg_ts = msgsz;
494*82657471SMarkus Pfeiffer 
495*82657471SMarkus Pfeiffer 	/*
496*82657471SMarkus Pfeiffer 	 * Allocate space for the message
497*82657471SMarkus Pfeiffer 	 */
498*82657471SMarkus Pfeiffer 	while (segs_needed > 0) {
499*82657471SMarkus Pfeiffer 		next = msqpptr->free_msgmaps;
500*82657471SMarkus Pfeiffer 		if (next < 0 || next > msginfo.msgseg) {
501*82657471SMarkus Pfeiffer 			sysv_print_err("out of range free_msgmaps %d #1\n", next);
502*82657471SMarkus Pfeiffer 			exit(-1);
503*82657471SMarkus Pfeiffer 		}
504*82657471SMarkus Pfeiffer 
505*82657471SMarkus Pfeiffer 		msqpptr->free_msgmaps = msqpptr->msgmaps[next].next;
506*82657471SMarkus Pfeiffer 		msqpptr->nfree_msgmaps--;
507*82657471SMarkus Pfeiffer 		msqpptr->msgmaps[next].next = msghdr->msg_spot;
508*82657471SMarkus Pfeiffer 		msghdr->msg_spot = next;
509*82657471SMarkus Pfeiffer 		segs_needed--;
510*82657471SMarkus Pfeiffer 	}
511*82657471SMarkus Pfeiffer 
512*82657471SMarkus Pfeiffer 	/*
513*82657471SMarkus Pfeiffer 	 * Copy in the message type
514*82657471SMarkus Pfeiffer 	 */
515*82657471SMarkus Pfeiffer 	memcpy(&msghdr->msg_type, auxmsgp, sizeof(msghdr->msg_type));
516*82657471SMarkus Pfeiffer 	auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type);
517*82657471SMarkus Pfeiffer 
518*82657471SMarkus Pfeiffer 	/*
519*82657471SMarkus Pfeiffer 	 * Validate the message type
520*82657471SMarkus Pfeiffer 	 */
521*82657471SMarkus Pfeiffer 	sysv_print("msg_type = %ld\n", msghdr->msg_type);
522*82657471SMarkus Pfeiffer 
523*82657471SMarkus Pfeiffer 	if (msghdr->msg_type < 1) {
524*82657471SMarkus Pfeiffer 		msg_freehdr(msqpptr, msghdr);
525*82657471SMarkus Pfeiffer 		umtx_wakeup((int *)&msqpptr->gen, 0);
526*82657471SMarkus Pfeiffer 		sysv_print_err("mtype (%ld) < 1\n", msghdr->msg_type);
527*82657471SMarkus Pfeiffer 		errno = EINVAL;
528*82657471SMarkus Pfeiffer 		goto done;
529*82657471SMarkus Pfeiffer 	}
530*82657471SMarkus Pfeiffer 
531*82657471SMarkus Pfeiffer 	/*
532*82657471SMarkus Pfeiffer 	 * Copy in the message body
533*82657471SMarkus Pfeiffer 	 */
534*82657471SMarkus Pfeiffer 	next = msghdr->msg_spot;
535*82657471SMarkus Pfeiffer 	while (msgsz > 0) {
536*82657471SMarkus Pfeiffer 		size_t tlen;
537*82657471SMarkus Pfeiffer 		if (msgsz > (size_t)msginfo.msgssz)
538*82657471SMarkus Pfeiffer 			tlen = msginfo.msgssz;
539*82657471SMarkus Pfeiffer 		else
540*82657471SMarkus Pfeiffer 			tlen = msgsz;
541*82657471SMarkus Pfeiffer 		if (next < 0 || next > msginfo.msgseg) {
542*82657471SMarkus Pfeiffer 			sysv_print_err("out of range free_msgmaps %d #2\n", next);
543*82657471SMarkus Pfeiffer 			exit(-1);
544*82657471SMarkus Pfeiffer 		}
545*82657471SMarkus Pfeiffer 
546*82657471SMarkus Pfeiffer 		memcpy(&msqpptr->msgpool[next * msginfo.msgssz], auxmsgp, tlen);
547*82657471SMarkus Pfeiffer 		msgsz -= tlen;
548*82657471SMarkus Pfeiffer 		auxmsgp = (char *)auxmsgp + tlen;
549*82657471SMarkus Pfeiffer 		next = msqpptr->msgmaps[next].next;
550*82657471SMarkus Pfeiffer 	}
551*82657471SMarkus Pfeiffer 
552*82657471SMarkus Pfeiffer 	/*
553*82657471SMarkus Pfeiffer 	 * Put the message into the queue
554*82657471SMarkus Pfeiffer 	 */
555*82657471SMarkus Pfeiffer 	_index = (msghdr - &msqpptr->msghdrs[0]) /
556*82657471SMarkus Pfeiffer 		sizeof(struct msg);
557*82657471SMarkus Pfeiffer 	sysv_print("index_msghdr = %d\n", _index);
558*82657471SMarkus Pfeiffer 	if (msqptr->first.msg_first_index == -1) {
559*82657471SMarkus Pfeiffer 		msqptr->first.msg_first_index = _index;
560*82657471SMarkus Pfeiffer 		msqptr->last.msg_last_index = _index;
561*82657471SMarkus Pfeiffer 	} else {
562*82657471SMarkus Pfeiffer 		msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = _index;
563*82657471SMarkus Pfeiffer 		msqptr->last.msg_last_index = _index;
564*82657471SMarkus Pfeiffer 	}
565*82657471SMarkus Pfeiffer 	msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = -1;
566*82657471SMarkus Pfeiffer 
567*82657471SMarkus Pfeiffer 	msqptr->msg_cbytes += msghdr->msg_ts;
568*82657471SMarkus Pfeiffer 	msqptr->msg_qnum++;
569*82657471SMarkus Pfeiffer 	msqptr->msg_lspid = getpid();
570*82657471SMarkus Pfeiffer 	msqptr->msg_stime = time(NULL);
571*82657471SMarkus Pfeiffer 
572*82657471SMarkus Pfeiffer 	umtx_wakeup((int *)&msqpptr->gen, 0);
573*82657471SMarkus Pfeiffer 	error = 0;
574*82657471SMarkus Pfeiffer 
575*82657471SMarkus Pfeiffer done:
576*82657471SMarkus Pfeiffer 	rwlock_unlock(msqid, msqpptr);
577*82657471SMarkus Pfeiffer 	put_shmdata(msqid);
578*82657471SMarkus Pfeiffer 	return(error);
579*82657471SMarkus Pfeiffer }
580*82657471SMarkus Pfeiffer 
581*82657471SMarkus Pfeiffer int
582*82657471SMarkus Pfeiffer sysvipc_msgrcv(int msqid, void *msgp, size_t msgsz, long mtype, int msgflg)
583*82657471SMarkus Pfeiffer {
584*82657471SMarkus Pfeiffer 	size_t len;
585*82657471SMarkus Pfeiffer 	struct msqid_pool *msqpptr, *auxmsqpptr;
586*82657471SMarkus Pfeiffer 	struct msqid_ds_internal *msqptr;
587*82657471SMarkus Pfeiffer 	struct msg *msghdr;
588*82657471SMarkus Pfeiffer 	short msghdr_index;
589*82657471SMarkus Pfeiffer 	int error;
590*82657471SMarkus Pfeiffer 	short next;
591*82657471SMarkus Pfeiffer 	int val_to_sleep;
592*82657471SMarkus Pfeiffer 	char *auxmsgp = (char *)msgp;
593*82657471SMarkus Pfeiffer 
594*82657471SMarkus Pfeiffer 	sysv_print("call to msgrcv(%d, %ld, %ld, %d)\n", msqid, msgsz, mtype, msgflg);
595*82657471SMarkus Pfeiffer /*
596*82657471SMarkus Pfeiffer 	if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
597*82657471SMarkus Pfeiffer 		return (ENOSYS);
598*82657471SMarkus Pfeiffer */
599*82657471SMarkus Pfeiffer 
600*82657471SMarkus Pfeiffer 	if (!msgp) {
601*82657471SMarkus Pfeiffer 		errno = EINVAL;
602*82657471SMarkus Pfeiffer 		return -1;
603*82657471SMarkus Pfeiffer 	}
604*82657471SMarkus Pfeiffer 
605*82657471SMarkus Pfeiffer 	msqpptr = get_msqpptr(msqid, 0, IPC_R);
606*82657471SMarkus Pfeiffer 	if (!msqpptr) {
607*82657471SMarkus Pfeiffer 		errno = EINVAL;
608*82657471SMarkus Pfeiffer 		return -1;
609*82657471SMarkus Pfeiffer 	}
610*82657471SMarkus Pfeiffer 
611*82657471SMarkus Pfeiffer 	error = -1;
612*82657471SMarkus Pfeiffer 
613*82657471SMarkus Pfeiffer 	if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
614*82657471SMarkus Pfeiffer 		errno = EIDRM;
615*82657471SMarkus Pfeiffer 		goto done;
616*82657471SMarkus Pfeiffer 	}
617*82657471SMarkus Pfeiffer 
618*82657471SMarkus Pfeiffer 	msqptr = &msqpptr->ds;
619*82657471SMarkus Pfeiffer 
620*82657471SMarkus Pfeiffer 	msghdr_index = -1;
621*82657471SMarkus Pfeiffer 	while (msghdr_index == -1) {
622*82657471SMarkus Pfeiffer 		if (mtype == 0) {
623*82657471SMarkus Pfeiffer 			msghdr_index = msqptr->first.msg_first_index;
624*82657471SMarkus Pfeiffer 			msghdr = &msqpptr->msghdrs[msghdr_index];
625*82657471SMarkus Pfeiffer 			if (msghdr_index != -1) {
626*82657471SMarkus Pfeiffer 				if (msgsz < msghdr->msg_ts &&
627*82657471SMarkus Pfeiffer 				    (msgflg & MSG_NOERROR) == 0) {
628*82657471SMarkus Pfeiffer 						sysv_print_err("first message on the queue is too big"
629*82657471SMarkus Pfeiffer 							"(want %d, got %d)\n",
630*82657471SMarkus Pfeiffer 							msgsz, msghdr->msg_ts);
631*82657471SMarkus Pfeiffer 					errno = E2BIG;
632*82657471SMarkus Pfeiffer 					goto done;
633*82657471SMarkus Pfeiffer 				}
634*82657471SMarkus Pfeiffer 				if (msqptr->first.msg_first_index == msqptr->last.msg_last_index) {
635*82657471SMarkus Pfeiffer 					msqptr->first.msg_first_index = -1;
636*82657471SMarkus Pfeiffer 					msqptr->last.msg_last_index = -1;
637*82657471SMarkus Pfeiffer 				} else {
638*82657471SMarkus Pfeiffer 					msqptr->first.msg_first_index = msghdr->msg_next;
639*82657471SMarkus Pfeiffer 					if (msqptr->first.msg_first_index == -1) {
640*82657471SMarkus Pfeiffer 						sysv_print_err("first.msg_first_index/last screwed up #1");
641*82657471SMarkus Pfeiffer 						exit(-1);
642*82657471SMarkus Pfeiffer 					}
643*82657471SMarkus Pfeiffer 				}
644*82657471SMarkus Pfeiffer 			}
645*82657471SMarkus Pfeiffer 		} else {
646*82657471SMarkus Pfeiffer 			short previous;
647*82657471SMarkus Pfeiffer 			short prev;
648*82657471SMarkus Pfeiffer 			previous = -1;
649*82657471SMarkus Pfeiffer 			prev = msqptr->first.msg_first_index;
650*82657471SMarkus Pfeiffer 			while ((msghdr_index = prev) != -1) {
651*82657471SMarkus Pfeiffer 				msghdr = &msqpptr->msghdrs[msghdr_index];
652*82657471SMarkus Pfeiffer 				/*
653*82657471SMarkus Pfeiffer 				 * Is this message's type an exact match or is
654*82657471SMarkus Pfeiffer 				 * this message's type less than or equal to
655*82657471SMarkus Pfeiffer 				 * the absolute value of a negative mtype?
656*82657471SMarkus Pfeiffer 				 * Note that the second half of this test can
657*82657471SMarkus Pfeiffer 				 * NEVER be true if mtype is positive since
658*82657471SMarkus Pfeiffer 				 * msg_type is always positive!
659*82657471SMarkus Pfeiffer 				 */
660*82657471SMarkus Pfeiffer 				if (mtype == msghdr->msg_type ||
661*82657471SMarkus Pfeiffer 				    msghdr->msg_type <= -mtype) {
662*82657471SMarkus Pfeiffer 					sysv_print("found message type %d, requested %d\n",
663*82657471SMarkus Pfeiffer 					    msghdr->msg_type, mtype);
664*82657471SMarkus Pfeiffer 					if (msgsz < msghdr->msg_ts &&
665*82657471SMarkus Pfeiffer 					    (msgflg & MSG_NOERROR) == 0) {
666*82657471SMarkus Pfeiffer 						sysv_print_err("requested message on the queue"
667*82657471SMarkus Pfeiffer 							" is too big (want %d, got %d)\n",
668*82657471SMarkus Pfeiffer 						    msgsz, msghdr->msg_ts);
669*82657471SMarkus Pfeiffer 						errno = E2BIG;
670*82657471SMarkus Pfeiffer 						goto done;
671*82657471SMarkus Pfeiffer 					}
672*82657471SMarkus Pfeiffer 					prev = msghdr->msg_next;
673*82657471SMarkus Pfeiffer 					if (msghdr_index == msqptr->last.msg_last_index) {
674*82657471SMarkus Pfeiffer 						if (previous == -1) {
675*82657471SMarkus Pfeiffer 							msqptr->first.msg_first_index = -1;
676*82657471SMarkus Pfeiffer 							msqptr->last.msg_last_index = -1;
677*82657471SMarkus Pfeiffer 						} else {
678*82657471SMarkus Pfeiffer 							msqptr->last.msg_last_index = previous;
679*82657471SMarkus Pfeiffer 						}
680*82657471SMarkus Pfeiffer 					}
681*82657471SMarkus Pfeiffer 					break;
682*82657471SMarkus Pfeiffer 				}
683*82657471SMarkus Pfeiffer 				previous = msghdr_index;
684*82657471SMarkus Pfeiffer 				prev = msghdr->msg_next;
685*82657471SMarkus Pfeiffer 			}
686*82657471SMarkus Pfeiffer 		}
687*82657471SMarkus Pfeiffer 
688*82657471SMarkus Pfeiffer 		/*
689*82657471SMarkus Pfeiffer 		 * We've either extracted the msghdr for the appropriate
690*82657471SMarkus Pfeiffer 		 * message or there isn't one.
691*82657471SMarkus Pfeiffer 		 * If there is one then bail out of this loop.
692*82657471SMarkus Pfeiffer 		 */
693*82657471SMarkus Pfeiffer 		if (msghdr_index != -1)
694*82657471SMarkus Pfeiffer 			break;
695*82657471SMarkus Pfeiffer 
696*82657471SMarkus Pfeiffer 		/*
697*82657471SMarkus Pfeiffer 		 * No message found.  Does the user want to wait?
698*82657471SMarkus Pfeiffer 		 */
699*82657471SMarkus Pfeiffer 		if ((msgflg & IPC_NOWAIT) != 0) {
700*82657471SMarkus Pfeiffer 			sysv_print_err("no appropriate message found (mtype=%d)\n",
701*82657471SMarkus Pfeiffer 			    mtype);
702*82657471SMarkus Pfeiffer 			errno = ENOMSG;
703*82657471SMarkus Pfeiffer 			goto done;
704*82657471SMarkus Pfeiffer 		}
705*82657471SMarkus Pfeiffer 
706*82657471SMarkus Pfeiffer 		/*
707*82657471SMarkus Pfeiffer 		 * Wait for something to happen
708*82657471SMarkus Pfeiffer 		 */
709*82657471SMarkus Pfeiffer 		sysv_print("goodnight\n");
710*82657471SMarkus Pfeiffer 		val_to_sleep = msqpptr->gen;
711*82657471SMarkus Pfeiffer 		rwlock_unlock(msqid, msqpptr);
712*82657471SMarkus Pfeiffer 		put_shmdata(msqid);
713*82657471SMarkus Pfeiffer 
714*82657471SMarkus Pfeiffer 		/* We don't sleep more than SYSV_TIMEOUT because we could
715*82657471SMarkus Pfeiffer 		 * go to sleep after another process calls wakeup and remain
716*82657471SMarkus Pfeiffer 		 * blocked.
717*82657471SMarkus Pfeiffer 		 */
718*82657471SMarkus Pfeiffer 		if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) {
719*82657471SMarkus Pfeiffer 			sysv_print_err("msgrcv:  interrupted system call\n");
720*82657471SMarkus Pfeiffer 			errno = EINTR;
721*82657471SMarkus Pfeiffer 			goto done;
722*82657471SMarkus Pfeiffer 		}
723*82657471SMarkus Pfeiffer 		sysv_print("msgrcv:  good morning (error=%d)\n", errno);
724*82657471SMarkus Pfeiffer 
725*82657471SMarkus Pfeiffer 		/* Check if another thread didn't remove the msg queue. */
726*82657471SMarkus Pfeiffer 		auxmsqpptr = get_msqpptr(msqid, 0, IPC_R);
727*82657471SMarkus Pfeiffer 		if (!auxmsqpptr) {
728*82657471SMarkus Pfeiffer 			errno = EIDRM;
729*82657471SMarkus Pfeiffer 			return -1;
730*82657471SMarkus Pfeiffer 		}
731*82657471SMarkus Pfeiffer 
732*82657471SMarkus Pfeiffer 		if (auxmsqpptr != msqpptr) {
733*82657471SMarkus Pfeiffer 			errno = EIDRM;
734*82657471SMarkus Pfeiffer 			goto done;
735*82657471SMarkus Pfeiffer 		}
736*82657471SMarkus Pfeiffer 
737*82657471SMarkus Pfeiffer 		/* Check if another process didn't remove the msg queue. */
738*82657471SMarkus Pfeiffer 		if (try_rwlock_wrlock(msqid, msqpptr) == -1) {
739*82657471SMarkus Pfeiffer 			errno = EIDRM;
740*82657471SMarkus Pfeiffer 			goto done;
741*82657471SMarkus Pfeiffer 		}
742*82657471SMarkus Pfeiffer 
743*82657471SMarkus Pfeiffer 		if (msqptr != &msqpptr->ds) {
744*82657471SMarkus Pfeiffer 			sysv_print_err("msqptr != &msqpptr->ds");
745*82657471SMarkus Pfeiffer 			exit(-1);
746*82657471SMarkus Pfeiffer 		}
747*82657471SMarkus Pfeiffer 	}
748*82657471SMarkus Pfeiffer 
749*82657471SMarkus Pfeiffer 	/*
750*82657471SMarkus Pfeiffer 	 * Return the message to the user.
751*82657471SMarkus Pfeiffer 	 */
752*82657471SMarkus Pfeiffer 	msqptr->msg_cbytes -= msghdr->msg_ts;
753*82657471SMarkus Pfeiffer 	msqptr->msg_qnum--;
754*82657471SMarkus Pfeiffer 	msqptr->msg_lrpid = getpid();
755*82657471SMarkus Pfeiffer 	msqptr->msg_rtime = time(NULL);
756*82657471SMarkus Pfeiffer 
757*82657471SMarkus Pfeiffer 	/*
758*82657471SMarkus Pfeiffer 	 * Make msgsz the actual amount that we'll be returning.
759*82657471SMarkus Pfeiffer 	 * Note that this effectively truncates the message if it is too long
760*82657471SMarkus Pfeiffer 	 * (since msgsz is never increased).
761*82657471SMarkus Pfeiffer 	 */
762*82657471SMarkus Pfeiffer 	sysv_print("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
763*82657471SMarkus Pfeiffer 	    msghdr->msg_ts);
764*82657471SMarkus Pfeiffer 	if (msgsz > msghdr->msg_ts)
765*82657471SMarkus Pfeiffer 		msgsz = msghdr->msg_ts;
766*82657471SMarkus Pfeiffer 
767*82657471SMarkus Pfeiffer 	/*
768*82657471SMarkus Pfeiffer 	 * Return the type to the user.
769*82657471SMarkus Pfeiffer 	 */
770*82657471SMarkus Pfeiffer 	memcpy(auxmsgp, (caddr_t)&(msghdr->msg_type), sizeof(msghdr->msg_type));
771*82657471SMarkus Pfeiffer 	auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type);
772*82657471SMarkus Pfeiffer 
773*82657471SMarkus Pfeiffer 	/*
774*82657471SMarkus Pfeiffer 	 * Return the segments to the user
775*82657471SMarkus Pfeiffer 	 */
776*82657471SMarkus Pfeiffer 	next = msghdr->msg_spot;
777*82657471SMarkus Pfeiffer 	for (len = 0; len < msgsz; len += msginfo.msgssz) {
778*82657471SMarkus Pfeiffer 		size_t tlen;
779*82657471SMarkus Pfeiffer 
780*82657471SMarkus Pfeiffer 		if (msgsz - len > (size_t)msginfo.msgssz)
781*82657471SMarkus Pfeiffer 			tlen = msginfo.msgssz;
782*82657471SMarkus Pfeiffer 		else
783*82657471SMarkus Pfeiffer 			tlen = msgsz - len;
784*82657471SMarkus Pfeiffer 		if (next < 0 || next > msginfo.msgseg) {
785*82657471SMarkus Pfeiffer 			sysv_print_err("out of range free_msgmaps %d #3\n", next);
786*82657471SMarkus Pfeiffer 			exit(-1);
787*82657471SMarkus Pfeiffer 		}
788*82657471SMarkus Pfeiffer 
789*82657471SMarkus Pfeiffer 		memcpy(auxmsgp, (caddr_t)&msqpptr->msgpool[next * msginfo.msgssz], tlen);
790*82657471SMarkus Pfeiffer 		auxmsgp = (char *)auxmsgp + tlen;
791*82657471SMarkus Pfeiffer 		next = msqpptr->msgmaps[next].next;
792*82657471SMarkus Pfeiffer 	}
793*82657471SMarkus Pfeiffer 
794*82657471SMarkus Pfeiffer 	/*
795*82657471SMarkus Pfeiffer 	 * Done, return the actual number of bytes copied out.
796*82657471SMarkus Pfeiffer 	 */
797*82657471SMarkus Pfeiffer 	msg_freehdr(msqpptr, msghdr);
798*82657471SMarkus Pfeiffer 	umtx_wakeup((int *)&msqpptr->gen, 0);
799*82657471SMarkus Pfeiffer 	error = msgsz;
800*82657471SMarkus Pfeiffer done:
801*82657471SMarkus Pfeiffer 	rwlock_unlock(msqid, msqpptr);
802*82657471SMarkus Pfeiffer 	put_shmdata(msqid);
803*82657471SMarkus Pfeiffer 	return(error);
804*82657471SMarkus Pfeiffer }
805