Raw socket I/O from inside C app

Hello, I am trying to set up a C function to be called by extensions.conf to open a socket, send raw data to the socket, wait for a response, and return a result. This is to create an IVR front-end for an old, legacy controller from the early 1990’s. Generally, something like this:

static int load_module(void)
{
// etc…
ast_register_application_xml(“query_at4”, query_at4)
// etc…
}

static int query_at4(struct ast_channel * chan, const char * data)
{
// open socket, send data, wait for response close socket
// …or grab an open socket from a pool and do it
}

Is this a reasonable approach? If so, are there any close examples? I’m unclear on how to use (for instance) select() to block/unblock channel threads without causing problems.

Asterisk provides the ast_waitfor_nandfds function (documented in channel.h) which waits for data on both channels passed in and file descriptors. It should be used, otherwise frames will pile up on the channel and cause problems.

There’s no real example specifically for what you’re wanting to do, as it’s rather a niche thing.

1 Like

Thank you. That was a very helpful starting point.

For posterity, one way to do this is to set up a worker pthread (or threads) to handle the messy socket protocol stuff – connecting, reconnecting, sending, receiving, blocking, etc. As needed, a pair of unnamed pipes (request and reply) gets set up for each “client” channel to access the worker thread. The worker thread serializes and proxies the pipe requests into socket transactions. To “talk” to the socket device, the channel threads send their request down their request pipe, then uses something wrapped around ast_waitfor_nandfds(…) to wait on the response pipe handle. The worker thread serializes these and converts them into socket activity. load_module() starts up the worker thread, and unload_module() tears it down. A good template to use for the code waiting on the pipe response is ast_waitfordigit_full(…). This is probably not the most perfect way, and even this way could be optimized significantly (like one shared request pipe) but it’s a way…

Also for posterity, don’t use select() in your worker threads. Use poll() instead. Or, at least research select() further. The asterisk headers re-write the select() related macros (eg, FD_ZERO) which will leave you studying core dumps.