* added 0.99 linux version
[mascara-docs.git] / i386 / linux / linux-0.99 / ipc / msg.c
blobfa900515ea7937aa30fbfaf0e7793cb45dd4399d
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>
12 #include <asm/segment.h>
14 extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
16 static void freeque (int id);
17 static int newque (key_t key, int msgflg);
18 static int findkey (key_t key);
20 static struct msqid_ds *msgque[MSGMNI];
21 static int msgbytes = 0;
22 static int msghdrs = 0;
23 static unsigned short msg_seq = 0;
24 static int used_queues = 0;
25 static int max_msqid = 0;
26 static struct wait_queue *msg_lock = NULL;
28 void msg_init (void)
30 int id;
32 for (id=0; id < MSGMNI; id++)
33 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
34 msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
35 msg_lock = NULL;
36 return;
39 int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
41 int id, err;
42 struct msqid_ds *msq;
43 struct ipc_perm *ipcp;
44 struct msg *msgh;
45 long mtype;
47 if (msgsz > MSGMAX || msgsz < 0 || msqid < 0)
48 return -EINVAL;
49 if (!msgp)
50 return -EFAULT;
51 err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
52 if (err)
53 return err;
54 if ((mtype = get_fs_long (&msgp->mtype)) < 1)
55 return -EINVAL;
56 id = msqid % MSGMNI;
57 msq = msgque [id];
58 if (msq == IPC_UNUSED || msq == IPC_NOID)
59 return -EINVAL;
60 ipcp = &msq->msg_perm;
62 slept:
63 if (ipcp->seq != (msqid / MSGMNI))
64 return -EIDRM;
65 if (ipcperms(ipcp, S_IWUGO))
66 return -EACCES;
68 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
69 /* no space in queue */
70 if (msgflg & IPC_NOWAIT)
71 return -EAGAIN;
72 if (current->signal & ~current->blocked)
73 return -EINTR;
74 interruptible_sleep_on (&msq->wwait);
75 goto slept;
78 /* allocate message header and text space*/
79 msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_USER);
80 if (!msgh)
81 return -ENOMEM;
82 msgh->msg_spot = (char *) (msgh + 1);
83 memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
85 if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
86 || ipcp->seq != msqid / MSGMNI) {
87 kfree_s (msgh, sizeof(*msgh) + msgsz);
88 return -EIDRM;
91 msgh->msg_next = NULL;
92 if (!msq->msg_first)
93 msq->msg_first = msq->msg_last = msgh;
94 else {
95 msq->msg_last->msg_next = msgh;
96 msq->msg_last = msgh;
98 msgh->msg_ts = msgsz;
99 msgh->msg_type = mtype;
100 msq->msg_cbytes += msgsz;
101 msgbytes += msgsz;
102 msghdrs++;
103 msq->msg_qnum++;
104 msq->msg_lspid = current->pid;
105 msq->msg_stime = CURRENT_TIME;
106 if (msq->rwait)
107 wake_up (&msq->rwait);
108 return msgsz;
111 int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
112 int msgflg)
114 struct msqid_ds *msq;
115 struct ipc_perm *ipcp;
116 struct msg *tmsg, *leastp = NULL;
117 struct msg *nmsg = NULL;
118 int id, err;
120 if (msqid < 0 || msgsz < 0)
121 return -EINVAL;
122 if (!msgp || !msgp->mtext)
123 return -EFAULT;
124 err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
125 if (err)
126 return err;
128 id = msqid % MSGMNI;
129 msq = msgque [id];
130 if (msq == IPC_NOID || msq == IPC_UNUSED)
131 return -EINVAL;
132 ipcp = &msq->msg_perm;
135 * find message of correct type.
136 * msgtyp = 0 => get first.
137 * msgtyp > 0 => get first message of matching type.
138 * msgtyp < 0 => get message with least type must be < abs(msgtype).
140 while (!nmsg) {
141 if(ipcp->seq != msqid / MSGMNI)
142 return -EIDRM;
143 if (ipcperms (ipcp, S_IRUGO))
144 return -EACCES;
145 if (msgtyp == 0)
146 nmsg = msq->msg_first;
147 else if (msgtyp > 0) {
148 if (msgflg & MSG_EXCEPT) {
149 for (tmsg = msq->msg_first; tmsg;
150 tmsg = tmsg->msg_next)
151 if (tmsg->msg_type != msgtyp)
152 break;
153 nmsg = tmsg;
154 } else {
155 for (tmsg = msq->msg_first; tmsg;
156 tmsg = tmsg->msg_next)
157 if (tmsg->msg_type == msgtyp)
158 break;
159 nmsg = tmsg;
161 } else {
162 for (leastp = tmsg = msq->msg_first; tmsg;
163 tmsg = tmsg->msg_next)
164 if (tmsg->msg_type < leastp->msg_type)
165 leastp = tmsg;
166 if (leastp && leastp->msg_type <= - msgtyp)
167 nmsg = leastp;
170 if (nmsg) { /* done finding a message */
171 if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR))
172 return -E2BIG;
173 msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
174 if (nmsg == msq->msg_first)
175 msq->msg_first = nmsg->msg_next;
176 else {
177 for (tmsg= msq->msg_first; tmsg;
178 tmsg = tmsg->msg_next)
179 if (tmsg->msg_next == nmsg)
180 break;
181 tmsg->msg_next = nmsg->msg_next;
182 if (nmsg == msq->msg_last)
183 msq->msg_last = tmsg;
185 if (!(--msq->msg_qnum))
186 msq->msg_last = msq->msg_first = NULL;
188 msq->msg_rtime = CURRENT_TIME;
189 msq->msg_lrpid = current->pid;
190 msgbytes -= nmsg->msg_ts;
191 msghdrs--;
192 msq->msg_cbytes -= nmsg->msg_ts;
193 if (msq->wwait)
194 wake_up (&msq->wwait);
195 put_fs_long (nmsg->msg_type, &msgp->mtype);
196 memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
197 kfree_s (nmsg, sizeof(*nmsg) + msgsz);
198 return msgsz;
199 } else { /* did not find a message */
200 if (msgflg & IPC_NOWAIT)
201 return -ENOMSG;
202 if (current->signal & ~current->blocked)
203 return -EINTR;
204 interruptible_sleep_on (&msq->rwait);
206 } /* end while */
207 return -1;
211 static int findkey (key_t key)
213 int id;
214 struct msqid_ds *msq;
216 for (id=0; id <= max_msqid; id++) {
217 while ((msq = msgque[id]) == IPC_NOID)
218 interruptible_sleep_on (&msg_lock);
219 if (msq == IPC_UNUSED)
220 continue;
221 if (key == msq->msg_perm.key)
222 return id;
224 return -1;
227 static int newque (key_t key, int msgflg)
229 int id;
230 struct msqid_ds *msq;
231 struct ipc_perm *ipcp;
233 for (id=0; id < MSGMNI; id++)
234 if (msgque[id] == IPC_UNUSED) {
235 msgque[id] = (struct msqid_ds *) IPC_NOID;
236 goto found;
238 return -ENOSPC;
240 found:
241 msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
242 if (!msq) {
243 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
244 if (msg_lock)
245 wake_up (&msg_lock);
246 return -ENOMEM;
248 ipcp = &msq->msg_perm;
249 ipcp->mode = (msgflg & S_IRWXUGO);
250 ipcp->key = key;
251 ipcp->cuid = ipcp->uid = current->euid;
252 ipcp->gid = ipcp->cgid = current->egid;
253 ipcp->seq = msg_seq;
254 msq->msg_first = msq->msg_last = NULL;
255 msq->rwait = msq->wwait = NULL;
256 msq->msg_cbytes = msq->msg_qnum = 0;
257 msq->msg_lspid = msq->msg_lrpid = 0;
258 msq->msg_stime = msq->msg_rtime = 0;
259 msq->msg_qbytes = MSGMNB;
260 msq->msg_ctime = CURRENT_TIME;
261 if (id > max_msqid)
262 max_msqid = id;
263 msgque[id] = msq;
264 used_queues++;
265 if (msg_lock)
266 wake_up (&msg_lock);
267 return (int) msg_seq * MSGMNI + id;
270 int sys_msgget (key_t key, int msgflg)
272 int id;
273 struct msqid_ds *msq;
275 if (key == IPC_PRIVATE)
276 return newque(key, msgflg);
277 if ((id = findkey (key)) == -1) { /* key not used */
278 if (!(msgflg & IPC_CREAT))
279 return -ENOENT;
280 return newque(key, msgflg);
282 if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
283 return -EEXIST;
284 msq = msgque[id];
285 if (msq == IPC_UNUSED || msq == IPC_NOID)
286 return -EIDRM;
287 if (ipcperms(&msq->msg_perm, msgflg))
288 return -EACCES;
289 return msq->msg_perm.seq * MSGMNI +id;
292 static void freeque (int id)
294 struct msqid_ds *msq = msgque[id];
295 struct msg *msgp, *msgh;
297 msq->msg_perm.seq++;
298 msg_seq++;
299 msgbytes -= msq->msg_cbytes;
300 if (id == max_msqid)
301 while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
302 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
303 used_queues--;
304 while (msq->rwait || msq->wwait) {
305 if (msq->rwait)
306 wake_up (&msq->rwait);
307 if (msq->wwait)
308 wake_up (&msq->wwait);
309 schedule();
311 for (msgp = msq->msg_first; msgp; msgp = msgh ) {
312 msgh = msgp->msg_next;
313 msghdrs--;
314 kfree_s (msgp, sizeof(*msgp) + msgp->msg_ts);
316 kfree_s (msq, sizeof (*msq));
319 int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
321 int id, err;
322 struct msqid_ds *msq, tbuf;
323 struct ipc_perm *ipcp;
325 if (msqid < 0 || cmd < 0)
326 return -EINVAL;
327 switch (cmd) {
328 case IPC_INFO:
329 case MSG_INFO:
330 if (!buf)
331 return -EFAULT;
333 struct msginfo msginfo;
334 msginfo.msgmni = MSGMNI;
335 msginfo.msgmax = MSGMAX;
336 msginfo.msgmnb = MSGMNB;
337 msginfo.msgmap = MSGMAP;
338 msginfo.msgpool = MSGPOOL;
339 msginfo.msgtql = MSGTQL;
340 msginfo.msgssz = MSGSSZ;
341 msginfo.msgseg = MSGSEG;
342 if (cmd == MSG_INFO) {
343 msginfo.msgpool = used_queues;
344 msginfo.msgmap = msghdrs;
345 msginfo.msgtql = msgbytes;
347 err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
348 if (err)
349 return err;
350 memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
351 return max_msqid;
353 case MSG_STAT:
354 if (!buf)
355 return -EFAULT;
356 err = verify_area (VERIFY_WRITE, buf, sizeof (*msq));
357 if (err)
358 return err;
359 if (msqid > max_msqid)
360 return -EINVAL;
361 msq = msgque[msqid];
362 if (msq == IPC_UNUSED || msq == IPC_NOID)
363 return -EINVAL;
364 if (ipcperms (&msq->msg_perm, S_IRUGO))
365 return -EACCES;
366 id = msqid + msq->msg_perm.seq * MSGMNI;
367 memcpy_tofs (buf, msq, sizeof(*msq));
368 return id;
369 case IPC_SET:
370 if (!buf)
371 return -EFAULT;
372 memcpy_fromfs (&tbuf, buf, sizeof (*buf));
373 break;
374 case IPC_STAT:
375 if (!buf)
376 return -EFAULT;
377 err = verify_area (VERIFY_WRITE, buf, sizeof(*msq));
378 if (err)
379 return err;
380 break;
383 id = msqid % MSGMNI;
384 msq = msgque [id];
385 if (msq == IPC_UNUSED || msq == IPC_NOID)
386 return -EINVAL;
387 ipcp = &msq->msg_perm;
388 if (ipcp->seq != msqid / MSGMNI)
389 return -EIDRM;
391 switch (cmd) {
392 case IPC_STAT:
393 if (ipcperms (ipcp, S_IRUGO))
394 return -EACCES;
395 memcpy_tofs (buf, msq, sizeof (*msq));
396 return 0;
397 break;
398 case IPC_RMID: case IPC_SET:
399 if (!suser() && current->euid != ipcp->cuid &&
400 current->euid != ipcp->uid)
401 return -EPERM;
402 if (cmd == IPC_RMID) {
403 freeque (id);
404 return 0;
406 if (tbuf.msg_qbytes > MSGMNB && !suser())
407 return -EPERM;
408 msq->msg_qbytes = tbuf.msg_qbytes;
409 ipcp->uid = tbuf.msg_perm.uid;
410 ipcp->gid = tbuf.msg_perm.gid;
411 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
412 (S_IRWXUGO & tbuf.msg_perm.mode);
413 msq->msg_ctime = CURRENT_TIME;
414 break;
415 default:
416 return -EINVAL;
417 break;
419 return 0;