|
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;
};
| sa | contains the data of the incoming connection, filled by accept() | | he | contains 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. | | ia | his 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;
};
| sockfd | contains the socket descriptor of the incoming connection | | a | above explained | | client_ip | contains always the IP of the incoming connection | | client_host | will contains the resolved hostname (only when DNS lookups is activated) | | sem | a initialized semaphore for you own use. | | s | see below | | SocketBase | Ready to use bsdsocket library base pointer. WARNING: you should use this pointer (not a global!) on your dispatcher or expect problems!... | | h_errno/errno | You 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 */
};
| sockfd | conatins the descriptor of the bind()'ed socket | | asock | contains a temporary descriptor of the current incoming connection | | port | the port number where we're listen for connections | | dispatcher | your 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.
| variable | type | default value | description |
|---|
| daemons_child_priority | int | 0 | default priority for child processes. | | daemons_child_stacksize | ULONG | 16384 | default Stack for child processes. | | daemons_child_xtratags | struct TagItem * | TAG_DONE | additional tags passed on child processes creation (see <dos/dostags.h>). | | daemons_forks | ushort | 26 | Maximun number of child processes. | | daemons_recvln_timeout | int | 240 | Timeout (in seconds) for select() call from daemons_recvln() function. | | daemons_recv_timeout | int | 4 | Timeout (in seconds) for select() call from daemons_recv() function. | | daemons_resolve | short | TRUE | controls whenever DNS lookup is performed on incoming connections. | | daemons_signals | ULONG | 0 | signals mask to Wait(), his initial value is 0, later filled with socket and break masks. | | progname | char * | Daemons IFace | Your daemon program name, used for syslog messages and childs process name. | | syslog_debug | short | FALSE | controls whenever syslog debuging messages are used. | | __malloc_memflags | ULONG | MEMF_PUBLIC |MEMF_CLEAR | default memory flags used on CreatePool(). | | __malloc_mempool_puddleSize | ULONG | 2048 | size for memory pool puddles. | | __malloc_mempool_threshSize | ULONG | 1024 | the largest size which fits into puddles. | | __malloc_minsize | ULONG | 16 | Minimun number of bytes to allocate by the build-in malloc() replacement. | | __services[] | ushort | 0 | services to listen on. |
|
top Public API
| FUNCTION | BOOL daemons_bind_setdispatcher( unsigned short port, BOOL (*dispatcher)(struct client *c)); | | DESCRIPTION | Assign the dispatcher dispatcher for the service pointed by port. | | RESULT | TRUE if the dispatcher was set, else you mispelled the port argument. |
|
| FUNCTION | void daemons_loop( void ); | | DESCRIPTION | Main loop to Wait() for signal handling, socket events, etc |
|
| FUNCTION | STRPTR daemons_recvln( STRPTR *dest, ULONG destlen, struct client *c ); | | DESCRIPTION | receives 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. |
|
| FUNCTION | LONG daemons_recv( STRPTR *dest, long destlen, struct client *c ); | | DESCRIPTION | receives destlen bytes of data from the socket (c->socket) and fills it into dest. | | RESULT | the number of bytes received, or 0 on error, which could be caused by 1) timeout reached, or 2) recv() error |
|
| FUNCTION | BOOL daemons_send( STRPTR string, long length, struct client *c ); | | DESCRIPTION | sends string to the socket (c->sockfd), length is the size of string (if that value es <=0 the length is calculated for you). | | RESULT | TRUE if length is equal to the value returned by send(). |
|
| FUNCTION | char *daemons_clock( void ); | | DESCRIPTION | Gets the localtime, returning a string as a digital clock (00:00:00) | | RESULT | the returned string isn't allocated, and valid only until the next call to this function. |
|
| FUNCTION | nfo(args...) | | DESCRIPTION | Macro to: syslog( LOG_INFO, args) |
|
| FUNCTION | warn(args...) | | DESCRIPTION | Macro to: syslog( LOG_WARN, args) |
|
| FUNCTION | err(args...) | | DESCRIPTION | Macro to: syslog( LOG_ERR, args) |
|
| FUNCTION | notice(args...) | | DESCRIPTION | Macro to: syslog( LOG_NOTICE, args) |
|
| FUNCTION | deb(args...) | | DESCRIPTION | Macro to: syslog( LOG_DEBUG, args) |
|
top Examples
Below is a list of some tiny and simply daemons examples for some protocols.
top FAQ
none yet.
top History
| DATE | CHANGELOG |
|---|
| 24.07.2005 | First 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.
|