Import 2.1.114
[davej-history.git] / ipc / msg.c
blobe505bcc0168430d1c5f94835ed6f7c9bb38f2012
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 extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
20 static void freeque (int id);
21 static int newque (key_t key, int msgflg);
22 static int findkey (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(void msg_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;
43 static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
45 int id, err;
46 struct msqid_ds *msq;
47 struct ipc_perm *ipcp;
48 struct msg *msgh;
49 long mtype;
50 unsigned long flags;
52 if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
53 return -EINVAL;
54 if (!msgp)
55 return -EFAULT;
56 err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
57 if (err)
58 return err;
59 get_user(mtype, &msgp->mtype);
60 if (mtype < 1)
61 return -EINVAL;
62 id = (unsigned int) msqid % MSGMNI;
63 msq = msgque [id];
64 if (msq == IPC_UNUSED || msq == IPC_NOID)
65 return -EINVAL;
66 ipcp = &msq->msg_perm;
68 slept:
69 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
70 return -EIDRM;
72 if (ipcperms(ipcp, S_IWUGO))
73 return -EACCES;
75 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
76 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
77 /* still no space in queue */
78 if (msgflg & IPC_NOWAIT)
79 return -EAGAIN;
80 if (signal_pending(current))
81 return -EINTR;
82 interruptible_sleep_on (&msq->wwait);
83 goto slept;
87 /* allocate message header and text space*/
88 msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_ATOMIC);
89 if (!msgh)
90 return -ENOMEM;
91 msgh->msg_spot = (char *) (msgh + 1);
93 copy_from_user (msgh->msg_spot, msgp->mtext, msgsz);
95 if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
96 || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
97 kfree(msgh);
98 return -EIDRM;
101 msgh->msg_next = NULL;
102 msgh->msg_ts = msgsz;
103 msgh->msg_type = mtype;
104 msgh->msg_stime = CURRENT_TIME;
106 save_flags(flags);
107 cli();
108 if (!msq->msg_first)
109 msq->msg_first = msq->msg_last = msgh;
110 else {
111 msq->msg_last->msg_next = msgh;
112 msq->msg_last = msgh;
114 msq->msg_cbytes += msgsz;
115 msgbytes += msgsz;
116 msghdrs++;
117 msq->msg_qnum++;
118 msq->msg_lspid = current->pid;
119 msq->msg_stime = CURRENT_TIME;
120 restore_flags(flags);
121 wake_up (&msq->rwait);
122 return 0;
125 static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
127 struct msqid_ds *msq;
128 struct ipc_perm *ipcp;
129 struct msg *tmsg, *leastp = NULL;
130 struct msg *nmsg = NULL;
131 int id, err;
132 unsigned long flags;
134 if (msqid < 0 || (long) msgsz < 0)
135 return -EINVAL;
136 if (!msgp || !msgp->mtext)
137 return -EFAULT;
139 err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
140 if (err)
141 return err;
143 id = (unsigned int) msqid % MSGMNI;
144 msq = msgque [id];
145 if (msq == IPC_NOID || msq == IPC_UNUSED)
146 return -EINVAL;
147 ipcp = &msq->msg_perm;
150 * find message of correct type.
151 * msgtyp = 0 => get first.
152 * msgtyp > 0 => get first message of matching type.
153 * msgtyp < 0 => get message with least type must be < abs(msgtype).
155 while (!nmsg) {
156 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
157 return -EIDRM;
159 if (ipcperms (ipcp, S_IRUGO)) {
160 return -EACCES;
163 save_flags(flags);
164 cli();
165 if (msgtyp == 0)
166 nmsg = msq->msg_first;
167 else if (msgtyp > 0) {
168 if (msgflg & MSG_EXCEPT) {
169 for (tmsg = msq->msg_first; tmsg;
170 tmsg = tmsg->msg_next)
171 if (tmsg->msg_type != msgtyp)
172 break;
173 nmsg = tmsg;
174 } else {
175 for (tmsg = msq->msg_first; tmsg;
176 tmsg = tmsg->msg_next)
177 if (tmsg->msg_type == msgtyp)
178 break;
179 nmsg = tmsg;
181 } else {
182 for (leastp = tmsg = msq->msg_first; tmsg;
183 tmsg = tmsg->msg_next)
184 if (tmsg->msg_type < leastp->msg_type)
185 leastp = tmsg;
186 if (leastp && leastp->msg_type <= - msgtyp)
187 nmsg = leastp;
189 restore_flags(flags);
191 if (nmsg) { /* done finding a message */
192 if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
193 return -E2BIG;
195 msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
196 save_flags(flags);
197 cli();
198 if (nmsg == msq->msg_first)
199 msq->msg_first = nmsg->msg_next;
200 else {
201 for (tmsg = msq->msg_first; tmsg;
202 tmsg = tmsg->msg_next)
203 if (tmsg->msg_next == nmsg)
204 break;
205 tmsg->msg_next = nmsg->msg_next;
206 if (nmsg == msq->msg_last)
207 msq->msg_last = tmsg;
209 if (!(--msq->msg_qnum))
210 msq->msg_last = msq->msg_first = NULL;
212 msq->msg_rtime = CURRENT_TIME;
213 msq->msg_lrpid = current->pid;
214 msgbytes -= nmsg->msg_ts;
215 msghdrs--;
216 msq->msg_cbytes -= nmsg->msg_ts;
217 restore_flags(flags);
218 wake_up (&msq->wwait);
219 put_user (nmsg->msg_type, &msgp->mtype);
220 copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz);
221 kfree(nmsg);
222 return msgsz;
223 } else { /* did not find a message */
224 if (msgflg & IPC_NOWAIT) {
225 return -ENOMSG;
227 if (signal_pending(current)) {
228 return -EINTR;
230 interruptible_sleep_on (&msq->rwait);
232 } /* end while */
233 return -1;
236 asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
238 int ret;
240 lock_kernel();
241 ret = real_msgsnd(msqid, msgp, msgsz, msgflg);
242 unlock_kernel();
243 return ret;
246 asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
247 long msgtyp, int msgflg)
249 int ret;
251 lock_kernel();
252 ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);
253 unlock_kernel();
254 return ret;
257 static int findkey (key_t key)
259 int id;
260 struct msqid_ds *msq;
262 for (id = 0; id <= max_msqid; id++) {
263 while ((msq = msgque[id]) == IPC_NOID)
264 interruptible_sleep_on (&msg_lock);
265 if (msq == IPC_UNUSED)
266 continue;
267 if (key == msq->msg_perm.key)
268 return id;
270 return -1;
273 static int newque (key_t key, int msgflg)
275 int id;
276 struct msqid_ds *msq;
277 struct ipc_perm *ipcp;
279 for (id = 0; id < MSGMNI; id++)
280 if (msgque[id] == IPC_UNUSED) {
281 msgque[id] = (struct msqid_ds *) IPC_NOID;
282 goto found;
284 return -ENOSPC;
286 found:
287 msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
288 if (!msq) {
289 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
290 wake_up (&msg_lock);
291 return -ENOMEM;
293 ipcp = &msq->msg_perm;
294 ipcp->mode = (msgflg & S_IRWXUGO);
295 ipcp->key = key;
296 ipcp->cuid = ipcp->uid = current->euid;
297 ipcp->gid = ipcp->cgid = current->egid;
298 msq->msg_perm.seq = msg_seq;
299 msq->msg_first = msq->msg_last = NULL;
300 msq->rwait = msq->wwait = NULL;
301 msq->msg_cbytes = msq->msg_qnum = 0;
302 msq->msg_lspid = msq->msg_lrpid = 0;
303 msq->msg_stime = msq->msg_rtime = 0;
304 msq->msg_qbytes = MSGMNB;
305 msq->msg_ctime = CURRENT_TIME;
306 if (id > max_msqid)
307 max_msqid = id;
308 msgque[id] = msq;
309 used_queues++;
310 wake_up (&msg_lock);
311 return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
314 asmlinkage int sys_msgget (key_t key, int msgflg)
316 int id, ret = -EPERM;
317 struct msqid_ds *msq;
319 lock_kernel();
320 if (key == IPC_PRIVATE)
321 ret = newque(key, msgflg);
322 else if ((id = findkey (key)) == -1) { /* key not used */
323 if (!(msgflg & IPC_CREAT))
324 ret = -ENOENT;
325 else
326 ret = newque(key, msgflg);
327 } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
328 ret = -EEXIST;
329 } else {
330 msq = msgque[id];
331 if (msq == IPC_UNUSED || msq == IPC_NOID)
332 ret = -EIDRM;
333 else if (ipcperms(&msq->msg_perm, msgflg))
334 ret = -EACCES;
335 else
336 ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
338 unlock_kernel();
339 return ret;
342 static void freeque (int id)
344 struct msqid_ds *msq = msgque[id];
345 struct msg *msgp, *msgh;
347 msq->msg_perm.seq++;
348 msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */
349 msgbytes -= msq->msg_cbytes;
350 if (id == max_msqid)
351 while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
352 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
353 used_queues--;
354 while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
355 wake_up (&msq->rwait);
356 wake_up (&msq->wwait);
357 schedule();
359 for (msgp = msq->msg_first; msgp; msgp = msgh ) {
360 msgh = msgp->msg_next;
361 msghdrs--;
362 kfree(msgp);
364 kfree(msq);
367 asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
369 int id, err = -EINVAL;
370 struct msqid_ds *msq;
371 struct msqid_ds tbuf;
372 struct ipc_perm *ipcp;
374 lock_kernel();
375 if (msqid < 0 || cmd < 0)
376 goto out;
377 err = -EFAULT;
378 switch (cmd) {
379 case IPC_INFO:
380 case MSG_INFO:
381 if (!buf)
382 goto out;
384 struct msginfo msginfo;
385 msginfo.msgmni = MSGMNI;
386 msginfo.msgmax = MSGMAX;
387 msginfo.msgmnb = MSGMNB;
388 msginfo.msgmap = MSGMAP;
389 msginfo.msgpool = MSGPOOL;
390 msginfo.msgtql = MSGTQL;
391 msginfo.msgssz = MSGSSZ;
392 msginfo.msgseg = MSGSEG;
393 if (cmd == MSG_INFO) {
394 msginfo.msgpool = used_queues;
395 msginfo.msgmap = msghdrs;
396 msginfo.msgtql = msgbytes;
398 err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
399 if (err)
400 goto out;
401 copy_to_user (buf, &msginfo, sizeof(struct msginfo));
402 err = max_msqid;
403 goto out;
405 case MSG_STAT:
406 if (!buf)
407 goto out;
408 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
409 if (err)
410 goto out;
411 err = -EINVAL;
412 if (msqid > max_msqid)
413 goto out;
414 msq = msgque[msqid];
415 if (msq == IPC_UNUSED || msq == IPC_NOID)
416 goto out;
417 err = -EACCES;
418 if (ipcperms (&msq->msg_perm, S_IRUGO))
419 goto out;
420 id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
421 tbuf.msg_perm = msq->msg_perm;
422 tbuf.msg_stime = msq->msg_stime;
423 tbuf.msg_rtime = msq->msg_rtime;
424 tbuf.msg_ctime = msq->msg_ctime;
425 tbuf.msg_cbytes = msq->msg_cbytes;
426 tbuf.msg_qnum = msq->msg_qnum;
427 tbuf.msg_qbytes = msq->msg_qbytes;
428 tbuf.msg_lspid = msq->msg_lspid;
429 tbuf.msg_lrpid = msq->msg_lrpid;
430 copy_to_user (buf, &tbuf, sizeof(*buf));
431 err = id;
432 goto out;
433 case IPC_SET:
434 if (!buf)
435 goto out;
436 err = verify_area (VERIFY_READ, buf, sizeof (*buf));
437 if (err)
438 goto out;
439 copy_from_user (&tbuf, buf, sizeof (*buf));
440 break;
441 case IPC_STAT:
442 if (!buf)
443 goto out;
444 err = verify_area (VERIFY_WRITE, buf, sizeof(*buf));
445 if (err)
446 goto out;
447 break;
450 id = (unsigned int) msqid % MSGMNI;
451 msq = msgque [id];
452 err = -EINVAL;
453 if (msq == IPC_UNUSED || msq == IPC_NOID)
454 goto out;
455 err = -EIDRM;
456 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
457 goto out;
458 ipcp = &msq->msg_perm;
460 switch (cmd) {
461 case IPC_STAT:
462 err = -EACCES;
463 if (ipcperms (ipcp, S_IRUGO))
464 goto out;
465 tbuf.msg_perm = msq->msg_perm;
466 tbuf.msg_stime = msq->msg_stime;
467 tbuf.msg_rtime = msq->msg_rtime;
468 tbuf.msg_ctime = msq->msg_ctime;
469 tbuf.msg_cbytes = msq->msg_cbytes;
470 tbuf.msg_qnum = msq->msg_qnum;
471 tbuf.msg_qbytes = msq->msg_qbytes;
472 tbuf.msg_lspid = msq->msg_lspid;
473 tbuf.msg_lrpid = msq->msg_lrpid;
474 copy_to_user (buf, &tbuf, sizeof (*buf));
475 err = 0;
476 goto out;
477 case IPC_SET:
478 err = -EPERM;
479 if (current->euid != ipcp->cuid &&
480 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
481 /* We _could_ check for CAP_CHOWN above, but we don't */
482 goto out;
483 if (tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE))
484 goto out;
485 msq->msg_qbytes = tbuf.msg_qbytes;
486 ipcp->uid = tbuf.msg_perm.uid;
487 ipcp->gid = tbuf.msg_perm.gid;
488 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
489 (S_IRWXUGO & tbuf.msg_perm.mode);
490 msq->msg_ctime = CURRENT_TIME;
491 err = 0;
492 goto out;
493 case IPC_RMID:
494 err = -EPERM;
495 if (current->euid != ipcp->cuid &&
496 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
497 goto out;
499 freeque (id);
500 err = 0;
501 goto out;
502 default:
503 err = -EINVAL;
504 goto out;
506 out:
507 unlock_kernel();
508 return err;