From dc706ef662bda4a843aa088696132078472400d5 Mon Sep 17 00:00:00 2001 From: ihm Date: Thu, 5 Mar 1998 17:31:32 +0000 Subject: Initial revision --- Makefile | 27 +++ bus.c | 693 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bus.h | 75 +++++++ list.h | 55 +++++ socket.c | 667 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ socket.h | 87 ++++++++ testbus.c | 184 +++++++++++++++++ timer.c | 160 +++++++++++++++ timer.h | 18 ++ 9 files changed, 1966 insertions(+) create mode 100644 Makefile create mode 100644 bus.c create mode 100644 bus.h create mode 100644 list.h create mode 100644 socket.c create mode 100644 socket.h create mode 100644 testbus.c create mode 100644 timer.c create mode 100644 timer.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c4e6a73 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +XTINC = +CC=gcc +OBJ = timer.o socket.o bus.o +GOBJ = timer.o socket.o gbus.o +XTOBJ = timer.o xtsocket.o bus.o + +all: libbus.a libgbus.a libxtbus.a testbus + +gbus.o: bus.c + $(CC) -DGNU_REGEXP -c $(CFLAGS) -o gbus.o bus.c + +xtsocket.o: socket.c + $(CC) -DXTMAINLOOP $(XTINC) -c $(CFLAGS) -o xtsocket.o socket.c + +testbus: testbus.o $(OBJ) + $(CC) -o testbus testbus.o $(OBJ) + +libbus.a: $(OBJ) + ar q libbus.a $(OBJ) + +libgbus.a: $(GOBJ) + ar q libgbus.a $(GOBJ) + +libxtbus.a: $(XTOBJ) + ar q libxtbus.a $(XTOBJ) + + diff --git a/bus.c b/bus.c new file mode 100644 index 0000000..18a5a67 --- /dev/null +++ b/bus.c @@ -0,0 +1,693 @@ +/* + * + * $Id$ + */ +#include + +#include +#include +#include + +#include + +#include + + +#include "socket.h" +#include "list.h" +#include "bus.h" + +#define VERSION 3 + +#define MAX_MATCHING_ARGS 20 + +#define ARG_START "\002" +#define ARG_END "\003" + + +typedef enum { + + Bye, /* l'application emettrice se termine */ + AddRegexp, /* expression reguliere d'un client */ + Msg, /* message reel */ + Error, /* error message */ + DelRegexp, /* Remove expression reguliere */ + EndRegexp, /* end of the regexp list */ + StartRegexp, /* debut des expressions */ + DirectMsg, /* message direct a destination de l'appli */ + Die /* demande de terminaison de l'appli */ + +}MsgType; + + +typedef struct _msg_snd *MsgSndPtr; + +struct _msg_rcv { /* requete d'emission d'un client */ + MsgRcvPtr next; + int id; + const char *regexp; /* regexp du message a recevoir */ + MsgCallback callback; /* callback a declanche a la reception */ + void *user_data; /* stokage d'info client */ +}; + + +struct _msg_snd { /* requete de reception d'un client */ + MsgSndPtr next; + int id; + char *str_regexp; /* la regexp sous forme inhumaine */ + regex_t regexp; /* la regexp sous forme machine */ +}; + +struct _clnt_lst { + BusClientPtr next; + Client client; /* la socket client */ + MsgSndPtr msg_send; /* liste des requetes recues */ + char *app_name; /* nom de l'application */ + unsigned short app_port; /* port de l'application */ + }; + +/* numero de port TCP en mode serveur */ +static unsigned short ApplicationPort; + +/* numero de port UDP */ +static unsigned short SupervisionPort; + +/* client pour la socket supervision */ +static Client broadcast; + +static const char *ApplicationName = NULL; + +/* classes de messages emis par l'application utilise pour le filtrage */ +static int messages_classes_count = 0; +static char **messages_classes = NULL; + +/* callback appele sur reception d'un message direct */ +static MsgDirectCallback direct_callback = NULL; +static *direct_user_data = NULL; + +/* callback appele sur changement d'etat d'application */ +static BusApplicationCallback application_callback; +static *application_user_data = NULL; + +/* callback appele sur demande de terminaison d'application */ +static BusDieCallback application_die_callback; +static *application_die_user_data = NULL; + +/* liste des messages a recevoir */ +static MsgRcvPtr msg_recv = NULL; + + +/* liste des clients connectes */ +static BusClientPtr clients = NULL; + +static const char *ready_message = NULL; + +static void MsgSendTo( Client client,MsgType msgtype, int id, const char *message ) +{ +SocketSend( client, "%d %d" ARG_START "%s\n",msgtype,id,message); +} +static void BusCleanup() +{ +BusClientPtr clnt; + + /* destruction des connexion clients */ + LIST_EACH( clients, clnt ) + { + /* on dit au revoir */ + MsgSendTo( clnt->client, Bye, 0, "" ); + SocketClose( clnt->client ); + LIST_EMPTY( clnt->msg_send ); + } + LIST_EMPTY( clients ); + +} +static int MsgCall( const char *message, MsgSndPtr msg, Client client ) +{ + regmatch_t match[MAX_MATCHING_ARGS+1]; +#ifdef GNU_REGEXP + regmatch_t* p; +#else + unsigned int i; +#endif + + if (regexec(&msg->regexp, message, MAX_MATCHING_ARGS, match, 0)==0) { +#ifdef DEBUG + printf( "Sending message id=%d '%s'\n",msg->id,message); +#endif + SocketSend( client, "%d %d" ARG_START ,Msg, msg->id); + +#ifdef DEBUG + printf( "Send matching args count %d\n",msg->regexp.re_nsub); +#endif //DEBUG + +#ifdef GNU_REGEXP + p = &match[1]; + while ( p->rm_so != -1 ) { + SocketSend( client, "%.*s" ARG_END , p->rm_eo - p->rm_so, + message + p->rm_so); + ++p; + } +#else + for ( i = 1; i < msg->regexp.re_nsub+1; i ++ ) + { + if ( match[i].rm_so != -1 ) + { +#ifdef DEBUG + printf( "Send matching arg%d %d %d\n",i,match[i].rm_so , match[i].rm_eo); + printf( "Send matching arg%d %.*s\n",i,match[i].rm_eo - match[i].rm_so, + message + match[i].rm_so); +#endif + SocketSend( client, "%.*s" ARG_END ,match[i].rm_eo - match[i].rm_so, + message + match[i].rm_so); + } + else + { + SocketSend( client, ARG_END ); +#ifdef DEBUG + printf( "Send matching arg%d VIDE\n",i); +#endif //DEBUG + } + } +#endif + + SocketSend( client, "\n"); + return 1; + } + return 0; +} + +int ClientCall( BusClientPtr clnt, const char *message ) +{ +MsgSndPtr msg; +int match_count = 0; + /* recherche dans la liste des requetes recues de ce client */ + LIST_EACH( clnt->msg_send, msg ) + { + match_count+= MsgCall( message, msg, clnt->client ); + } + return match_count; +} +static int CheckRegexp(char *exp) +{ + /* accepte tout par default */ + int i; + int regexp_ok = 1; + if ( *exp =='^' && messages_classes_count !=0 ) + { + regexp_ok = 0; + for ( i = 0 ; i < messages_classes_count; i++ ) + { + if (strncmp( messages_classes[i], exp+1, strlen( messages_classes[i] )) == 0) + return 1; + } + } + return regexp_ok; +} +static int CheckConnected( BusClientPtr clnt ) +{ +BusClientPtr client; +struct in_addr *addr1; +struct in_addr *addr2; + if ( clnt->app_port == 0 ) + return 0; + /* recherche dans la liste des clients de la presence de clnt */ + LIST_EACH( clients, client ) + { + /* client different mais port identique */ + if ( (client != clnt) && (clnt->app_port == client->app_port) ) + { + /* et meme machine */ + addr1 = SocketGetRemoteAddr( client->client ); + addr2 = SocketGetRemoteAddr( clnt->client ); + if ( addr1->s_addr == addr2->s_addr ) + return 1; + } + + } + return 0; +} +static void Receive( Client client, void *data, char *line ) +{ + BusClientPtr clnt; + int err,id,reg; + MsgSndPtr snd; + MsgRcvPtr rcv; + int argc = 0; + char *argv[MAX_MATCHING_ARGS]; + char *arg; + int kind_of_msg = Bye; + regex_t regexp; + + err = sscanf( line ,"%d %d", &kind_of_msg, &id ); + arg = strstr( line , ARG_START ); + if ( (err != 2) || (arg == NULL) ) + { + printf("Quitting bad format %s\n", line); + MsgSendTo( client, Error, Error, "bad format request expected 'type id ...'" ); + MsgSendTo( client, Bye, 0, "" ); + SocketClose( client ); + return; + } + arg++; + clnt = (BusClientPtr)data; + switch( kind_of_msg ) + { + case Bye: + +#ifdef DEBUG + printf("Quitting %s\n", line); +#endif //DEBUG + + SocketClose( client ); + break; + case Error: + printf("Receive error %d %s\n", id, arg); + break; + case AddRegexp: + +#ifdef DEBUG + printf("Regexp id=%d exp='%s'\n", id, arg); +#endif //DEBUG + if ( !CheckRegexp( arg ) ) + { +#ifdef DEBUG + printf("Warning exp='%s' can't match removing from %s\n",arg,ApplicationName); +#endif //DEBUG + return; + } + reg = regcomp(®exp, arg, REG_ICASE|REG_EXTENDED); + if ( reg == 0 ) + { + LIST_ADD( clnt->msg_send, snd ) + if ( snd ) + { + snd->id = id; + snd->str_regexp = strdup( arg ); + snd->regexp = regexp; + } + } + else + { + char errbuf[4096]; + regerror (reg, ®exp, errbuf, 4096); + printf("Error compiling '%s', %s\n",arg,errbuf); + MsgSendTo( client, Error, reg, errbuf ); + } + break; + case DelRegexp: + +#ifdef DEBUG + printf("Regexp Delete id=%d\n", id); +#endif //DEBUG + + LIST_ITER( clnt->msg_send, snd, ( id != snd->id )); + if ( snd ) + { + free( snd->str_regexp ); + LIST_REMOVE( clnt->msg_send, snd ); + } + break; + case StartRegexp: + +#ifdef DEBUG + printf("Regexp Start id=%d App='%s'\n", id, arg); +#endif //DEBUG + clnt->app_name = strdup( arg ); + clnt->app_port = id; + if ( CheckConnected( clnt ) ) + { +#ifdef DEBUG + printf("Quitting already connected %s\n", line); +#endif //DEBUG + SendError( clnt, 0, "Application already connected" ); + SocketClose( client ); + } + break; + case EndRegexp: + +#ifdef DEBUG + printf("Regexp End id=%d\n", id); +#endif //DEBUG + if ( application_callback ) + { + (*application_callback)( clnt, application_user_data, BusApplicationConnected ); + } + if ( ready_message ) + { + int count; + count = ClientCall( clnt, ready_message ); + +#ifdef DEBUG + printf(" Sendind ready message %d\n", count); +#endif //DEBUG + + } + break; + case Msg: + +#ifdef DEBUG + printf("Message id=%d msg='%s'\n", id, arg); +#endif //DEBUG + + LIST_EACH( msg_recv, rcv ) + { + if ( id == rcv->id ) + { + arg = strtok( arg, ARG_END); + while ( arg ) + { + argv[argc++] = arg; + arg = strtok( NULL, ARG_END ); + } +#ifdef DEBUG + printf("Calling id=%d argc=%d for %s\n", id, argc,rcv->regexp); +#endif + if ( rcv->callback ) (*rcv->callback)( clnt, rcv->user_data, argc, argv ); + return; + } + } + printf("Callback Message id=%d not found!!!'\n", id); + break; + case DirectMsg: + +#ifdef DEBUG + printf("Direct Message id=%d msg='%s'\n", id, arg); +#endif //DEBUG + + if ( direct_callback) + (*direct_callback)( clnt, direct_user_data, id, arg ); + break; + + case Die: + +#ifdef DEBUG + printf("Die Message\n",); +#endif //DEBUG + + if ( application_die_callback) + (*application_die_callback)( clnt, application_die_user_data, id ); + BusCleanup(); + exit(0); + break; + + default: + printf("Receive unhandled message %s\n", line); + break; + } + +} + +static BusClientPtr SendService( Client client ) +{ + BusClientPtr clnt; + MsgRcvPtr msg; + LIST_ADD( clients, clnt ) + if ( clnt ) + { + clnt->msg_send = NULL; + clnt->client = client; + clnt->app_name = strdup("Unknown"); + clnt->app_port = 0; + MsgSendTo( client, StartRegexp, ApplicationPort, ApplicationName); + LIST_EACH(msg_recv, msg ) + { + MsgSendTo( client, AddRegexp,msg->id,msg->regexp); + } + MsgSendTo( client, EndRegexp, 0, ""); + } + return clnt; +} + +static void ClientDelete( Client client, void *data ) +{ + BusClientPtr clnt; + MsgSndPtr msg; + char *remotehost; + unsigned short remoteport; + clnt = (BusClientPtr)data; + if ( application_callback ) + { + (*application_callback)( clnt, application_user_data, BusApplicationDisconnected ); + } + SocketGetRemote( client, &remotehost, &remoteport ); + +#ifdef DEBUG + printf("Deconnexion de %s:%hu\n", remotehost, remoteport ); +#endif //DEBUG + + if ( clnt->app_name ) free( clnt->app_name ); + LIST_EACH( clnt->msg_send, msg) + { + /*regfree(msg->regexp);*/ + free( msg->str_regexp); + } + LIST_EMPTY( clnt->msg_send ); + LIST_REMOVE( clients, clnt ); +} + +static void *ClientCreate( Client client ) +{ + char *remotehost; + unsigned short remoteport; + SocketGetRemote( client, &remotehost, &remoteport ); + +#ifdef DEBUG + printf("Connexion de %s:%hu\n", remotehost, remoteport ); +#endif //DEBUG + + return SendService( client ); +} + +static void BroadcastReceive( Client client, void *data, char *line ) +{ + Client app; + char *remotehost; + int err; + int version; + unsigned short remoteport; + unsigned short serviceport; + SocketGetRemote( client, &remotehost, &remoteport ); + + err = sscanf(line,"%d %hu",&version, &serviceport); + if ( err != 2 ) + { + /* ignore the message */ + printf(" Bad Supervision message expected 'version port' from %s:%d\n",remotehost, remoteport); + return; + } + if ( version != VERSION ) + { + /* ignore the message */ + printf(" Bad Bus verion number expected %d receive %d from %s:%d\n", VERSION,version,remotehost, remoteport); + return; + } + /* check if we receive our own message + should test also the host */ + if ( serviceport == ApplicationPort ) return; + +#ifdef DEBUG + printf(" Broadcast de %s:%hu port %hu\n", remotehost, remoteport, serviceport ); +#endif //DEBUG + + /* connect to the service and send the regexp */ + app = SocketConnectAddr(SocketGetRemoteAddr(client), serviceport, NULL, Receive, ClientDelete ); + if ( app ) + { + BusClientPtr clnt; + clnt = SendService( app ); + SocketSetData( app, clnt); + } +} +void BusInit(const char *AppName, unsigned short busnumber, const char *ready, + BusApplicationCallback callback, void *data, + BusDieCallback die_callback, void *die_data + ) +{ + ChannelInit(); + + ApplicationName = AppName; + SupervisionPort = busnumber; + application_callback = callback; + application_user_data = data; + application_die_callback = die_callback; + application_die_user_data = die_data; + ready_message = ready; + ApplicationPort = SocketServer( ANYPORT, ClientCreate, ClientDelete, Receive ); + broadcast = SocketBroadcastCreate( SupervisionPort, NULL, BroadcastReceive ); + +} + +void BusClasses( int argc, char **argv) +{ + messages_classes_count = argc; + messages_classes = argv; +} + +void BusStart() +{ + + SocketSendBroadcast( broadcast, 143 << 24 | 196 << 16 | 1 << 8 | 255, SupervisionPort, "%d %hu\n", VERSION, ApplicationPort); + SocketSendBroadcast( broadcast, 143 << 24 | 196 << 16 | 2 << 8 | 255, SupervisionPort, "%d %hu\n", VERSION, ApplicationPort); + + fprintf(stderr,"Server Ready TCP:%hu\n",ApplicationPort); +} +void BusLoop() +{ + ChannelMainLoop(NULL); +} +/* desabonnements */ +void UnbindMsg( MsgRcvPtr msg ) +{ +BusClientPtr clnt; + /* Send to already connected clients */ + LIST_EACH( clients, clnt ) + { + MsgSendTo( clnt->client, DelRegexp,msg->id, ""); + } +} + +/* demande de reception d'un message */ +static MsgRcvPtr _BindMsg( MsgCallback callback, void *user_data, const char *regexp ) +{ + static int recv_id = 0; + BusClientPtr clnt; + MsgRcvPtr msg; + /* add Msg to the query list */ + LIST_ADD( msg_recv, msg ); + if ( msg ) + { + msg->id = recv_id++; + msg->regexp = strdup(regexp); + msg->callback = callback; + msg->user_data = user_data; + } + /* Send to already connected clients */ + /* recherche dans la liste des requetes recues de mes clients */ + LIST_EACH( clients, clnt ) + { + MsgSendTo( clnt->client, AddRegexp,msg->id,msg->regexp); + } + return msg; +} +MsgRcvPtr BindMsg( MsgCallback callback, void *user_data, const char *fmt_regex, ... ) +{ + char buffer[4096]; + va_list ap; + + va_start( ap, fmt_regex ); + vsprintf( buffer, fmt_regex, ap ); + va_end ( ap ); + return _BindMsg( callback, user_data, buffer ); +} +static int _SendMsg( const char *message ) +{ +BusClientPtr clnt; +int match_count = 0; + + /* recherche dans la liste des requetes recues de mes clients */ + LIST_EACH( clients, clnt ) + { + match_count+= ClientCall( clnt, message ); + } +#ifdef DEBUG + if ( match_count == 0 ) printf( "Warning no recipient for %s\n",message); +#endif + return match_count; +} +int SendMsg(const char *fmt, ...) +{ + char buffer[4096]; + va_list ap; + + va_start( ap, fmt ); + vsprintf( buffer, fmt, ap ); + va_end ( ap ); + return _SendMsg( buffer ); +} +void SendError( BusClientPtr app, int id, const char *fmt, ... ) +{ + char buffer[4096]; + va_list ap; + + va_start( ap, fmt ); + vsprintf( buffer, fmt, ap ); + va_end ( ap ); + MsgSendTo( app->client, Error, id, buffer); +} +void BindDirectMsg( MsgDirectCallback callback, void *user_data) +{ +direct_callback = callback; +direct_user_data = user_data; +} +void SendDirectMsg( BusClientPtr app, int id, char *msg ) +{ + MsgSendTo( app->client, DirectMsg, id, msg); +} + +void SendDieMsg( BusClientPtr app ) +{ + MsgSendTo( app->client, Die, 0, "" ); +} + +char *GetApplicationName( BusClientPtr app ) +{ + if ( app && app->app_name ) + return app->app_name; + else return "Unknown"; +} + +char *GetApplicationHost( BusClientPtr app ) +{ + if ( app && app->client ) + return SocketGetPeerHost( app->client ); + else return NULL; +} + +void BusDefaultApplicationCallback( BusClientPtr app, void *user_data, BusApplicationEvent event) +{ + switch ( event ) { + case BusApplicationConnected: + printf("Application: %s ready on %s\n",GetApplicationName( app ), GetApplicationHost(app)); + break; + case BusApplicationDisconnected: + printf("Application: %s bye on %s\n",GetApplicationName( app ), GetApplicationHost(app)); + break; + default: + printf("Application: %s unkown event %d\n",GetApplicationName( app ), event); + break; + } +} + +BusClientPtr GetApplication( char *name ) +{ + BusClientPtr app = NULL; + LIST_ITER( clients, app, strcmp(name, app->app_name) != 0 ); + return app; +} + +char *GetApplicationList() +{ + static char applist[4096]; + BusClientPtr app; + applist[0] = '\0'; + LIST_EACH( clients, app ) + { + strcat( applist, app->app_name ); + strcat( applist, " " ); + } + return applist; +} + +char **GetApplicationMessages( BusClientPtr app ) +{ + static char *messagelist[200]; + MsgSndPtr msg; + int msgCount= 0; + memset( messagelist, 0 , sizeof( messagelist )); + /* recherche dans la liste des requetes recues de ce client */ + LIST_EACH( app->msg_send, msg ) + { + messagelist[msgCount++]= msg->str_regexp; + } + return messagelist; +} diff --git a/bus.h b/bus.h new file mode 100644 index 0000000..b0971a3 --- /dev/null +++ b/bus.h @@ -0,0 +1,75 @@ +#ifndef _BUS_H +#define _BUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* numero par default du bus */ + +#define DEFAULT_BUS 2010 + +typedef struct _clnt_lst *BusClientPtr; + +typedef enum { BusApplicationConnected, BusApplicationDisconnected } BusApplicationEvent; + +extern void BusDefaultApplicationCallback( BusClientPtr app, void *user_data, BusApplicationEvent event ) ; +/* callback callback appele sur connection deconnection d'une appli */ +typedef void (*BusApplicationCallback)( BusClientPtr app, void *user_data, BusApplicationEvent event ) ; +/* callback appele sur reception de die */ +typedef void (*BusDieCallback)( BusClientPtr app, void *user_data, int id ) ; + +/* callback appele sur reception de messages normaux */ +typedef void (*MsgCallback)( BusClientPtr app, void *user_data, int argc, char **argv ) ; +/* callback appele sur reception de messages directs */ +typedef void (*MsgDirectCallback)( BusClientPtr app, void *user_data, int id, char *msg ) ; + +/* identifiant d'une expression reguliere ( Bind/Unbind ) */ +typedef struct _msg_rcv *MsgRcvPtr; + +/* filtrage des regexps */ +void BusClasses( int argc, char **argv); + +void BusInit( + const char *AppName, /* nom de l'application */ + unsigned short busnumber, /* numero de bus ( port UDP ) */ + const char *ready, /* ready Message peut etre NULL */ + BusApplicationCallback callback, /* callback appele sur connection deconnection d'une appli */ + void *data, /* user data passe au callback */ + BusDieCallback die_callback, /* last change callback before die */ + void *die_data ); /* user data */ + +void BusStart(); /* emission du bonjour */ +void BusLoop(); /* boucle principale */ + +/* query sur les applications connectees */ +char *GetApplicationName( BusClientPtr app ); +char *GetApplicationHost( BusClientPtr app ); +BusClientPtr GetApplication( char *name ); +char *GetApplicationList(); +char **GetApplicationMessages( BusClientPtr app); +/* demande de reception d'un message */ + +MsgRcvPtr BindMsg( MsgCallback callback, void *user_data, const char *fmt_regexp, ... ); /* avec sprintf prealable */ +void UnbindMsg( MsgRcvPtr id ); + +/* emmission d'un message d'erreur */ +void SendError( BusClientPtr app, int id, const char *fmt, ... ); + +/* emmission d'un message die pour terminer l'application */ +void SendDieMsg( BusClientPtr app ); + +/* emmission d'un message retourne le nb effectivement emis */ + +int SendMsg( const char *fmt_message, ... ); /* avec sprintf prealable */ + +/* Message Direct Inter-application */ + +void BindDirectMsg( MsgDirectCallback callback, void *user_data); +void SendDirectMsg( BusClientPtr app, int id, char *msg ); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/list.h b/list.h new file mode 100644 index 0000000..4a6fe26 --- /dev/null +++ b/list.h @@ -0,0 +1,55 @@ +#define LIST_ITER( list, p, cond ) \ + p = list; \ + while ( p && (cond) ) p = p->next + +#define LIST_REMOVE( list, p ) \ + { \ + void *toRemove; \ + if ( list == p ) \ + { \ + list = p->next; \ + free(p);\ + } \ + else \ + {\ + toRemove = p;\ + LIST_ITER( list, p, ( p->next != toRemove ));\ + if ( p )\ + {\ + /* somme tricky swapping to use a untyped variable */\ + void *suiv; \ + void *prec = p;\ + p = toRemove;\ + suiv = p->next;\ + p = prec;\ + p->next = suiv;\ + free(toRemove);\ + }\ + } \ + } + +#define LIST_ADD(list, p ) \ + if ((p = malloc( sizeof( *p ))))\ + { \ + memset( p, 0 , sizeof( *p ));\ + p->next = list; \ + list = p; \ + } + +#define LIST_EACH( list, p ) \ + for ( p = list ; p ; p = p -> next ) + +#define LIST_EACH_SAFE( list, p, next )\ +for ( p = list ; (next = p ? p->next: p ),p ; p = next ) + + +#define LIST_EMPTY( list ) \ + { \ + void *p; \ + while( list ) \ + { \ + p = list;\ + list = list->next; \ + free(p);\ + } \ + } diff --git a/socket.c b/socket.c new file mode 100644 index 0000000..17d0fe1 --- /dev/null +++ b/socket.c @@ -0,0 +1,667 @@ +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#define close closesocket +#define perror( a ) printf(a" error=%d\n",WSAGetLastError()); +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#ifdef XTMAINLOOP +#include +#endif +#include "list.h" +#include "socket.h" +#include "timer.h" + +#define MAX_BUFFER 2048 + +struct _channel { + Channel next; + HANDLE fd; +#ifdef XTMAINLOOP + XtInputId id; +#endif + void *data; + int tobedeleted; + void (*handle_delete)( void *data ); + void (*handle_read)( Channel channel, HANDLE fd, void *data); + }; + +typedef struct _server *Server; + +struct _server { + Server next; + Channel channel; + void *(*create)(Client client); + void (*handle_delete)(Client client, void *data); + SocketInterpretation interpretation; + }; + +struct _client { + Client next; + Channel channel; + unsigned short port; + struct sockaddr_in from; + SocketInterpretation interpretation; + void (*handle_delete)(Client client, void *data); + char buffer[MAX_BUFFER+2]; + char *ptr; + void *data; + }; + +static Channel channels_list = NULL; +static Server servers_list = NULL; +static Client clients_list = NULL; +static int channel_initialized = 0; + +#ifdef XTMAINLOOP +static XtAppContext app; +#else +static fd_set open_fds; +static int MainLoop = 1; +#endif + + + +#ifdef WIN32 +WSADATA WsaData; +#endif + +void ChannelClose( Channel channel ) +{ +#ifdef XTMAINLOOP + if ( channel->handle_delete ) + (*channel->handle_delete)( channel->data ); + close(channel->fd); + XtRemoveInput( channel->id ); + LIST_REMOVE( channels_list, channel ); +#else + channel->tobedeleted = 1; +#endif +} +#ifdef XTMAINLOOP +static void HandleChannel( XtPointer closure, int* source, XtInputId* id ) +{ + Channel channel = (Channel)closure; +#ifdef DEBUG + printf("Handle Channel read %d\n",*source ); +#endif + (*channel->handle_read)(channel,channel->fd,channel->data); +} +#else +static void ChannelDelete( Channel channel ) +{ + if ( channel->handle_delete ) + (*channel->handle_delete)( channel->data ); + close(channel->fd); + + FD_CLR(channel->fd, &open_fds); + LIST_REMOVE( channels_list, channel ); +} +static void ChannelDefferedDelete() +{ + Channel channel,next; + LIST_EACH_SAFE( channels_list, channel,next) + { + if ( channel->tobedeleted ) + { + ChannelDelete( channel ); + } + } +} +#endif +Channel ChannelSetUp(HANDLE fd, void *data, + void (*handle_delete)( void *data ), + void (*handle_read)( Channel channel, HANDLE fd, void *data) + ) +{ + Channel channel; + + LIST_ADD( channels_list, channel ); + if ( !channel ) + { + fprintf(stderr,"NOK Memory Alloc Error\n"); + close( fd ); + exit(0); + } + channel->fd = fd; + channel->tobedeleted = 0; + channel->handle_delete = handle_delete; + channel->handle_read = handle_read; + channel->data = data; +#ifdef XTMAINLOOP + channel->id = XtAppAddInput( app, fd, (XtPointer)XtInputReadMask, HandleChannel, channel); +#else + FD_SET( channel->fd, &open_fds ); +#endif + return channel; +} +#ifndef XTMAINLOOP +static void ChannelHandleRead(fd_set *current) +{ + Channel channel,next; + + LIST_EACH_SAFE( channels_list, channel, next ) + { + if ( FD_ISSET( channel->fd, current ) ) + { + (*channel->handle_read)(channel,channel->fd,channel->data); + } + } +} +static void ChannelHandleExcpt(fd_set *current) +{ + Channel channel,next; + LIST_EACH_SAFE( channels_list, channel, next ) + { + if (FD_ISSET( channel->fd, current ) ) + { + ChannelClose( channel ); + } + } +} +#endif +static void DeleteSocket(void *data) +{ + Client client = ( Client )data; + if ( client->handle_delete ) + (*client->handle_delete)( client, client->data ); + shutdown( client->channel->fd, 2 ); + LIST_REMOVE( clients_list, client ); +} +static void HandleSocket( Channel channel, HANDLE fd, void *data) +{ + Client client = (Client)data; + char *ptr; + char *ptr_nl; + long nb_to_read = 0; + long nb; + int len; + + /* limitation taille buffer */ + nb_to_read = MAX_BUFFER - ( client->ptr - client->buffer ); + if( nb_to_read == 0 ) { + fprintf(stderr, "Erreur message trop long sans LF\n"); + client->ptr = client->buffer; + return; + }; + len = sizeof( client->from ); + nb = recvfrom( fd, client->ptr, nb_to_read,0,(struct sockaddr *)&client->from,&len); + if (nb < 0) { + perror(" Read Socket "); + ChannelClose( client->channel ); + return; + }; + if ( nb == 0 ) + { + ChannelClose( client->channel ); + return; + } + + client->ptr += nb; + *(client->ptr) = '\0'; + ptr = client->buffer; + while( (ptr_nl = strchr( ptr, '\n' ))) + { + *ptr_nl = '\0'; + if ( client->interpretation ) + (*client->interpretation)( client, client->data, ptr ); + else fprintf( stderr,"Socket No interpretation function ???!\n"); + ptr = ++ptr_nl; + } + if ( *ptr != '\0' ) + { /* recopie ligne incomplete au debut du buffer */ + strcpy( client->buffer, ptr ); + client->ptr = client->buffer + strlen(client->buffer); + } + else + { + client->ptr = client->buffer; + } +} +static void HandleServer(Channel channel, HANDLE fd, void *data) +{ + Server server = ( Server ) data; + Client client; + HANDLE ns; + int addrlen; + struct sockaddr_in remote2; + + addrlen = sizeof( remote2 ); + if ((ns = accept( fd, (struct sockaddr *)&remote2, &addrlen)) <0) + { + perror ( "*** accept ***"); + return; + }; + LIST_ADD( clients_list, client ); + if ( !client ) + { + fprintf(stderr,"NOK Memory Alloc Error\n"); + close( fd ); + exit(0); + } + client->from = remote2; + client->channel = ChannelSetUp( ns, client, DeleteSocket, HandleSocket ); + client->interpretation = server->interpretation; + client->ptr = client->buffer; + client->handle_delete = server->handle_delete; + client->data = (*server->create)( client ); + +} +int SocketServer(unsigned short port, + void*(*create)(Client client), + void(*handle_delete)(Client client, void *data), + void(*interpretation)( Client client, void *data, char *ligne)) +{ + Server server; + HANDLE fd; + int one=1; + struct sockaddr_in local; + int addrlen; + + + if ((fd = socket( AF_INET, SOCK_STREAM, 0)) < 0){ + perror( "***open socket ***"); + exit(0); + }; + + + local.sin_family = AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port = htons (port); + + if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)&one,sizeof(one)) < 0) + { + perror( "*** set socket option SO_REUSEADDR ***"); + exit(0); + } + +#ifdef SO_REUSEPORT + + if (setsockopt( fd, SOL_SOCKET, SO_REUSEPORT, (char *)&one, sizeof( one)) < 0) + { + perror( "*** set socket option REUSEPORT ***"); + exit(0); + } +#endif + + if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) + { + perror( "*** bind ***"); + exit(0); + } + + addrlen = sizeof( local ); + if (getsockname(fd,(struct sockaddr *)&local, &addrlen) < 0) + { + perror( "***get socket name ***"); + exit(0); + } + + if (listen( fd, 128) < 0){ + perror( "*** listen ***"); + exit(0); + }; + + + LIST_ADD( servers_list, server ); + if ( !server ) + { + fprintf(stderr,"NOK Memory Alloc Error\n"); + exit(0); + } + server->channel = ChannelSetUp( fd, server, DeleteSocket, HandleServer ); + server->create = create; + server->handle_delete = handle_delete; + server->interpretation = interpretation; + return ntohs(local.sin_port); +} +char *SocketGetPeerHost( Client client ) +{ + int err; + struct sockaddr_in name; + struct hostent *host; + int len = sizeof(name); + err = getpeername( client->channel->fd, (struct sockaddr *)&name, &len ); + if ( err < 0 ) return "can't get peer"; + host = gethostbyaddr( (char *)&name.sin_addr.s_addr,sizeof(name.sin_addr.s_addr),name.sin_family); + if ( host == NULL ) return "can't translate addr"; + return host->h_name; +} +struct in_addr * SocketGetRemoteAddr( Client client ) +{ + return &client->from.sin_addr; +} +void SocketGetRemote( Client client, char **host, unsigned short *port ) +{ + struct hostent *hostp; + /* extract hostname and port from last message received */ + hostp = gethostbyaddr( (char *)&client->from.sin_addr.s_addr, + sizeof(client->from.sin_addr.s_addr),client->from.sin_family); + if ( hostp == NULL ) *host = "unknown"; + else *host = hostp->h_name; + *port = ntohs( client->from.sin_port ); +} +void SocketClose( Client client ) +{ + ChannelClose( client->channel ); +} + +void SocketSendRaw( Client client, char *buffer, int len ) +{ + int err; + err = send( client->channel->fd, buffer, len, 0 ); + if ( err != len ) + perror( "*** send ***"); +} +void SocketSetData( Client client, void *data ) +{ + client->data = data; +} +void SocketSend( Client client, char *fmt, ... ) +{ + char buffer[4096]; + va_list ap; + int len; + va_start( ap, fmt ); + len = vsprintf( buffer, fmt, ap ); + SocketSendRaw( client, buffer, len ); + va_end ( ap ); +} +void *SocketGetData( Client client ) +{ + return client->data; +} +void SocketBroadcast( char *fmt, ... ) +{ + Client client; + char buffer[4096]; + va_list ap; + int len; + + va_start( ap, fmt ); + len = vsprintf( buffer, fmt, ap ); + va_end ( ap ); + LIST_EACH( clients_list, client ) + { + SocketSendRaw( client, buffer, len ); + } +} +/* +Ouverture d'un canal TCP/IP en mode client +*/ +Client SocketConnect( char * host, unsigned short port, + void *data, + SocketInterpretation interpretation, + void (*handle_delete)(Client client, void *data) + ) +{ +struct hostent *rhost; + + +if ((rhost = gethostbyname( host )) == NULL){ + fprintf(stderr, "Erreur %s Calculateur inconnu !\n",host); + return NULL; + }; +return SocketConnectAddr( (struct in_addr*)(rhost->h_addr), port, data, interpretation, handle_delete); +} +Client SocketConnectAddr( struct in_addr * addr, unsigned short port, + void *data, + SocketInterpretation interpretation, + void (*handle_delete)(Client client, void *data) + ) +{ +HANDLE handle; +Client client; +struct sockaddr_in remote; + +remote.sin_family = AF_INET; +remote.sin_addr = *addr; +remote.sin_port = htons (port); + +if ((handle = socket( AF_INET, SOCK_STREAM, 0)) < 0){ + perror( "*** client socket ***"); + return NULL; + }; + +if ( connect( handle, (struct sockaddr *)&remote, sizeof(remote) ) < 0){ + perror( "*** client connect ***"); + return NULL; + }; + +LIST_ADD( clients_list, client ); +if ( !client ) + { + fprintf(stderr,"NOK Memory Alloc Error\n"); + close( handle ); + exit(0); + } + + +client->channel = ChannelSetUp( handle, client, DeleteSocket, HandleSocket ); +client->interpretation = interpretation; +client->ptr = client->buffer; +client->data = data; +client->handle_delete = handle_delete; +client->from.sin_family = AF_INET; +client->from.sin_addr = *addr; +client->from.sin_port = htons (port); + +return client; +} +int SocketWaitForReply( Client client, char *buffer, int size, int delai) +{ + fd_set rdset; + struct timeval timeout; + struct timeval *timeoutptr = &timeout; + int ready; + char *ptr; + char *ptr_nl; + long nb_to_read = 0; + long nb; + HANDLE fd; + + fd = client->channel->fd; + ptr = buffer; + timeout.tv_sec = delai; + timeout.tv_usec = 0; + do { + /* limitation taille buffer */ + nb_to_read = size - ( ptr - buffer ); + if( nb_to_read == 0 ) + { + fprintf(stderr, "Erreur message trop long sans LF\n"); + ptr = buffer; + return -1; + } + FD_ZERO( &rdset ); + FD_SET( fd, &rdset ); + ready = select(fd+1, &rdset, 0, 0, timeoutptr); + if ( ready < 0 ) + { + perror("select"); + return -1; + } + if ( ready == 0 ) + { + return -2; + } + if ((nb = recv( fd , ptr, nb_to_read, 0 )) < 0) + { + perror(" Read Socket "); + return -1; + } + if ( nb == 0 ) + return 0; + + ptr += nb; + *ptr = '\0'; + ptr_nl = strchr( buffer, '\n' ); + } while ( !ptr_nl ); + *ptr_nl = '\0'; + return (ptr_nl - buffer); +} +void ChannelInit(void) +{ +#ifdef WIN32 + int error; +#else + signal( SIGPIPE, SIG_IGN); +#endif + if ( channel_initialized ) return; +#ifndef XTMAINLOOP + FD_ZERO( &open_fds ); +#endif +#ifdef WIN32 + error = WSAStartup( 0x0101, &WsaData ); + if ( error == SOCKET_ERROR ) { + printf( "WSAStartup failed.\n" ); + } +#endif + channel_initialized = 1; +} + +#ifdef XTMAINLOOP + +void SetSocketAppContext( XtAppContext cntx ) +{ + app = cntx; +} + +void ChannelMainLoop(void(*hook)(void)) +{ + printf("Compiled for Use of XtMainLoop not ChannelMainLoop\n"); + exit(-1); +} +#else +void ChannelStop(void) +{ + MainLoop = 0; +} +void ChannelMainLoop(void(*hook)(void)) +{ + +fd_set rdset; +fd_set exset; +int ready; + + + + while (MainLoop) { + ChannelDefferedDelete(); + if ( hook ) (*hook)(); + rdset = open_fds; + exset = open_fds; + ready = select(64, &rdset, 0, &exset, TimerGetSmallestTimeout()); + if ( ready < 0 && ( errno != EINTR )) + { + perror("select"); + return; + } + TimerScan(); + if ( ready > 0 ) + { + ChannelHandleExcpt(&exset); + ChannelHandleRead(&rdset); + continue; + } + } +} +#endif +/* Socket UDP */ + +Client SocketBroadcastCreate( unsigned short port, + void *data, + SocketInterpretation interpretation + ) +{ +HANDLE handle; +Client client; +struct sockaddr_in local; +int on = 1; + +local.sin_family = AF_INET; +local.sin_addr.s_addr = INADDR_ANY; +local.sin_port = htons (port); + +if ((handle = socket( AF_INET, SOCK_DGRAM, 0)) < 0){ + perror( "*** dgram socket ***"); + return NULL; + }; + +/* wee need to used multiple client on the same host */ +if (setsockopt( handle, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof( on)) < 0) + { + perror( "*** set socket option REUSEADDR ***"); + return NULL; + }; +#ifdef SO_REUSEPORT + +if (setsockopt( fd, SOL_SOCKET, SO_REUSEPORT, (char *)&on, sizeof( on)) < 0) + { + perror( "*** set socket option REUSEPORT ***"); + return NULL; + } +#endif +/* wee need to broadcast */ +if (setsockopt( handle, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof( on)) < 0) + { + perror( "*** BROADCAST ***"); + return NULL; + }; + +if (bind(handle, (struct sockaddr *)&local, sizeof(local)) < 0) + { + perror( "*** test BIND ***"); + return NULL; + }; + +LIST_ADD( clients_list, client ); +if ( !client ) + { + fprintf(stderr,"NOK Memory Alloc Error\n"); + close( handle ); + exit(0); + } + + +client->channel = ChannelSetUp( handle, client, DeleteSocket, HandleSocket ); +client->interpretation = interpretation; +client->ptr = client->buffer; +client->data = data; + +return client; +} + +void SocketSendBroadcast( Client client, unsigned long host, unsigned short port, char *fmt, ... ) +{ + struct sockaddr_in remote; + char buffer[4096]; + va_list ap; + int err,len; + + va_start( ap, fmt ); + len = vsprintf( buffer, fmt, ap ); + /* Send UDP packet to the dest */ + remote.sin_family = AF_INET; + remote.sin_addr.s_addr = htonl( host ); + remote.sin_port = htons(port); + err = sendto( client->channel->fd, + buffer, len,0, + (struct sockaddr *)&remote,sizeof(remote)); + if ( err != len ) + { + perror( "*** send ***"); + } va_end ( ap ); +} diff --git a/socket.h b/socket.h new file mode 100644 index 0000000..99b9abd --- /dev/null +++ b/socket.h @@ -0,0 +1,87 @@ +#ifndef _SOCKET_H +#define _SOCKET_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* general Handle */ + +#define ANYPORT 0 + +#ifdef WIN32 +#include +#define HANDLE SOCKET +#else +#define HANDLE int +#include +#endif + +typedef struct _channel *Channel; + +extern void ChannelInit(void); +extern void ChannelStop(void); +extern void ChannelMainLoop(void(*hook)(void) ); +extern Channel ChannelSetUp( + HANDLE fd, + void *data, + void (*handle_delete)( void *data ), + void (*handle_read)( Channel channel, HANDLE fd, void *data) + ); + +extern void ChannelClose( Channel channel ); + +/* Server Part */ +typedef struct _client *Client; +typedef void (*SocketInterpretation)( Client client, void *data, char *ligne); + +extern void SocketClose( Client client ); +extern void SocketSend( Client client, char *fmt, ... ); +extern void SocketSendRaw( Client client, char *buffer, int len ); +extern char *SocketGetPeerHost( Client client ); +extern void SocketSetData( Client client, void *data ); +extern void *SocketGetData( Client client ); +extern void SocketBroadcast( char *fmt, ... ); + +extern int SocketServer(unsigned short port, + void*(*create)(Client client), + void(*handle_delete)(Client client, void *data), + SocketInterpretation interpretation); + +/* Client Part */ + +extern Client SocketConnect( char * host, unsigned short port, + void *data, + SocketInterpretation interpretation, + void (*handle_delete)(Client client, void *data) + ); +extern Client SocketConnectAddr( struct in_addr * addr, unsigned short port, + void *data, + SocketInterpretation interpretation, + void (*handle_delete)(Client client, void *data) + ); +extern int SocketWaitForReply( Client client, char *buffer, int size, int delai); + +/* Socket UDP */ +/* Creation d'une socket en mode non connecte */ +/* et ecoute des messages */ +extern Client SocketBroadcastCreate( + unsigned short port, + void *data, + SocketInterpretation interpretation + ); +/* recuperation de l'emetteur du message */ +extern struct in_addr * SocketGetRemoteAddr( Client client ); +extern void SocketGetRemote( Client client, char **host, unsigned short *port ); +/* emmission d'un broadcast UDP */ +extern void SocketSendBroadcast( Client client, unsigned long host, unsigned short port, char *fmt, ... ); + +#ifdef XTMAINLOOP +extern void SetSocketAppContext( XtAppContext cntx ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/testbus.c b/testbus.c new file mode 100644 index 0000000..a2b3323 --- /dev/null +++ b/testbus.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include "socket.h" +#include "bus.h" +#include "timer.h" +#ifdef XTMAINLOOP +#include +#endif + +int app_count = 0; +int wait_count = 0; + +void Callback( BusClientPtr app, void *user_data, int argc, char *argv[]) +{ + int i; + printf(" %s Called function %d args:",GetApplicationName(app),argc); + for ( i = 0; i < argc; i++ ) + printf(" '%s'",argv[i]); + printf("\n"); +} +void HandleStdin( Channel channel, int fd, void *data) +{ + char buf[4096]; + char *line; + char *cmd; + char *arg; + int id; + BusClientPtr app; + int err; + line = gets( buf); + if ( !line ) + { + ChannelClose( channel ); + ChannelStop(); + return; + } + if ( *line == '.' ) + { + cmd = strtok( line, ".: "); + if ( strcmp(cmd, "die" ) == 0 ) + { + arg = strtok( NULL, " " ); + if ( arg ) + { + app = GetApplication( arg ); + if ( app ) + SendDieMsg( app ); + else printf( "No Application %s!!!\n",arg); + } + } + if ( strcmp(cmd, "bind" ) == 0 ) + { + arg = strtok( NULL, "'" ); + if ( arg ) + { + BindMsg( Callback, NULL, arg ); + } + } + if ( strcmp(cmd, "where" ) == 0 ) + { + arg = strtok( NULL, " " ); + if ( arg ) + { + app = GetApplication( arg ); + if ( app ) + printf( "Application %s on %s\n",arg, GetApplicationHost( app )); + else printf( "No Application %s!!!\n",arg); + } + } + if ( strcmp(cmd, "direct" ) == 0 ) + { + arg = strtok( NULL, " " ); + if ( arg ) + { + app = GetApplication( arg ); + if ( app ) + { + arg = strtok( NULL, " " ); + id = atoi( arg ) ; + arg = strtok( NULL, "'" ); + SendDirectMsg( app, id, arg ); + } + else printf( "No Application %s!!!\n",arg); + } + + } + if ( strcmp(cmd, "who") == 0 ) + { + printf("Apps: %s\n", GetApplicationList()); + } + if ( strcmp(cmd, "help") == 0 ) + { + printf("Commands list:\n"); + printf(" .help - this help\n"); + printf(" .quit - terminate this application\n"); + printf(" .die appname - send die msg to appname\n"); + printf(" .direct appname id 'arg' - send direct msg to appname\n"); + printf(" .where appname - on which host is appname\n"); + printf(" .bind 'regexp' - add a msg to receive\n"); + printf(" .who - who is on the bus\n"); + } + if ( strcmp(cmd, "quit") == 0 ) + { + exit(0); + } + } + else + { + err = SendMsg( buf ); + printf("Sent:%d\n",err); + } +} + +void ApplicationCallback( BusClientPtr app, void *user_data, BusApplicationEvent event) +{ + char *appname; + char *host; + appname = GetApplicationName( app ); + host = GetApplicationHost( app ); + switch ( event ) { + case BusApplicationConnected: + app_count++; + printf("Application(%d): %s ready on %s\n",app_count, appname, host); + if ( app_count == wait_count ) + ChannelSetUp( 0, NULL, NULL, HandleStdin); + break; + case BusApplicationDisconnected: + app_count--; + printf("Application(%d): %s bye on %s\n",app_count, appname, host); + break; + default: + printf("Application(%d): %s unkown event %d\n", app_count, appname, event); + break; + } + +} +void TimerCall(TimerId id, void *user_data, unsigned long delta) +{ + printf("Timer callback: %d delta %lu ms\n", (int)user_data, delta ); + SendMsg( "TEST TIMER %d", (int)user_data); + /*if ( (int)user_data == 5 ) TimerModify( id, 2000 );*/ +} +int main(int argc, char *argv[]) +{ + + unsigned short bport = DEFAULT_BUS; + int c; + int timer_test = 0; + while ((c = getopt(argc, argv, "b:w:t")) != EOF) + switch (c) + { + case 'b': + bport = atoi(optarg) ; + break; + case 'w': + wait_count = atoi(optarg) ; + break; + case 't': + timer_test = 1; + break; + } +#ifdef XTMAINLOOP + XtToolkitInitialize(); + +#endif + BusInit("TEST",bport,"TEST READY",ApplicationCallback,NULL,NULL,NULL); + for ( ; optind < argc; optind++ ) + BindMsg( Callback, NULL, argv[optind] ); + if ( wait_count == 0 ) + ChannelSetUp( 0, NULL, NULL, HandleStdin); + BusStart( ); + if ( timer_test ) + { + TimerRepeatAfter( TIMER_LOOP, 1000, TimerCall, (void*)1 ); + TimerRepeatAfter( 5, 5000, TimerCall, (void*)5 ); + } +#ifdef XTMAINLOOP + XtMainLoop(); +#else + BusLoop(); +#endif + return 0; +} diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..a1fb791 --- /dev/null +++ b/timer.c @@ -0,0 +1,160 @@ +/* Module de gestion des timers autour d'un select */ +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include "list.h" +#include "timer.h" + +#define BIGVALUE 2147483647 +#define MILLISEC 1000 + +static struct timeval *timeoutptr = NULL; +static struct timeval selectTimeout = { BIGVALUE, 0 }; +/* la prochaine echeance */ +static unsigned long nextTimeout = BIGVALUE; + +struct _timer { + struct _timer *next; + int repeat; + unsigned long period; + unsigned long when; + TimerCb callback; + void *user_data; + }Timer; + +/* liste des timers */ +TimerId timers = NULL; + +static long currentTime() +{ + struct timeval tv; + unsigned long current; + gettimeofday (&tv, 0); + current = 1000 * tv.tv_sec + tv.tv_usec / 1000; + return current; + +#if 0 + unsigned long current; + current = clock(); + return current; +#endif +} +static void SetNewTimeout( unsigned long current, unsigned long when ) +{ + unsigned long time; + time = when - current; + nextTimeout = when; + selectTimeout.tv_sec = time / MILLISEC; + selectTimeout.tv_usec = (time - selectTimeout.tv_sec* MILLISEC) * MILLISEC; + if ( timeoutptr == NULL ) + timeoutptr = &selectTimeout; + /*printf("New timeout %lu\n", time );*/ +} +static void AdjTimeout(unsigned long current) +{ + unsigned long newTimeout; + TimerId timer; + if ( timers ) + { + /* recherche de la plus courte echeance dans la liste */ + newTimeout = timers->when ; /* remise a la premiere valeur */ + LIST_EACH( timers , timer ) + { + if ( timer->when < newTimeout ) + newTimeout = timer->when; + + } + SetNewTimeout( current, newTimeout ); + } + else + { + timeoutptr = NULL; + } +} + +/* API */ + +TimerId TimerRepeatAfter( int count, long time, TimerCb cb, void *user_data ) +{ + unsigned long stamp; + TimerId timer; + + /* si y a rien a faire et ben on fait rien */ + if ( cb == NULL ) return NULL; + + LIST_ADD( timers, timer ) + if ( timer ) + { + timer->repeat = count; + timer->callback = cb; + timer->user_data = user_data; + stamp = currentTime(); + timer->period = time; + timer->when = stamp + time; + if ( (timer->when < nextTimeout) || (timeoutptr == NULL)) + SetNewTimeout( stamp, timer->when ); + } + return timer; +} +void TimerRemove( TimerId timer ) +{ + unsigned long stamp; + if ( !timer ) return; + LIST_REMOVE( timers, timer ); + stamp = currentTime(); + AdjTimeout(stamp); +} +void TimerModify( TimerId timer, long time ) +{ + unsigned long stamp; + if ( !timer ) return; + + stamp = currentTime(); + timer->period = time; + timer->when = stamp + time; + AdjTimeout(stamp); +} +/* Interface avec select */ + +struct timeval *TimerGetSmallestTimeout() +{ + return timeoutptr; +} + +void TimerScan() +{ + unsigned long stamp; + TimerId timer; + TimerId next; + unsigned long delta; + int timer_echu = 0; + + stamp = currentTime(); + /* recherche des timers echu dans la liste */ + LIST_EACH_SAFE( timers , timer, next ) + { + if ( timer->when <= stamp ) + { + timer_echu++; + delta = stamp - timer->when; + /* call callback */ + (*timer->callback)( timer, timer->user_data, delta ); + if ( timer->repeat == TIMER_LOOP || --(timer->repeat) ) + { + timer->when = stamp + timer->period; + } + else + { + LIST_REMOVE( timers, timer ); + } + } + } + /* recalcul du prochain timeout */ + AdjTimeout( stamp ); +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..7aa425f --- /dev/null +++ b/timer.h @@ -0,0 +1,18 @@ +/* Module de gestion des timers autour d'un select */ + +typedef struct _timer *TimerId; +typedef void (*TimerCb)( TimerId id , void *user_data, unsigned long delta ); + +/* API le temp est en milli secondes */ +#define TIMER_LOOP -1 /* timer en boucle infinie */ +TimerId TimerRepeatAfter( int count, long time, TimerCb cb, void *user_data ); + +void TimerModify( TimerId id, long time ); + +void TimerRemove( TimerId id ); + +/* Interface avec select */ + +struct timeval *TimerGetSmallestTimeout(); + +void TimerScan(); -- cgit v1.1