- Revert TCP delayed ACK fix, and fix correctly.
[davej-history.git] / ipc / msg.c
blob806f692dd5803d952904b6f875161653ec9331d8
1 /*
2 * linux/ipc/msg.c
3 * Copyright (C) 1992 Krishna Balasubramanian
4 */
6 #include <linux/errno.h>
7 #include <linux/sched.h>
8 #include <linux/msg.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))
34 int id;
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;
39 msg_lock = NULL;
40 return;
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)
50 struct msg *nmsg;
51 unsigned long flags;
52 int flushed =0;
54 save_flags(flags);
55 cli();
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;
61 msghdrs--;
62 msq->msg_cbytes -= nmsg->msg_ts;
63 msq->msg_qnum--;
64 msq->msg_first = nmsg->msg_next;
65 ++flushed;
66 kfree(nmsg);
69 if(msq->msg_qnum ==0)
70 msq->msg_first = msq->msg_last = NULL;
71 restore_flags(flags);
72 if(flushed)
73 printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed);
76 static intreal_msgsnd(int msqid,struct msgbuf *msgp,size_t msgsz,int msgflg)
78 int id, err;
79 struct msqid_ds *msq;
80 struct ipc_perm *ipcp;
81 struct msg *msgh;
82 long mtype;
83 unsigned long flags;
85 if(msgsz > MSGMAX || (long) msgsz <0|| msqid <0)
86 return-EINVAL;
87 if(!msgp)
88 return-EFAULT;
89 err =verify_area(VERIFY_READ, msgp->mtext, msgsz);
90 if(err)
91 return err;
92 get_user(mtype, &msgp->mtype);
93 if(mtype <1)
94 return-EINVAL;
95 id = (unsigned int) msqid % MSGMNI;
96 msq = msgque [id];
97 if(msq == IPC_UNUSED || msq == IPC_NOID)
98 return-EINVAL;
99 ipcp = &msq->msg_perm;
101 slept:
102 if(msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
103 return-EIDRM;
105 if(ipcperms(ipcp, S_IWUGO))
106 return-EACCES;
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)
112 return-EAGAIN;
113 if(signal_pending(current))
114 return-EINTR;
115 interruptible_sleep_on(&msq->wwait);
116 goto slept;
120 /* allocate message header and text space*/
121 msgh = (struct msg *)kmalloc(sizeof(*msgh) + msgsz, GFP_ATOMIC);
122 if(!msgh)
123 return-ENOMEM;
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) {
130 kfree(msgh);
131 return-EIDRM;
134 msgh->msg_next = NULL;
135 msgh->msg_ts = msgsz;
136 msgh->msg_type = mtype;
137 msgh->msg_stime = CURRENT_TIME;
139 save_flags(flags);
140 cli();
141 if(!msq->msg_first)
142 msq->msg_first = msq->msg_last = msgh;
143 else{
144 msq->msg_last->msg_next = msgh;
145 msq->msg_last = msgh;
147 msq->msg_cbytes += msgsz;
148 msgbytes += msgsz;
149 msghdrs++;
150 msq->msg_qnum++;
151 msq->msg_lspid = current->pid;
152 msq->msg_stime = CURRENT_TIME;
153 restore_flags(flags);
154 wake_up(&msq->rwait);
155 return0;
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;
164 int id, err;
165 unsigned long flags;
167 if(msqid <0|| (long) msgsz <0)
168 return-EINVAL;
169 if(!msgp || !msgp->mtext)
170 return-EFAULT;
172 err =verify_area(VERIFY_WRITE, msgp->mtext, msgsz);
173 if(err)
174 return err;
176 id = (unsigned int) msqid % MSGMNI;
177 msq = msgque [id];
178 if(msq == IPC_NOID || msq == IPC_UNUSED)
179 return-EINVAL;
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).
188 while(!nmsg) {
189 if(msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
190 return-EIDRM;
192 if(ipcperms(ipcp, S_IRUGO)) {
193 return-EACCES;
196 save_flags(flags);
197 cli();
198 if(msgtyp ==0)
199 nmsg = msq->msg_first;
200 else if(msgtyp >0) {
201 if(msgflg & MSG_EXCEPT) {
202 for(tmsg = msq->msg_first; tmsg;
203 tmsg = tmsg->msg_next)
204 if(tmsg->msg_type != msgtyp)
205 break;
206 nmsg = tmsg;
207 }else{
208 for(tmsg = msq->msg_first; tmsg;
209 tmsg = tmsg->msg_next)
210 if(tmsg->msg_type == msgtyp)
211 break;
212 nmsg = tmsg;
214 }else{
215 for(leastp = tmsg = msq->msg_first; tmsg;
216 tmsg = tmsg->msg_next)
217 if(tmsg->msg_type < leastp->msg_type)
218 leastp = tmsg;
219 if(leastp && leastp->msg_type <= - msgtyp)
220 nmsg = leastp;
222 restore_flags(flags);
224 if(nmsg) {/* done finding a message */
225 if((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
226 return-E2BIG;
228 msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
229 save_flags(flags);
230 cli();
231 if(nmsg == msq->msg_first)
232 msq->msg_first = nmsg->msg_next;
233 else{
234 for(tmsg = msq->msg_first; tmsg;
235 tmsg = tmsg->msg_next)
236 if(tmsg->msg_next == nmsg)
237 break;
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;
248 msghdrs--;
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);
254 kfree(nmsg);
255 return msgsz;
256 }else{/* did not find a message */
257 if(msgflg & IPC_NOWAIT) {
258 return-ENOMSG;
260 if(signal_pending(current)) {
261 return-EINTR;
263 interruptible_sleep_on(&msq->rwait);
265 }/* end while */
266 return-1;
269 asmlinkage intsys_msgsnd(int msqid,struct msgbuf *msgp,size_t msgsz,int msgflg)
271 int ret;
273 lock_kernel();
274 ret =real_msgsnd(msqid, msgp, msgsz, msgflg);
275 unlock_kernel();
276 return ret;
279 asmlinkage intsys_msgrcv(int msqid,struct msgbuf *msgp,size_t msgsz,
280 long msgtyp,int msgflg)
282 int ret;
284 lock_kernel();
285 ret =real_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
286 unlock_kernel();
287 return ret;
290 static intfindkey(key_t key)
292 int id;
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)
299 continue;
300 if(key == msq->msg_perm.key)
301 return id;
303 return-1;
306 static intnewque(key_t key,int msgflg)
308 int id;
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;
315 goto found;
317 return-ENOSPC;
319 found:
320 msq = (struct msqid_ds *)kmalloc(sizeof(*msq), GFP_KERNEL);
321 if(!msq) {
322 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
323 wake_up(&msg_lock);
324 return-ENOMEM;
326 ipcp = &msq->msg_perm;
327 ipcp->mode = (msgflg & S_IRWXUGO);
328 ipcp->key = key;
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;
339 if(id > max_msqid)
340 max_msqid = id;
341 msgque[id] = msq;
342 used_queues++;
343 wake_up(&msg_lock);
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;
352 lock_kernel();
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))
357 ret = -ENOENT;
358 else
359 ret =newque(key, msgflg);
360 }else if(msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
361 ret = -EEXIST;
362 }else{
363 msq = msgque[id];
364 if(msq == IPC_UNUSED || msq == IPC_NOID)
365 ret = -EIDRM;
366 else if(ipcperms(&msq->msg_perm, msgflg))
367 ret = -EACCES;
368 else
369 ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
371 unlock_kernel();
372 return ret;
375 static voidfreeque(int id)
377 struct msqid_ds *msq = msgque[id];
378 struct msg *msgp, *msgh;
380 msq->msg_perm.seq++;
381 msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI);/* increment, but avoid overflow */
382 msgbytes -= msq->msg_cbytes;
383 if(id == max_msqid)
384 while(max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
385 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
386 used_queues--;
387 while(waitqueue_active(&msq->rwait) ||waitqueue_active(&msq->wwait)) {
388 wake_up(&msq->rwait);
389 wake_up(&msq->wwait);
390 schedule();
392 for(msgp = msq->msg_first; msgp; msgp = msgh ) {
393 msgh = msgp->msg_next;
394 msghdrs--;
395 kfree(msgp);
397 kfree(msq);
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;
407 lock_kernel();
408 if(msqid <0|| cmd <0)
409 goto out;
410 err = -EFAULT;
411 switch(cmd) {
412 case IPC_INFO:
413 case MSG_INFO:
414 if(!buf)
415 goto out;
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));
432 if(err)
433 goto out;
434 copy_to_user(buf, &msginfo,sizeof(struct msginfo));
435 err = max_msqid;
436 goto out;
438 case MSG_STAT:
439 if(!buf)
440 goto out;
441 err =verify_area(VERIFY_WRITE, buf,sizeof(*buf));
442 if(err)
443 goto out;
444 err = -EINVAL;
445 if(msqid > max_msqid)
446 goto out;
447 msq = msgque[msqid];
448 if(msq == IPC_UNUSED || msq == IPC_NOID)
449 goto out;
450 err = -EACCES;
451 if(ipcperms(&msq->msg_perm, S_IRUGO))
452 goto out;
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));
464 err = id;
465 goto out;
466 case IPC_SET:
467 if(!buf)
468 goto out;
469 err =verify_area(VERIFY_READ, buf,sizeof(*buf));
470 if(err)
471 goto out;
472 copy_from_user(&tbuf, buf,sizeof(*buf));
473 break;
474 case IPC_STAT:
475 if(!buf)
476 goto out;
477 err =verify_area(VERIFY_WRITE, buf,sizeof(*buf));
478 if(err)
479 goto out;
480 break;
483 id = (unsigned int) msqid % MSGMNI;
484 msq = msgque [id];
485 err = -EINVAL;
486 if(msq == IPC_UNUSED || msq == IPC_NOID)
487 goto out;
488 err = -EIDRM;
489 if(msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
490 goto out;
491 ipcp = &msq->msg_perm;
493 switch(cmd) {
494 case IPC_STAT:
495 err = -EACCES;
496 if(ipcperms(ipcp, S_IRUGO))
497 goto out;
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));
508 err =0;
509 goto out;
510 case IPC_SET:
511 err = -EPERM;
512 if(current->euid != ipcp->cuid &&
513 current->euid != ipcp->uid && !suser())
514 goto out;
515 if(tbuf.msg_qbytes > MSGMNB && !suser())
516 goto out;
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;
523 err =0;
524 goto out;
525 case IPC_RMID:
526 err = -EPERM;
527 if(current->euid != ipcp->cuid &&
528 current->euid != ipcp->uid && !suser())
529 goto out;
531 freeque(id);
532 err =0;
533 goto out;
534 default:
535 err = -EINVAL;
536 goto out;
538 out:
539 unlock_kernel();
540 return err;
close