I am sure I am missing something obvious but my problem is as follows.
As a programming exercise I am trying to create an async posix message queue server to handle incoming messages asynchronously in a handler.
Now according to my documentation sigaction(2), mq_notify(3) and sigevent(7) my thought process should work. Before I post my code I'll outline my thought process on how it should be working.
sigaction
and SA_SIGINFO
I will set the signal handler for SIGIO
. mq_notify
and struct sigevent
I will tell my message queue to send SIGIO
to the current process when a message arrives. mqd_t
for my message queue into the struct sigevent.sigen_value.sigval_ptr
I will be able to retrieve this value in my signal handler through struct siginfo
. sigevent(7)
tells me that when SIGEV_SIGNAL
is set in sigev_notify
then si_code
, si_signo
and si_value
are set in the signal handler's siginfo
. Specifically si_value
is set to the value passed in for sigev_value
when calling mq_nofity
. mqd_t
by doing something like (mqd_t *)(info->si_value.sival_ptr)
where info is the siginfo_t*
passed as an argument to my signal handler. I have my mqd_t
in a struct mq_info_t
as I will later do some more stuff with this.Now my problem seems to be stemming from something different, but I thought I would outline my thought process in case it is somehow relevant. According to sigaction(2)
, siginfo_t
is a large struct containing all sorts of information, including si_value
.
Now in my gdb I am getting the error that my siginfo_t
in my signal handler does not have a member named si_value
. The same idea is mentioned in this question here.
(gdb) p *info $1 = {si_signo = 29, si_errno = 0, si_code = -3, __pad0 = 0, _sifields = {_pad = {128611, 1000, -8896, 32767, 0 }, _kill = {si_pid = 128611, si_uid = 1000}, _timer = {si_tid = 128611, si_overrun = 1000, si_sigval = { sival_int = -8896, sival_ptr = 0x7fffffffdd40}}, _rt = {si_pid = 128611, si_uid = 1000, si_sigval = {sival_int = -8896, sival_ptr = 0x7fffffffdd40}}, _sigchld = {si_pid = 128611, si_uid = 1000, si_status = -8896, si_utime = 0, si_stime = 0}, _sigfault = {si_addr = 0x3e80001f663, si_addr_lsb = -8896, _bounds = {_addr_bnd = {_lower = 0x0, _upper = 0x0}, _pkey = 0}}, _sigpoll = {si_band = 4294967424611, si_fd = -8896}, _sigsys = {_call_addr = 0x3e80001f663, _syscall = -8896, _arch = 32767}}}
Looking at where siginfo_t
is defined for my build I found that the structure defined in /usr/include/bits/types/siginfo_t.h
had the same structure. I am thinking this is some issue to do with the feature macros of my builds but I am do not know much in that area. My build has #define _POSIX_C_SOURCE 199309L
.
Now the key components of my code are as follows
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <mqueue.h>
#include <string.h>
typedef struct mq_info {
mqd_t fd;
} mq_info_t;
/** Handler for receiving async messages */
void sigHandler(int signal, siginfo_t *info, void *context)
{
char buffer[MAX_SIZE + 1];
int must_stop = 0;
ssize_t bytes_read;
mq_info_t *mq_i = (mq_info_t *)(info->si_value.sival_ptr);
mqd_t mq = mq_i->fd;
printf("In handler\n");
do {
bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);
printf("MQ received: %s\n", buffer);
} while (bytes_read > 0);
printf("left handler\n");
}
int openMessageQueue(char *name, long max_msg_num, long max_msg_size)
{
mq_info_t *mq_i = calloc(1, sizeof(mq_info_t));
struct mq_attr attr;
struct sigaction sa;
struct sigevent ev;
union sigval sv = { .sival_ptr = &mq_i };
attr.mq_flags = O_NONBLOCK; // Async
attr.mq_maxmsg = max_msg_num;
attr.mq_msgsize = max_msg_size;
attr.mq_curmsgs = 0; // Num of messages currently in queue
if (-1 == (mq_i->fd = mq_open(name, O_CREAT | O_RDONLY, 0644, &attr)))
goto error;
printf("queue opened\n");
/** Setup handler for SIGIO */
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sigHandler;
printf("Sighandler: %p\n", sigHandler);
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGIO);
if (sigaction(SIGIO, &sa, NULL))
goto error;
printf("sigaction done\n");
/** Set up process to be informed about async queue event */
ev.sigev_notify = SIGEV_SIGNAL; // Specify a signal should be sen
ev.sigev_signo = SIGIO; // Signal of interest
ev.sigev_value =
sv; // Suplementary data passed to signal handling fuction
ev.sigev_notify_function = NULL; // Used by SIGEV_THREAD
ev.sigev_notify_attributes = NULL; // Used by SIGEV_THREAD
/** Register this process to receive async notifications when a new message */
/** arrives on the specified message queue */
if (mq_notify(mq_i->fd, &ev) < 0) {
perror("notify failed");
goto error;
}
printf("notify done\n");
return 0;
error:
mq_unlink(name);
return -1;
}
Thanks in advance, I hope I have provided all relevant infromation.
Arch Linux w 5.3.8 kernel
Look at the very bottom of bits/types/siginfo_t.h
and you will see this:
/* X/Open requires some more fields with fixed names. */
#define si_pid _sifields._kill.si_pid
#define si_uid _sifields._kill.si_uid
#define si_timerid _sifields._timer.si_tid
#define si_overrun _sifields._timer.si_overrun
#define si_status _sifields._sigchld.si_status
#define si_utime _sifields._sigchld.si_utime
#define si_stime _sifields._sigchld.si_stime
#define si_value _sifields._rt.si_sigval
// ...
So, in your code, when you write info->si_value.sival_ptr
it gets macro-expanded to info->_sifields._rt.si_sigval.sival_ptr
and the compiler "proper" is happy. But GDB doesn't know about the macro definition of si_value
, so you have to type that out yourself when accessing the field from the debugger.
(Yes, this is horrible, but we can't clean it up without breaking the ABI.)
(Why are you using #define _POSIX_C_SOURCE 199309L
? It's not the immediate cause of your problem, but you will probably be happier working with a more modern conformance mode, I recommend #define _XOPEN_SOURCE 700
for new code that should be portable among current-generation Unixes.)