MsgRegisterEvent(), MsgRegisterEvent_r()

Updated: May 06, 2022

Register a secure event

Synopsis:

#include <sys/neutrino.h>

int MsgRegisterEvent( struct sigevent *event,
                      int coid );

int MsgRegisterEvent_r( struct sigevent *event,
                        int coid );

Arguments:

event
A pointer to the sigevent that you want to register as a secure event.
coid
The connection ID to the only server permitted to send the event, or -1 to allow all servers to send it. Using a value of -1 is more convenient because you don't need to learn the server's connection ID, but makes the event and registration mechanism far less secure, so this setting is not recommended.

The coid argument can contain any type of connection ID, including a side channel ID, message queue descriptor, socket descriptor, or file descriptor.

If you want to receive events from the process manager, you can pass in SYSMGR_COID for this argument.

Note: If you're registering a SIGEV_PULSE event, make sure you pass the correct connection ID to MsgRegisterEvent(). When you initialize the pulse event, you give it the connection ID on which to deliver the pulse, but the connection ID argument to MsgRegisterEvent() must be for the server that's permitted to deliver the event.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The MsgRegisterEvent() and MsgRegisterEvent_r() kernel calls register the given sigevent as a secure event. These functions are identical except in the way they indicate errors. See the Returns section for details.

MsgRegisterEvent() registers the given sigevent with the kernel and converts it into a handle that you can use in any API that passes an event. When the server calls MsgDeliverEvent() with a handle event, the kernel looks up the handle. If the handle exists and the server is allowed to deliver it, the kernel delivers the registered event. For more information about secure events, see Events in the “Interprocess Communication (IPC)” chapter of the System Architecture guide.

Note:
  • Calling MsgRegisterEvent() replaces the given event with a handle. If you want to use the same event later (e.g., you want to unregister it and register it again), save a copy of the original event before you call MsgRegisterEvent().
  • (QNX Neutrino 7.1 or later) By default, you can use only registered events, but you can use the -U option to procnto to allow unregistered events to be used temporarily while you're converting your programs to use registered events.
  • (QNX Neutrino 7.1 or later) You can detect when processes use unregistered events using secpolgenerate (via the file /dev/ secpolgenerate/errors) or secpolmonitor (when -u is specified).
  • (QNX Neutrino 7.0.4 or later) In order to register an event of type SIGEV_THREAD, your process must have the PROCMGR_AID_SIGEV_THREAD ability enabled. For more information, see procmgr_ability().
  • A server is allowed to provide its own value to the registered event if and only if the SIGEV_FLAG_UPDATEABLE flag is set on the event.
  • (QNX Neutrino 7.0.4 or later) A server is allowed to update the code for the registered event if and only if the SIGEV_FLAG_CODE_UPDATEABLE flag is set on the event.
  • If a server modifies the code or a value of a registered event before delivering it, and the client has not marked the UPDATEABLE field, then the event is successfully delivered with the original value that the client registered.

You can use MsgUnregisterEvent() to unregister the secure event.

Blocking states

None.

Returns:

The only difference between these functions is the way they indicate errors:

MsgRegisterEvent()
If successful, this function returns EOK. If an error occurs, it returns -1 and sets errno.
MsgRegisterEvent_r()
If successful, this function returns EOK. This function does NOT set errno, even on success. If an error occurs, it may return any value from the Errors section.

Errors:

EAGAIN
No more events can be registered for the process. See the RLIMIT_SIGEVENT_NP resource for setrlimit().
EINVAL
One of the following occurred:
  • The connection ID is invalid.
  • The event is already registered as a secure event.
ENOMEM
There wasn't enough memory available to register the event.

Examples:

In this example, the parent process acts as the server, and the child acts as the client.

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <sys/wait.h>

struct message_s
{
    uint16_t        type;
    uint16_t        subtype;
    struct sigevent event;
    struct sigevent updateable;
    struct sigevent forbidden;
};

static int
child(int chid)
{
    // Mask SIGUSR1 and SIGUSR2.
    sigset_t    sigset;
    siginfo_t   siginfo;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGUSR1);
    sigaddset(&sigset, SIGUSR2);

    if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
        perror("sigprocmask");
        return 1;
    }

    // Attach to the server, indicating that we don't want unregistered events.
    int coid = ConnectAttach(0, getppid(), chid, _NTO_SIDE_CHANNEL,
                             _NTO_COF_REG_EVENTS);
    if (coid < 0) {
        perror("ConnectAttach");
        return 1;
    }

    // Register three events:
    // 1. Non-updateable, on a connection to the parent server
    // 2. Updateable, on a connection to the parent server
    // 3. On a connection to procnto
    struct message_s    msg = { .type = 1 };

    // First event.
    SIGEV_SIGNAL_VALUE_INIT(&msg.event, SIGUSR1, 13);

    if (MsgRegisterEvent(&msg.event, coid) < 0) {
        perror("MsgRegisterEvent(&msg.event)");
        return 1;
    }

    // Second event.
    SIGEV_SIGNAL_VALUE_INIT(&msg.updateable, SIGUSR2, 34);
    SIGEV_MAKE_UPDATEABLE(&msg.updateable);

    if (MsgRegisterEvent(&msg.updateable, coid) < 0) {
        perror("MsgRegisterEvent(&msg.updateable)");
        return 1;
    }

    // Third event.
    SIGEV_SIGNAL_VALUE_INIT(&msg.forbidden, SIGUSR2, 34);

    if (MsgRegisterEvent(&msg.forbidden, SYSMGR_COID) < 0) {
        perror("MsgRegisterEvent(&msg.forbidden)");
        return 1;
    }

    // Send the server a message that includes the sigevents.
    if (MsgSend(coid, &msg, sizeof(msg), NULL, 0) < 0) {
        perror("MsgSend");
        return 1;
    }

    // Wait for event delivery.
    int i = 0;
    for (i = 0; i < 2; i++) {
        printf("Waiting for SIGUSR1/SIGUSR2\n");
        if (sigwaitinfo(&sigset, &siginfo) < 0) {
            perror("sigwaitinfo");
            return 1;
        }

        switch (siginfo.si_signo) {
        case SIGUSR1:
            printf("Received SIGUSR1 with value %d\n", siginfo.si_value.sival_int);
            if (siginfo.si_value.sival_int != 13) {
                fprintf(stderr, "SIGUSR1: Expected value 13, got %d\n",
                        siginfo.si_value.sival_int);
                return 1;
            }
            break;

        case SIGUSR2:
            printf("Received SIGUSR2 with value %d\n", siginfo.si_value.sival_int);
            if (siginfo.si_value.sival_int != 57) {
                fprintf(stderr, "SIGUSR1: Expected value 57, got %d\n",
                        siginfo.si_value.sival_int);
                return 1;
            }
            break;

        default:
            fprintf(stderr, "Unexpected signal %d\n", siginfo.si_signo);
            return 1;
        }
    }

    return 0;
}

int
main(int argc, char **argv)
{
    // Create a channel.
    int chid = ChannelCreate(0);
    if (chid < 0) {
        perror("ChannelCreate");
        return 1;
    }

    // Create a child process.
    pid_t   pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        return child(chid);
    }

    // Handle messages from the child.
    int kill_child = 1;
    for (;;) {
        struct message_s    msg;
        int                 rcvid;

        rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL);
        if (rcvid < 0) {
            perror("MsgReceive");
            break;
        }

        if (rcvid == 0) {
            continue;
        }

        printf("Received message type %d\n", msg.type);
        MsgReply(rcvid, 0, NULL, 0);
        kill_child = 0;

        // Try a handle for an event that this server isn't allowed to send.
        if ((MsgDeliverEvent(rcvid, &msg.forbidden) != -1) || (errno != EACCES)) {
            perror("MsgDeliverEvent(forbidden)");
            break;
        }

        // Try an unregistered event. It shouldn't be delivered.
        struct sigevent ev;
        SIGEV_SIGNAL_INIT(&ev, SIGUSR1);
        if ((MsgDeliverEvent(rcvid, &ev) != -1) || (errno != EACCES)) {
            perror("MsgDeliverEvent(unregistered)");
            break;
        }

        // Try a legitimate handle that doesn't have an updateable value.
        // The client should get the original value that it registered.
        msg.event.sigev_value.sival_int = 57;
        if (MsgDeliverEvent(rcvid, &msg.event) < 0) {
            perror("MsgDeliverEvent");
            break;
        }

        // Try a legitimate handle that has an updateable value.
        msg.updateable.sigev_value.sival_int = 57;
        if (MsgDeliverEvent(rcvid, &msg.updateable) < 0) {
            perror("MsgDeliverEvent");
            break;
        }

        break;
    }

    if (kill_child) {
        kill(pid, SIGKILL);
    }

    int     status;
    pid_t   wpid = waitpid(pid, &status, 0);
    if (wpid != pid) {
        perror("waitpid");
        return 1;
    }

    printf("Child exited with status %d\n", WEXITSTATUS(status));
    return 0;
}

Classification:

QNX Neutrino

Safety:  
Cancellation point No
Interrupt handler No
Signal handler Yes
Thread Yes