MsgRegisterEvent(), MsgRegisterEvent_r()
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 _NTO_REGEVENT_ALLOW_ANY_SERVER
to allow any server to send it. Using the special value of _NTO_REGEVENT_ALLOW_ANY_SERVER 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.
There are two other special values that you can pass in:- _NTO_REGEVENT_ALLOW_SELF — only the caller can send this event to themselves
- SYSMGR_COID — only the process manager can send this event
The special values are mutually exclusive; you can set only one of them.
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.
- 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().
- 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.
- You can detect when processes use unregistered events using secpolgenerate (via the file /dev/ secpolgenerate/errors) or secpolmonitor (when -u is specified).
- 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.
- 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.
int coid = ConnectAttach(0, getppid(), chid, _NTO_SIDE_CHANNEL, 0);
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:
Safety: | |
---|---|
Cancellation point | No |
Signal handler | Yes |
Thread | Yes |