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] = (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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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                 /* no space in queue */
  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         /* allocate message header and text space*/ 
  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, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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          *  find message of correct type.
 133          *  msgtyp = 0 => get first.
 134          *  msgtyp > 0 => get first message of matching type.
 135          *  msgtyp < 0 => get message with least type must be < abs(msgtype).  
 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) { /* done finding a message */
 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 {  /* did not find a message */
 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         } /* end while */
 204         return -1;
 205 }
 206 
 207 
 208 static int findkey (key_t key)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) { /* key not used */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

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