3 * Copyright (C) 1992 Krishna Balasubramanian 6 #include <linux/errno.h> 7 #include <linux/sched.h> 9 #include <linux/stat.h> 10 #include <linux/malloc.h> 11 #include <linux/interrupt.h> 12 #include <linux/smp.h> 13 #include <linux/smp_lock.h> 14 #include <linux/init.h> 16 #include <asm/uaccess.h> 18 externintipcperms(struct ipc_perm
*ipcp
,short msgflg
); 20 static voidfreeque(int id
); 21 static intnewque(key_t key
,int msgflg
); 22 static intfindkey(key_t key
); 24 static struct msqid_ds
*msgque
[MSGMNI
]; 25 static int msgbytes
=0; 26 static int msghdrs
=0; 27 static unsigned short msg_seq
=0; 28 static int used_queues
=0; 29 static int max_msqid
=0; 30 static struct wait_queue
*msg_lock
= NULL
; 32 __initfunc(voidmsg_init(void)) 36 for(id
=0; id
< MSGMNI
; id
++) 37 msgque
[id
] = (struct msqid_ds
*) IPC_UNUSED
; 38 msgbytes
= msghdrs
= msg_seq
= max_msqid
= used_queues
=0; 44 * If the send queue is full, try to free any old messages. 45 * These are most probably unwanted, since no one has picked them up... 47 #define MSG_FLUSH_TIME 10/* seconds */ 48 static voidflush_msg(struct msqid_ds
*msq
) 57 /* messages were put on the queue in time order */ 58 while( (nmsg
= msq
->msg_first
) && 59 ((CURRENT_TIME
- nmsg
->msg_stime
) > MSG_FLUSH_TIME
)) { 60 msgbytes
-= nmsg
->msg_ts
; 62 msq
->msg_cbytes
-= nmsg
->msg_ts
; 64 msq
->msg_first
= nmsg
->msg_next
; 70 msq
->msg_first
= msq
->msg_last
= NULL
; 73 printk(KERN_WARNING
"flushed %d old SYSVIPC messages", flushed
); 76 static intreal_msgsnd(int msqid
,struct msgbuf
*msgp
,size_t msgsz
,int msgflg
) 80 struct ipc_perm
*ipcp
; 85 if(msgsz
> MSGMAX
|| (long) msgsz
<0|| msqid
<0) 89 err
=verify_area(VERIFY_READ
, msgp
->mtext
, msgsz
); 92 get_user(mtype
, &msgp
->mtype
); 95 id
= (unsigned int) msqid
% MSGMNI
; 97 if(msq
== IPC_UNUSED
|| msq
== IPC_NOID
) 99 ipcp
= &msq
->msg_perm
; 102 if(msq
->msg_perm
.seq
!= (unsigned int) msqid
/ MSGMNI
) 105 if(ipcperms(ipcp
, S_IWUGO
)) 108 if(msgsz
+ msq
->msg_cbytes
> msq
->msg_qbytes
) { 109 if(msgsz
+ msq
->msg_cbytes
> msq
->msg_qbytes
) { 110 /* still no space in queue */ 111 if(msgflg
& IPC_NOWAIT
) 113 if(signal_pending(current
)) 115 interruptible_sleep_on(&msq
->wwait
); 120 /* allocate message header and text space*/ 121 msgh
= (struct msg
*)kmalloc(sizeof(*msgh
) + msgsz
, GFP_ATOMIC
); 124 msgh
->msg_spot
= (char*) (msgh
+1); 126 copy_from_user(msgh
->msg_spot
, msgp
->mtext
, msgsz
); 128 if(msgque
[id
] == IPC_UNUSED
|| msgque
[id
] == IPC_NOID
129 || msq
->msg_perm
.seq
!= (unsigned int) msqid
/ MSGMNI
) { 134 msgh
->msg_next
= NULL
; 135 msgh
->msg_ts
= msgsz
; 136 msgh
->msg_type
= mtype
; 137 msgh
->msg_stime
= CURRENT_TIME
; 142 msq
->msg_first
= msq
->msg_last
= msgh
; 144 msq
->msg_last
->msg_next
= msgh
; 145 msq
->msg_last
= msgh
; 147 msq
->msg_cbytes
+= msgsz
; 151 msq
->msg_lspid
= current
->pid
; 152 msq
->msg_stime
= CURRENT_TIME
; 153 restore_flags(flags
); 154 wake_up(&msq
->rwait
); 158 static intreal_msgrcv(int msqid
,struct msgbuf
*msgp
,size_t msgsz
,long msgtyp
,int msgflg
) 160 struct msqid_ds
*msq
; 161 struct ipc_perm
*ipcp
; 162 struct msg
*tmsg
, *leastp
= NULL
; 163 struct msg
*nmsg
= NULL
; 167 if(msqid
<0|| (long) msgsz
<0) 169 if(!msgp
|| !msgp
->mtext
) 172 err
=verify_area(VERIFY_WRITE
, msgp
->mtext
, msgsz
); 176 id
= (unsigned int) msqid
% MSGMNI
; 178 if(msq
== IPC_NOID
|| msq
== IPC_UNUSED
) 180 ipcp
= &msq
->msg_perm
; 183 * find message of correct type. 184 * msgtyp = 0 => get first. 185 * msgtyp > 0 => get first message of matching type. 186 * msgtyp < 0 => get message with least type must be < abs(msgtype). 189 if(msq
->msg_perm
.seq
!= (unsigned int) msqid
/ MSGMNI
) { 192 if(ipcperms(ipcp
, S_IRUGO
)) { 199 nmsg
= msq
->msg_first
; 201 if(msgflg
& MSG_EXCEPT
) { 202 for(tmsg
= msq
->msg_first
; tmsg
; 203 tmsg
= tmsg
->msg_next
) 204 if(tmsg
->msg_type
!= msgtyp
) 208 for(tmsg
= msq
->msg_first
; tmsg
; 209 tmsg
= tmsg
->msg_next
) 210 if(tmsg
->msg_type
== msgtyp
) 215 for(leastp
= tmsg
= msq
->msg_first
; tmsg
; 216 tmsg
= tmsg
->msg_next
) 217 if(tmsg
->msg_type
< leastp
->msg_type
) 219 if(leastp
&& leastp
->msg_type
<= - msgtyp
) 222 restore_flags(flags
); 224 if(nmsg
) {/* done finding a message */ 225 if((msgsz
< nmsg
->msg_ts
) && !(msgflg
& MSG_NOERROR
)) { 228 msgsz
= (msgsz
> nmsg
->msg_ts
)? nmsg
->msg_ts
: msgsz
; 231 if(nmsg
== msq
->msg_first
) 232 msq
->msg_first
= nmsg
->msg_next
; 234 for(tmsg
= msq
->msg_first
; tmsg
; 235 tmsg
= tmsg
->msg_next
) 236 if(tmsg
->msg_next
== nmsg
) 238 tmsg
->msg_next
= nmsg
->msg_next
; 239 if(nmsg
== msq
->msg_last
) 240 msq
->msg_last
= tmsg
; 242 if(!(--msq
->msg_qnum
)) 243 msq
->msg_last
= msq
->msg_first
= NULL
; 245 msq
->msg_rtime
= CURRENT_TIME
; 246 msq
->msg_lrpid
= current
->pid
; 247 msgbytes
-= nmsg
->msg_ts
; 249 msq
->msg_cbytes
-= nmsg
->msg_ts
; 250 restore_flags(flags
); 251 wake_up(&msq
->wwait
); 252 put_user(nmsg
->msg_type
, &msgp
->mtype
); 253 copy_to_user(msgp
->mtext
, nmsg
->msg_spot
, msgsz
); 256 }else{/* did not find a message */ 257 if(msgflg
& IPC_NOWAIT
) { 260 if(signal_pending(current
)) { 263 interruptible_sleep_on(&msq
->rwait
); 269 asmlinkage
intsys_msgsnd(int msqid
,struct msgbuf
*msgp
,size_t msgsz
,int msgflg
) 274 ret
=real_msgsnd(msqid
, msgp
, msgsz
, msgflg
); 279 asmlinkage
intsys_msgrcv(int msqid
,struct msgbuf
*msgp
,size_t msgsz
, 280 long msgtyp
,int msgflg
) 285 ret
=real_msgrcv(msqid
, msgp
, msgsz
, msgtyp
, msgflg
); 290 static intfindkey(key_t key
) 293 struct msqid_ds
*msq
; 295 for(id
=0; id
<= max_msqid
; id
++) { 296 while((msq
= msgque
[id
]) == IPC_NOID
) 297 interruptible_sleep_on(&msg_lock
); 298 if(msq
== IPC_UNUSED
) 300 if(key
== msq
->msg_perm
.key
) 306 static intnewque(key_t key
,int msgflg
) 309 struct msqid_ds
*msq
; 310 struct ipc_perm
*ipcp
; 312 for(id
=0; id
< MSGMNI
; id
++) 313 if(msgque
[id
] == IPC_UNUSED
) { 314 msgque
[id
] = (struct msqid_ds
*) IPC_NOID
; 320 msq
= (struct msqid_ds
*)kmalloc(sizeof(*msq
), GFP_KERNEL
); 322 msgque
[id
] = (struct msqid_ds
*) IPC_UNUSED
; 326 ipcp
= &msq
->msg_perm
; 327 ipcp
->mode
= (msgflg
& S_IRWXUGO
); 329 ipcp
->cuid
= ipcp
->uid
= current
->euid
; 330 ipcp
->gid
= ipcp
->cgid
= current
->egid
; 331 msq
->msg_perm
.seq
= msg_seq
; 332 msq
->msg_first
= msq
->msg_last
= NULL
; 333 msq
->rwait
= msq
->wwait
= NULL
; 334 msq
->msg_cbytes
= msq
->msg_qnum
=0; 335 msq
->msg_lspid
= msq
->msg_lrpid
=0; 336 msq
->msg_stime
= msq
->msg_rtime
=0; 337 msq
->msg_qbytes
= MSGMNB
; 338 msq
->msg_ctime
= CURRENT_TIME
; 344 return(unsigned int) msq
->msg_perm
.seq
* MSGMNI
+ id
; 347 asmlinkage
intsys_msgget(key_t key
,int msgflg
) 349 int id
, ret
= -EPERM
; 350 struct msqid_ds
*msq
; 353 if(key
== IPC_PRIVATE
) 354 ret
=newque(key
, msgflg
); 355 else if((id
=findkey(key
)) == -1) {/* key not used */ 356 if(!(msgflg
& IPC_CREAT
)) 359 ret
=newque(key
, msgflg
); 360 }else if(msgflg
& IPC_CREAT
&& msgflg
& IPC_EXCL
) { 364 if(msq
== IPC_UNUSED
|| msq
== IPC_NOID
) 366 else if(ipcperms(&msq
->msg_perm
, msgflg
)) 369 ret
= (unsigned int) msq
->msg_perm
.seq
* MSGMNI
+ id
; 375 static voidfreeque(int id
) 377 struct msqid_ds
*msq
= msgque
[id
]; 378 struct msg
*msgp
, *msgh
; 381 msg_seq
= (msg_seq
+1) % ((unsigned)(1<<31)/MSGMNI
);/* increment, but avoid overflow */ 382 msgbytes
-= msq
->msg_cbytes
; 384 while(max_msqid
&& (msgque
[--max_msqid
] == IPC_UNUSED
)); 385 msgque
[id
] = (struct msqid_ds
*) IPC_UNUSED
; 387 while(waitqueue_active(&msq
->rwait
) ||waitqueue_active(&msq
->wwait
)) { 388 wake_up(&msq
->rwait
); 389 wake_up(&msq
->wwait
); 392 for(msgp
= msq
->msg_first
; msgp
; msgp
= msgh
) { 393 msgh
= msgp
->msg_next
; 400 asmlinkage
intsys_msgctl(int msqid
,int cmd
,struct msqid_ds
*buf
) 402 int id
, err
= -EINVAL
; 403 struct msqid_ds
*msq
; 404 struct msqid_ds tbuf
; 405 struct ipc_perm
*ipcp
; 408 if(msqid
<0|| cmd
<0) 417 struct msginfo msginfo
; 418 msginfo
.msgmni
= MSGMNI
; 419 msginfo
.msgmax
= MSGMAX
; 420 msginfo
.msgmnb
= MSGMNB
; 421 msginfo
.msgmap
= MSGMAP
; 422 msginfo
.msgpool
= MSGPOOL
; 423 msginfo
.msgtql
= MSGTQL
; 424 msginfo
.msgssz
= MSGSSZ
; 425 msginfo
.msgseg
= MSGSEG
; 426 if(cmd
== MSG_INFO
) { 427 msginfo
.msgpool
= used_queues
; 428 msginfo
.msgmap
= msghdrs
; 429 msginfo
.msgtql
= msgbytes
; 431 err
=verify_area(VERIFY_WRITE
, buf
,sizeof(struct msginfo
)); 434 copy_to_user(buf
, &msginfo
,sizeof(struct msginfo
)); 441 err
=verify_area(VERIFY_WRITE
, buf
,sizeof(*buf
)); 445 if(msqid
> max_msqid
) 448 if(msq
== IPC_UNUSED
|| msq
== IPC_NOID
) 451 if(ipcperms(&msq
->msg_perm
, S_IRUGO
)) 453 id
= (unsigned int) msq
->msg_perm
.seq
* MSGMNI
+ msqid
; 454 tbuf
.msg_perm
= msq
->msg_perm
; 455 tbuf
.msg_stime
= msq
->msg_stime
; 456 tbuf
.msg_rtime
= msq
->msg_rtime
; 457 tbuf
.msg_ctime
= msq
->msg_ctime
; 458 tbuf
.msg_cbytes
= msq
->msg_cbytes
; 459 tbuf
.msg_qnum
= msq
->msg_qnum
; 460 tbuf
.msg_qbytes
= msq
->msg_qbytes
; 461 tbuf
.msg_lspid
= msq
->msg_lspid
; 462 tbuf
.msg_lrpid
= msq
->msg_lrpid
; 463 copy_to_user(buf
, &tbuf
,sizeof(*buf
)); 469 err
=verify_area(VERIFY_READ
, buf
,sizeof(*buf
)); 472 copy_from_user(&tbuf
, buf
,sizeof(*buf
)); 477 err
=verify_area(VERIFY_WRITE
, buf
,sizeof(*buf
)); 483 id
= (unsigned int) msqid
% MSGMNI
; 486 if(msq
== IPC_UNUSED
|| msq
== IPC_NOID
) 489 if(msq
->msg_perm
.seq
!= (unsigned int) msqid
/ MSGMNI
) 491 ipcp
= &msq
->msg_perm
; 496 if(ipcperms(ipcp
, S_IRUGO
)) 498 tbuf
.msg_perm
= msq
->msg_perm
; 499 tbuf
.msg_stime
= msq
->msg_stime
; 500 tbuf
.msg_rtime
= msq
->msg_rtime
; 501 tbuf
.msg_ctime
= msq
->msg_ctime
; 502 tbuf
.msg_cbytes
= msq
->msg_cbytes
; 503 tbuf
.msg_qnum
= msq
->msg_qnum
; 504 tbuf
.msg_qbytes
= msq
->msg_qbytes
; 505 tbuf
.msg_lspid
= msq
->msg_lspid
; 506 tbuf
.msg_lrpid
= msq
->msg_lrpid
; 507 copy_to_user(buf
, &tbuf
,sizeof(*buf
)); 512 if(current
->euid
!= ipcp
->cuid
&& 513 current
->euid
!= ipcp
->uid
&& !suser()) 515 if(tbuf
.msg_qbytes
> MSGMNB
&& !suser()) 517 msq
->msg_qbytes
= tbuf
.msg_qbytes
; 518 ipcp
->uid
= tbuf
.msg_perm
.uid
; 519 ipcp
->gid
= tbuf
.msg_perm
.gid
; 520 ipcp
->mode
= (ipcp
->mode
& ~S_IRWXUGO
) | 521 (S_IRWXUGO
& tbuf
.msg_perm
.mode
); 522 msq
->msg_ctime
= CURRENT_TIME
; 527 if(current
->euid
!= ipcp
->cuid
&& 528 current
->euid
!= ipcp
->uid
&& !suser())