Keywords: lpg

Next: msgtool: An interactive message Up: 6.4.2 Message Queues Previous: SYSTEM CALL: msgsnd()

### SYSTEM CALL: msgctl()

Through the development of the wrapper functions presented earlier, you now have a simple, somewhat elegant approach to creating and utilizing message queues in your applications. Now, we will turn the discussion to directly manipulating the internal structures associated with a given message queue.

To perform control operations on a message queue, you use the `msgctl()` system call.

      SYSTEM CALL: msgctl();
      PROTOTYPE: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
        RETURNS: 0 on success
                 -1 on error: errno = EACCES (No read permission and cmd is IPC_STAT)
                                      EFAULT (Address pointed to by buf is invalid with IPC_SET and
                                              IPC_STAT commands)
                                      EIDRM  (Queue was removed during retrieval)
                                      EINVAL (msgqid invalid, or msgsz less than 0)
                                      EPERM  (IPC_SET or IPC_RMID command was issued, but
                                              calling process does not have write (alter)
                                              access to the queue)
      NOTES:

Now, common sense dictates that direct manipulation of the internal kernel data structures could lead to some late night fun. Unfortunately, the resulting duties on the part of the programmer could only be classified as fun if you like trashing the IPC subsystem. By using `msgctl()` with a selective set of commands, you have the ability to manipulate those items which are less likely to cause grief. Let's look at these commands:

IPC_STAT

  Retrieves the msqid_ds structure for a queue, and stores it in the address of the buf argument.

IPC_SET

  Sets the value of the ipc_perm member of the msqid_ds structure for a queue. Takes the values from the buf argument.

IPC_RMID

  Removes the queue from the kernel.

Recall our discussion about the internal data structure for message queues (`msqid_ds`). The kernel maintains an instance of this structure for each queue which exists in the system. By using the IPC_STAT command, we can retrieve a copy of this structure for examination. Let's look at a quick wrapper function that will retrieve the internal structure and copy it into a passed address:

    int get_queue_ds( int qid, struct msgqid_ds *qbuf )
    {
            if( msgctl( qid, IPC_STAT, qbuf) == -1)
            {
                    return(-1);
            }

            return(0);
    }

If we are unable to copy the internal buffer, -1 is returned to the calling function. If all went well, a value of 0 (zero) is returned, and the passed buffer should contain a copy of the internal data structure for the message queue represented by the passed queue identifier (`qid`).

Now that we have a copy of the internal data structure for a queue, what attributes can be manipulated, and how can we alter them? The only modifiable item in the data structure is the `ipc_perm` member. This contains the permissions for the queue, as well as information about the owner and creator. However, the only members of the `ipc_perm` structure that are modifiable are `mode`, `uid`, and `gid`. You can change the owner's user id, the owner's group id, and the access permissions for the queue.

Let's create a wrapper function designed to change the mode of a queue. The mode must be passed in as a character array (i.e. ``660'').

    int change_queue_mode( int qid, char *mode )
    {
            struct msqid_ds tmpbuf;

            /* Retrieve a current copy of the internal data structure */
            get_queue_ds( qid, &tmpbuf);

            /* Change the permissions using an old trick */
            sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);

            /* Update the internal data structure */
            if( msgctl( qid, IPC_SET, &tmpbuf) == -1)
            {
                    return(-1);
            }

            return(0);
    }

We retrieve a current copy of the internal data structure by a quick call to our `get_queue_ds` wrapper function. We then make a call to `sscanf()` to alter the `mode` member of the associated `msg_perm` structure. No changes take place, however, until the new copy is used to update the internal version. This duty is performed by a call to `msgctl()` using the IPC_SET command.

BE CAREFUL! It is possible to alter the permissions on a queue, and in doing so, inadvertently lock yourself out! Remember, these IPC objects don't go away unless they are properly removed, or the system is rebooted. So, even if you can't see a queue with `ipcs` doesn't mean that it isn't there.

> To illustrate this point, a somewhat humorous anecdote seems to be in order. While teaching a class on UNIX internals at the University of South Florida, I ran into a rather embarrassing stumbling block. I had dialed into their lab server the night before, in order to compile and test the labwork to be used in the week-long class. In the process of my testing, I realized that I had made a typo in the code used to alter the permissions on a message queue. I created a simple message queue, and tested the sending and receiving capabilities with no incident. However, when I attempted to change the mode of the queue from ``660'' to ``600'', the resulting action was that I was locked out of my own queue! As a result, I could not test the message queue labwork in the same area of my source directory. Since I used the ftok() function to create the IPC key, I was trying to access a queue that I did not have proper permissions for. I ended up contacting the local system administrator on the morning of the class, only to spend an hour explaining to him what a message queue was, and why I needed him to run the ipcrm command for me. grrrr.

After successfully retrieving a message from a queue, the message is removed. However, as mentioned earlier, IPC objects remain in the system unless explicitly removed, or the system is rebooted. Therefore, our message queue still exists within the kernel, available for use long after a single message disappears. To complete the life cycle of a message queue, they should be removed with a call to `msgctl()`, using the IPC_RMID command:

    int remove_queue( int qid )
    {
            if( msgctl( qid, IPC_RMID, 0) == -1)
            {
                    return(-1);
            }

            return(0);
    }

This wrapper function returns 0 if the queue was removed without incident, else a value of -1. The removal of the queue is atomic in nature, and any subsequent accesses to the queue for whatever purpose will fail miserably.

Next: msgtool: An interactive message Up: 6.4.2 Message Queues Previous: SYSTEM CALL: msgsnd()