* Implementation of SVID messages
* Copyright 1993 Daniel Boulet and RTMX Inc.
* This system call was implemented by Daniel Boulet under contract from RTMX.
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
* This software is provided ``AS IS'' without any warranties of any kind.
static int msgctl(), msgget(), msgsnd(), msgrcv();
int (*msgcalls
[])() = { msgctl
, msgget
, msgsnd
, msgrcv
};
int nfree_msgmaps
; /* # of free map entries */
short free_msgmaps
; /* head of linked list of free map entries */
struct msg
*free_msghdrs
; /* list of free msg headers */
vm_offset_t whocares1
, whocares2
;
* msginfo.msgssz should be a power of two for efficiency reasons.
* It is also pretty silly if msginfo.msgssz is less than 8
* or greater than about 256 so ...
while ( i
< 1024 && i
!= msginfo
.msgssz
) {
if ( i
!= msginfo
.msgssz
) {
printf("msginfo.msgssz=%d (0x%x)\n",msginfo
.msgssz
,msginfo
.msgssz
);
panic("msginfo.msgssz not a small power of 2");
if ( msginfo
.msgseg
> 32767 ) {
printf("msginfo.msgseg=%d\n",msginfo
.msgseg
);
panic("msginfo.msgseg > 32767");
panic("msgmaps is NULL");
for ( i
= 0; i
< msginfo
.msgseg
; i
+= 1 ) {
msgmaps
[i
].next
= -1; /* implies entry is available */
nfree_msgmaps
= msginfo
.msgseg
;
panic("msghdrs is NULL");
for ( i
= 0; i
< msginfo
.msgtql
; i
+= 1 ) {
msghdrs
[i
-1].msg_next
= &msghdrs
[i
];
msghdrs
[i
].msg_next
= NULL
;
free_msghdrs
= &msghdrs
[0];
for ( i
= 0; i
< msginfo
.msgmni
; i
+= 1 ) {
msqids
[i
].msg_qbytes
= 0; /* implies entry is available */
msqids
[i
].msg_perm
.seq
= 0; /* reset to a known value */
TEXT_SET(pseudo_set
, msginit
);
* Entry point for all MSG calls
if (uap
->which
>= sizeof(msgcalls
)/sizeof(msgcalls
[0]))
return ((*msgcalls
[uap
->which
])(p
, &uap
[1], retval
));
while ( msghdr
->msg_ts
> 0 ) {
if ( msghdr
->msg_spot
< 0 || msghdr
->msg_spot
>= msginfo
.msgseg
) {
panic("msghdr->msg_spot out of range");
next
= msgmaps
[msghdr
->msg_spot
].next
;
msgmaps
[msghdr
->msg_spot
].next
= free_msgmaps
;
free_msgmaps
= msghdr
->msg_spot
;
if ( msghdr
->msg_ts
>= msginfo
.msgssz
) {
msghdr
->msg_ts
-= msginfo
.msgssz
;
if ( msghdr
->msg_spot
!= -1 ) {
panic("msghdr->msg_spot != -1");
msghdr
->msg_next
= free_msghdrs
;
struct msqid_ds
*user_msqptr
;
register struct msgctl_args
*uap
;
struct msqid_ds
*user_msqptr
= uap
->user_msqptr
;
struct ucred
*cred
= p
->p_ucred
;
register struct msqid_ds
*msqptr
;
printf("call to msgctl(%d,%d,0x%x)\n",msqid
,cmd
,user_msqptr
);
msqid
= IPCID_TO_IX(msqid
);
if ( msqid
< 0 || msqid
>= msginfo
.msgmni
) {
printf("msqid (%d) out of range (0<=msqid<%d)\n",msqid
,msginfo
.msgmni
);
if ( msqptr
->msg_qbytes
== 0 ) {
printf("no such msqid\n");
if ( msqptr
->msg_perm
.seq
!= IPCID_TO_SEQ(uap
->msqid
) ) {
printf("wrong sequence number\n");
&& msqptr
->msg_perm
.cuid
!= cred
->cr_uid
&& msqptr
->msg_perm
.uid
!= cred
->cr_uid
) {
msghdr
= msqptr
->msg_first
;
/* Free the message headers */
while ( msghdr
!= NULL
) {
/* Free the segments of each message */
msqptr
->msg_cbytes
-= msghdr
->msg_ts
;
msghdr
= msghdr
->msg_next
;
if ( msqptr
->msg_cbytes
!= 0 ) {
panic("msg_cbytes is screwed up");
if ( msqptr
->msg_qnum
!= 0 ) {
panic("msg_qnum is screwed up");
msqptr
->msg_qbytes
= 0; /* Mark it as free */
/* Make sure that anybody who is waiting notices the deletion */
wakeup( (caddr_t
)msqptr
);
&& msqptr
->msg_perm
.cuid
!= cred
->cr_uid
&& msqptr
->msg_perm
.uid
!= cred
->cr_uid
) {
if ( (eval
= copyin(user_msqptr
, &msqbuf
, sizeof(msqbuf
))) != 0 ) {
if ( msqbuf
.msg_qbytes
> msqptr
->msg_qbytes
if ( msqbuf
.msg_qbytes
> msginfo
.msgmnb
) {
printf("can't increase msg_qbytes beyond %d (truncating)\n",msginfo
.msgmnb
);
msqbuf
.msg_qbytes
= msginfo
.msgmnb
; /* silently restrict qbytes to system limit */
if ( msqbuf
.msg_qbytes
== 0 ) {
printf("can't reduce msg_qbytes to 0\n");
return(EINVAL
); /* non-standard errno! */
msqptr
->msg_perm
.uid
= msqbuf
.msg_perm
.uid
; /* change the owner */
msqptr
->msg_perm
.gid
= msqbuf
.msg_perm
.gid
; /* change the owner */
msqptr
->msg_perm
.mode
= (msqptr
->msg_perm
.mode
& ~0777)
| (msqbuf
.msg_perm
.mode
& 0777);
msqptr
->msg_qbytes
= msqbuf
.msg_qbytes
;
msqptr
->msg_ctime
= time
.tv_sec
;
if ( (eval
= ipcaccess(&msqptr
->msg_perm
, IPC_R
, cred
)) ) {
printf("requester doesn't have read access\n");
eval
= copyout((caddr_t
)msqptr
, user_msqptr
, sizeof(struct msqid_ds
));
printf("invalid command %d\n",cmd
);
register struct msgget_args
*uap
;
int msgflg
= uap
->msgflg
;
struct ucred
*cred
= p
->p_ucred
;
register struct msqid_ds
*msqptr
= NULL
;
printf("msgget(0x%x,0%o)\n",key
,msgflg
);
if ( key
== IPC_PRIVATE
) {
for ( msqid
= 0; msqid
< msginfo
.msgmni
; msqid
+= 1 ) {
if ( msqptr
->msg_qbytes
!= 0 && msqptr
->msg_perm
.key
== key
) {
if ( msqid
< msginfo
.msgmni
) {
printf("found public key\n");
if ( (msgflg
& IPC_CREAT
) && (msgflg
& IPC_EXCL
) ) {
printf("not exclusive\n");
if ( (eval
= ipcaccess(&msqptr
->msg_perm
, msgflg
& 0700, cred
)) ) {
printf("requester doesn't have 0%o access\n",msgflg
& 0700);
printf("didn't find public key\n");
if ( msqid
== msginfo
.msgmni
) {
printf("need to allocate the msqid_ds\n");
if ( key
== IPC_PRIVATE
|| (msgflg
& IPC_CREAT
) ) {
for ( msqid
= 0; msqid
< msginfo
.msgmni
; msqid
+= 1 ) {
* Look for an unallocated and unlocked msqid_ds.
* msqid_ds's can be locked by msgsnd or msgrcv while they
* are copying the message in/out. We can't re-use the
* entry until they release it.
if ( msqptr
->msg_qbytes
== 0
&& (msqptr
->msg_perm
.mode
& MSG_LOCKED
) == 0 ) {
if ( msqid
== msginfo
.msgmni
) {
printf("no more msqid_ds's available\n");
printf("msqid %d is available\n",msqid
+1);
msqptr
->msg_perm
.key
= key
;
msqptr
->msg_perm
.cuid
= cred
->cr_uid
;
msqptr
->msg_perm
.uid
= cred
->cr_uid
;
msqptr
->msg_perm
.cgid
= cred
->cr_gid
;
msqptr
->msg_perm
.gid
= cred
->cr_gid
;
msqptr
->msg_perm
.mode
= (msgflg
& 0777);
msqptr
->msg_perm
.seq
+= 1; /* Make sure that the returned msqid is unique */
msqptr
->msg_first
= NULL
;
msqptr
->msg_qbytes
= msginfo
.msgmnb
;
msqptr
->msg_ctime
= time
.tv_sec
;
printf("didn't find it and wasn't asked to create it\n");
*retval
= IXSEQ_TO_IPCID(msqid
,msqptr
->msg_perm
); /* Construct the unique msqid */
register struct msgsnd_args
*uap
;
void *user_msgp
= uap
->user_msgp
;
size_t msgsz
= uap
->msgsz
;
int msgflg
= uap
->msgflg
;
struct ucred
*cred
= p
->p_ucred
;
register struct msqid_ds
*msqptr
;
register struct msg
*msghdr
;
printf("call to msgsnd(%d,0x%x,%d,%d)\n",msqid
,user_msgp
,msgsz
,msgflg
);
msqid
= IPCID_TO_IX(msqid
);
if ( msqid
< 0 || msqid
>= msginfo
.msgmni
) {
printf("msqid (%d) out of range (0<=msqid<%d)\n",msqid
,msginfo
.msgmni
);
if ( msqptr
->msg_qbytes
== 0 ) {
printf("no such message queue id\n");
if ( msqptr
->msg_perm
.seq
!= IPCID_TO_SEQ(uap
->msqid
) ) {
printf("wrong sequence number\n");
if ( (eval
= ipcaccess(&msqptr
->msg_perm
, IPC_W
, cred
)) ) {
printf("requester doesn't have write access\n");
segs_needed
= (msgsz
+ msginfo
.msgssz
- 1) / msginfo
.msgssz
;
printf("msgsz=%d, msgssz=%d, segs_needed=%d\n",msgsz
,msginfo
.msgssz
,segs_needed
);
int need_more_resources
= 0;
* (inside this loop in case msg_qbytes changes while we sleep)
if ( msgsz
> msqptr
->msg_qbytes
) {
printf("msgsz > msqptr->msg_qbytes\n");
if ( msqptr
->msg_perm
.mode
& MSG_LOCKED
) {
printf("msqid is locked\n");
if ( msgsz
+ msqptr
->msg_cbytes
> msqptr
->msg_qbytes
) {
printf("msgsz + msg_cbytes > msg_qbytes\n");
if ( segs_needed
> nfree_msgmaps
) {
printf("segs_needed > nfree_msgmaps\n");
if ( free_msghdrs
== NULL
) {
printf("no more msghdrs\n");
if ( need_more_resources
) {
if ( (msgflg
& IPC_NOWAIT
) != 0 ) {
printf("need more resources but caller doesn't want to wait\n");
if ( (msqptr
->msg_perm
.mode
& MSG_LOCKED
) != 0 ) {
printf("we don't own the msqid_ds\n");
/* Force later arrivals to wait for our request */
printf("we own the msqid_ds\n");
msqptr
->msg_perm
.mode
|= MSG_LOCKED
;
eval
= tsleep( (caddr_t
)msqptr
, (PZERO
- 4) | PCATCH
, "msg wait", 0 );
printf("good morning, eval=%d\n",eval
);
msqptr
->msg_perm
.mode
&= ~MSG_LOCKED
;
printf("msgsnd: interrupted system call\n");
* Make sure that the msq queue still exists
if ( msqptr
->msg_qbytes
== 0 ) {
printf("msqid deleted\n");
/* The SVID says to return EIDRM. */
/* Unfortunately, BSD doesn't define that code (yet)! */
printf("got all the resources that we need\n");
* We have the resources that we need.
if ( msqptr
->msg_perm
.mode
& MSG_LOCKED
) {
panic("msg_perm.mode & MSG_LOCKED"); /* bug somewhere */
if ( segs_needed
> nfree_msgmaps
) {
panic("segs_needed > nfree_msgmaps"); /* bug somewhere */
if ( msgsz
+ msqptr
->msg_cbytes
> msqptr
->msg_qbytes
) {
panic("msgsz + msg_cbytes > msg_qbytes"); /* bug somewhere */
if ( free_msghdrs
== NULL
) {
panic("no more msghdrs"); /* bug somewhere */
* Re-lock the msqid_ds in case we page-fault when copying in the message
if ( (msqptr
->msg_perm
.mode
& MSG_LOCKED
) != 0 ) {
panic("msqid_ds is already locked");
msqptr
->msg_perm
.mode
|= MSG_LOCKED
;
* Allocate a message header
free_msghdrs
= msghdr
->msg_next
;
* Allocate space for the message
while ( segs_needed
> 0 ) {
if ( nfree_msgmaps
<= 0 ) {
panic("not enough msgmaps");
if ( free_msgmaps
== -1 ) {
panic("nil free_msgmaps");
panic("next too low #1");
if ( next
>= msginfo
.msgseg
) {
panic("next out of range #1");
printf("allocating segment %d to message\n",next
);
free_msgmaps
= msgmaps
[next
].next
;
msgmaps
[next
].next
= msghdr
->msg_spot
;
* Copy in the message type
if ( (eval
= copyin(user_msgp
,&msghdr
->msg_type
,sizeof(msghdr
->msg_type
))) != 0 ) {
printf("error %d copying the message type\n",eval
);
msqptr
->msg_perm
.mode
&= ~MSG_LOCKED
;
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
user_msgp
+= sizeof(msghdr
->msg_type
);
* Validate the message type
if ( msghdr
->msg_type
< 1 ) {
msqptr
->msg_perm
.mode
&= ~MSG_LOCKED
;
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
printf("mtype (%d) < 1\n",msghdr
->msg_type
);
* Copy in the message body
if ( msgsz
> msginfo
.msgssz
) {
panic("next too low #2");
if ( next
>= msginfo
.msgseg
) {
panic("next out of range #2");
if ( (eval
= copyin(user_msgp
, &msgpool
[next
* msginfo
.msgssz
], tlen
)) != 0 ) {
printf("error %d copying in message segment\n",eval
);
msqptr
->msg_perm
.mode
&= ~MSG_LOCKED
;
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
next
= msgmaps
[next
].next
;
panic("didn't use all the msg segments");
* We've got the message. Unlock the msqid_ds.
msqptr
->msg_perm
.mode
&= ~MSG_LOCKED
;
* Make sure that the msqid_ds is still allocated.
if ( msqptr
->msg_qbytes
== 0 ) {
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
/* The SVID says to return EIDRM. */
/* Unfortunately, BSD doesn't define that code (yet)! */
* Put the message into the queue
if ( msqptr
->msg_first
== NULL
) {
msqptr
->msg_first
= msghdr
;
msqptr
->msg_last
= msghdr
;
msqptr
->msg_last
->msg_next
= msghdr
;
msqptr
->msg_last
= msghdr
;
msqptr
->msg_last
->msg_next
= NULL
;
msqptr
->msg_cbytes
+= msghdr
->msg_ts
;
msqptr
->msg_lspid
= p
->p_pid
;
msqptr
->msg_stime
= time
.tv_sec
;
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
register struct msgrcv_args
*uap
;
void *user_msgp
= uap
->msgp
;
size_t msgsz
= uap
->msgsz
;
long msgtyp
= uap
->msgtyp
;
int msgflg
= uap
->msgflg
;
struct ucred
*cred
= p
->p_ucred
;
register struct msqid_ds
*msqptr
;
register struct msg
*msghdr
;
printf("call to msgrcv(%d,0x%x,%d,%ld,%d)\n",msqid
,user_msgp
,msgsz
,msgtyp
,msgflg
);
msqid
= IPCID_TO_IX(msqid
);
if ( msqid
< 0 || msqid
>= msginfo
.msgmni
) {
printf("msqid (%d) out of range (0<=msqid<%d)\n",msqid
,msginfo
.msgmni
);
if ( msqptr
->msg_qbytes
== 0 ) {
printf("no such message queue id\n");
if ( msqptr
->msg_perm
.seq
!= IPCID_TO_SEQ(uap
->msqid
) ) {
printf("wrong sequence number\n");
if ( (eval
= ipcaccess(&msqptr
->msg_perm
, IPC_R
, cred
)) ) {
printf("requester doesn't have read access\n");
while ( msghdr
== NULL
) {
msghdr
= msqptr
->msg_first
;
if ( msgsz
< msghdr
->msg_ts
&& (msgflg
& MSG_NOERROR
) == 0 ) {
printf("first message on the queue is too big (want %d, got %d)\n",msgsz
,msghdr
->msg_ts
);
if ( msqptr
->msg_first
== msqptr
->msg_last
) {
msqptr
->msg_first
= NULL
;
msqptr
->msg_first
= msghdr
->msg_next
;
if ( msqptr
->msg_first
== NULL
) {
panic("msg_first/last screwed up #1");
prev
= &(msqptr
->msg_first
);
while ( (msghdr
= *prev
) != NULL
) {
* Is this message's type an exact match or is this message's
* type less than or equal to the absolute value of a negative msgtyp?
* Note that the second half of this test can NEVER be true
* if msgtyp is positive since msg_type is always positive!
if ( msgtyp
== msghdr
->msg_type
|| msghdr
->msg_type
<= -msgtyp
) {
printf("found message type %d, requested %d\n",msghdr
->msg_type
,msgtyp
);
if ( msgsz
< msghdr
->msg_ts
&& (msgflg
& MSG_NOERROR
) == 0 ) {
printf("requested message on the queue is too big (want %d, got %d)\n",msgsz
,msghdr
->msg_ts
);
*prev
= msghdr
->msg_next
;
if ( msghdr
== msqptr
->msg_last
) {
if ( previous
== NULL
) {
if ( prev
!= &msqptr
->msg_first
) {
panic("msg_first/last screwed up #2");
msqptr
->msg_first
= NULL
;
if ( prev
== &msqptr
->msg_first
) {
panic("msg_first/last screwed up #3");
msqptr
->msg_last
= previous
;
prev
= &(msghdr
->msg_next
);
* We've either extracted the msghdr for the appropriate message
* If there is one then bail out of this loop.
* Hmph! No message found. Does the user want to wait?
if ( (msgflg
& IPC_NOWAIT
) != 0 ) {
printf("no appropriate message found (msgtyp=%d)\n",msgtyp
);
/* The SVID says to return ENOMSG. */
/* Unfortunately, BSD doesn't define that code (yet)! */
* Wait for something to happen
printf("msgrcv: goodnight\n");
eval
= tsleep( (caddr_t
)msqptr
, (PZERO
- 4) | PCATCH
, "msg wait", 0 );
printf("msgrcv: good morning (eval=%d)\n",eval
);
printf("msgsnd: interrupted system call\n");
* Make sure that the msq queue still exists
if ( msqptr
->msg_qbytes
== 0
|| msqptr
->msg_perm
.seq
!= IPCID_TO_SEQ(uap
->msqid
) ) {
printf("msqid deleted\n");
/* The SVID says to return EIDRM. */
/* Unfortunately, BSD doesn't define that code (yet)! */
* Return the message to the user.
* First, do the bookkeeping (before we risk being interrupted).
msqptr
->msg_cbytes
-= msghdr
->msg_ts
;
msqptr
->msg_lrpid
= p
->p_pid
;
msqptr
->msg_rtime
= time
.tv_sec
;
* Make msgsz the actual amount that we'll be returning.
* Note that this effectively truncates the message if it is too long
* (since msgsz is never increased).
printf("found a message, msgsz=%d, msg_ts=%d\n",msgsz
,msghdr
->msg_ts
);
if ( msgsz
> msghdr
->msg_ts
) {
* Return the type to the user.
eval
= copyout((caddr_t
)&(msghdr
->msg_type
), user_msgp
, sizeof(msghdr
->msg_type
));
printf("error (%d) copying out message type\n",eval
);
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
user_msgp
+= sizeof(msghdr
->msg_type
);
* Return the segments to the user
for ( len
= 0; len
< msgsz
; len
+= msginfo
.msgssz
) {
if ( msgsz
> msginfo
.msgssz
) {
panic("next too low #3");
if ( next
>= msginfo
.msgseg
) {
panic("next out of range #3");
eval
= copyout((caddr_t
)&msgpool
[next
* msginfo
.msgssz
], user_msgp
, tlen
);
printf("error (%d) copying out message segment\n",eval
);
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */
next
= msgmaps
[next
].next
;
* Done, return the actual number of bytes copied out.
wakeup( (caddr_t
)msqptr
); /* Somebody might care - we should check! */