root/ipc/msg.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. msg_init
  2. sys_msgsnd
  3. sys_msgrcv
  4. findkey
  5. newque
  6. sys_msgget
  7. freeque
  8. sys_msgctl

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

/* [previous][next][first][last][top][bottom][index][help] */