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

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