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 <linux/sched.h>
   8 #include <linux/msg.h>
   9 #include <linux/stat.h>
  10 #include <linux/malloc.h>
  11 
  12 #include <asm/segment.h>
  13 
  14 extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
  15 
  16 static void freeque (int id);
  17 static int newque (key_t key, int msgflg);
  18 static int findkey (key_t key);
  19 
  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;
  27 
  28 void msg_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30         int id;
  31         
  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;
  37 }
  38 
  39 int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41         int id, err;
  42         struct msqid_ds *msq;
  43         struct ipc_perm *ipcp;
  44         struct msg *msgh;
  45         long mtype;
  46         
  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; 
  61 
  62  slept:
  63         if (ipcp->seq != (msqid / MSGMNI)) 
  64                 return -EIDRM;
  65         if (ipcperms(ipcp, S_IWUGO)) 
  66                 return -EACCES;
  67         
  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;
  76         }
  77         
  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); 
  84         
  85         if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
  86                 || ipcp->seq != msqid / MSGMNI) {
  87                 kfree(msgh);
  88                 return -EIDRM;
  89         }
  90 
  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;
  97         }
  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;
 109 }
 110 
 111 int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, 
     /* [previous][next][first][last][top][bottom][index][help] */
 112                 int msgflg)
 113 {
 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;
 119 
 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;
 127 
 128         id = msqid % MSGMNI;
 129         msq = msgque [id];
 130         if (msq == IPC_NOID || msq == IPC_UNUSED)
 131                 return -EINVAL;
 132         ipcp = &msq->msg_perm; 
 133 
 134         /* 
 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).  
 139          */
 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;
 160                         }
 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;
 168                 }
 169                 
 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;
 184                         }
 185                         if (!(--msq->msg_qnum))
 186                                 msq->msg_last = msq->msg_first = NULL;
 187                         
 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(nmsg);
 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);
 205                 }
 206         } /* end while */
 207         return -1;
 208 }
 209 
 210 
 211 static int findkey (key_t key)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213         int id;
 214         struct msqid_ds *msq;
 215         
 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;
 223         }
 224         return -1;
 225 }
 226 
 227 static int newque (key_t key, int msgflg)
     /* [previous][next][first][last][top][bottom][index][help] */
 228 {
 229         int id;
 230         struct msqid_ds *msq;
 231         struct ipc_perm *ipcp;
 232 
 233         for (id=0; id < MSGMNI; id++) 
 234                 if (msgque[id] == IPC_UNUSED) {
 235                         msgque[id] = (struct msqid_ds *) IPC_NOID;
 236                         goto found;
 237                 }
 238         return -ENOSPC;
 239 
 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;
 247         }
 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;
 268 }
 269 
 270 int sys_msgget (key_t key, int msgflg)
     /* [previous][next][first][last][top][bottom][index][help] */
 271 {
 272         int id;
 273         struct msqid_ds *msq;
 274         
 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);
 281         }
 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;
 290 } 
 291 
 292 static void freeque (int id)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294         struct msqid_ds *msq = msgque[id];
 295         struct msg *msgp, *msgh;
 296 
 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(); 
 310         }
 311         for (msgp = msq->msg_first; msgp; msgp = msgh ) {
 312                 msgh = msgp->msg_next;
 313                 msghdrs--;
 314                 kfree(msgp);
 315         }
 316         kfree(msq);
 317 }
 318 
 319 int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 320 {
 321         int id, err;
 322         struct msqid_ds *msq, tbuf;
 323         struct ipc_perm *ipcp;
 324         
 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;
 332         { 
 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;
 346                 }
 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;
 352         }
 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;
 381         }
 382 
 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;
 390 
 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;
 405                 }
 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;
 418         }
 419         return 0;
 420 }

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