1
2
3
4
5
6
7
8
9
10#include <linux/config.h>
11#include <linux/errno.h>
12#include <linux/sched.h>
13#include <linux/msg.h>
14#include <linux/stat.h>
15#include <linux/malloc.h>
16#include <linux/kerneld.h>
17#include <linux/interrupt.h>
18
19#include <asm/segment.h>
20
21extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
22
23static void freeque (int id);
24static int newque (key_t key, int msgflg);
25static int findkey (key_t key);
26
27static struct msqid_ds *msgque[MSGMNI];
28static int msgbytes = 0;
29static int msghdrs = 0;
30static unsigned short msg_seq = 0;
31static int used_queues = 0;
32static int max_msqid = 0;
33static struct wait_queue *msg_lock = NULL;
34static int kerneld_msqid = -1;
35
36#define MAX_KERNELDS 20
37static int kerneld_arr[MAX_KERNELDS];
38static int n_kernelds = 0;
39
40void msg_init (void)
41{
42 int id;
43
44 for (id = 0; id < MSGMNI; id++)
45 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
46 msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
47 msg_lock = NULL;
48 return;
49}
50
51
52
53
54
55#define MSG_FLUSH_TIME 10
56static void flush_msg(struct msqid_ds *msq)
57{
58 struct msg *nmsg;
59 unsigned long flags;
60 int flushed = 0;
61
62 save_flags(flags);
63 cli();
64
65
66 while ( (nmsg = msq->msg_first) &&
67 ((CURRENT_TIME - nmsg->msg_stime) > MSG_FLUSH_TIME)) {
68 msgbytes -= nmsg->msg_ts;
69 msghdrs--;
70 msq->msg_cbytes -= nmsg->msg_ts;
71 msq->msg_qnum--;
72 msq->msg_first = nmsg->msg_next;
73 ++flushed;
74 kfree(nmsg);
75 }
76
77 if (msq->msg_qnum == 0)
78 msq->msg_first = msq->msg_last = NULL;
79 restore_flags(flags);
80 if (flushed)
81 printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed);
82}
83
84static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
85{
86 int id, err;
87 struct msqid_ds *msq;
88 struct ipc_perm *ipcp;
89 struct msg *msgh;
90 long mtype;
91 unsigned long flags;
92
93 if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
94 return -EINVAL;
95 if (!msgp)
96 return -EFAULT;
97
98
99
100
101 if ((msgflg & IPC_KERNELD))
102 mtype = msgp->mtype;
103 else {
104 err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
105 if (err)
106 return err;
107 if ((mtype = get_user (&msgp->mtype)) < 1)
108 return -EINVAL;
109 }
110 id = (unsigned int) msqid % MSGMNI;
111 msq = msgque [id];
112 if (msq == IPC_UNUSED || msq == IPC_NOID)
113 return -EINVAL;
114 ipcp = &msq->msg_perm;
115
116 slept:
117 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
118 return -EIDRM;
119
120
121
122
123
124 if ((msgflg & IPC_KERNELD) == 0)
125 if (ipcperms(ipcp, S_IWUGO))
126 return -EACCES;
127
128 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
129 if ((kerneld_msqid != -1) && (kerneld_msqid == msqid))
130 flush_msg(msq);
131 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
132
133 if (msgflg & IPC_NOWAIT)
134 return -EAGAIN;
135 if (current->signal & ~current->blocked)
136 return -EINTR;
137 if (intr_count) {
138
139 printk(KERN_WARNING "Ouch, kerneld:msgsnd buffers full!\n");
140 return -EINTR;
141 }
142 interruptible_sleep_on (&msq->wwait);
143 goto slept;
144 }
145 }
146
147
148 msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_ATOMIC);
149 if (!msgh)
150 return -ENOMEM;
151 msgh->msg_spot = (char *) (msgh + 1);
152
153
154
155
156
157 if (msgflg & IPC_KERNELD) {
158 struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp;
159
160
161
162
163
164 memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), KDHDR);
165 memcpy(msgh->msg_spot + KDHDR, kdmp->text, msgsz - KDHDR);
166 }
167 else
168 memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
169
170 if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
171 || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
172 kfree(msgh);
173 return -EIDRM;
174 }
175
176 msgh->msg_next = NULL;
177 msgh->msg_ts = msgsz;
178 msgh->msg_type = mtype;
179 msgh->msg_stime = CURRENT_TIME;
180
181 save_flags(flags);
182 cli();
183 if (!msq->msg_first)
184 msq->msg_first = msq->msg_last = msgh;
185 else {
186 msq->msg_last->msg_next = msgh;
187 msq->msg_last = msgh;
188 }
189 msq->msg_cbytes += msgsz;
190 msgbytes += msgsz;
191 msghdrs++;
192 msq->msg_qnum++;
193 msq->msg_lspid = current->pid;
194 msq->msg_stime = CURRENT_TIME;
195 restore_flags(flags);
196 wake_up (&msq->rwait);
197 return 0;
198}
199
200
201
202
203#define KERNELD_TIMEOUT 1 * (HZ)
204#define DROP_TIMER del_timer(&kd_timer)
205
206
207static void kd_timeout(unsigned long msgid)
208{
209 struct msqid_ds *msq;
210 struct msg *tmsg;
211 unsigned long flags;
212
213 msq = msgque [ (unsigned int) kerneld_msqid % MSGMNI ];
214 if (msq == IPC_NOID || msq == IPC_UNUSED)
215 return;
216
217 save_flags(flags);
218 cli();
219 for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next)
220 if (*(long *)(tmsg->msg_spot) == msgid)
221 break;
222 restore_flags(flags);
223 if (tmsg) {
224 struct kerneld_msg kmsp = { msgid, NULL_KDHDR, "" };
225
226 printk(KERN_ALERT "Ouch, no kerneld for message %ld\n", msgid);
227 kmsp.id = -ENODEV;
228 real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, KDHDR,
229 S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR);
230 }
231}
232
233static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
234{
235 struct timer_list kd_timer = { NULL, NULL, 0, 0, 0};
236 struct msqid_ds *msq;
237 struct ipc_perm *ipcp;
238 struct msg *tmsg, *leastp = NULL;
239 struct msg *nmsg = NULL;
240 int id, err;
241 unsigned long flags;
242
243 if (msqid < 0 || (long) msgsz < 0)
244 return -EINVAL;
245 if (!msgp || !msgp->mtext)
246 return -EFAULT;
247
248
249
250
251 if ((msgflg & IPC_KERNELD) == 0) {
252 err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
253 if (err)
254 return err;
255 }
256
257 id = (unsigned int) msqid % MSGMNI;
258 msq = msgque [id];
259 if (msq == IPC_NOID || msq == IPC_UNUSED)
260 return -EINVAL;
261 ipcp = &msq->msg_perm;
262
263
264
265
266 if (msgflg & IPC_KERNELD) {
267 kd_timer.data = (unsigned long)msgtyp;
268 kd_timer.expires = jiffies + KERNELD_TIMEOUT;
269 kd_timer.function = kd_timeout;
270 add_timer(&kd_timer);
271 }
272
273
274
275
276
277
278
279 while (!nmsg) {
280 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
281 DROP_TIMER;
282 return -EIDRM;
283 }
284 if ((msgflg & IPC_KERNELD) == 0) {
285
286
287
288
289
290 if (ipcperms (ipcp, S_IRUGO)) {
291 DROP_TIMER;
292 return -EACCES;
293 }
294 }
295
296 save_flags(flags);
297 cli();
298 if (msgtyp == 0)
299 nmsg = msq->msg_first;
300 else if (msgtyp > 0) {
301 if (msgflg & MSG_EXCEPT) {
302 for (tmsg = msq->msg_first; tmsg;
303 tmsg = tmsg->msg_next)
304 if (tmsg->msg_type != msgtyp)
305 break;
306 nmsg = tmsg;
307 } else {
308 for (tmsg = msq->msg_first; tmsg;
309 tmsg = tmsg->msg_next)
310 if (tmsg->msg_type == msgtyp)
311 break;
312 nmsg = tmsg;
313 }
314 } else {
315 for (leastp = tmsg = msq->msg_first; tmsg;
316 tmsg = tmsg->msg_next)
317 if (tmsg->msg_type < leastp->msg_type)
318 leastp = tmsg;
319 if (leastp && leastp->msg_type <= - msgtyp)
320 nmsg = leastp;
321 }
322 restore_flags(flags);
323
324 if (nmsg) {
325 DROP_TIMER;
326 if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
327 return -E2BIG;
328 }
329 msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
330 save_flags(flags);
331 cli();
332 if (nmsg == msq->msg_first)
333 msq->msg_first = nmsg->msg_next;
334 else {
335 for (tmsg = msq->msg_first; tmsg;
336 tmsg = tmsg->msg_next)
337 if (tmsg->msg_next == nmsg)
338 break;
339 tmsg->msg_next = nmsg->msg_next;
340 if (nmsg == msq->msg_last)
341 msq->msg_last = tmsg;
342 }
343 if (!(--msq->msg_qnum))
344 msq->msg_last = msq->msg_first = NULL;
345
346 msq->msg_rtime = CURRENT_TIME;
347 msq->msg_lrpid = current->pid;
348 msgbytes -= nmsg->msg_ts;
349 msghdrs--;
350 msq->msg_cbytes -= nmsg->msg_ts;
351 restore_flags(flags);
352 wake_up (&msq->wwait);
353
354
355
356
357 if (msgflg & IPC_KERNELD) {
358 struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
359
360 memcpy((char *)(&(kdmp->id)),
361 nmsg->msg_spot, KDHDR);
362
363
364
365
366 if ((msgsz > KDHDR) && kdmp->text)
367 memcpy(kdmp->text,
368 nmsg->msg_spot + KDHDR,
369 msgsz - KDHDR);
370 }
371 else {
372 put_user (nmsg->msg_type, &msgp->mtype);
373 memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
374 }
375 kfree(nmsg);
376 return msgsz;
377 } else {
378 if (msgflg & IPC_NOWAIT) {
379 DROP_TIMER;
380 return -ENOMSG;
381 }
382 if (current->signal & ~current->blocked) {
383 DROP_TIMER;
384 return -EINTR;
385 }
386 interruptible_sleep_on (&msq->rwait);
387 }
388 }
389 DROP_TIMER;
390 return -1;
391}
392
393asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
394{
395
396 return real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
397}
398
399asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
400 long msgtyp, int msgflg)
401{
402
403 return real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
404}
405
406static int findkey (key_t key)
407{
408 int id;
409 struct msqid_ds *msq;
410
411 for (id = 0; id <= max_msqid; id++) {
412 while ((msq = msgque[id]) == IPC_NOID)
413 interruptible_sleep_on (&msg_lock);
414 if (msq == IPC_UNUSED)
415 continue;
416 if (key == msq->msg_perm.key)
417 return id;
418 }
419 return -1;
420}
421
422static int newque (key_t key, int msgflg)
423{
424 int id;
425 struct msqid_ds *msq;
426 struct ipc_perm *ipcp;
427
428 for (id = 0; id < MSGMNI; id++)
429 if (msgque[id] == IPC_UNUSED) {
430 msgque[id] = (struct msqid_ds *) IPC_NOID;
431 goto found;
432 }
433 return -ENOSPC;
434
435found:
436 msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
437 if (!msq) {
438 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
439 wake_up (&msg_lock);
440 return -ENOMEM;
441 }
442 ipcp = &msq->msg_perm;
443 ipcp->mode = (msgflg & S_IRWXUGO);
444 ipcp->key = key;
445 ipcp->cuid = ipcp->uid = current->euid;
446 ipcp->gid = ipcp->cgid = current->egid;
447 msq->msg_perm.seq = msg_seq;
448 msq->msg_first = msq->msg_last = NULL;
449 msq->rwait = msq->wwait = NULL;
450 msq->msg_cbytes = msq->msg_qnum = 0;
451 msq->msg_lspid = msq->msg_lrpid = 0;
452 msq->msg_stime = msq->msg_rtime = 0;
453 msq->msg_qbytes = MSGMNB;
454 msq->msg_ctime = CURRENT_TIME;
455 if (id > max_msqid)
456 max_msqid = id;
457 msgque[id] = msq;
458 used_queues++;
459 wake_up (&msg_lock);
460 return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
461}
462
463asmlinkage int sys_msgget (key_t key, int msgflg)
464{
465 int id;
466 struct msqid_ds *msq;
467
468
469
470
471
472 if ((msgflg & IPC_KERNELD)) {
473 int i;
474 if (!suser())
475 return -EPERM;
476#ifdef NEW_KERNELD_PROTOCOL
477 if ((msgflg & IPC_KERNELD) == OLDIPC_KERNELD) {
478 printk(KERN_ALERT "Please recompile your kerneld daemons!\n");
479 return -EPERM;
480 }
481#endif
482 if ((kerneld_msqid == -1) && (kerneld_msqid =
483 newque(IPC_PRIVATE, msgflg & S_IRWXU)) < 0)
484 return -ENOSPC;
485 for (i = 0; i < MAX_KERNELDS; ++i) {
486 if (kerneld_arr[i] == 0) {
487 kerneld_arr[i] = current->pid;
488 ++n_kernelds;
489 return kerneld_msqid;
490 }
491 }
492 return -ENOSPC;
493 }
494
495 if (key == IPC_PRIVATE)
496 return newque(key, msgflg);
497 if ((id = findkey (key)) == -1) {
498 if (!(msgflg & IPC_CREAT))
499 return -ENOENT;
500 return newque(key, msgflg);
501 }
502 if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
503 return -EEXIST;
504 msq = msgque[id];
505 if (msq == IPC_UNUSED || msq == IPC_NOID)
506 return -EIDRM;
507 if (ipcperms(&msq->msg_perm, msgflg))
508 return -EACCES;
509 return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
510}
511
512static void freeque (int id)
513{
514 struct msqid_ds *msq = msgque[id];
515 struct msg *msgp, *msgh;
516
517 msq->msg_perm.seq++;
518 msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI);
519 msgbytes -= msq->msg_cbytes;
520 if (id == max_msqid)
521 while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
522 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
523 used_queues--;
524 while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
525 wake_up (&msq->rwait);
526 wake_up (&msq->wwait);
527 schedule();
528 }
529 for (msgp = msq->msg_first; msgp; msgp = msgh ) {
530 msgh = msgp->msg_next;
531 msghdrs--;
532 kfree(msgp);
533 }
534 kfree(msq);
535}
536
537asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
538{
539 int id, err;
540 struct msqid_ds *msq;
541 struct msqid_ds tbuf;
542 struct ipc_perm *ipcp;
543
544 if (msqid < 0 || cmd < 0)
545 return -EINVAL;
546 switch (cmd) {
547 case IPC_INFO:
548 case MSG_INFO:
549 if (!buf)
550 return -EFAULT;
551 {
552 struct msginfo msginfo;
553 msginfo.msgmni = MSGMNI;
554 msginfo.msgmax = MSGMAX;
555 msginfo.msgmnb = MSGMNB;
556 msginfo.msgmap = MSGMAP;
557 msginfo.msgpool = MSGPOOL;
558 msginfo.msgtql = MSGTQL;
559 msginfo.msgssz = MSGSSZ;
560 msginfo.msgseg = MSGSEG;
561 if (cmd == MSG_INFO) {
562 msginfo.msgpool = used_queues;
563 msginfo.msgmap = msghdrs;
564 msginfo.msgtql = msgbytes;
565 }
566 err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
567 if (err)
568 return err;
569 memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
570 return max_msqid;
571 }
572 case MSG_STAT:
573 if (!buf)
574 return -EFAULT;
575 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
576 if (err)
577 return err;
578 if (msqid > max_msqid)
579 return -EINVAL;
580 msq = msgque[msqid];
581 if (msq == IPC_UNUSED || msq == IPC_NOID)
582 return -EINVAL;
583 if (ipcperms (&msq->msg_perm, S_IRUGO))
584 return -EACCES;
585 id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
586 tbuf.msg_perm = msq->msg_perm;
587 tbuf.msg_stime = msq->msg_stime;
588 tbuf.msg_rtime = msq->msg_rtime;
589 tbuf.msg_ctime = msq->msg_ctime;
590 tbuf.msg_cbytes = msq->msg_cbytes;
591 tbuf.msg_qnum = msq->msg_qnum;
592 tbuf.msg_qbytes = msq->msg_qbytes;
593 tbuf.msg_lspid = msq->msg_lspid;
594 tbuf.msg_lrpid = msq->msg_lrpid;
595 memcpy_tofs (buf, &tbuf, sizeof(*buf));
596 return id;
597 case IPC_SET:
598 if (!buf)
599 return -EFAULT;
600 err = verify_area (VERIFY_READ, buf, sizeof (*buf));
601 if (err)
602 return err;
603 memcpy_fromfs (&tbuf, buf, sizeof (*buf));
604 break;
605 case IPC_STAT:
606 if (!buf)
607 return -EFAULT;
608 err = verify_area (VERIFY_WRITE, buf, sizeof(*buf));
609 if (err)
610 return err;
611 break;
612 }
613
614 id = (unsigned int) msqid % MSGMNI;
615 msq = msgque [id];
616 if (msq == IPC_UNUSED || msq == IPC_NOID)
617 return -EINVAL;
618 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
619 return -EIDRM;
620 ipcp = &msq->msg_perm;
621
622 switch (cmd) {
623 case IPC_STAT:
624 if (ipcperms (ipcp, S_IRUGO))
625 return -EACCES;
626 tbuf.msg_perm = msq->msg_perm;
627 tbuf.msg_stime = msq->msg_stime;
628 tbuf.msg_rtime = msq->msg_rtime;
629 tbuf.msg_ctime = msq->msg_ctime;
630 tbuf.msg_cbytes = msq->msg_cbytes;
631 tbuf.msg_qnum = msq->msg_qnum;
632 tbuf.msg_qbytes = msq->msg_qbytes;
633 tbuf.msg_lspid = msq->msg_lspid;
634 tbuf.msg_lrpid = msq->msg_lrpid;
635 memcpy_tofs (buf, &tbuf, sizeof (*buf));
636 return 0;
637 case IPC_SET:
638 if (!suser() && current->euid != ipcp->cuid &&
639 current->euid != ipcp->uid)
640 return -EPERM;
641 if (tbuf.msg_qbytes > MSGMNB && !suser())
642 return -EPERM;
643 msq->msg_qbytes = tbuf.msg_qbytes;
644 ipcp->uid = tbuf.msg_perm.uid;
645 ipcp->gid = tbuf.msg_perm.gid;
646 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
647 (S_IRWXUGO & tbuf.msg_perm.mode);
648 msq->msg_ctime = CURRENT_TIME;
649 return 0;
650 case IPC_RMID:
651 if (!suser() && current->euid != ipcp->cuid &&
652 current->euid != ipcp->uid)
653 return -EPERM;
654
655
656
657
658 if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid))
659 kerneld_msqid = -1;
660 freeque (id);
661 return 0;
662 default:
663 return -EINVAL;
664 }
665}
666
667
668
669
670
671
672
673
674void kerneld_exit(void)
675{
676 int i;
677
678 if (kerneld_msqid == -1)
679 return;
680 for (i = 0; i < MAX_KERNELDS; ++i) {
681 if (kerneld_arr[i] == current->pid) {
682 kerneld_arr[i] = 0;
683 --n_kernelds;
684 if (n_kernelds == 0)
685 sys_msgctl(kerneld_msqid, IPC_RMID, NULL);
686 break;
687 }
688 }
689}
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723int kerneld_send(int msgtype, int ret_size, int msgsz,
724 const char *text, const char *ret_val)
725{
726 int status = -ENOSYS;
727#ifdef CONFIG_KERNELD
728 static int id = KERNELD_MINSEQ;
729 struct kerneld_msg kmsp = { msgtype, NULL_KDHDR, (char *)text };
730 int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
731 unsigned long flags;
732
733 if (kerneld_msqid == -1)
734 return -ENODEV;
735
736
737 if (intr_count)
738 ret_size &= ~KERNELD_WAIT;
739#ifdef NEW_KERNELD_PROTOCOL
740 else
741 kmsp.pid = current->pid;
742#endif
743
744 msgsz += KDHDR;
745 if (ret_size & KERNELD_WAIT) {
746 save_flags(flags);
747 cli();
748 if (++id <= 0)
749 id = KERNELD_MINSEQ;
750 kmsp.id = id;
751 restore_flags(flags);
752 }
753
754 status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
755 if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
756 ret_size &= ~KERNELD_WAIT;
757 kmsp.text = (char *)ret_val;
758 status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
759 KDHDR + ((ret_val)?ret_size:0),
760 kmsp.id, msgflg);
761 if (status > 0)
762 status = kmsp.id;
763 }
764
765#endif
766 return status;
767}
768