Generalised Internet Listener Daemon
This commit is contained in:
commit
f8cb76e849
23
Makefile
Normal file
23
Makefile
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Makefile for itinerary engine utilities
|
||||||
|
|
||||||
|
CC= gcc
|
||||||
|
CFLAGS= -g
|
||||||
|
LD= gcc
|
||||||
|
LDFLAGS= -g
|
||||||
|
BINDIR= /usr/local/bin
|
||||||
|
TARGETS= gild
|
||||||
|
COMPONENTS = gild.o wrapper.o config.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) $(BINDIR)
|
115
config.c
Normal file
115
config.c
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/**************************************************************************\
|
||||||
|
* *
|
||||||
|
* Project: Gild *
|
||||||
|
* config.c *
|
||||||
|
* *
|
||||||
|
* Purpose: Handle configuration file *
|
||||||
|
* *
|
||||||
|
* Author : Simon Brooke *
|
||||||
|
* Copyright: (c) Simon Brooke 1997 *
|
||||||
|
* Version : 0.1 *
|
||||||
|
* Created : 8th October 1997 *
|
||||||
|
* *
|
||||||
|
\**************************************************************************/
|
||||||
|
|
||||||
|
#include "gild.h"
|
||||||
|
|
||||||
|
extern char errorBuff[]; /* where I assemble logging messages */
|
||||||
|
handler * handlers = ( handler *) null;
|
||||||
|
/* the list of handlers I handle */
|
||||||
|
|
||||||
|
void 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 */
|
||||||
|
|
||||||
|
|
||||||
|
sprintf( errorBuff, "Loading configuration from %s", path);
|
||||||
|
error( NOTICE);
|
||||||
|
|
||||||
|
configFile = fopen( path, "r");
|
||||||
|
|
||||||
|
if ( configFile == ( FILE *)null)
|
||||||
|
{
|
||||||
|
sprintf( errorBuff, "failed to open configuration file %s: %s",
|
||||||
|
path, strerror( errno));
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
while( ! feof( configFile))
|
||||||
|
{
|
||||||
|
char buff[ 1024], protocol[ 1024], pattern[ 1024], command[ 1024];
|
||||||
|
int port;
|
||||||
|
|
||||||
|
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 %d %s %s", protocol, &port,
|
||||||
|
pattern, command) == 4)
|
||||||
|
/* otherwise look for four fields. If
|
||||||
|
we find them... */
|
||||||
|
{
|
||||||
|
handler * newhandler =
|
||||||
|
( struct handler *) malloc( sizeof( struct handler));
|
||||||
|
/* create a handler */
|
||||||
|
|
||||||
|
if ( newhandler == ( handler *) null)
|
||||||
|
{ /* unlikely, but... best check */
|
||||||
|
sprintf( errorBuff,
|
||||||
|
"out of memory whilst allocating handler?");
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* and load it with the values we found */
|
||||||
|
newhandler->port = port;
|
||||||
|
newhandler->protocol = strdup( protocol);
|
||||||
|
newhandler->pattern = regcomp( pattern);
|
||||||
|
newhandler->command = strdup( command);
|
||||||
|
/* then splice it into the handler chain */
|
||||||
|
newhandler->next = handlers;
|
||||||
|
handlers = newhandler;
|
||||||
|
|
||||||
|
/* and log it. */
|
||||||
|
sprintf( errorBuff,
|
||||||
|
"registering handler [%s] for protocol %s",
|
||||||
|
newhandler->command, newhandler->protocol);
|
||||||
|
error( NOTICE);
|
||||||
|
|
||||||
|
/* ultimately we will deal with
|
||||||
|
listening on multiple ports, but
|
||||||
|
not yet! */
|
||||||
|
if ( newhandler->next != null)
|
||||||
|
{
|
||||||
|
if ( newhandler->next->port != port)
|
||||||
|
{
|
||||||
|
sprintf( errorBuff,
|
||||||
|
"port for %s [%d] differs from %s [%d] - this version of GILD only handles one port!",
|
||||||
|
protocol, port, newhandler->next->protocol,
|
||||||
|
newhandler->next->port);
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char * get_handler_command( char * match)
|
||||||
|
/* find a handler whose pattern matches match, and return it's command */
|
||||||
|
{
|
||||||
|
handler * h;
|
||||||
|
char * command = null;
|
||||||
|
|
||||||
|
for ( h = handlers; ( command == null) && ( h != null); h = h->next)
|
||||||
|
/* scan down the list of handlers looking
|
||||||
|
for a match... */
|
||||||
|
{
|
||||||
|
if ( 0) /* ( regexec( h->pattern, match)) */
|
||||||
|
{
|
||||||
|
command = h->command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return( command);
|
||||||
|
}
|
166
gild.c
Normal file
166
gild.c
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/***********************************************************************\
|
||||||
|
* *
|
||||||
|
* 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 *
|
||||||
|
* *
|
||||||
|
\***********************************************************************/
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
FILE * logstream = stderr; /* where I stick log messages */
|
||||||
|
|
||||||
|
|
||||||
|
void error( int severity)
|
||||||
|
/* log the current contents of errorBuff and then if severity is bad die */
|
||||||
|
{
|
||||||
|
fprintf( logstream, "%s\n", errorBuff);
|
||||||
|
|
||||||
|
switch ( severity)
|
||||||
|
{
|
||||||
|
case FATAL_ERROR:
|
||||||
|
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 */
|
||||||
|
|
||||||
|
sprintf( errorBuff, "GILD starting...");
|
||||||
|
error( NOTICE);
|
||||||
|
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
sprintf( errorBuff,
|
||||||
|
"unrecognised command line switch [-%c]",
|
||||||
|
argv[ arg][ 1]);
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
sprintf( errorBuff, "unrecognised command line token [%s]",
|
||||||
|
argv[ arg]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_config( configPath);
|
||||||
|
|
||||||
|
keyhole = socket( AF_INET, SOCK_STREAM, 0);
|
||||||
|
if ( keyhole == -1)
|
||||||
|
{
|
||||||
|
sprintf( errorBuff, "failed to intialise socket");
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
sprintf( errorBuff, "failed to bind socket");
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DEBUG
|
||||||
|
/* we become a real class one daemon */
|
||||||
|
if ( fork() != 0) exit(0);
|
||||||
|
/* the parent exits */
|
||||||
|
setsid(); /* release the controlling tty */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( listen( keyhole, MAX_PENDING_REQUESTS) == -1)
|
||||||
|
{
|
||||||
|
sprintf( errorBuff, "failed in listen()?");
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
sprintf( errorBuff, "GILD: awaiting requests");
|
||||||
|
error( NOTICE);
|
||||||
|
|
||||||
|
for ever
|
||||||
|
{
|
||||||
|
struct sockaddr_in * client =
|
||||||
|
( struct sockaddr_in *) malloc( sizeof( struct sockaddr_in));
|
||||||
|
/* the address of the client calling in */
|
||||||
|
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( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
memset( client, 0, sizeof( client));
|
||||||
|
/* sanitation */
|
||||||
|
|
||||||
|
conversation = accept( keyhole, ( struct sockaddr *)client,
|
||||||
|
&clientsize);
|
||||||
|
|
||||||
|
if ( conversation == -1)
|
||||||
|
{ /* again, check we set it up OK */
|
||||||
|
sprintf( errorBuff, "Could not establish conversation [%d]",
|
||||||
|
errno);
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf( errorBuff, "Connection received");
|
||||||
|
error( NOTICE);
|
||||||
|
|
||||||
|
switch( fork())
|
||||||
|
{
|
||||||
|
case -1: /* Blew it: whinge and die */
|
||||||
|
sprintf( errorBuff, "failed to fork?");
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
break;
|
||||||
|
case 0: /* I'm the child */
|
||||||
|
close( keyhole);
|
||||||
|
|
||||||
|
wrapper( conversation);
|
||||||
|
/* wrapper (q.v.) handles the conversation */
|
||||||
|
break;
|
||||||
|
default: /* I'm the parent */
|
||||||
|
close( conversation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
gild.conf
Normal file
4
gild.conf
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
http 8080 HTTP/0\.9 handlers/http
|
||||||
|
crp 8080 CRP/1.* handlers/crp
|
||||||
|
|
||||||
|
|
67
gild.h
Normal file
67
gild.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/**************************************************************************\
|
||||||
|
* *
|
||||||
|
* 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 *
|
||||||
|
* *
|
||||||
|
\**************************************************************************/
|
||||||
|
|
||||||
|
#define _POSIX_SOURCE
|
||||||
|
/* this is a fudge. The whole thing depends on fdopen, and I'm having
|
||||||
|
great difficulty making it work */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <regexp.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <linux/in.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define CONFIG_PATH "/usr/local/etc/gild/gild.conf"
|
||||||
|
#define DEFAULT_PORT_NO 1984
|
||||||
|
#define MAX_PENDING_REQUESTS 5
|
||||||
|
|
||||||
|
#define null (((void *) 0))
|
||||||
|
|
||||||
|
#define FATAL_ERROR 1
|
||||||
|
#define NOTICE 0
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
|
||||||
|
#define ever (;;)
|
||||||
|
|
||||||
|
typedef struct handler
|
||||||
|
{
|
||||||
|
struct handler * next; /* next one down the chain */
|
||||||
|
int port; /* the port on which I listen */
|
||||||
|
char * protocol; /* a name for the protocol handled */
|
||||||
|
regexp * pattern; /* the pattern to match to select this
|
||||||
|
handler */
|
||||||
|
char * command; /* the command to invoke this handler */
|
||||||
|
} handler;
|
||||||
|
|
||||||
|
void error( int severity);
|
||||||
|
/* log the current contents of errorBuff and then if severity is bad die */
|
||||||
|
|
||||||
|
char * get_handler_command( char * match);
|
||||||
|
/* find a handler whose pattern matches match, and return it's command */
|
||||||
|
|
||||||
|
void parse_config( char * path);
|
||||||
|
/* parse the config file and identify the handlers I handle */
|
||||||
|
|
||||||
|
void wrapper( int conversation);
|
||||||
|
/* conversation is the handle on an open socket communicating with a
|
||||||
|
client. What I want to do is quite simple, and there must be a
|
||||||
|
straightforward way of doing it: attach the conversation to both
|
||||||
|
the standard input and the standard output of a process, and then
|
||||||
|
exec the handler within that process... I can't (at present) find
|
||||||
|
an easy way of doing that, however */
|
102
wrapper.c
Normal file
102
wrapper.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/**************************************************************************\
|
||||||
|
* *
|
||||||
|
* 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 *
|
||||||
|
* *
|
||||||
|
\**************************************************************************/
|
||||||
|
|
||||||
|
#include "gild.h"
|
||||||
|
|
||||||
|
extern char * errorBuff;
|
||||||
|
|
||||||
|
void wrapper( int conversation)
|
||||||
|
/* conversation is the handle on an open socket communicating with a
|
||||||
|
client. What I want to do is quite simple, and there must be a
|
||||||
|
straightforward way of doing it: attach the conversation to both
|
||||||
|
the standard input and the standard output of a process, and then
|
||||||
|
exec the handler within that process... I can't (at present) find
|
||||||
|
an easy way of doing that, however */
|
||||||
|
{
|
||||||
|
char firstln[ 1024];
|
||||||
|
char * command;
|
||||||
|
int i, hear, say = conversation;
|
||||||
|
FILE * c_rx, * c_tx;
|
||||||
|
|
||||||
|
printf( "wrapper started with fdes [%d]\n",
|
||||||
|
conversation);
|
||||||
|
|
||||||
|
hear = dup( conversation); /* creat two handles on conversation */
|
||||||
|
say = dup( conversation); /* one for reading and one for writing */
|
||||||
|
|
||||||
|
#ifdef globba
|
||||||
|
for( i = 0; ( i < 1024 ) && ( ( i == 0) || ( firstln[ i - 1] > ' ')); i++)
|
||||||
|
{
|
||||||
|
read( hear, ( char *)firstln + i, 1);
|
||||||
|
/* read a control-terminated line from the
|
||||||
|
conversation, one byte at a time
|
||||||
|
(prolly a better way of doing this) */
|
||||||
|
puts( firstln);
|
||||||
|
}
|
||||||
|
firstln[ i] = '\0'; /* terminate the string */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
recv( hear, firstln, 80, MSG_PEEK);
|
||||||
|
|
||||||
|
puts( "looking for command");
|
||||||
|
|
||||||
|
command = get_handler_command( firstln);
|
||||||
|
/* and find the appropriate handler */
|
||||||
|
|
||||||
|
printf( "got command [%s]\n", command);
|
||||||
|
|
||||||
|
if ( ! command) /* didn't find one */
|
||||||
|
{
|
||||||
|
sprintf( errorBuff, "no handler registered for %s", firstln);
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
else /* did find one */
|
||||||
|
{
|
||||||
|
sprintf( errorBuff, "using handler %s for protocol %s",
|
||||||
|
command, firstln);
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
recv( hear, firstln, 1024, 0);
|
||||||
|
exit( 0);
|
||||||
|
|
||||||
|
if ( dup2( hear, 0) == -1)
|
||||||
|
{
|
||||||
|
sprintf( errorBuff,
|
||||||
|
"failed to duplicate conversation [%d] onto stdin: %s",
|
||||||
|
hear, strerror( errno));
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
dup2( say, 1);
|
||||||
|
{
|
||||||
|
sprintf( errorBuff,
|
||||||
|
"failed to duplicate conversation [%d] onto stdout: %s",
|
||||||
|
say, strerror( errno));
|
||||||
|
error( FATAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gets( firstln); */
|
||||||
|
puts( "hello!\n");
|
||||||
|
/* puts( firstln); */
|
||||||
|
|
||||||
|
puts("\tClosing...");
|
||||||
|
exit( 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue