fdman (short for fd manager) is a short and simple callback driven file descriptor (fd) multiplexer. It is useful when dealing with a large set of mixed types of descriptors. This is another component of my free time project. At the moment I'm working on a secure UDP communication library that uses fdman. Since UDP isn't the only communication that daemon needs to do (it needs to do local (unix) domain communication for example), I need something that centrally manages the descriptors.
The header:
#include <sys/select.h>
#include <sys/types.h>
#ifndef FDMAN_H
#define FDMAN_H
#define FDMAN_MAX FD_SETSIZE
#define FDMAN_READ 0x01
#define FDMAN_WRITE 0x02
#define FDMAN_EXCEPT 0x04
struct fdarg {
int fd;
uint8_t opt;
void *arg;
void (*cb)( struct fdarg *fda );
};
struct fdman {
struct fdarg fda[FDMAN_MAX];
fd_set rfds, wfds, efds;
int maxfd;
};
void fdman_init( struct fdman *fdm );
void fdman_set( struct fdman *fdm, struct fdarg *fda );
void fdman_unset( struct fdman *fdm, int fd );
int fdman_select( struct fdman *fdm, struct timeval *to );
#endif
Parameters
struct fdarg {
int fd; /* Descriptor */
uint8_t opt; /* Option bit mask, can be OR'd FDMAN_READ, FDMAN_WRITE, FDMAN_EXCEPT */
void *arg; /* Arbitrary argument pointer ... useful for the callback function */
void (*cb)( struct fdarg *fda ); /* Callback function */
};
When used as a parameter for fdman_set, the opt bitmask is used to set desired events. When used with the callback function, the opt bitmask reflects the events that occurred.
Functions
void fdman_init( struct fdman *fdm ); /* Initializes a fdman struct */
void fdman_set( struct fdman *fdm, struct fdarg *fda ); /* Sets or updates settings for a descriptor */
void fdman_unset( struct fdman *fdm, int fd ); /* Unsets the descriptor */
int fdman_select( struct fdman *fdm, struct timeval *to ); /* A front to select(), however it performs all the callbacks ... a negative return reflects an error */
Example
This is a simple example that uses two descriptors. One for the keyboard, and one for an arbitrary TCP/IP connection.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sysexits.h>
#include <fcntl.h>
#include "fdman.h"
struct chat {
int fd;
struct fdman *fdm;
};
void kbdcb( struct fdarg *fda ) {
struct chat *ch = (struct chat *)(fda->arg);
/* Read from keyboard here */
}
void botcb( struct fdarg *fda ) {
uint8_t opt = fda->opt;
ctruct chat *ch = (struct chat *)(fda->arg);
if ( opt & FDMAN_WRITE ) {
printf( "Connected!\n" );
fda->opt = FDMAN_READ;
fdman_set( ch->fdm, fda );
}
if ( opt & FDMAN_READ ) {
/* Read from socket here */
}
}
int main( int argc, char **argv ) {
int arg;
struct fdman fdm;
struct fdarg fda;
struct chat ch;
fdman_init( &fdm );
fda.fd = STDIN_FILENO;
fda.arg = &ch;
fda.opt = FDMAN_READ;
fda.cb = kbdcb;
fdman_set( &fdm, &fda );
ch.fdm = &fdm;
ch.fd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( ch.fd < 0 ) {
perror( "socket()" );
exit( EX_OSERR );
}
arg = fcntl( ch.fd, F_GETFL );
fcntl( ch.fd, F_SETFL, arg | O_NONBLOCK );
fda.fd = ch.fd;
fda.arg = &ch;
fda.cb = botcb;
fda.opt = FDMAN_READ | FDMAN_WRITE;
fdman_set( &fdm, &fda );
/* Connect and other stuff here */
while ( fdman_select( &fdm, NULL ) >= 0 );
return 0;
}