Project   |   Downloads   |   Forums   |   Tracker   |   CVS  

  Table Of Contents

·  What is this ?
·  Getting started
·  How it works?
·  Globals
·  Public API
·  Examples
·  FAQ
·  History
·  Final Notes

 
 

 
port   service

 


top

What is this ?

Daemons IFace is a library to help you to write a daemon in minutes, what you need basically is creating a dispatcher for the service your daemon is for and linking with the library everything is started up for you at program run.

The library has been designed on a multitasking fashion, incorporating replacements for stdlib memory allocation functions with the advantage of using native pools protected by semaphores. as well it uses global variables to setup default options for (almost) everything, with the meaning that you could override them by defining them on your program with your own defaults.

The number of services for a single daemon is unlimited!, why you'll have some tasks running to manage each service? you could have a process only to manage all services, instead of several tasks squandering memory and system resources.

last but no least, it has as well basic features like syslog, signals handling, helpful functions to receive and send data through sockets (and therefore simplify even more the dispatchers), etc. And remember thats currently a early library version!.

top

Getting started

lets figure, (as a quick example), you want to create a echo daemon, first thing to do is include the Daemons header file:
 #include <daemons.h>


next is defining the service your daemon is for, in this example, the port number for the echo service is the #7.

 ushort __services[] = { 7, 0 };


we use an array since, as above mentioned, the number of services is unlimited. At this point, before writing the dispatcher (preferably before the dispatcher, for structural reasons, imho), you could configure your daemon by using symbols/variables defined on the library, which once defined on your program the ones on the lib (the default options) get replaced, but we are curently writing a quick daemon, we will explain all those things later.

next come the echo dispatcher:

 
short echo_dispatcher(struct client *c)
{
	u_char buffer[1024], *ptr=buffer;
	
	while(daemons_recv( &ptr, sizeof(buffer) -1, c ))
	{
		daemons_send( buffer, -1, c );
	}
	
	return 1;
}


as you should know, the echo service just reply with what you send to it, therefore obviously should be clear what that tiny dispatcher does :)

The next (and last!) step is writing the main() function:

 
int main()
{
	daemons_bind_setdispatcher( 7, echo_dispatcher);
	
	daemons_loop();
	
	return 0;
}


obviously daemons_bind_setdispatcher() is used to define the dispatcher for the service(s) you're creating, daemons_loop() is the signals dispatcher, the function which manages all incoming connections/events/etc, it does a infinite loop until a 'break_c' signal is received.

At this point, once compiled you'll have a fully functional and complete daemon!, isn't easy? ...nothing is more! :-)

top

How it works?

How the library core is started and shutdown is based on constructors and destructors, therefore when main() is called everything is up and working, else the program will fail to load.

On startup a named message port is created, the name used is from the progname variable which you should declare somewhere on your code with the program name (isn't a good idea to use the dafault one, since you're forbidden to use more than a single Daemons process running with the same named port).

bsdsocket stuff is initialized to use asynchronous I/O sockets and syslog by default, syslog messages is set to upto LOG_DEBUG when syslog_debug variable is TRUE else defaults to LOG_INFO.

Once the above as been setup correctly its time to bind the requested services, here the contents of the __services[] array is traversed, in case some bind failed you'll be informed with the reason for the failure, if the bind isn't succesfull the function will continue with the next requested service, on fatal errors only (i.e out of memory) the program will die, all binded services will be saved on a private context (well, not so private.. you can access them from the childs, but isn't recomended..) which are protected by semaphores.

At this point, all constructors have been called and now we are on the main() function, after setting the dispatcher(s) for your service(s) we entered on the loop context by calling daemons_loop(), here we Wait() for socket/input events defined on the daemons_signals variable, if you need to manage signals yourself you can add them to that variable, unknown signals will be passed to the function :
void daemons_signal_dispatcher(ULONG sigs) which you must create to manage your owns.


Now we are ready to accept incoming connections, once we receive a FD_ACCEPT event and the socket file descriptor is accept()'ed (which shouldn't noramlly fail), we launch a new process to manage that connection. The name used for that process is as defined on the progname variable, his default priority is 0 and the stack size 16384 bytes.

If the process could not be created 1) because CreateProcNew() failed for some reason, or 2) the maximun number of childs has been reached, the function daemons_forkerror() will be called, the default action for this function is to inform you of the error through syslog (by using strerror(errno)), just override it, creating your own, if you need some more sofisticated error reporting... (which indeed, in most cases). For example, if you're creating a FTP server you should sent a 421 error message:

 void daemons_forkerror(struct service *bcfg)
{
   char *msg = "421 Too many users, try again later\r\n";
   
   send( bcfg->asock, msg, strlen(msg), 0 );
}


The maximun number of child processes has been defined to be 26 by default, if you need more (or want less) declare somewhere on your program the variable daemons_forks with the number you wishes.

Now we are into the child process, before the incoming connection is passed to your dispatcher, we initialize our context, preparing the struct client * data, which contents as follows:

 
struct accept
{
	struct sockaddr_in   sa;
	struct hostent     * he;
	struct in_addr       ia;
};


sacontains the data of the incoming connection, filled by accept()
hecontains the hostent returned by gethostbyaddr().
NOTE: DNS lookups is ativated by default, if you want to deactivate it declare "short daemons_resolve = FALSE;" somewhere on your program.
iahis internal use is undefined... use it for your needs if you want

 
struct client
{
	long sockfd;
	
	struct accept a;
	char *client_host, *client_ip;
	
	struct SignalSemaphore *sem;
	
	struct service *s;
	struct Library *SocketBase;
	int h_errno, errno;
};


sockfdcontains the socket descriptor of the incoming connection
aabove explained
client_ipcontains always the IP of the incoming connection
client_hostwill contains the resolved hostname (only when DNS lookups is activated)
sema initialized semaphore for you own use.
ssee below
SocketBaseReady to use bsdsocket library base pointer.
WARNING: you should use this pointer (not a global!) on your dispatcher or expect problems!...
h_errno/errnoYou MUST use them instead the globals, or error checking will not work properly into your dispatcher!.

 
struct service
{
	long sockfd , asock;
	unsigned short port;
	
	BOOL (*dispatcher)(struct client *c);
	
	/* private fields omitted */
};


sockfdconatins the descriptor of the bind()'ed socket
asockcontains a temporary descriptor of the current incoming connection
portthe port number where we're listen for connections
dispatcheryour dispatcher, obviously
You shouldn't normally need to use that data...

Next we call your dispatcher with that client data as first parameter, remember that if you use bsdsocket functions directly on your dispatcher (ie, you aren't only using build-in wrappers, like daemons_recv() and/or daemons_send()) you must define his library base pointer and variables to use the newly created on the child process context.

Example:

 
#define SocketBase	c->SocketBase
#define h_errno		c->h_errno
#define errno		c->errno

BOOL dispatcher( struct client *c )
{
   // ...
}

#undef SocketBase
#undef h_errno
#undef errno


And thats all!, once your dispatcher has finished we free our client data ofcourse, also we close the socket descriptor (if wasn't closed by you).

If you need some more technical information about the internals just take a look at the sources.

top

Globals

Below is a list of currently used Global variables to define default settings, declare any of them on your program to override defaults options.

variabletypedefault valuedescription
daemons_child_priorityint0default priority for child processes.
daemons_child_stacksizeULONG16384default Stack for child processes.
daemons_child_xtratagsstruct TagItem *TAG_DONEadditional tags passed on child processes creation (see <dos/dostags.h>).
daemons_forksushort26Maximun number of child processes.
daemons_recvln_timeoutint240Timeout (in seconds) for select() call from daemons_recvln() function.
daemons_recv_timeoutint4Timeout (in seconds) for select() call from daemons_recv() function.
daemons_resolveshortTRUEcontrols whenever DNS lookup is performed on incoming connections.
daemons_signalsULONG0signals mask to Wait(), his initial value is 0, later filled with socket and break masks.
prognamechar *Daemons IFaceYour daemon program name, used for syslog messages and childs process name.
syslog_debugshortFALSEcontrols whenever syslog debuging messages are used.
__malloc_memflagsULONGMEMF_PUBLIC |MEMF_CLEARdefault memory flags used on CreatePool().
__malloc_mempool_puddleSizeULONG2048size for memory pool puddles.
__malloc_mempool_threshSizeULONG1024the largest size which fits into puddles.
__malloc_minsizeULONG16Minimun number of bytes to allocate by the build-in malloc() replacement.
__services[]ushort0services to listen on.

 

top

Public API


FUNCTIONBOOL daemons_bind_setdispatcher( unsigned short port, BOOL (*dispatcher)(struct client *c));
DESCRIPTIONAssign the dispatcher dispatcher for the service pointed by port.
RESULTTRUE if the dispatcher was set, else you mispelled the port argument.

FUNCTIONvoid daemons_loop( void );
DESCRIPTIONMain loop to Wait() for signal handling, socket events, etc

FUNCTIONSTRPTR daemons_recvln( STRPTR *dest, ULONG destlen, struct client *c );
DESCRIPTIONreceives data from the socket (c->sockfd) until a CR or LF is reached (which characters aren't included).
dest is where data is stored and destlen the size of dest.
RESULT(*dest) when everything was OK, else NULL which could be caused by 1) select() failed, 2) timeout reached, 3) there isn't more space left on dest, or 4) recv() error.

FUNCTIONLONG daemons_recv( STRPTR *dest, long destlen, struct client *c );
DESCRIPTIONreceives destlen bytes of data from the socket (c->socket) and fills it into dest.
RESULTthe number of bytes received, or 0 on error, which could be caused by 1) timeout reached, or 2) recv() error

FUNCTIONBOOL daemons_send( STRPTR string, long length, struct client *c );
DESCRIPTIONsends string to the socket (c->sockfd), length is the size of string (if that value es <=0 the length is calculated for you).
RESULTTRUE if length is equal to the value returned by send().

FUNCTIONchar *daemons_clock( void );
DESCRIPTIONGets the localtime, returning a string as a digital clock (00:00:00)
RESULTthe returned string isn't allocated, and valid only until the next call to this function.

FUNCTIONnfo(args...)
DESCRIPTIONMacro to: syslog( LOG_INFO, args)

FUNCTIONwarn(args...)
DESCRIPTIONMacro to: syslog( LOG_WARN, args)

FUNCTIONerr(args...)
DESCRIPTIONMacro to: syslog( LOG_ERR, args)

FUNCTIONnotice(args...)
DESCRIPTIONMacro to: syslog( LOG_NOTICE, args)

FUNCTIONdeb(args...)
DESCRIPTIONMacro to: syslog( LOG_DEBUG, args)

 

top

Examples

Below is a list of some tiny and simply daemons examples for some protocols.

exampledescription
echod.cecho daemon
httpd.cHTTP daemon

 

top

FAQ

none yet.

 

top

History

DATECHANGELOG
24.07.2005First Public Release.

 

top

Final Notes

Daemons IFace Library and example daemons have been developed using LIBNIX library on an AmigaOS v3.x system ( m68k ).

His portable status is unknown... we have not tested it on other system that where we have developed it, even if should compile on any AmigaOS compliant system, we dunno how it will work on others enviroments, feel free to submit reports and/or patches.

If you haven't deduced it, all daemons compiled with our library are running on STANDALONE mode, (thats the best way and there is no plan for inetd based, atleast for now), due the meaning of a standalone daemon is to be run on the background, you can either build your daemons using libnix's detach.o object or just running it using "RUN <NIL: >NIL:".


Dont hesitate to post your comments, suggestion, patches, whatever!, we want your feedback!.

Apologizes for my bad english.



  $Id: index.php,v 0.1 2005/07/24 14:47:22 diegocr Exp $ Copyright (C) 2005 Diego Casorran, All Rights Reserved.  
-->