This source file includes following definitions.
- msg_init
- sys_msgsnd
- sys_msgrcv
- findkey
- newque
- sys_msgget
- freeque
- sys_msgctl
1
2
3
4
5
6 #include <linux/errno.h>
7 #include <asm/segment.h>
8 #include <linux/sched.h>
9 #include <linux/msg.h>
10
11 extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
12
13 static void freeque (int id);
14 static int newque (key_t key, int msgflg);
15 static int findkey (key_t key);
16
17 static struct msqid_ds *msgque[MSGMNI];
18 static int msgbytes = 0;
19 static int msghdrs = 0;
20 static int msg_seq = 0;
21 static int used_queues = 0;
22 static int max_msqid = 0;
23 static struct wait_queue *msg_lock = NULL;
24
25 void msg_init (void)
26 {
27 int id;
28
29 for (id=0; id < MSGMNI; id++)
30 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
31 msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
32 msg_lock = NULL;
33 return;
34 }
35
36 int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
37 {
38 int id, err;
39 struct msqid_ds *msq;
40 struct ipc_perm *ipcp;
41 struct msg *msgh;
42 long mtype;
43
44 if (msgsz > MSGMAX || msgsz < 0 || msqid < 0)
45 return -EINVAL;
46 if (!msgp)
47 return -EFAULT;
48 err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
49 if (err)
50 return err;
51 if ((mtype = get_fs_long (&msgp->mtype)) < 1)
52 return -EINVAL;
53 id = msqid % MSGMNI;
54 msq = msgque [id];
55 if (msq == IPC_UNUSED || msq == IPC_NOID)
56 return -EINVAL;
57 ipcp = &msq->msg_perm;
58
59 slept:
60 if (ipcp->seq != (msqid / MSGMNI))
61 return -EIDRM;
62 if (ipcperms(ipcp, 0222))
63 return -EACCES;
64
65 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
66
67 if (msgflg & IPC_NOWAIT)
68 return -EAGAIN;
69 if (current->signal & ~current->blocked)
70 return -EINTR;
71 interruptible_sleep_on (&msq->wwait);
72 goto slept;
73 }
74
75
76 msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_USER);
77 if (!msgh)
78 return -ENOMEM;
79 msgh->msg_spot = (char *) (msgh + 1);
80 memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
81
82 if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
83 || ipcp->seq != msqid / MSGMNI) {
84 kfree_s (msgh, sizeof(*msgh) + msgsz);
85 return -EIDRM;
86 }
87
88 msgh->msg_next = NULL;
89 if (!msq->msg_first)
90 msq->msg_first = msq->msg_last = msgh;
91 else {
92 msq->msg_last->msg_next = msgh;
93 msq->msg_last = msgh;
94 }
95 msgh->msg_ts = msgsz;
96 msgh->msg_type = mtype;
97 msq->msg_cbytes += msgsz;
98 msgbytes += msgsz;
99 msghdrs++;
100 msq->msg_qnum++;
101 msq->msg_lspid = current->pid;
102 msq->msg_stime = CURRENT_TIME;
103 if (msq->rwait)
104 wake_up (&msq->rwait);
105 return msgsz;
106 }
107
108 int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
109 int msgflg)
110 {
111 struct msqid_ds *msq;
112 struct ipc_perm *ipcp;
113 struct msg *tmsg, *leastp = NULL;
114 struct msg *nmsg = NULL;
115 int id, err;
116
117 if (msqid < 0 || msgsz < 0)
118 return -EINVAL;
119 if (!msgp || !msgp->mtext)
120 return -EFAULT;
121 err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
122 if (err)
123 return err;
124
125 id = msqid % MSGMNI;
126 msq = msgque [id];
127 if (msq == IPC_NOID || msq == IPC_UNUSED)
128 return -EINVAL;
129 ipcp = &msq->msg_perm;
130
131
132
133
134
135
136
137 while (!nmsg) {
138 if(ipcp->seq != msqid / MSGMNI)
139 return -EIDRM;
140 if (ipcperms (ipcp, 0444))
141 return -EACCES;
142 if (msgtyp == 0)
143 nmsg = msq->msg_first;
144 else if (msgtyp > 0) {
145 if (msgflg & MSG_EXCEPT) {
146 for (tmsg = msq->msg_first; tmsg;
147 tmsg = tmsg->msg_next)
148 if (tmsg->msg_type != msgtyp)
149 break;
150 nmsg = tmsg;
151 } else {
152 for (tmsg = msq->msg_first; tmsg;
153 tmsg = tmsg->msg_next)
154 if (tmsg->msg_type == msgtyp)
155 break;
156 nmsg = tmsg;
157 }
158 } else {
159 for (leastp = tmsg = msq->msg_first; tmsg;
160 tmsg = tmsg->msg_next)
161 if (tmsg->msg_type < leastp->msg_type)
162 leastp = tmsg;
163 if (leastp && leastp->msg_type <= - msgtyp)
164 nmsg = leastp;
165 }
166
167 if (nmsg) {
168 if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR))
169 return -E2BIG;
170 msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
171 if (nmsg == msq->msg_first)
172 msq->msg_first = nmsg->msg_next;
173 else {
174 for (tmsg= msq->msg_first; tmsg;
175 tmsg = tmsg->msg_next)
176 if (tmsg->msg_next == nmsg)
177 break;
178 tmsg->msg_next = nmsg->msg_next;
179 if (nmsg == msq->msg_last)
180 msq->msg_last = tmsg;
181 }
182 if (!(--msq->msg_qnum))
183 msq->msg_last = msq->msg_first = NULL;
184
185 msq->msg_rtime = CURRENT_TIME;
186 msq->msg_lrpid = current->pid;
187 msgbytes -= nmsg->msg_ts;
188 msghdrs--;
189 msq->msg_cbytes -= nmsg->msg_ts;
190 if (msq->wwait)
191 wake_up (&msq->wwait);
192 put_fs_long (nmsg->msg_type, &msgp->mtype);
193 memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
194 kfree_s (nmsg, sizeof(*nmsg) + msgsz);
195 return msgsz;
196 } else {
197 if (msgflg & IPC_NOWAIT)
198 return -ENOMSG;
199 if (current->signal & ~current->blocked)
200 return -EINTR;
201 interruptible_sleep_on (&msq->rwait);
202 }
203 }
204 return -1;
205 }
206
207
208 static int findkey (key_t key)
209 {
210 int id;
211 struct msqid_ds *msq;
212
213 for (id=0; id <= max_msqid; id++) {
214 while ((msq = msgque[id]) == IPC_NOID)
215 interruptible_sleep_on (&msg_lock);
216 if (msq == IPC_UNUSED)
217 continue;
218 if (key == msq->msg_perm.key)
219 return id;
220 }
221 return -1;
222 }
223
224 static int newque (key_t key, int msgflg)
225 {
226 int id;
227 struct msqid_ds *msq;
228 struct ipc_perm *ipcp;
229
230 for (id=0; id < MSGMNI; id++)
231 if (msgque[id] == IPC_UNUSED) {
232 msgque[id] = (struct msqid_ds *) IPC_NOID;
233 goto found;
234 }
235 return -ENOSPC;
236
237 found:
238 msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
239 if (!msq) {
240 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
241 if (msg_lock)
242 wake_up (&msg_lock);
243 return -ENOMEM;
244 }
245 ipcp = &msq->msg_perm;
246 ipcp->mode = (msgflg & 0x01FF);
247 ipcp->key = key;
248 ipcp->cuid = ipcp->uid = current->euid;
249 ipcp->gid = ipcp->cgid = current->egid;
250 ipcp->seq = msg_seq;
251 msq->msg_first = msq->msg_last = NULL;
252 msq->rwait = msq->wwait = NULL;
253 msq->msg_cbytes = msq->msg_qnum = 0;
254 msq->msg_lspid = msq->msg_lrpid = 0;
255 msq->msg_stime = msq->msg_rtime = 0;
256 msq->msg_qbytes = MSGMNB;
257 msq->msg_ctime = CURRENT_TIME;
258 if (id > max_msqid)
259 max_msqid = id;
260 msgque[id] = msq;
261 used_queues++;
262 if (msg_lock)
263 wake_up (&msg_lock);
264 return msg_seq * MSGMNI + id;
265 }
266
267 int sys_msgget (key_t key, int msgflg)
268 {
269 int id;
270 struct msqid_ds *msq;
271
272 if (key == IPC_PRIVATE)
273 return newque(key, msgflg);
274 if ((id = findkey (key)) == -1) {
275 if (!(msgflg & IPC_CREAT))
276 return -ENOENT;
277 return newque(key, msgflg);
278 }
279 if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
280 return -EEXIST;
281 msq = msgque[id];
282 if (msq == IPC_UNUSED || msq == IPC_NOID)
283 return -EIDRM;
284 if (ipcperms(&msq->msg_perm, msgflg))
285 return -EACCES;
286 return msq->msg_perm.seq * MSGMNI +id;
287 }
288
289 static void freeque (int id)
290 {
291 struct msqid_ds *msq = msgque[id];
292 struct msg *msgp, *msgh;
293
294 msq->msg_perm.seq++;
295 if ((int)((++msg_seq + 1) * MSGMNI) < 0)
296 msg_seq = 0;
297 msgbytes -= msq->msg_cbytes;
298 if (id == max_msqid)
299 while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
300 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
301 used_queues--;
302 while (msq->rwait || msq->wwait) {
303 if (msq->rwait)
304 wake_up (&msq->rwait);
305 if (msq->wwait)
306 wake_up (&msq->wwait);
307 schedule();
308 }
309 for (msgp = msq->msg_first; msgp; msgp = msgh ) {
310 msgh = msgp->msg_next;
311 msghdrs--;
312 kfree_s (msgp, sizeof(*msgp) + msgp->msg_ts);
313 }
314 kfree_s (msq, sizeof (*msq));
315 }
316
317 int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
318 {
319 int id, err;
320 struct msqid_ds *msq, tbuf;
321 struct ipc_perm *ipcp;
322
323 if (msqid < 0 || cmd < 0)
324 return -EINVAL;
325 switch (cmd) {
326 case IPC_INFO:
327 case MSG_INFO:
328 if (!buf)
329 return -EFAULT;
330 {
331 struct msginfo msginfo;
332 msginfo.msgmni = MSGMNI;
333 msginfo.msgmax = MSGMAX;
334 msginfo.msgmnb = MSGMNB;
335 msginfo.msgmap = MSGMAP;
336 msginfo.msgpool = MSGPOOL;
337 msginfo.msgtql = MSGTQL;
338 msginfo.msgssz = MSGSSZ;
339 msginfo.msgseg = MSGSEG;
340 if (cmd == MSG_INFO) {
341 msginfo.msgpool = used_queues;
342 msginfo.msgmap = msghdrs;
343 msginfo.msgtql = msgbytes;
344 }
345 err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
346 if (err)
347 return err;
348 memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
349 return max_msqid;
350 }
351 case MSG_STAT:
352 if (!buf)
353 return -EFAULT;
354 err = verify_area (VERIFY_WRITE, buf, sizeof (*msq));
355 if (err)
356 return err;
357 if (msqid > max_msqid)
358 return -EINVAL;
359 msq = msgque[msqid];
360 if (msq == IPC_UNUSED || msq == IPC_NOID)
361 return -EINVAL;
362 if (ipcperms (&msq->msg_perm, 0444))
363 return -EACCES;
364 id = msqid + msq->msg_perm.seq * MSGMNI;
365 memcpy_tofs (buf, msq, sizeof(*msq));
366 return id;
367 case IPC_SET:
368 if (!buf)
369 return -EFAULT;
370 memcpy_fromfs (&tbuf, buf, sizeof (*buf));
371 break;
372 case IPC_STAT:
373 if (!buf)
374 return -EFAULT;
375 err = verify_area (VERIFY_WRITE, buf, sizeof(*msq));
376 if (err)
377 return err;
378 break;
379 }
380
381 id = msqid % MSGMNI;
382 msq = msgque [id];
383 if (msq == IPC_UNUSED || msq == IPC_NOID)
384 return -EINVAL;
385 ipcp = &msq->msg_perm;
386 if (ipcp->seq != msqid / MSGMNI)
387 return -EIDRM;
388
389 switch (cmd) {
390 case IPC_STAT:
391 if (ipcperms (ipcp, 0444))
392 return -EACCES;
393 memcpy_tofs (buf, msq, sizeof (*msq));
394 return 0;
395 break;
396 case IPC_RMID: case IPC_SET:
397 if (!suser() && current->euid != ipcp->cuid &&
398 current->euid != ipcp->uid)
399 return -EPERM;
400 if (cmd == IPC_RMID) {
401 freeque (id);
402 return 0;
403 }
404 if (tbuf.msg_qbytes > MSGMNB && !suser())
405 return -EPERM;
406 msq->msg_qbytes = tbuf.msg_qbytes;
407 ipcp->uid = tbuf.msg_perm.uid;
408 ipcp->gid = tbuf.msg_perm.gid;
409 ipcp->mode = (ipcp->mode & ~0x1FF) |
410 (0x1FF & tbuf.msg_perm.mode);
411 msq->msg_ctime = CURRENT_TIME;
412 break;
413 default:
414 return -EINVAL;
415 break;
416 }
417 return 0;
418 }