Seems like there is some interest in sms service types as loadable modules so I'm submitting my implementation.
Also included are two sample modules. They are very simple and lack proper logging and error handling. As has been mentioned, the loadable module interface has to be carefully designed with generallity in mind. The current one feels a bit crude and might need some further refinement. One way of deriving a good interface could be taking a look at what is needed to make all current sms service types into loadable libraries. Note: loading libraries into smsbox requires smsbox to be linked with -rdynamic. /Dennis Dennis Malmström Erda Technology AB S:t Larsgatan 12, 582 24 Linköping, Sweden http://www.erda.se mail: [EMAIL PROTECTED] phone: +46 (0)13 377218 gsm: +46 (0)706483090
Index: gw/smsbox.c =================================================================== RCS file: /home/cvs/gateway/gw/smsbox.c,v retrieving revision 1.170 diff -u -r1.170 smsbox.c --- gw/smsbox.c 2002/02/07 22:16:35 1.170 +++ gw/smsbox.c 2002/02/22 12:18:57 @@ -6,6 +6,7 @@ #include <unistd.h> #include <signal.h> #include <string.h> +#include <dlfcn.h> #include "gwlib/gwlib.h" @@ -58,9 +59,21 @@ static Numhash *black_list; static List *smsbox_requests = NULL; +static List *loaded_modules = NULL; int charset_processing (Octstr *charset, Octstr *text, int coding); + + +typedef Octstr* loadable_doit( Msg* msg, Octstr* pattern ); + +typedef struct { + Octstr* name; + void* handle; + loadable_doit* doit; +} Module; + + /*********************************************************************** * Communication with the bearerbox. */ @@ -655,6 +668,87 @@ } break; + case TRANSTYPE_CALL_LOADABLE_MODULE: + { + Module* module = NULL; + Octstr* moduleName = urltrans_name(trans); /* What is the purpose of +name? Is this use appropriate? */ + + debug("sms.module", 0, "calling module '%s'", octstr_get_cstr( moduleName +) ); + + /* is module already loaded? */ + if ( loaded_modules == NULL ) { + loaded_modules = list_create(); + } else { + int I = list_len( loaded_modules ); + int i = 0; + + for ( i = 0; i < I; i++ ) { + Module* x = (Module*) list_get( loaded_modules, i ); + + if ( ! octstr_compare( x->name, moduleName ) ) { + module = x; + break; + } + } + } + + /* If not, load it */ + if ( module == NULL ) { + void *moduleHandle = NULL; + loadable_doit* doit; + + debug("sms.module", 0, "loading module '%s'", octstr_get_cstr( +moduleName ) ); + + /* load module */ + { + Octstr* fileName = octstr_duplicate( moduleName ); + octstr_append_cstr( fileName, ".so" ); + moduleHandle = dlopen( octstr_get_cstr( fileName ), RTLD_NOW ); + octstr_destroy( fileName ); + + if ( ! moduleHandle ) { + error(0, "dlopen failed: %s", dlerror() ); + *result = NULL; + octstr_destroy(pattern); + goto error; + } + } + + /* look up symbol */ + { + const char* errmsg = NULL; + doit = dlsym( moduleHandle, octstr_get_cstr( moduleName ) ); + errmsg = dlerror(); + + if ( errmsg ) { + error(0, "dlsym failed: %s", errmsg ); + *result = NULL; + octstr_destroy(pattern); + goto error; + } + + if ( ! doit ) { /* we cant't call NULL */ + error(0, "dlsym returned NULL" ); + *result = NULL; + octstr_destroy(pattern); + goto error; + } + } + + /* keep track of this module */ + module = (Module*) gw_malloc( sizeof( Module ) ); + module->name = octstr_duplicate( moduleName ); + module->handle = moduleHandle; + module->doit = doit; + list_insert( loaded_modules, 0, module ); + } + + /* call module */ + *result = (*module->doit)( msg, pattern ); + octstr_destroy(pattern); + } + break; + case TRANSTYPE_GET_URL: request_headers = http_create_empty_headers(); http_header_add(request_headers, "User-Agent", "Kannel " VERSION); @@ -2105,8 +2199,30 @@ octstr_destroy(reply_couldnotrepresent); numhash_destroy(black_list); numhash_destroy(white_list); + + /* close all loaded modules */ + if ( loaded_modules != NULL ) { + int I = list_len( loaded_modules ); + int i = 0; + for ( i = 0; i < I; i++ ) { + Module* module = (Module*) list_get( loaded_modules, i ); + + if ( dlclose( module->handle ) ) { + error(0, "dlclose failed: %s", dlerror() ); + } + module->handle = NULL; + + octstr_destroy( module->name ); + gw_free( module ); + } + + list_destroy( loaded_modules, NULL ); + } + + cfg_destroy(cfg); gwlib_shutdown(); + return 0; } Index: gw/urltrans.c =================================================================== RCS file: /home/cvs/gateway/gw/urltrans.c,v retrieving revision 1.70 diff -u -r1.70 urltrans.c --- gw/urltrans.c 2002/02/07 22:16:46 1.70 +++ gw/urltrans.c 2002/02/22 12:19:00 @@ -670,7 +670,7 @@ static URLTranslation *create_onetrans(CfgGroup *grp) { URLTranslation *ot; - Octstr *aliases, *url, *post_url, *text, *file, *exec; + Octstr *aliases, *url, *post_url, *text, *file, *exec, *module; Octstr *accepted_smsc, *forced_smsc, *default_smsc; Octstr *grpname, *sendsms_user, *sms_service; int is_sms_service; @@ -730,6 +730,7 @@ file = cfg_get(grp, octstr_imm("file")); text = cfg_get(grp, octstr_imm("text")); exec = cfg_get(grp, octstr_imm("exec")); + module = cfg_get(grp, octstr_imm("module")); if (url != NULL) { ot->type = TRANSTYPE_GET_URL; ot->pattern = url; @@ -743,9 +744,12 @@ } else if (text != NULL) { ot->type = TRANSTYPE_TEXT; ot->pattern = text; - } else if (exec != NULL) { - ot->type = TRANSTYPE_EXECUTE; - ot->pattern = exec; + } else if (exec != NULL) { + ot->type = TRANSTYPE_EXECUTE; + ot->pattern = exec; + } else if (module != NULL) { + ot->type = TRANSTYPE_CALL_LOADABLE_MODULE; + ot->pattern = module; } else { error(0, "Configuration group `sms-service' " "did not specify get-url, post-url, file or text."); Index: gw/urltrans.h =================================================================== RCS file: /home/cvs/gateway/gw/urltrans.h,v retrieving revision 1.24 diff -u -r1.24 urltrans.h --- gw/urltrans.h 2002/02/07 22:16:46 1.24 +++ gw/urltrans.h 2002/02/22 12:19:00 @@ -48,7 +48,8 @@ TRANSTYPE_TEXT, TRANSTYPE_FILE, TRANSTYPE_EXECUTE, - TRANSTYPE_SENDSMS + TRANSTYPE_SENDSMS, + TRANSTYPE_CALL_LOADABLE_MODULE }; Index: gwlib/cfg.def =================================================================== RCS file: /home/cvs/gateway/gwlib/cfg.def,v retrieving revision 1.38 diff -u -r1.38 cfg.def --- gwlib/cfg.def 2002/02/07 22:16:04 1.38 +++ gwlib/cfg.def 2002/02/22 12:19:00 @@ -196,6 +196,7 @@ OCTSTR(file) OCTSTR(text) OCTSTR(exec) + OCTSTR(module) OCTSTR(accepted-smsc) OCTSTR(faked-sender) OCTSTR(max-messages)
#include <stdio.h> #include "gwlib/octstr.h" #include "gw/msg.h" static FILE *file = NULL; /* Note: smsbox must be linked with -rdynamic kannel.conf: group = sms-service keyword = default name = "sampleModule" module = "%p %P %t %a" omit-empty = 1 */ Octstr* sampleModule( Msg* msg, Octstr* pattern ) /* return reply text */ { fprintf( file, "logged by sampleModule: %s <%s>\n", octstr_get_cstr( msg->sms.sender ), octstr_get_cstr( pattern ) ); fflush( file ); return NULL; } void sampleModule_initialize() /* see -init in man ld */ { if ( ! file ) { fprintf( stderr, "initialize sampleModule\n" ); file = fopen( "/tmp/sampleModule.log", "a" ); } } void sampleModule_destroy() /* see -fini in man ld */ { fprintf( stderr, "cleanup sampleModule\n" ); if ( file ) { fclose( file ); file = NULL; } }
#include <stdio.h> #include "gwlib/octstr.h" #include "gw/msg.h" static FILE *file = NULL; /* Note: smsbox must be linked with -rdynamic kannel.conf: group = sms-service keyword = s2 name = "sampleModule" module = "%p %P %t %a" omit-empty = 1 */ Octstr* sampleModule2( Msg* msg, Octstr* pattern ) /* return reply text. */ { fprintf( file, "logged by sampleModule2: %s <%s>\n", octstr_get_cstr( msg->sms.sender ), octstr_get_cstr( pattern ) ); fflush( file ); return NULL; } void sampleModule2_initialize() /* see -init in man ld */ { if ( ! file ) { fprintf( stderr, "initialize sampleModule2\n" ); file = fopen( "/tmp/sampleModule.log", "a" ); } } void sampleModule2_destroy() /* see -fini in man ld */ { fprintf( stderr, "cleanup sampleModule2\n" ); if ( file ) { fclose( file ); file = NULL; } }
KANNEL_HOME=../kannel/gateway CFLAGS=-D_REENTRANT=1 -I$(KANNEL_HOME) -fPIC -g -O2 -I/usr/include/libxml2/libxml -I/usr/include/libxml2 -DBROKEN_PTHREADS=1 all: sampleModule.so sampleModule2.so sampleModule.so: sampleModule.c gcc $(CFLAGS) -c sampleModule.c ld -init sampleModule_initialize -fini sampleModule_destroy -shared -o sampleModule.so sampleModule.o # gcc -Xlinker -init -Xlinker sampleModule_initialize -Xlinker -fini -Xlinker sampleModule_destroy -shared -o sampleModule.so sampleModule.o sampleModule2.so: sampleModule2.c gcc $(CFLAGS) -c sampleModule2.c ld -init sampleModule2_initialize -fini sampleModule2_destroy -shared -o sampleModule2.so sampleModule2.o # gcc -Xlinker -init -Xlinker sampleModuled_initialize -Xlinker -fini -Xlinker sampleModule2_destroy -shared -o sampleModule2.so sampleModule2.o clean: rm *.o *.so *~