]>
Commit | Line | Data |
---|---|---|
282113ba CV |
1 | /* |
2 | * Implementation of SVID messages | |
3 | * | |
4 | * Author: Daniel Boulet | |
5 | * | |
6 | * Copyright 1993 Daniel Boulet and RTMX Inc. | |
7 | * | |
8 | * This system call was implemented by Daniel Boulet under contract from RTMX. | |
9 | * | |
10 | * Redistribution and use in source forms, with and without modification, | |
11 | * are permitted provided that this entire comment appears intact. | |
12 | * | |
13 | * Redistribution in binary form may occur without any restrictions. | |
14 | * Obviously, it would be nice if you gave credit where credit is due | |
15 | * but requiring it would be too onerous. | |
16 | * | |
17 | * This software is provided ``AS IS'' without any warranties of any kind. | |
18 | */ | |
19 | ||
20 | /* | |
21 | * This file is heavily changed to become part of Cygwin's cygserver. | |
22 | */ | |
23 | ||
24 | #ifdef __OUTSIDE_CYGWIN__ | |
25 | #include "woutsup.h" | |
26 | #include <sys/cdefs.h> | |
27 | #ifndef __FBSDID | |
28 | #define __FBSDID(s) const char version[] = (s) | |
29 | #endif | |
30 | __FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/kern/sysv_msg.c,v 1.52 2003/11/07 04:47:14 rwatson Exp $"); | |
e3786825 | 31 | /* CV, 2006-01-09: Inspected upstream up to version 1.60. */ |
282113ba CV |
32 | |
33 | #define _KERNEL 1 | |
34 | #define __BSD_VISIBLE 1 | |
35 | #include <sys/types.h> | |
36 | #include <sys/sysproto.h> | |
37 | #include <sys/ipc.h> | |
38 | #include <sys/param.h> | |
39 | #include <sys/msg.h> | |
40 | #include <malloc.h> | |
41 | #include <errno.h> | |
42 | #include <time.h> | |
43 | #include "cygserver.h" | |
44 | #include "process.h" | |
45 | #include "cygserver_ipc.h" | |
46 | ||
47 | #ifdef __CYGWIN__ | |
48 | #define MSG_DEBUG | |
49 | #endif /* __CYGWIN__ */ | |
50 | ||
51 | #ifdef MSG_DEBUG | |
52 | #define DPRINTF(a) debug_printf a | |
53 | #else | |
54 | #define DPRINTF(a) | |
55 | #endif | |
56 | ||
57 | static void msg_freehdr(struct msg *msghdr); | |
58 | ||
59 | #ifndef __CYGWIN__ | |
60 | int msgctl(struct thread *, struct msgctl_args *); | |
61 | int msgget(struct thread *, struct msgget_args *); | |
62 | int msgsnd(struct thread *, struct msgsnd_args *); | |
63 | int msgrcv(struct thread *, struct msgrcv_args *); | |
64 | ||
65 | static sy_call_t *msgcalls[] = { | |
66 | (sy_call_t *)msgctl, (sy_call_t *)msgget, | |
67 | (sy_call_t *)msgsnd, (sy_call_t *)msgrcv | |
68 | }; | |
69 | #endif /* __CYGWIN__ */ | |
70 | ||
71 | ||
72 | struct msg { | |
73 | struct msg *msg_next; /* next msg in the chain */ | |
74 | long msg_type; /* type of this message */ | |
75 | /* >0 -> type of this message */ | |
76 | /* 0 -> free header */ | |
77 | u_short msg_ts; /* size of this message */ | |
78 | short msg_spot; /* location of start of msg in buffer */ | |
79 | }; | |
80 | ||
81 | ||
82 | #ifndef MSGSSZ | |
83 | #define MSGSSZ 8 /* Each segment must be 2^N long */ | |
84 | #endif | |
85 | #ifndef MSGSEG | |
86 | #define MSGSEG 2048 /* must be less than 32767 */ | |
87 | #endif | |
88 | #define MSGMAX (MSGSSZ*MSGSEG) | |
89 | #ifndef MSGMNB | |
90 | #define MSGMNB 2048 /* max # of bytes in a queue */ | |
91 | #endif | |
92 | #ifndef MSGMNI | |
93 | #define MSGMNI 40 | |
94 | #endif | |
95 | #ifndef MSGTQL | |
96 | #define MSGTQL 40 | |
97 | #endif | |
98 | ||
99 | /* | |
100 | * Based on the configuration parameters described in an SVR2 (yes, two) | |
101 | * config(1m) man page. | |
102 | * | |
103 | * Each message is broken up and stored in segments that are msgssz bytes | |
104 | * long. For efficiency reasons, this should be a power of two. Also, | |
105 | * it doesn't make sense if it is less than 8 or greater than about 256. | |
106 | * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of | |
107 | * two between 8 and 1024 inclusive (and panic's if it isn't). | |
108 | */ | |
109 | struct msginfo msginfo = { | |
110 | MSGMAX, /* max chars in a message */ | |
111 | MSGMNB, /* max chars in a queue */ | |
112 | MSGMNI, /* # of message queue identifiers */ | |
113 | MSGTQL, /* max messages in system */ | |
114 | MSGSSZ, /* size of a message segment */ | |
115 | /* (must be small power of 2 greater than 4) */ | |
116 | MSGSEG /* number of message segments */ | |
117 | }; | |
118 | ||
119 | /* | |
120 | * macros to convert between msqid_ds's and msqid's. | |
121 | * (specific to this implementation) | |
122 | */ | |
123 | #define MSQID(ix,ds) ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000)) | |
124 | #define MSQID_IX(id) ((id) & 0xffff) | |
125 | #define MSQID_SEQ(id) (((id) >> 16) & 0xffff) | |
126 | ||
127 | /* | |
128 | * The rest of this file is specific to this particular implementation. | |
129 | */ | |
130 | ||
131 | struct msgmap { | |
132 | short next; /* next segment in buffer */ | |
133 | /* -1 -> available */ | |
134 | /* 0..(MSGSEG-1) -> index of next segment */ | |
135 | }; | |
136 | ||
137 | #define MSG_LOCKED 01000 /* Is this msqid_ds locked? */ | |
138 | ||
139 | static int nfree_msgmaps; /* # of free map entries */ | |
140 | static short free_msgmaps; /* head of linked list of free map entries */ | |
141 | static struct msg *free_msghdrs;/* list of free msg headers */ | |
142 | static char *msgpool; /* MSGMAX byte long msg buffer pool */ | |
143 | static struct msgmap *msgmaps; /* MSGSEG msgmap structures */ | |
144 | static struct msg *msghdrs; /* MSGTQL msg headers */ | |
145 | static struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ | |
146 | static struct mtx msq_mtx; /* global mutex for message queues. */ | |
147 | ||
148 | #ifdef __CYGWIN__ | |
149 | static struct msg_info msg_info; | |
150 | #endif /* __CYGWIN__ */ | |
151 | ||
152 | void | |
153 | msginit() | |
154 | { | |
155 | int i; | |
156 | ||
157 | TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg); | |
158 | TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz); | |
159 | msginfo.msgmax = msginfo.msgseg * msginfo.msgssz; | |
11377ba6 | 160 | TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb); |
282113ba | 161 | TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni); |
11377ba6 | 162 | TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql); |
282113ba CV |
163 | |
164 | msgpool = (char *) sys_malloc(msginfo.msgmax, M_MSG, M_WAITOK); | |
165 | if (msgpool == NULL) | |
166 | panic("msgpool is NULL"); | |
167 | msgmaps = (msgmap *) sys_malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK); | |
168 | if (msgmaps == NULL) | |
169 | panic("msgmaps is NULL"); | |
170 | msghdrs = (msg *) sys_malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK); | |
171 | if (msghdrs == NULL) | |
172 | panic("msghdrs is NULL"); | |
173 | msqids = (msqid_ds *) sys_malloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK); | |
174 | if (msqids == NULL) | |
175 | panic("msqids is NULL"); | |
176 | ||
177 | /* | |
178 | * msginfo.msgssz should be a power of two for efficiency reasons. | |
179 | * It is also pretty silly if msginfo.msgssz is less than 8 | |
180 | * or greater than about 256 so ... | |
181 | */ | |
182 | ||
183 | i = 8; | |
184 | while (i < 1024 && i != msginfo.msgssz) | |
185 | i <<= 1; | |
186 | if (i != msginfo.msgssz) { | |
187 | DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, | |
188 | msginfo.msgssz)); | |
189 | panic("msginfo.msgssz not a small power of 2"); | |
190 | } | |
191 | ||
192 | if (msginfo.msgseg > 32767) { | |
193 | DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg)); | |
194 | panic("msginfo.msgseg > 32767"); | |
195 | } | |
196 | ||
197 | if (msgmaps == NULL) | |
198 | panic("msgmaps is NULL"); | |
199 | ||
200 | for (i = 0; i < msginfo.msgseg; i++) { | |
201 | if (i > 0) | |
202 | msgmaps[i-1].next = i; | |
203 | msgmaps[i].next = -1; /* implies entry is available */ | |
204 | } | |
205 | free_msgmaps = 0; | |
206 | nfree_msgmaps = msginfo.msgseg; | |
207 | ||
208 | if (msghdrs == NULL) | |
209 | panic("msghdrs is NULL"); | |
210 | ||
211 | for (i = 0; i < msginfo.msgtql; i++) { | |
212 | msghdrs[i].msg_type = 0; | |
213 | if (i > 0) | |
214 | msghdrs[i-1].msg_next = &msghdrs[i]; | |
215 | msghdrs[i].msg_next = NULL; | |
216 | } | |
217 | free_msghdrs = &msghdrs[0]; | |
218 | ||
219 | if (msqids == NULL) | |
220 | panic("msqids is NULL"); | |
221 | ||
222 | for (i = 0; i < msginfo.msgmni; i++) { | |
223 | msqids[i].msg_qbytes = 0; /* implies entry is available */ | |
224 | msqids[i].msg_perm.seq = 0; /* reset to a known value */ | |
225 | msqids[i].msg_perm.mode = 0; | |
226 | } | |
227 | mtx_init(&msq_mtx, "msq", NULL, MTX_DEF); | |
228 | } | |
229 | ||
230 | int | |
231 | msgunload() | |
232 | { | |
233 | struct msqid_ds *msqptr; | |
234 | int msqid; | |
235 | ||
236 | for (msqid = 0; msqid < msginfo.msgmni; msqid++) { | |
237 | /* | |
238 | * Look for an unallocated and unlocked msqid_ds. | |
239 | * msqid_ds's can be locked by msgsnd or msgrcv while | |
240 | * they are copying the message in/out. We can't | |
241 | * re-use the entry until they release it. | |
242 | */ | |
243 | msqptr = &msqids[msqid]; | |
244 | if (msqptr->msg_qbytes != 0 || | |
245 | (msqptr->msg_perm.mode & MSG_LOCKED) != 0) | |
246 | break; | |
247 | } | |
248 | #ifndef __CYGWIN__ | |
249 | if (msqid != msginfo.msgmni) | |
250 | return (EBUSY); | |
251 | #endif /* __CYGWIN__ */ | |
252 | ||
253 | sys_free(msgpool, M_MSG); | |
254 | sys_free(msgmaps, M_MSG); | |
255 | sys_free(msghdrs, M_MSG); | |
256 | sys_free(msqids, M_MSG); | |
257 | mtx_destroy(&msq_mtx); | |
258 | return (0); | |
259 | } | |
260 | ||
261 | ||
262 | #ifndef __CYGWIN__ | |
263 | static int | |
264 | sysvmsg_modload(struct module *module, int cmd, void *arg) | |
265 | { | |
266 | int error = 0; | |
267 | ||
268 | switch (cmd) { | |
269 | case MOD_LOAD: | |
270 | msginit(); | |
271 | break; | |
272 | case MOD_UNLOAD: | |
273 | error = msgunload(); | |
274 | break; | |
275 | case MOD_SHUTDOWN: | |
276 | break; | |
277 | default: | |
278 | error = EINVAL; | |
279 | break; | |
280 | } | |
281 | return (error); | |
282 | } | |
283 | ||
284 | static moduledata_t sysvmsg_mod = { | |
285 | "sysvmsg", | |
286 | &sysvmsg_modload, | |
287 | NULL | |
288 | }; | |
289 | ||
290 | SYSCALL_MODULE_HELPER(msgsys); | |
291 | SYSCALL_MODULE_HELPER(msgctl); | |
292 | SYSCALL_MODULE_HELPER(msgget); | |
293 | SYSCALL_MODULE_HELPER(msgsnd); | |
294 | SYSCALL_MODULE_HELPER(msgrcv); | |
295 | ||
296 | DECLARE_MODULE(sysvmsg, sysvmsg_mod, | |
297 | SI_SUB_SYSV_MSG, SI_ORDER_FIRST); | |
298 | MODULE_VERSION(sysvmsg, 1); | |
299 | ||
300 | /* | |
301 | * Entry point for all MSG calls | |
302 | * | |
303 | * MPSAFE | |
304 | */ | |
305 | int | |
306 | msgsys(thread *td, struct msgsys_args *uap) | |
307 | { | |
308 | int error; | |
309 | ||
310 | if (!jail_sysvipc_allowed && jailed(td->td_ucred)) | |
311 | return (ENOSYS); | |
312 | if (uap->which < 0 || | |
313 | (unsigned) uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) | |
314 | return (EINVAL); | |
315 | error = (*msgcalls[uap->which])(td, &uap->a2); | |
316 | return (error); | |
317 | } | |
318 | #endif | |
319 | ||
320 | static void | |
321 | msg_freehdr(struct msg *msghdr) | |
322 | { | |
323 | while (msghdr->msg_ts > 0) { | |
324 | short next; | |
325 | if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) | |
326 | panic("msghdr->msg_spot out of range"); | |
327 | next = msgmaps[msghdr->msg_spot].next; | |
328 | msgmaps[msghdr->msg_spot].next = free_msgmaps; | |
329 | free_msgmaps = msghdr->msg_spot; | |
330 | nfree_msgmaps++; | |
331 | msghdr->msg_spot = next; | |
332 | if (msghdr->msg_ts >= msginfo.msgssz) | |
333 | msghdr->msg_ts -= msginfo.msgssz; | |
334 | else | |
335 | msghdr->msg_ts = 0; | |
336 | } | |
337 | if (msghdr->msg_spot != -1) | |
338 | panic("msghdr->msg_spot != -1"); | |
339 | msghdr->msg_next = free_msghdrs; | |
340 | free_msghdrs = msghdr; | |
341 | } | |
342 | ||
343 | #ifndef _SYS_SYSPROTO_H_ | |
344 | struct msgctl_args { | |
345 | int msqid; | |
346 | int cmd; | |
347 | struct msqid_ds *buf; | |
348 | }; | |
349 | #endif | |
350 | ||
351 | /* | |
352 | * MPSAFE | |
353 | */ | |
354 | int | |
355 | msgctl(struct thread *td, struct msgctl_args *uap) | |
356 | { | |
357 | int msqid = uap->msqid; | |
358 | int cmd = uap->cmd; | |
359 | struct msqid_ds *user_msqptr = uap->buf; | |
360 | int rval, error; | |
361 | struct msqid_ds msqbuf; | |
362 | register struct msqid_ds *msqptr; | |
363 | ||
364 | DPRINTF(("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr)); | |
365 | ||
366 | if (!jail_sysvipc_allowed && jailed(td->td_ucred)) | |
367 | return (ENOSYS); | |
368 | ||
369 | #ifdef __CYGWIN__ | |
370 | if (cmd == IPC_INFO) { | |
371 | if (!msqid) { | |
372 | error = copyout(&msginfo, user_msqptr, | |
373 | sizeof(struct msginfo)); | |
374 | td->td_retval[0] = error ? -1 : 0; | |
375 | return (error); | |
376 | } | |
377 | if (msqid > msginfo.msgmni) | |
378 | msqid = msginfo.msgmni; | |
379 | error = copyout(msqids, user_msqptr, | |
380 | msqid * sizeof(struct msqid_ds)); | |
381 | td->td_retval[0] = error ? -1 : 0; | |
382 | return (error); | |
383 | } else if (cmd == MSG_INFO) { | |
384 | mtx_lock(&msq_mtx); | |
385 | error = copyout(&msg_info, user_msqptr, | |
386 | sizeof(struct msg_info)); | |
387 | td->td_retval[0] = error ? -1 : 0; | |
388 | mtx_unlock(&msq_mtx); | |
389 | return (error); | |
390 | } | |
391 | #endif /* __CYGWIN__ */ | |
392 | msqid = IPCID_TO_IX(msqid); | |
393 | ||
394 | if (msqid < 0 || msqid >= msginfo.msgmni) { | |
395 | DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, | |
396 | msginfo.msgmni)); | |
397 | return (EINVAL); | |
398 | } | |
399 | if (cmd == IPC_SET && | |
400 | (error = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) | |
401 | return (error); | |
402 | ||
403 | msqptr = &msqids[msqid]; | |
404 | ||
405 | mtx_lock(&msq_mtx); | |
406 | if (msqptr->msg_qbytes == 0) { | |
407 | DPRINTF(("no such msqid\n")); | |
408 | error = EINVAL; | |
409 | goto done2; | |
410 | } | |
411 | if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { | |
412 | DPRINTF(("wrong sequence number\n")); | |
413 | error = EINVAL; | |
414 | goto done2; | |
415 | } | |
416 | ||
417 | error = 0; | |
418 | rval = 0; | |
419 | ||
420 | switch (cmd) { | |
421 | ||
422 | case IPC_RMID: | |
423 | { | |
424 | struct msg *msghdr; | |
425 | if ((error = ipcperm(td, &msqptr->msg_perm, IPC_M))) | |
426 | goto done2; | |
427 | /* Free the message headers */ | |
428 | msghdr = msqptr->msg_first; | |
429 | while (msghdr != NULL) { | |
430 | struct msg *msghdr_tmp; | |
431 | ||
432 | /* Free the segments of each message */ | |
433 | msqptr->msg_cbytes -= msghdr->msg_ts; | |
434 | msqptr->msg_qnum--; | |
435 | msghdr_tmp = msghdr; | |
436 | msghdr = msghdr->msg_next; | |
437 | msg_freehdr(msghdr_tmp); | |
438 | } | |
439 | ||
440 | if (msqptr->msg_cbytes != 0) | |
441 | panic("msg_cbytes is screwed up"); | |
442 | if (msqptr->msg_qnum != 0) | |
443 | panic("msg_qnum is screwed up"); | |
444 | ||
445 | msqptr->msg_qbytes = 0; /* Mark it as free */ | |
446 | #ifdef __CYGWIN__ | |
447 | msg_info.msg_ids--; | |
448 | #endif /* __CYGWIN__ */ | |
449 | ||
450 | wakeup(msqptr); | |
451 | } | |
452 | ||
453 | break; | |
454 | ||
455 | case IPC_SET: | |
456 | if ((error = ipcperm(td, &msqptr->msg_perm, IPC_M))) | |
457 | goto done2; | |
458 | if (msqbuf.msg_qbytes > msqptr->msg_qbytes) { | |
459 | error = suser(td); | |
460 | if (error) | |
461 | goto done2; | |
462 | } | |
463 | if (msqbuf.msg_qbytes > (unsigned long) msginfo.msgmnb) { | |
464 | DPRINTF(("can't increase msg_qbytes beyond %d" | |
465 | "(truncating)\n", msginfo.msgmnb)); | |
466 | msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ | |
467 | } | |
468 | if (msqbuf.msg_qbytes == 0) { | |
469 | DPRINTF(("can't reduce msg_qbytes to 0\n")); | |
470 | error = EINVAL; /* non-standard errno! */ | |
471 | goto done2; | |
472 | } | |
473 | msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ | |
474 | msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ | |
475 | msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | | |
476 | (msqbuf.msg_perm.mode & 0777); | |
477 | msqptr->msg_qbytes = msqbuf.msg_qbytes; | |
478 | msqptr->msg_ctime = time (NULL); | |
479 | break; | |
480 | ||
481 | case IPC_STAT: | |
482 | if ((error = ipcperm(td, &msqptr->msg_perm, IPC_R))) { | |
483 | DPRINTF(("requester doesn't have read access\n")); | |
484 | goto done2; | |
485 | } | |
486 | break; | |
487 | ||
488 | default: | |
489 | DPRINTF(("invalid command %d\n", cmd)); | |
490 | error = EINVAL; | |
491 | goto done2; | |
492 | } | |
493 | ||
494 | if (error == 0) | |
495 | td->td_retval[0] = rval; | |
496 | done2: | |
497 | mtx_unlock(&msq_mtx); | |
498 | if (cmd == IPC_STAT && error == 0) | |
499 | error = copyout(msqptr, user_msqptr, sizeof(struct msqid_ds)); | |
500 | return(error); | |
501 | } | |
502 | ||
503 | #ifndef _SYS_SYSPROTO_H_ | |
504 | struct msgget_args { | |
505 | key_t key; | |
506 | int msgflg; | |
507 | }; | |
508 | #endif | |
509 | ||
510 | /* | |
511 | * MPSAFE | |
512 | */ | |
513 | int | |
514 | msgget(struct thread *td, struct msgget_args *uap) | |
515 | { | |
516 | int msqid, error = 0; | |
517 | key_t key = uap->key; | |
518 | unsigned msgflg = uap->msgflg; | |
519 | register struct msqid_ds *msqptr = NULL; | |
520 | ||
521 | DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg)); | |
522 | ||
523 | if (!jail_sysvipc_allowed && jailed(td->td_ucred)) | |
524 | return (ENOSYS); | |
525 | ||
526 | mtx_lock(&msq_mtx); | |
527 | if (key != IPC_PRIVATE) { | |
528 | for (msqid = 0; msqid < msginfo.msgmni; msqid++) { | |
529 | msqptr = &msqids[msqid]; | |
530 | if (msqptr->msg_qbytes != 0 && | |
531 | msqptr->msg_perm.key == key) | |
532 | break; | |
533 | } | |
534 | if (msqid < msginfo.msgmni) { | |
535 | DPRINTF(("found public key\n")); | |
536 | if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { | |
537 | DPRINTF(("not exclusive\n")); | |
538 | error = EEXIST; | |
539 | goto done2; | |
540 | } | |
541 | if ((error = ipcperm(td, &msqptr->msg_perm, msgflg & 0700))) { | |
542 | DPRINTF(("requester doesn't have 0%o access\n", | |
543 | msgflg & 0700)); | |
544 | goto done2; | |
545 | } | |
546 | goto found; | |
547 | } | |
548 | } | |
549 | ||
550 | DPRINTF(("need to allocate the msqid_ds\n")); | |
551 | if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { | |
552 | for (msqid = 0; msqid < msginfo.msgmni; msqid++) { | |
553 | /* | |
554 | * Look for an unallocated and unlocked msqid_ds. | |
555 | * msqid_ds's can be locked by msgsnd or msgrcv while | |
556 | * they are copying the message in/out. We can't | |
557 | * re-use the entry until they release it. | |
558 | */ | |
559 | msqptr = &msqids[msqid]; | |
560 | if (msqptr->msg_qbytes == 0 && | |
561 | (msqptr->msg_perm.mode & MSG_LOCKED) == 0) | |
562 | break; | |
563 | } | |
564 | if (msqid == msginfo.msgmni) { | |
565 | DPRINTF(("no more msqid_ds's available\n")); | |
566 | error = ENOSPC; | |
567 | goto done2; | |
568 | } | |
569 | DPRINTF(("msqid %d is available\n", msqid)); | |
570 | msqptr->msg_perm.key = key; | |
571 | #ifdef __CYGWIN__ | |
572 | msqptr->msg_perm.cuid = td->ipcblk->uid; | |
573 | msqptr->msg_perm.uid = td->ipcblk->uid; | |
574 | msqptr->msg_perm.cgid = td->ipcblk->gid; | |
575 | msqptr->msg_perm.gid = td->ipcblk->gid; | |
576 | #else | |
577 | msqptr->msg_perm.cuid = cred->cr_uid; | |
578 | msqptr->msg_perm.uid = cred->cr_uid; | |
579 | msqptr->msg_perm.cgid = cred->cr_gid; | |
580 | msqptr->msg_perm.gid = cred->cr_gid; | |
581 | #endif /* __CYGWIN__ */ | |
582 | msqptr->msg_perm.mode = (msgflg & 0777); | |
583 | /* Make sure that the returned msqid is unique */ | |
584 | msqptr->msg_perm.seq = (msqptr->msg_perm.seq + 1) & 0x7fff; | |
585 | msqptr->msg_first = NULL; | |
586 | msqptr->msg_last = NULL; | |
587 | msqptr->msg_cbytes = 0; | |
588 | msqptr->msg_qnum = 0; | |
589 | msqptr->msg_qbytes = msginfo.msgmnb; | |
590 | msqptr->msg_lspid = 0; | |
591 | msqptr->msg_lrpid = 0; | |
592 | msqptr->msg_stime = 0; | |
593 | msqptr->msg_rtime = 0; | |
594 | msqptr->msg_ctime = time (NULL); | |
595 | #ifdef __CYGWIN__ | |
596 | msg_info.msg_ids++; | |
597 | #endif /* __CYGWIN__ */ | |
598 | } else { | |
599 | DPRINTF(("didn't find it and wasn't asked to create it\n")); | |
600 | error = ENOENT; | |
601 | goto done2; | |
602 | } | |
603 | ||
604 | found: | |
605 | /* Construct the unique msqid */ | |
606 | td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); | |
607 | done2: | |
608 | mtx_unlock(&msq_mtx); | |
609 | return (error); | |
610 | } | |
611 | ||
612 | #ifndef _SYS_SYSPROTO_H_ | |
613 | struct msgsnd_args { | |
614 | int msqid; | |
615 | const void *msgp; | |
616 | size_t msgsz; | |
617 | int msgflg; | |
618 | }; | |
619 | #endif | |
620 | ||
621 | /* | |
622 | * MPSAFE | |
623 | */ | |
624 | int | |
625 | msgsnd(struct thread *td, struct msgsnd_args *uap) | |
626 | { | |
627 | int msqid = uap->msqid; | |
628 | const void *user_msgp = uap->msgp; | |
629 | size_t msgsz = uap->msgsz; | |
630 | int msgflg = uap->msgflg; | |
631 | int segs_needed, error = 0; | |
632 | register struct msqid_ds *msqptr; | |
633 | register struct msg *msghdr; | |
634 | short next; | |
635 | ||
636 | DPRINTF(("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, | |
637 | msgflg)); | |
638 | ||
639 | if (!jail_sysvipc_allowed && jailed(td->td_ucred)) | |
640 | return (ENOSYS); | |
641 | ||
642 | mtx_lock(&msq_mtx); | |
643 | msqid = IPCID_TO_IX(msqid); | |
644 | ||
645 | if (msqid < 0 || msqid >= msginfo.msgmni) { | |
646 | DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, | |
647 | msginfo.msgmni)); | |
648 | error = EINVAL; | |
649 | goto done2; | |
650 | } | |
651 | ||
652 | msqptr = &msqids[msqid]; | |
653 | if (msqptr->msg_qbytes == 0) { | |
654 | DPRINTF(("no such message queue id\n")); | |
655 | error = EINVAL; | |
656 | goto done2; | |
657 | } | |
658 | if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { | |
659 | DPRINTF(("wrong sequence number\n")); | |
660 | error = EINVAL; | |
661 | goto done2; | |
662 | } | |
663 | ||
664 | if ((error = ipcperm(td, &msqptr->msg_perm, IPC_W))) { | |
665 | DPRINTF(("requester doesn't have write access\n")); | |
666 | goto done2; | |
667 | } | |
668 | ||
669 | segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; | |
670 | DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, | |
671 | segs_needed)); | |
672 | for (;;) { | |
673 | int need_more_resources = 0; | |
674 | ||
675 | /* | |
676 | * check msgsz | |
677 | * (inside this loop in case msg_qbytes changes while we sleep) | |
678 | */ | |
679 | ||
680 | if (msgsz > msqptr->msg_qbytes) { | |
681 | DPRINTF(("msgsz > msqptr->msg_qbytes\n")); | |
682 | error = EINVAL; | |
683 | goto done2; | |
684 | } | |
685 | ||
686 | if (msqptr->msg_perm.mode & MSG_LOCKED) { | |
687 | DPRINTF(("msqid is locked\n")); | |
688 | need_more_resources = 1; | |
689 | } | |
690 | if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { | |
691 | DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); | |
692 | need_more_resources = 1; | |
693 | } | |
694 | if (segs_needed > nfree_msgmaps) { | |
695 | DPRINTF(("segs_needed > nfree_msgmaps\n")); | |
696 | need_more_resources = 1; | |
697 | } | |
698 | if (free_msghdrs == NULL) { | |
699 | DPRINTF(("no more msghdrs\n")); | |
700 | need_more_resources = 1; | |
701 | } | |
702 | ||
703 | if (need_more_resources) { | |
704 | int we_own_it; | |
705 | ||
706 | if ((msgflg & IPC_NOWAIT) != 0) { | |
707 | DPRINTF(("need more resources but caller " | |
708 | "doesn't want to wait\n")); | |
709 | error = EAGAIN; | |
710 | goto done2; | |
711 | } | |
712 | ||
713 | if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { | |
714 | DPRINTF(("we don't own the msqid_ds\n")); | |
715 | we_own_it = 0; | |
716 | } else { | |
717 | /* Force later arrivals to wait for our | |
718 | request */ | |
719 | DPRINTF(("we own the msqid_ds\n")); | |
720 | msqptr->msg_perm.mode |= MSG_LOCKED; | |
721 | we_own_it = 1; | |
722 | } | |
723 | DPRINTF(("goodnight\n")); | |
724 | error = msleep(msqptr, &msq_mtx, (PZERO - 4) | PCATCH, | |
cce741c4 | 725 | "msgsnd", 50); |
282113ba CV |
726 | DPRINTF(("good morning, error=%d\n", error)); |
727 | if (we_own_it) | |
728 | msqptr->msg_perm.mode &= ~MSG_LOCKED; | |
cce741c4 CV |
729 | if (error == EWOULDBLOCK) { |
730 | DPRINTF(("timed out\n")); | |
731 | continue; | |
732 | } | |
282113ba CV |
733 | if (error != 0) { |
734 | DPRINTF(("msgsnd: interrupted system call\n")); | |
735 | #ifdef __CYGWIN__ | |
6699e21f PF |
736 | if (error == EIDRM) |
737 | goto done2; | |
282113ba CV |
738 | #endif /* __CYGWIN__ */ |
739 | error = EINTR; | |
740 | goto done2; | |
741 | } | |
742 | ||
743 | /* | |
744 | * Make sure that the msq queue still exists | |
745 | */ | |
746 | ||
747 | if (msqptr->msg_qbytes == 0) { | |
748 | DPRINTF(("msqid deleted\n")); | |
749 | error = EIDRM; | |
750 | goto done2; | |
751 | } | |
752 | ||
753 | } else { | |
754 | DPRINTF(("got all the resources that we need\n")); | |
755 | break; | |
756 | } | |
757 | } | |
758 | ||
759 | /* | |
760 | * We have the resources that we need. | |
761 | * Make sure! | |
762 | */ | |
763 | ||
764 | if (msqptr->msg_perm.mode & MSG_LOCKED) | |
765 | panic("msg_perm.mode & MSG_LOCKED"); | |
766 | if (segs_needed > nfree_msgmaps) | |
767 | panic("segs_needed > nfree_msgmaps"); | |
768 | if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) | |
769 | panic("msgsz + msg_cbytes > msg_qbytes"); | |
770 | if (free_msghdrs == NULL) | |
771 | panic("no more msghdrs"); | |
772 | ||
773 | /* | |
774 | * Re-lock the msqid_ds in case we page-fault when copying in the | |
775 | * message | |
776 | */ | |
777 | ||
778 | if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) | |
779 | panic("msqid_ds is already locked"); | |
780 | msqptr->msg_perm.mode |= MSG_LOCKED; | |
781 | ||
782 | /* | |
783 | * Allocate a message header | |
784 | */ | |
785 | ||
786 | msghdr = free_msghdrs; | |
787 | free_msghdrs = msghdr->msg_next; | |
788 | msghdr->msg_spot = -1; | |
789 | msghdr->msg_ts = msgsz; | |
790 | ||
791 | /* | |
792 | * Allocate space for the message | |
793 | */ | |
794 | ||
795 | while (segs_needed > 0) { | |
796 | if (nfree_msgmaps <= 0) | |
797 | panic("not enough msgmaps"); | |
798 | if (free_msgmaps == -1) | |
799 | panic("nil free_msgmaps"); | |
800 | next = free_msgmaps; | |
801 | if (next <= -1) | |
802 | panic("next too low #1"); | |
803 | if (next >= msginfo.msgseg) | |
804 | panic("next out of range #1"); | |
805 | DPRINTF(("allocating segment %d to message\n", next)); | |
806 | free_msgmaps = msgmaps[next].next; | |
807 | nfree_msgmaps--; | |
808 | msgmaps[next].next = msghdr->msg_spot; | |
809 | msghdr->msg_spot = next; | |
810 | segs_needed--; | |
811 | } | |
812 | ||
813 | /* | |
814 | * Copy in the message type | |
815 | */ | |
816 | ||
817 | mtx_unlock(&msq_mtx); | |
818 | if ((error = copyin(user_msgp, &msghdr->msg_type, | |
819 | sizeof(msghdr->msg_type))) != 0) { | |
820 | mtx_lock(&msq_mtx); | |
821 | DPRINTF(("error %d copying the message type\n", error)); | |
822 | msg_freehdr(msghdr); | |
823 | msqptr->msg_perm.mode &= ~MSG_LOCKED; | |
824 | wakeup(msqptr); | |
825 | goto done2; | |
826 | } | |
827 | mtx_lock(&msq_mtx); | |
828 | user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type); | |
829 | ||
830 | /* | |
831 | * Validate the message type | |
832 | */ | |
833 | ||
834 | if (msghdr->msg_type < 1) { | |
835 | msg_freehdr(msghdr); | |
836 | msqptr->msg_perm.mode &= ~MSG_LOCKED; | |
837 | wakeup(msqptr); | |
838 | DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type)); | |
839 | error = EINVAL; | |
840 | goto done2; | |
841 | } | |
842 | ||
843 | /* | |
844 | * Copy in the message body | |
845 | */ | |
846 | ||
847 | next = msghdr->msg_spot; | |
848 | while (msgsz > 0) { | |
849 | size_t tlen; | |
850 | if (msgsz > (unsigned long) msginfo.msgssz) | |
851 | tlen = msginfo.msgssz; | |
852 | else | |
853 | tlen = msgsz; | |
854 | if (next <= -1) | |
855 | panic("next too low #2"); | |
856 | if (next >= msginfo.msgseg) | |
857 | panic("next out of range #2"); | |
858 | mtx_unlock(&msq_mtx); | |
859 | if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], | |
860 | tlen)) != 0) { | |
861 | mtx_lock(&msq_mtx); | |
862 | DPRINTF(("error %d copying in message segment\n", | |
863 | error)); | |
864 | msg_freehdr(msghdr); | |
865 | msqptr->msg_perm.mode &= ~MSG_LOCKED; | |
866 | wakeup(msqptr); | |
867 | goto done2; | |
868 | } | |
869 | mtx_lock(&msq_mtx); | |
870 | msgsz -= tlen; | |
871 | user_msgp = (const char *)user_msgp + tlen; | |
872 | next = msgmaps[next].next; | |
873 | } | |
874 | if (next != -1) | |
875 | panic("didn't use all the msg segments"); | |
876 | ||
877 | /* | |
878 | * We've got the message. Unlock the msqid_ds. | |
879 | */ | |
880 | ||
881 | msqptr->msg_perm.mode &= ~MSG_LOCKED; | |
882 | ||
883 | /* | |
884 | * Make sure that the msqid_ds is still allocated. | |
885 | */ | |
886 | ||
887 | if (msqptr->msg_qbytes == 0) { | |
888 | msg_freehdr(msghdr); | |
889 | wakeup(msqptr); | |
890 | error = EIDRM; | |
891 | goto done2; | |
892 | } | |
893 | ||
894 | /* | |
895 | * Put the message into the queue | |
896 | */ | |
897 | ||
898 | if (msqptr->msg_first == NULL) { | |
899 | msqptr->msg_first = msghdr; | |
900 | msqptr->msg_last = msghdr; | |
901 | } else { | |
902 | msqptr->msg_last->msg_next = msghdr; | |
903 | msqptr->msg_last = msghdr; | |
904 | } | |
905 | msqptr->msg_last->msg_next = NULL; | |
906 | ||
907 | msqptr->msg_cbytes += msghdr->msg_ts; | |
908 | msqptr->msg_qnum++; | |
909 | msqptr->msg_lspid = td->td_proc->p_pid; | |
910 | msqptr->msg_stime = time (NULL); | |
911 | ||
912 | #ifdef __CYGWIN__ | |
913 | msg_info.msg_num++; | |
914 | msg_info.msg_tot += uap->msgsz; | |
915 | #endif /* __CYGWIN__ */ | |
916 | ||
917 | wakeup(msqptr); | |
918 | td->td_retval[0] = 0; | |
919 | done2: | |
920 | mtx_unlock(&msq_mtx); | |
921 | return (error); | |
922 | } | |
923 | ||
924 | #ifndef _SYS_SYSPROTO_H_ | |
925 | struct msgrcv_args { | |
926 | int msqid; | |
927 | void *msgp; | |
928 | size_t msgsz; | |
929 | long msgtyp; | |
930 | int msgflg; | |
931 | }; | |
932 | #endif | |
933 | ||
934 | /* | |
935 | * MPSAFE | |
936 | */ | |
937 | int | |
938 | msgrcv(struct thread *td, struct msgrcv_args *uap) | |
939 | { | |
940 | int msqid = uap->msqid; | |
941 | void *user_msgp = uap->msgp; | |
942 | size_t msgsz = uap->msgsz; | |
943 | long msgtyp = uap->msgtyp; | |
944 | int msgflg = uap->msgflg; | |
945 | size_t len; | |
946 | register struct msqid_ds *msqptr; | |
947 | register struct msg *msghdr; | |
948 | int error = 0; | |
949 | short next; | |
950 | ||
951 | DPRINTF(("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, | |
952 | msgsz, msgtyp, msgflg)); | |
953 | ||
954 | if (!jail_sysvipc_allowed && jailed(td->td_ucred)) | |
955 | return (ENOSYS); | |
956 | ||
957 | msqid = IPCID_TO_IX(msqid); | |
958 | ||
959 | if (msqid < 0 || msqid >= msginfo.msgmni) { | |
960 | DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, | |
961 | msginfo.msgmni)); | |
962 | return (EINVAL); | |
963 | } | |
964 | ||
965 | msqptr = &msqids[msqid]; | |
966 | mtx_lock(&msq_mtx); | |
967 | if (msqptr->msg_qbytes == 0) { | |
968 | DPRINTF(("no such message queue id\n")); | |
969 | error = EINVAL; | |
970 | goto done2; | |
971 | } | |
972 | if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { | |
973 | DPRINTF(("wrong sequence number\n")); | |
974 | error = EINVAL; | |
975 | goto done2; | |
976 | } | |
977 | ||
978 | if ((error = ipcperm(td, &msqptr->msg_perm, IPC_R))) { | |
979 | DPRINTF(("requester doesn't have read access\n")); | |
980 | goto done2; | |
981 | } | |
982 | ||
983 | msghdr = NULL; | |
984 | while (msghdr == NULL) { | |
985 | if (msgtyp == 0) { | |
986 | msghdr = msqptr->msg_first; | |
987 | if (msghdr != NULL) { | |
988 | if (msgsz < msghdr->msg_ts && | |
989 | (msgflg & MSG_NOERROR) == 0) { | |
990 | DPRINTF(("first message on the queue " | |
991 | "is too big (want %d, got %d)\n", | |
992 | msgsz, msghdr->msg_ts)); | |
993 | error = E2BIG; | |
994 | goto done2; | |
995 | } | |
996 | if (msqptr->msg_first == msqptr->msg_last) { | |
997 | msqptr->msg_first = NULL; | |
998 | msqptr->msg_last = NULL; | |
999 | } else { | |
1000 | msqptr->msg_first = msghdr->msg_next; | |
1001 | if (msqptr->msg_first == NULL) | |
1002 | panic("msg_first/last screwed up #1"); | |
1003 | } | |
1004 | } | |
1005 | } else { | |
1006 | struct msg *previous; | |
1007 | struct msg **prev; | |
1008 | ||
1009 | previous = NULL; | |
1010 | prev = &(msqptr->msg_first); | |
1011 | while ((msghdr = *prev) != NULL) { | |
1012 | /* | |
1013 | * Is this message's type an exact match or is | |
1014 | * this message's type less than or equal to | |
1015 | * the absolute value of a negative msgtyp? | |
1016 | * Note that the second half of this test can | |
1017 | * NEVER be true if msgtyp is positive since | |
1018 | * msg_type is always positive! | |
1019 | */ | |
1020 | ||
1021 | if (msgtyp == msghdr->msg_type || | |
1022 | msghdr->msg_type <= -msgtyp) { | |
1023 | DPRINTF(("found message type %d, " | |
1024 | "requested %d\n", | |
1025 | msghdr->msg_type, msgtyp)); | |
1026 | if (msgsz < msghdr->msg_ts && | |
1027 | (msgflg & MSG_NOERROR) == 0) { | |
1028 | DPRINTF(("requested message " | |
1029 | "on the queue is too big " | |
1030 | "(want %d, got %d)\n", | |
1031 | msgsz, msghdr->msg_ts)); | |
1032 | error = E2BIG; | |
1033 | goto done2; | |
1034 | } | |
1035 | *prev = msghdr->msg_next; | |
1036 | if (msghdr == msqptr->msg_last) { | |
1037 | if (previous == NULL) { | |
1038 | if (prev != | |
1039 | &msqptr->msg_first) | |
1040 | panic("msg_first/last screwed up #2"); | |
1041 | msqptr->msg_first = | |
1042 | NULL; | |
1043 | msqptr->msg_last = | |
1044 | NULL; | |
1045 | } else { | |
1046 | if (prev == | |
1047 | &msqptr->msg_first) | |
1048 | panic("msg_first/last screwed up #3"); | |
1049 | msqptr->msg_last = | |
1050 | previous; | |
1051 | } | |
1052 | } | |
1053 | break; | |
1054 | } | |
1055 | previous = msghdr; | |
1056 | prev = &(msghdr->msg_next); | |
1057 | } | |
1058 | } | |
1059 | ||
1060 | /* | |
1061 | * We've either extracted the msghdr for the appropriate | |
1062 | * message or there isn't one. | |
1063 | * If there is one then bail out of this loop. | |
1064 | */ | |
1065 | ||
1066 | if (msghdr != NULL) | |
1067 | break; | |
1068 | ||
1069 | /* | |
1070 | * Hmph! No message found. Does the user want to wait? | |
1071 | */ | |
1072 | ||
1073 | if ((msgflg & IPC_NOWAIT) != 0) { | |
1074 | DPRINTF(("no appropriate message found (msgtyp=%d)\n", | |
1075 | msgtyp)); | |
1076 | /* The SVID says to return ENOMSG. */ | |
1077 | error = ENOMSG; | |
1078 | goto done2; | |
1079 | } | |
1080 | ||
1081 | /* | |
1082 | * Wait for something to happen | |
1083 | */ | |
1084 | ||
1085 | DPRINTF(("msgrcv: goodnight\n")); | |
1086 | error = msleep(msqptr, &msq_mtx, (PZERO - 4) | PCATCH, | |
cce741c4 | 1087 | "msgrcv", 0); |
282113ba CV |
1088 | DPRINTF(("msgrcv: good morning (error=%d)\n", error)); |
1089 | ||
1090 | if (error != 0) { | |
cce741c4 | 1091 | DPRINTF(("msgrcv: interrupted system call\n")); |
282113ba | 1092 | #ifdef __CYGWIN__ |
6699e21f PF |
1093 | if (error == EIDRM) |
1094 | goto done2; | |
282113ba CV |
1095 | #endif /* __CYGWIN__ */ |
1096 | error = EINTR; | |
1097 | goto done2; | |
1098 | } | |
1099 | ||
1100 | /* | |
1101 | * Make sure that the msq queue still exists | |
1102 | */ | |
1103 | ||
1104 | if (msqptr->msg_qbytes == 0 || | |
1105 | msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { | |
1106 | DPRINTF(("msqid deleted\n")); | |
1107 | error = EIDRM; | |
1108 | goto done2; | |
1109 | } | |
1110 | } | |
1111 | ||
1112 | /* | |
1113 | * Return the message to the user. | |
1114 | * | |
1115 | * First, do the bookkeeping (before we risk being interrupted). | |
1116 | */ | |
1117 | ||
1118 | msqptr->msg_cbytes -= msghdr->msg_ts; | |
1119 | msqptr->msg_qnum--; | |
1120 | msqptr->msg_lrpid = td->td_proc->p_pid; | |
1121 | msqptr->msg_rtime = time (NULL); | |
1122 | ||
1123 | /* | |
1124 | * Make msgsz the actual amount that we'll be returning. | |
1125 | * Note that this effectively truncates the message if it is too long | |
1126 | * (since msgsz is never increased). | |
1127 | */ | |
1128 | ||
1129 | DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz, | |
1130 | msghdr->msg_ts)); | |
1131 | if (msgsz > msghdr->msg_ts) | |
1132 | msgsz = msghdr->msg_ts; | |
1133 | ||
1134 | /* | |
1135 | * Return the type to the user. | |
1136 | */ | |
1137 | ||
1138 | mtx_unlock(&msq_mtx); | |
1139 | error = copyout(&(msghdr->msg_type), user_msgp, | |
1140 | sizeof(msghdr->msg_type)); | |
1141 | mtx_lock(&msq_mtx); | |
1142 | if (error != 0) { | |
1143 | DPRINTF(("error (%d) copying out message type\n", error)); | |
1144 | msg_freehdr(msghdr); | |
1145 | wakeup(msqptr); | |
1146 | goto done2; | |
1147 | } | |
1148 | user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); | |
1149 | ||
1150 | /* | |
1151 | * Return the segments to the user | |
1152 | */ | |
1153 | ||
1154 | next = msghdr->msg_spot; | |
1155 | for (len = 0; len < msgsz; len += msginfo.msgssz) { | |
1156 | size_t tlen; | |
1157 | ||
1158 | if (msgsz - len > (unsigned long) msginfo.msgssz) | |
1159 | tlen = msginfo.msgssz; | |
1160 | else | |
1161 | tlen = msgsz - len; | |
1162 | if (next <= -1) | |
1163 | panic("next too low #3"); | |
1164 | if (next >= msginfo.msgseg) | |
1165 | panic("next out of range #3"); | |
1166 | mtx_unlock(&msq_mtx); | |
1167 | error = copyout(&msgpool[next * msginfo.msgssz], | |
1168 | user_msgp, tlen); | |
1169 | mtx_lock(&msq_mtx); | |
1170 | if (error != 0) { | |
1171 | DPRINTF(("error (%d) copying out message segment\n", | |
1172 | error)); | |
1173 | msg_freehdr(msghdr); | |
1174 | wakeup(msqptr); | |
1175 | goto done2; | |
1176 | } | |
1177 | user_msgp = (char *)user_msgp + tlen; | |
1178 | next = msgmaps[next].next; | |
1179 | } | |
1180 | ||
1181 | /* | |
1182 | * Done, return the actual number of bytes copied out. | |
1183 | */ | |
1184 | ||
1185 | #ifdef __CYGWIN__ | |
1186 | msg_info.msg_num--; | |
1187 | msg_info.msg_tot -= msgsz; | |
1188 | #endif /* __CYGWIN__ */ | |
1189 | ||
1190 | msg_freehdr(msghdr); | |
1191 | wakeup(msqptr); | |
1192 | td->td_retval[0] = msgsz; | |
1193 | done2: | |
1194 | mtx_unlock(&msq_mtx); | |
1195 | return (error); | |
1196 | } | |
1197 | ||
1198 | #ifndef __CYGWIN__ | |
1199 | static int | |
1200 | sysctl_msqids(SYSCTL_HANDLER_ARGS) | |
1201 | { | |
1202 | ||
1203 | return (SYSCTL_OUT(req, msqids, | |
1204 | sizeof(struct msqid_ds) * msginfo.msgmni)); | |
1205 | } | |
1206 | ||
1207 | SYSCTL_DECL(_kern_ipc); | |
1208 | SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, ""); | |
1209 | SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0, ""); | |
1210 | SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RD, &msginfo.msgmnb, 0, ""); | |
1211 | SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RD, &msginfo.msgtql, 0, ""); | |
1212 | SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0, ""); | |
1213 | SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0, ""); | |
1214 | SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD, | |
1215 | NULL, 0, sysctl_msqids, "", "Message queue IDs"); | |
1216 | #endif /* __CYGWIN__ */ | |
1217 | #endif /* __OUTSIDE_CYGWIN__ */ |