linux-old/ipc/msg.c
<<
>>
Prefs
   1/*
   2 * linux/ipc/msg.c
   3 * Copyright (C) 1992 Krishna Balasubramanian 
   4 *
   5 * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995, and May 1996
   6 *
   7 * See <linux/kerneld.h> for the (optional) new kerneld protocol
   8 */
   9
  10#include <linux/config.h>
  11#include <linux/errno.h>
  12#include <linux/sched.h>
  13#include <linux/msg.h>
  14#include <linux/stat.h>
  15#include <linux/malloc.h>
  16#include <linux/kerneld.h>
  17#include <linux/interrupt.h>
  18
  19#include <asm/segment.h>
  20
  21extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
  22
  23static void freeque (int id);
  24static int newque (key_t key, int msgflg);
  25static int findkey (key_t key);
  26
  27static struct msqid_ds *msgque[MSGMNI];
  28static int msgbytes = 0;
  29static int msghdrs = 0;
  30static unsigned short msg_seq = 0;
  31static int used_queues = 0;
  32static int max_msqid = 0;
  33static struct wait_queue *msg_lock = NULL;
  34static int kerneld_msqid = -1;
  35
  36#define MAX_KERNELDS 20
  37static int kerneld_arr[MAX_KERNELDS];
  38static int n_kernelds = 0;
  39
  40void msg_init (void)
  41{
  42        int id;
  43        
  44        for (id = 0; id < MSGMNI; id++) 
  45                msgque[id] = (struct msqid_ds *) IPC_UNUSED;
  46        msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
  47        msg_lock = NULL;
  48        return;
  49}
  50
  51/*
  52 * If the send queue is full, try to free any old messages.
  53 * These are most probably unwanted, since no one has picked them up...
  54 */
  55#define MSG_FLUSH_TIME 10 /* seconds */
  56static void flush_msg(struct msqid_ds *msq)
  57{
  58        struct msg *nmsg;
  59        unsigned long flags;
  60        int flushed = 0;
  61
  62        save_flags(flags);
  63        cli();
  64
  65        /* messages were put on the queue in time order */
  66        while ( (nmsg = msq->msg_first) &&
  67                ((CURRENT_TIME - nmsg->msg_stime) > MSG_FLUSH_TIME)) {
  68                msgbytes -= nmsg->msg_ts; 
  69                msghdrs--; 
  70                msq->msg_cbytes -= nmsg->msg_ts;
  71                msq->msg_qnum--;
  72                msq->msg_first = nmsg->msg_next;
  73                ++flushed;
  74                kfree(nmsg);
  75        }
  76
  77        if (msq->msg_qnum == 0)
  78                msq->msg_first = msq->msg_last = NULL;
  79        restore_flags(flags);
  80        if (flushed)
  81                printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed);
  82}
  83
  84static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
  85{
  86        int id, err;
  87        struct msqid_ds *msq;
  88        struct ipc_perm *ipcp;
  89        struct msg *msgh;
  90        long mtype;
  91        unsigned long flags;
  92        
  93        if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
  94                return -EINVAL;
  95        if (!msgp) 
  96                return -EFAULT;
  97        /*
  98         * Calls from kernel level (IPC_KERNELD set)
  99         * have the message somewhere in kernel space already!
 100         */
 101        if ((msgflg & IPC_KERNELD))
 102                mtype = msgp->mtype;
 103        else {
 104                err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
 105                if (err) 
 106                        return err;
 107                if ((mtype = get_user (&msgp->mtype)) < 1)
 108                        return -EINVAL;
 109        }
 110        id = (unsigned int) msqid % MSGMNI;
 111        msq = msgque [id];
 112        if (msq == IPC_UNUSED || msq == IPC_NOID)
 113                return -EINVAL;
 114        ipcp = &msq->msg_perm; 
 115
 116 slept:
 117        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) 
 118                return -EIDRM;
 119        /*
 120         * Non-root kernel level processes may send to kerneld! 
 121         * i.e. no permission check if called from the kernel
 122         * otoh we don't want user level non-root snoopers...
 123         */
 124        if ((msgflg & IPC_KERNELD) == 0)
 125                if (ipcperms(ipcp, S_IWUGO)) 
 126                        return -EACCES;
 127        
 128        if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
 129                if ((kerneld_msqid != -1) && (kerneld_msqid == msqid))
 130                        flush_msg(msq); /* flush the kerneld channel only */
 131                if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
 132                        /* still no space in queue */
 133                        if (msgflg & IPC_NOWAIT)
 134                                return -EAGAIN;
 135                        if (current->signal & ~current->blocked)
 136                                return -EINTR;
 137                        if (intr_count) {
 138                                /* Very unlikely, but better safe than sorry */
 139                                printk(KERN_WARNING "Ouch, kerneld:msgsnd buffers full!\n");
 140                                return -EINTR;
 141                        }
 142                        interruptible_sleep_on (&msq->wwait);
 143                        goto slept;
 144                }
 145        }
 146        
 147        /* allocate message header and text space*/ 
 148        msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_ATOMIC);
 149        if (!msgh)
 150                return -ENOMEM;
 151        msgh->msg_spot = (char *) (msgh + 1);
 152
 153        /*
 154         * Calls from kernel level (IPC_KERNELD set)
 155         * have the message somewhere in kernel space already!
 156         */
 157        if (msgflg & IPC_KERNELD) {
 158                struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp;
 159
 160                /*
 161                 * Note that the kernel supplies a pointer
 162                 * but the user-level kerneld uses a char array...
 163                 */
 164                memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), KDHDR); 
 165                memcpy(msgh->msg_spot + KDHDR, kdmp->text, msgsz - KDHDR); 
 166        }
 167        else
 168                memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz); 
 169        
 170        if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
 171                || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
 172                kfree(msgh);
 173                return -EIDRM;
 174        }
 175
 176        msgh->msg_next = NULL;
 177        msgh->msg_ts = msgsz;
 178        msgh->msg_type = mtype;
 179        msgh->msg_stime = CURRENT_TIME;
 180
 181        save_flags(flags);
 182        cli();
 183        if (!msq->msg_first)
 184                msq->msg_first = msq->msg_last = msgh;
 185        else {
 186                msq->msg_last->msg_next = msgh;
 187                msq->msg_last = msgh;
 188        }
 189        msq->msg_cbytes += msgsz;
 190        msgbytes  += msgsz;
 191        msghdrs++;
 192        msq->msg_qnum++;
 193        msq->msg_lspid = current->pid;
 194        msq->msg_stime = CURRENT_TIME;
 195        restore_flags(flags);
 196        wake_up (&msq->rwait);
 197        return 0;
 198}
 199
 200/*
 201 * Take care of missing kerneld, especially in case of multiple daemons
 202 */
 203#define KERNELD_TIMEOUT 1 * (HZ)
 204#define DROP_TIMER del_timer(&kd_timer)
 205/*#define DROP_TIMER if ((msgflg & IPC_KERNELD) && kd_timer.next && kd_timer.prev) del_timer(&kd_timer)*/
 206
 207static void kd_timeout(unsigned long msgid)
 208{
 209        struct msqid_ds *msq;
 210        struct msg *tmsg;
 211        unsigned long flags;
 212
 213        msq = msgque [ (unsigned int) kerneld_msqid % MSGMNI ];
 214        if (msq == IPC_NOID || msq == IPC_UNUSED)
 215                return;
 216
 217        save_flags(flags);
 218        cli();
 219        for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next)
 220                if (*(long *)(tmsg->msg_spot) == msgid)
 221                        break;
 222        restore_flags(flags);
 223        if (tmsg) { /* still there! */
 224                struct kerneld_msg kmsp = { msgid, NULL_KDHDR, "" };
 225
 226                printk(KERN_ALERT "Ouch, no kerneld for message %ld\n", msgid);
 227                kmsp.id = -ENODEV;
 228                real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, KDHDR,
 229                        S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR);
 230        }
 231}
 232
 233static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
 234{
 235        struct timer_list kd_timer = { NULL, NULL, 0, 0, 0};
 236        struct msqid_ds *msq;
 237        struct ipc_perm *ipcp;
 238        struct msg *tmsg, *leastp = NULL;
 239        struct msg *nmsg = NULL;
 240        int id, err;
 241        unsigned long flags;
 242
 243        if (msqid < 0 || (long) msgsz < 0)
 244                return -EINVAL;
 245        if (!msgp || !msgp->mtext)
 246            return -EFAULT;
 247        /*
 248         * Calls from kernel level (IPC_KERNELD set)
 249         * wants the message put in kernel space!
 250         */
 251        if ((msgflg & IPC_KERNELD) == 0) {
 252                err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
 253                if (err)
 254                        return err;
 255        }
 256
 257        id = (unsigned int) msqid % MSGMNI;
 258        msq = msgque [id];
 259        if (msq == IPC_NOID || msq == IPC_UNUSED)
 260                return -EINVAL;
 261        ipcp = &msq->msg_perm; 
 262
 263        /*
 264         * Start timer for missing kerneld
 265         */
 266        if (msgflg & IPC_KERNELD) {
 267                kd_timer.data = (unsigned long)msgtyp;
 268                kd_timer.expires = jiffies + KERNELD_TIMEOUT;
 269                kd_timer.function = kd_timeout;
 270                add_timer(&kd_timer);
 271        }
 272
 273        /* 
 274         *  find message of correct type.
 275         *  msgtyp = 0 => get first.
 276         *  msgtyp > 0 => get first message of matching type.
 277         *  msgtyp < 0 => get message with least type must be < abs(msgtype).  
 278         */
 279        while (!nmsg) {
 280                if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
 281                        DROP_TIMER;
 282                        return -EIDRM;
 283                }
 284                if ((msgflg & IPC_KERNELD) == 0) {
 285                        /*
 286                         * All kernel level processes may receive from kerneld! 
 287                         * i.e. no permission check if called from the kernel
 288                         * otoh we don't want user level non-root snoopers...
 289                         */
 290                        if (ipcperms (ipcp, S_IRUGO)) {
 291                                DROP_TIMER; /* Not needed, but doesn't hurt */
 292                                return -EACCES;
 293                        }
 294                }
 295
 296                save_flags(flags);
 297                cli();
 298                if (msgtyp == 0) 
 299                        nmsg = msq->msg_first;
 300                else if (msgtyp > 0) {
 301                        if (msgflg & MSG_EXCEPT) { 
 302                                for (tmsg = msq->msg_first; tmsg; 
 303                                     tmsg = tmsg->msg_next)
 304                                        if (tmsg->msg_type != msgtyp)
 305                                                break;
 306                                nmsg = tmsg;
 307                        } else {
 308                                for (tmsg = msq->msg_first; tmsg; 
 309                                     tmsg = tmsg->msg_next)
 310                                        if (tmsg->msg_type == msgtyp)
 311                                                break;
 312                                nmsg = tmsg;
 313                        }
 314                } else {
 315                        for (leastp = tmsg = msq->msg_first; tmsg; 
 316                             tmsg = tmsg->msg_next) 
 317                                if (tmsg->msg_type < leastp->msg_type) 
 318                                        leastp = tmsg;
 319                        if (leastp && leastp->msg_type <= - msgtyp)
 320                                nmsg = leastp;
 321                }
 322                restore_flags(flags);
 323                
 324                if (nmsg) { /* done finding a message */
 325                        DROP_TIMER;
 326                        if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
 327                                return -E2BIG;
 328                        }
 329                        msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
 330                        save_flags(flags);
 331                        cli();
 332                        if (nmsg ==  msq->msg_first)
 333                                msq->msg_first = nmsg->msg_next;
 334                        else {
 335                                for (tmsg = msq->msg_first; tmsg; 
 336                                     tmsg = tmsg->msg_next)
 337                                        if (tmsg->msg_next == nmsg) 
 338                                                break;
 339                                tmsg->msg_next = nmsg->msg_next;
 340                                if (nmsg == msq->msg_last)
 341                                        msq->msg_last = tmsg;
 342                        }
 343                        if (!(--msq->msg_qnum))
 344                                msq->msg_last = msq->msg_first = NULL;
 345                        
 346                        msq->msg_rtime = CURRENT_TIME;
 347                        msq->msg_lrpid = current->pid;
 348                        msgbytes -= nmsg->msg_ts; 
 349                        msghdrs--; 
 350                        msq->msg_cbytes -= nmsg->msg_ts;
 351                        restore_flags(flags);
 352                        wake_up (&msq->wwait);
 353                        /*
 354                         * Calls from kernel level (IPC_KERNELD set)
 355                         * wants the message copied to kernel space!
 356                         */
 357                        if (msgflg & IPC_KERNELD) {
 358                                struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
 359
 360                                memcpy((char *)(&(kdmp->id)),
 361                                        nmsg->msg_spot, KDHDR); 
 362                                /*
 363                                 * Note that kdmp->text is a pointer
 364                                 * when called from kernel space!
 365                                 */
 366                                if ((msgsz > KDHDR) && kdmp->text)
 367                                        memcpy(kdmp->text,
 368                                                nmsg->msg_spot + KDHDR,
 369                                                msgsz - KDHDR); 
 370                        }
 371                        else {
 372                                put_user (nmsg->msg_type, &msgp->mtype);
 373                                memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
 374                        }
 375                        kfree(nmsg);
 376                        return msgsz;
 377                } else {  /* did not find a message */
 378                        if (msgflg & IPC_NOWAIT) {
 379                                DROP_TIMER;
 380                                return -ENOMSG;
 381                        }
 382                        if (current->signal & ~current->blocked) {
 383                                DROP_TIMER;
 384                                return -EINTR; 
 385                        }
 386                        interruptible_sleep_on (&msq->rwait);
 387                }
 388        } /* end while */
 389        DROP_TIMER;
 390        return -1;
 391}
 392
 393asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 394{
 395        /* IPC_KERNELD is used as a marker for kernel level calls */
 396        return real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
 397}
 398
 399asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
 400        long msgtyp, int msgflg)
 401{
 402        /* IPC_KERNELD is used as a marker for kernel level calls */
 403        return real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
 404}
 405
 406static int findkey (key_t key)
 407{
 408        int id;
 409        struct msqid_ds *msq;
 410        
 411        for (id = 0; id <= max_msqid; id++) {
 412                while ((msq = msgque[id]) == IPC_NOID) 
 413                        interruptible_sleep_on (&msg_lock);
 414                if (msq == IPC_UNUSED)
 415                        continue;
 416                if (key == msq->msg_perm.key)
 417                        return id;
 418        }
 419        return -1;
 420}
 421
 422static int newque (key_t key, int msgflg)
 423{
 424        int id;
 425        struct msqid_ds *msq;
 426        struct ipc_perm *ipcp;
 427
 428        for (id = 0; id < MSGMNI; id++) 
 429                if (msgque[id] == IPC_UNUSED) {
 430                        msgque[id] = (struct msqid_ds *) IPC_NOID;
 431                        goto found;
 432                }
 433        return -ENOSPC;
 434
 435found:
 436        msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
 437        if (!msq) {
 438                msgque[id] = (struct msqid_ds *) IPC_UNUSED;
 439                wake_up (&msg_lock);
 440                return -ENOMEM;
 441        }
 442        ipcp = &msq->msg_perm;
 443        ipcp->mode = (msgflg & S_IRWXUGO);
 444        ipcp->key = key;
 445        ipcp->cuid = ipcp->uid = current->euid;
 446        ipcp->gid = ipcp->cgid = current->egid;
 447        msq->msg_perm.seq = msg_seq;
 448        msq->msg_first = msq->msg_last = NULL;
 449        msq->rwait = msq->wwait = NULL;
 450        msq->msg_cbytes = msq->msg_qnum = 0;
 451        msq->msg_lspid = msq->msg_lrpid = 0;
 452        msq->msg_stime = msq->msg_rtime = 0;
 453        msq->msg_qbytes = MSGMNB;
 454        msq->msg_ctime = CURRENT_TIME;
 455        if (id > max_msqid)
 456                max_msqid = id;
 457        msgque[id] = msq;
 458        used_queues++;
 459        wake_up (&msg_lock);
 460        return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
 461}
 462
 463asmlinkage int sys_msgget (key_t key, int msgflg)
 464{
 465        int id;
 466        struct msqid_ds *msq;
 467        
 468        /*
 469         * If the IPC_KERNELD flag is set, the key is forced to IPC_PRIVATE,
 470         * and a designated kerneld message queue is created/referred to
 471         */
 472        if ((msgflg & IPC_KERNELD)) {
 473                int i;
 474                if (!suser())
 475                        return -EPERM;
 476#ifdef NEW_KERNELD_PROTOCOL
 477                if ((msgflg & IPC_KERNELD) == OLDIPC_KERNELD) {
 478                        printk(KERN_ALERT "Please recompile your kerneld daemons!\n");
 479                        return -EPERM;
 480                }
 481#endif
 482                if ((kerneld_msqid == -1) && (kerneld_msqid =
 483                                newque(IPC_PRIVATE, msgflg & S_IRWXU)) < 0)
 484                        return -ENOSPC;
 485                for (i = 0; i < MAX_KERNELDS; ++i) {
 486                        if (kerneld_arr[i] == 0) {
 487                                kerneld_arr[i] = current->pid;
 488                                ++n_kernelds;
 489                                return kerneld_msqid;
 490                        }
 491                }
 492                return -ENOSPC;
 493        }
 494        /* else it is a "normal" request */
 495        if (key == IPC_PRIVATE) 
 496                return newque(key, msgflg);
 497        if ((id = findkey (key)) == -1) { /* key not used */
 498                if (!(msgflg & IPC_CREAT))
 499                        return -ENOENT;
 500                return newque(key, msgflg);
 501        }
 502        if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
 503                return -EEXIST;
 504        msq = msgque[id];
 505        if (msq == IPC_UNUSED || msq == IPC_NOID)
 506                return -EIDRM;
 507        if (ipcperms(&msq->msg_perm, msgflg))
 508                return -EACCES;
 509        return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
 510} 
 511
 512static void freeque (int id)
 513{
 514        struct msqid_ds *msq = msgque[id];
 515        struct msg *msgp, *msgh;
 516
 517        msq->msg_perm.seq++;
 518        msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */
 519        msgbytes -= msq->msg_cbytes;
 520        if (id == max_msqid)
 521                while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
 522        msgque[id] = (struct msqid_ds *) IPC_UNUSED;
 523        used_queues--;
 524        while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
 525                wake_up (&msq->rwait); 
 526                wake_up (&msq->wwait);
 527                schedule(); 
 528        }
 529        for (msgp = msq->msg_first; msgp; msgp = msgh ) {
 530                msgh = msgp->msg_next;
 531                msghdrs--;
 532                kfree(msgp);
 533        }
 534        kfree(msq);
 535}
 536
 537asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
 538{
 539        int id, err;
 540        struct msqid_ds *msq;
 541        struct msqid_ds tbuf;
 542        struct ipc_perm *ipcp;
 543        
 544        if (msqid < 0 || cmd < 0)
 545                return -EINVAL;
 546        switch (cmd) {
 547        case IPC_INFO: 
 548        case MSG_INFO: 
 549                if (!buf)
 550                        return -EFAULT;
 551        { 
 552                struct msginfo msginfo;
 553                msginfo.msgmni = MSGMNI;
 554                msginfo.msgmax = MSGMAX;
 555                msginfo.msgmnb = MSGMNB;
 556                msginfo.msgmap = MSGMAP;
 557                msginfo.msgpool = MSGPOOL;
 558                msginfo.msgtql = MSGTQL;
 559                msginfo.msgssz = MSGSSZ;
 560                msginfo.msgseg = MSGSEG;
 561                if (cmd == MSG_INFO) {
 562                        msginfo.msgpool = used_queues;
 563                        msginfo.msgmap = msghdrs;
 564                        msginfo.msgtql = msgbytes;
 565                }
 566                err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
 567                if (err)
 568                        return err;
 569                memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
 570                return max_msqid;
 571        }
 572        case MSG_STAT:
 573                if (!buf)
 574                        return -EFAULT;
 575                err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
 576                if (err)
 577                        return err;
 578                if (msqid > max_msqid)
 579                        return -EINVAL;
 580                msq = msgque[msqid];
 581                if (msq == IPC_UNUSED || msq == IPC_NOID)
 582                        return -EINVAL;
 583                if (ipcperms (&msq->msg_perm, S_IRUGO))
 584                        return -EACCES;
 585                id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
 586                tbuf.msg_perm   = msq->msg_perm;
 587                tbuf.msg_stime  = msq->msg_stime;
 588                tbuf.msg_rtime  = msq->msg_rtime;
 589                tbuf.msg_ctime  = msq->msg_ctime;
 590                tbuf.msg_cbytes = msq->msg_cbytes;
 591                tbuf.msg_qnum   = msq->msg_qnum;
 592                tbuf.msg_qbytes = msq->msg_qbytes;
 593                tbuf.msg_lspid  = msq->msg_lspid;
 594                tbuf.msg_lrpid  = msq->msg_lrpid;
 595                memcpy_tofs (buf, &tbuf, sizeof(*buf));
 596                return id;
 597        case IPC_SET:
 598                if (!buf)
 599                        return -EFAULT;
 600                err = verify_area (VERIFY_READ, buf, sizeof (*buf));
 601                if (err)
 602                        return err;
 603                memcpy_fromfs (&tbuf, buf, sizeof (*buf));
 604                break;
 605        case IPC_STAT:
 606                if (!buf)
 607                        return -EFAULT;
 608                err = verify_area (VERIFY_WRITE, buf, sizeof(*buf));
 609                if (err)
 610                        return err;
 611                break;
 612        }
 613
 614        id = (unsigned int) msqid % MSGMNI;
 615        msq = msgque [id];
 616        if (msq == IPC_UNUSED || msq == IPC_NOID)
 617                return -EINVAL;
 618        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
 619                return -EIDRM;
 620        ipcp = &msq->msg_perm;
 621
 622        switch (cmd) {
 623        case IPC_STAT:
 624                if (ipcperms (ipcp, S_IRUGO))
 625                        return -EACCES;
 626                tbuf.msg_perm   = msq->msg_perm;
 627                tbuf.msg_stime  = msq->msg_stime;
 628                tbuf.msg_rtime  = msq->msg_rtime;
 629                tbuf.msg_ctime  = msq->msg_ctime;
 630                tbuf.msg_cbytes = msq->msg_cbytes;
 631                tbuf.msg_qnum   = msq->msg_qnum;
 632                tbuf.msg_qbytes = msq->msg_qbytes;
 633                tbuf.msg_lspid  = msq->msg_lspid;
 634                tbuf.msg_lrpid  = msq->msg_lrpid;
 635                memcpy_tofs (buf, &tbuf, sizeof (*buf));
 636                return 0;
 637        case IPC_SET:
 638                if (!suser() && current->euid != ipcp->cuid && 
 639                    current->euid != ipcp->uid)
 640                        return -EPERM;
 641                if (tbuf.msg_qbytes > MSGMNB && !suser())
 642                        return -EPERM;
 643                msq->msg_qbytes = tbuf.msg_qbytes;
 644                ipcp->uid = tbuf.msg_perm.uid;
 645                ipcp->gid =  tbuf.msg_perm.gid;
 646                ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | 
 647                        (S_IRWXUGO & tbuf.msg_perm.mode);
 648                msq->msg_ctime = CURRENT_TIME;
 649                return 0;
 650        case IPC_RMID:
 651                if (!suser() && current->euid != ipcp->cuid && 
 652                    current->euid != ipcp->uid)
 653                        return -EPERM;
 654                /*
 655                 * There is only one kerneld message queue,
 656                 * mark it as non-existent
 657                 */
 658                if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid))
 659                        kerneld_msqid = -1;
 660                freeque (id); 
 661                return 0;
 662        default:
 663                return -EINVAL;
 664        }
 665}
 666
 667/*
 668 * We do perhaps need a "flush" for waiting processes,
 669 * so that if they are terminated, a call from do_exit
 670 * will minimize the possibility of orphaned received
 671 * messages in the queue.  For now we just make sure
 672 * that the queue is shut down whenever all kernelds have died.
 673 */
 674void kerneld_exit(void)
 675{
 676        int i;
 677
 678        if (kerneld_msqid == -1)
 679                return;
 680        for (i = 0; i < MAX_KERNELDS; ++i) {
 681                if (kerneld_arr[i] == current->pid) {
 682                        kerneld_arr[i] = 0;
 683                        --n_kernelds;
 684                        if (n_kernelds == 0)
 685                                sys_msgctl(kerneld_msqid, IPC_RMID, NULL);
 686                        break;
 687                }
 688        }
 689}
 690
 691/*
 692 * Kerneld internal message format/syntax:
 693 *
 694 * The message type from the kernel to kerneld is used to specify _what_
 695 * function we want kerneld to perform. 
 696 *
 697 * The "normal" message area is divided into a header, followed by a char array.
 698 * The header is used to hold the sequence number of the request, which will
 699 * be used as the return message type from kerneld back to the kernel.
 700 * In the return message, the header will be used to store the exit status
 701 * of the kerneld "job", or task.
 702 * The character array is used to pass parameters to kerneld and (optional)
 703 * return information from kerneld back to the kernel.
 704 * It is the responsibility of kerneld and the kernel level caller
 705 * to set usable sizes on the parameter/return value array, since
 706 * that information is _not_ included in the message format
 707 */
 708
 709/*
 710 * The basic kernel level entry point to kerneld.
 711 *      msgtype should correspond to a task type for (a) kerneld
 712 *      ret_size is the size of the (optional) return _value,
 713 *              OR-ed with KERNELD_WAIT if we want an answer
 714 *      msgsize is the size (in bytes) of the message, not including
 715 *              the header that is always sent first in a kerneld message
 716 *      text is the parameter for the kerneld specific task
 717 *      ret_val is NULL or the kernel address where an expected answer
 718 *              from kerneld should be placed.
 719 *
 720 * See <linux/kerneld.h> for usage (inline convenience functions)
 721 *
 722 */
 723int kerneld_send(int msgtype, int ret_size, int msgsz,
 724                const char *text, const char *ret_val)
 725{
 726        int status = -ENOSYS;
 727#ifdef CONFIG_KERNELD
 728        static int id = KERNELD_MINSEQ;
 729        struct kerneld_msg kmsp = { msgtype, NULL_KDHDR, (char *)text };
 730        int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
 731        unsigned long flags;
 732
 733        if (kerneld_msqid == -1)
 734                return -ENODEV;
 735
 736        /* Do not wait for an answer at interrupt-time! */
 737        if (intr_count)
 738                ret_size &= ~KERNELD_WAIT;
 739#ifdef NEW_KERNELD_PROTOCOL
 740        else
 741                kmsp.pid = current->pid;
 742#endif
 743
 744        msgsz += KDHDR;
 745        if (ret_size & KERNELD_WAIT) {
 746                save_flags(flags);
 747                cli();
 748                if (++id <= 0) /* overflow */
 749                        id = KERNELD_MINSEQ;
 750                kmsp.id = id;
 751                restore_flags(flags);
 752        }
 753
 754        status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
 755        if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
 756                ret_size &= ~KERNELD_WAIT;
 757                kmsp.text = (char *)ret_val;
 758                status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
 759                                KDHDR + ((ret_val)?ret_size:0),
 760                                kmsp.id, msgflg);
 761                if (status > 0) /* a valid answer contains at least a long */
 762                        status = kmsp.id;
 763        }
 764
 765#endif /* CONFIG_KERNELD */
 766        return status;
 767}
 768
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.