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