diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..fc4d426 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,30 @@ +# Makefile for itinerary engine utilities +# $Header$ + +CC= gcc +CFLAGS= -g +LD= gcc +LDFLAGS= -g +HOMEDIR= /usr/local/etc/gild +TARGETS= gild +COMPONENTS = gild.o wrapper.o config.o log.o + +all: $(TARGETS) + +.c.o: + $(CC) $(CFLAGS) -c $< + +gild: $(COMPONENTS) gild.h Makefile + $(LD) $(LDFLAGS) -o gild $(COMPONENTS) + +clean: + rm core *.o $(TARGETS) + +install: $(TARGETS) + install --strip $(TARGETS) $(HOMEDIR) + install gild.conf $(HOMEDIR) + cd handlers; make install + +version: $(TARGETS) + cvs commit gild.c wrapper.c config.c log.c Makefile gild.h \ + gild.conf handlers \ No newline at end of file diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..563a331 --- /dev/null +++ b/src/config.c @@ -0,0 +1,113 @@ +/**************************************************************************\ +* * +* Project: Gild * +* config.c * +* * +* Purpose: Handle configuration file * +* * +* Author : Simon Brooke * +* Copyright: (c) Simon Brooke 1997 * +* Version : 0.1 * +* Created : 8th October 1997 * +* * +\**************************************************************************/ + +/* $Header$ */ + +#include "gild.h" + +extern char errorBuff[]; /* where I assemble logging messages */ +handler * handlers = ( handler *) null; + /* the list of handlers I handle */ + +int parse_config( char * path) +/* parse the config file and identify the handlers I handle */ +{ + FILE * configFile; /* the file handle from which to load config */ + char * line[ 1024]; /* a buffer to read lines into */ + int n = 0; /* number of handlers we find */ + + sprintf( errorBuff, "Loading configuration from %s", path); + error( LOG_NOTICE); + + configFile = fopen( path, "r"); + + if ( configFile == ( FILE *)null) + { + sprintf( errorBuff, "failed to open configuration file %s: %s", + path, strerror( errno)); + error( LOG_ERR); + } + + while( ! feof( configFile)) + { + char buff[ 1024], protocol[ 1024], pattern[ 1024], command[ 1024]; + int timeout; /* how long to wait for the handler to + complete */ + + fgets( buff, 1024, configFile); + /* get a line from the config file */ + if ( buff[ 0] == '#'); + /* it's a comment, and can be ignored */ + else if ( fscanf( configFile, "%s %s %s %d", protocol, + pattern, command, &timeout) == 4) + /* otherwise look for four fields. If + we find them... */ + { + handler * newhandler = + ( struct handler *) malloc( sizeof( struct handler)); + /* create a handler */ + struct re_pattern_buffer * patternBuff = + ( struct re_pattern_buffer *) + malloc( sizeof( struct re_pattern_buffer)); + /* and reserve space for a compiled regex */ + + if ( newhandler == ( handler *) null) + { /* unlikely, but... best check */ + sprintf( errorBuff, + "out of memory whilst allocating handler?"); + error( LOG_ERR); + } + + regcomp( patternBuff, pattern, + REG_ICASE | REG_NEWLINE); + /* and load it with the values we found */ + newhandler->protocol = strdup( protocol); + newhandler->pattern = patternBuff; + newhandler->command = strdup( command); + newhandler->timeout = timeout; + + /* then splice it into the handler chain */ + newhandler->next = handlers; + handlers = newhandler; + + n ++; /* increment the counter */ + + /* and log it. */ + sprintf( errorBuff, + "registering handler [%s] for protocol %s", + newhandler->command, newhandler->protocol); + error( LOG_NOTICE); + } + } + return ( n); /* say how many we found */ +} + +handler * get_handler( char * match) +/* find a handler whose pattern matches match, and return it's command */ +{ + handler * h = null, * i; + regmatch_t pmatch[ 2]; + + for ( i = handlers; ( h == null) && ( i != null); i = i->next) + /* scan down the list of handlers looking + for a match... */ + { + if ( regexec( i->pattern, match, 2, pmatch, 0) == 0) + { + h = i; + } + } + + return( h); +} diff --git a/src/gild.c b/src/gild.c new file mode 100644 index 0000000..5fbb963 --- /dev/null +++ b/src/gild.c @@ -0,0 +1,186 @@ +/***********************************************************************\ +* * +* Project: Gild * +* gild.c * +* * +* Purpose: Generalised Internet Listener Daemon * +* -- a daemon which listens for requests on a specified * +* IP port, and despatches them to specified handlers * +* depending on the content of the first line. * +* * +* Author : Simon Brooke * +* Copyright: (c) Simon Brooke 1997 * +* Version : 0.1 * +* Created : 5th October 1997 * +* * +\***********************************************************************/ + +/* $Header$ */ + +#include "gild.h" + +int port = DEFAULT_PORT_NO; /* the port I shall listen on */ +char errorBuff[ 1024]; /* somewhere to assemble error messages */ +extern handler * handlers; + /* the list of handlers I support */ + +void error( int severity) +/* log the current contents of errorBuff and then if severity is bad die */ +{ + log( severity, errorBuff); + + switch ( severity) + { + case LOG_ERR: + exit( 1); + break; + } +} + + +int main( int argc, char * argv[]) +{ + char * configPath = CONFIG_PATH; /* the path to my default config file */ + int arg; /* the argument being handled */ + int keyhole; /* where I listen, of course */ + struct sockaddr_in * address = + ( struct sockaddr_in *)malloc( sizeof( struct sockaddr_in)); + /* the address I bind the keyhole to */ + + for ( arg = 1; argc > arg; arg ++) + { /* process arguments */ + switch ( argv[ arg][ 0]) + { + case '-': + switch( argv[ arg][ 1]) + { + case 'f': /* specify a config file to load */ + arg ++; + configPath = argv[ arg]; + break; + case 'p': /* specify a port to listen on */ + arg ++; + port = atoi( argv[ arg]); + break; + default: + fprintf( stderr, + "unrecognised command line switch [-%c]", + argv[ arg][ 1]); + exit( 1); + break; + } + break; + default: + fprintf( stderr, "unrecognised command line token [%s]", + argv[ arg]); + exit( 1); + } + } + + if ( parse_config( configPath) == 0) + { + fprintf( stderr, "failed to load any handlers"); + exit( 1); + } + + keyhole = socket( AF_INET, SOCK_STREAM, 0); + if ( keyhole == -1) + { + fprintf( stderr, "failed to intialise socket"); + exit( 1); + } + + memset( address, 0, sizeof( address)); + /* sanitation */ + + /* now set up the address preparatory + to binding it */ + address->sin_family = AF_INET; + /* it's an internet address */ + address->sin_addr.s_addr = htonl( INADDR_ANY); + /* and we don't mind where it comes from */ + address->sin_port = htons( port); + /* that's the port we listen on, remember? */ + + if ( bind( keyhole, ( struct sockaddr *)address, + sizeof( struct sockaddr_in)) == -1) + { /* attempt to bind keyhole to address */ + fprintf( stderr, "failed to bind socket"); + exit( 1); + } + +#ifndef DEBUG + /* we become a real class one daemon */ + if ( fork() != 0) /* we're the parent */ + { + exit(0); /* so die */ + } + /* otherwise (we're the child)... */ + setsid(); /* release the controlling tty */ +#endif + + signal( SIGCHLD, SIG_IGN); /* allow chidren to die naturally */ + + if ( listen( keyhole, MAX_PENDING_REQUESTS) == -1) + { + sprintf( errorBuff, "failed in listen()?"); + error( LOG_ERR); + } + sprintf( errorBuff, "started; awaiting requests on port %d", port); + error( LOG_NOTICE); + + for ever + { + struct sockaddr_in * client = + ( struct sockaddr_in *) malloc( sizeof( struct sockaddr_in)); + /* the address of the client calling in */ + char * client_addr = null; + /* the network address of the client */ + int conversation; /* a handle on the conversation we're having */ + int clientsize = sizeof( struct sockaddr_in); + /* the size of the client object */ + + if ( client == 0) + { /* unlikely, but might as well check... */ + sprintf( errorBuff, "Out of memory?"); + error( LOG_ERR); + } + memset( client, 0, sizeof( client)); + /* sanitation */ + + conversation = accept( keyhole, ( struct sockaddr *)client, + &clientsize); + + client_addr = strdup( inet_ntoa( client->sin_addr)); + + + if ( conversation == -1) + { /* again, check we set it up OK */ + sprintf( errorBuff, "Could not establish conversation [%d]", + errno); + error( LOG_ERR); + } + + switch( fork()) + { + case -1: /* Blew it: whinge and die */ + sprintf( errorBuff, "failed to fork?"); + error( LOG_ERR); + break; + case 0: /* I'm the child */ + close( keyhole); + + wrapper( conversation, client_addr); + /* wrapper (q.v.) handles the conversation */ + + exit( 0); /* after completing wrapper, die */ + break; + default: /* I'm the parent */ + close( conversation); + free( client); /* prevent memory bloat */ + if ( client_addr != null) + free( client_addr); + break; + } + } +} diff --git a/src/gild.h b/src/gild.h new file mode 100644 index 0000000..d7dd3cf --- /dev/null +++ b/src/gild.h @@ -0,0 +1,72 @@ +/**************************************************************************\ +* * +* Project: Gild * +* gild.h * +* * +* Purpose: public header file for gild server. * +* * +* Author : Simon Brooke * +* Copyright: (c) Simon Brooke 1997 * +* Version : 0.1 * +* Created : 6th October 1997 * +* * +\**************************************************************************/ + +/* $Header$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define GILD_NAME "gild" +#define GILD_VERSION "$Revision$" +#define GILD_ID "gild $Revision$" + +#define CONFIG_PATH "/usr/local/etc/gild/gild.conf" +#define DEFAULT_PORT_NO 8421 +#define MAX_PENDING_REQUESTS 5 + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#define ever (;;) +#define null (((void *) 0)) + + +typedef struct handler +{ + struct handler * next; /* next one down the chain */ + char * protocol; /* a name for the protocol handled */ + regex_t * pattern; /* the pattern to match to select this + handler */ + char * command; /* the command to invoke this handler */ + int timeout; /* how long to allow for this handler to + complete, or 0 if infinite */ +} handler; + +void error( int severity); +/* log the current contents of errorBuff and then if severity is bad die */ + +handler * get_handler( char * match); +/* find a handler whose pattern matches match, and return it's command */ + +int log( int level, char *message); +/* hand this message over to the syslog daemon for recording */ + +int parse_config( char * path); +/* parse the config file and identify the handlers I handle */ + +void wrapper( int conversation, char * client_id); +/* conversation is the handle on an open socket communicating with a + client. */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..c3d405c --- /dev/null +++ b/src/log.c @@ -0,0 +1,32 @@ +/***********************************************************************\ +* * +* Project: Gild * +* log.c * +* * +* Purpose: Interface to syslogd. Why do our own logging, * +* which only adds to sysadmin hassle and confusion, * +* when we can use the system log? * +* * +* Author : Simon Brooke * +* Copyright: (c) Simon Brooke 1997 * +* Version : 0.1 * +* Created : 15th October 1997 * +* * +\***********************************************************************/ + +/* $Header$ */ + +#include "gild.h" + +int log( int level, char *message) +/* hand this message over to the syslog daemon for recording */ +{ +#ifdef DEBUG + fprintf( stderr, "%s: DEBUG: %s\n", GILD_ID, message); +#else + openlog( GILD_NAME, 0, LOG_DAEMON); + syslog( level, message); + closelog(); +#endif +} + diff --git a/src/wrapper.c b/src/wrapper.c new file mode 100644 index 0000000..e16e0fb --- /dev/null +++ b/src/wrapper.c @@ -0,0 +1,100 @@ +/**************************************************************************\ +* * +* Project: Gild * +* wrapper.c * +* * +* Purpose: Given a open socket connected to a client, handle * +* the conversation with the client by detecting the protocol * +* in use and invoking the appropriate handler * +* * +* Author : Simon Brooke * +* Copyright: (c) Simon Brooke 1997 * +* Version : 0.1 * +* Created : 7th October 1997 * +* * +\**************************************************************************/ + +/* $Header */ + +#include "gild.h" + +extern char errorBuff[ 1024]; +extern char **environ; + +void die( void) +/* inherited from cgi-lib; a way of getting rid of errant programs */ +{ + sprintf( errorBuff, "potentially serious: handler over-ran alloted time"); + error( LOG_ERR); + exit( 1); /* belt and braces; should never be called */ +} + + +void wrapper( int conversation, char * client_id) +/* conversation is the handle on an open socket communicating with a + client. */ +{ + char firstln[ 1024]; + char * exec_args[1]; /* 'arguments' to pass to execve */ + handler * command = null; + + recv( conversation, firstln, 80, MSG_PEEK); + /* get the first thing the client + says, but leave it on the input + stream for the handler. */ + + if ( dup2( conversation, STDIN_FILENO) == -1) + { + sprintf( errorBuff, + "failed to duplicate conversation [%d] onto stdin: %s", + conversation, strerror( errno)); + error( LOG_ERR); + } + + if ( dup2( conversation, STDOUT_FILENO) == -1) + { + sprintf( errorBuff, + "failed to duplicate conversation [%d] onto stdout: %s", + conversation, strerror( errno)); + error( LOG_ERR); + } + + command = get_handler( firstln); + /* and find the appropriate handler */ + if ( command == null) /* didn't find one */ + { + sprintf( errorBuff, "no handler registered for %s", firstln); + error( LOG_ERR); + } + else /* did find one */ + { + setenv( "REMOTE_HOST", client_id, 1); + /* set up the handler environment */ + + sprintf( errorBuff, + "using handler '%s' [%d] to handle %s request from %s", + command->command, ( int)getpid(), command->protocol, + client_id); + error( LOG_NOTICE); + /* log the request, and... */ + + exec_args[ 0] = command->command; + exec_args[ 1] = null; + /* set up the dummy arguments */ + + if ( execve( command->command, exec_args, environ) == -1) + /* ...execute the command (shouldn't return) */ + { /* if it did we've got an error */ + sprintf( errorBuff, + "error [errno %d] whislt execing handler '%s' [%d]\n", + errno, command->command, ( int)getpid()), + error( LOG_ERR); + } + } + + exit( 0); +} + + + +