/* dictd.c -- * Created: Fri Feb 21 20:09:09 1997 by faith@dict.org * Revised: Sat May 4 21:58:16 2002 by faith@dict.org * Copyright 1997-2000, 2002 Rickard E. Faith (faith@dict.org) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 1, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "dictd.h" #include "str.h" #include "servparse.h" #include "strategy.h" #include "index.h" #include "data.h" #include "parse.h" #ifdef USE_PLUGIN #include "plugin.h" #endif #include /* initgroups */ #include /* getpwuid */ #include /* setlocale */ #include #include #include #define MAXPROCTITLE 2048 /* Maximum amount of proc title we'll use. */ #undef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #ifndef UID_NOBODY #define UID_NOBODY 65534 #endif #ifndef GID_NOGROUP #define GID_NOGROUP 65534 #endif extern int yy_flex_debug; static int _dict_daemon; static int _dict_reaps; static char *_dict_argvstart; static int _dict_argvlen; int _dict_forks; int default_strategy_set; /* 1 if set by command line option */ int logOptions = 0; const char *logFile = NULL; int logFile_set; /* 1 if set by command line option */ const char *pidFile = "/var/run/dictd.pid"; int pidFile_set; /* 1 if set by command line option */ const char *daemon_service = DICT_DEFAULT_SERVICE; int daemon_service_set; /* 1 if set by command line option */ int _dict_daemon_limit_childs = DICT_DAEMON_LIMIT_CHILDS; int _dict_daemon_limit_childs_set; /* 1 if set by command line option */ int _dict_daemon_limit_matches = DICT_DAEMON_LIMIT_MATCHES; int _dict_daemon_limit_defs = DICT_DAEMON_LIMIT_DEFS; int _dict_daemon_limit_time = DICT_DAEMON_LIMIT_TIME; int _dict_daemon_limit_queries = DICT_DAEMON_LIMIT_QUERIES; int _dict_markTime = 0; int _dict_markTime_set; /* 1 if set by command line option */ const char *locale = NULL; int locale_set; /* 1 if set by command line option */ int client_delay = DICT_DEFAULT_DELAY; int client_delay_set; /* 1 if set by command line option */ int depth = DICT_QUEUE_DEPTH; int depth_set; /* 1 if set by command line option */ int useSyslog = 0; int syslog_facility_set; /* 1 if set by command line option */ const char *preprocessor = NULL; const char *bind_to = NULL; int bind_to_set; /* 1 if set by command line option */ /* information about dict server, i.e. text returned by SHOW SERVER command */ const char *site_info = NULL; int site_info_no_banner = 0; int site_info_no_uptime = 0; int site_info_no_dblist = 0; int inetd = 0; int need_reload_config = 0; int need_unload_databases = 0; int databases_unloaded = 0; static const char *configFile = DICT_CONFIG_PATH DICTD_CONFIG_NAME; static void dict_close_databases (dictConfig *c); static void sanity (const char *confFile); static void dict_init_databases (dictConfig *c); static void dict_config_print (FILE *stream, dictConfig *c); static void postprocess_filenames (dictConfig *dc); void dict_initsetproctitle( int argc, char **argv, char **envp ) { int i; _dict_argvstart = argv[0]; for (i = 0; envp[i]; i++) continue; if (i) _dict_argvlen = envp[i-1] + strlen(envp[i-1]) - _dict_argvstart; else _dict_argvlen = argv[argc-1] + strlen(argv[argc-1]) - _dict_argvstart; argv[1] = NULL; } void dict_setproctitle( const char *format, ... ) { va_list ap; int len; char buf[MAXPROCTITLE]; va_start( ap, format ); vsnprintf( buf, MAXPROCTITLE, format, ap ); va_end( ap ); if ((len = strlen(buf)) > MAXPROCTITLE-1) err_fatal( __FUNCTION__, "buffer overflow (%d)\n", len ); buf[ MIN(_dict_argvlen,MAXPROCTITLE) - 1 ] = '\0'; strcpy( _dict_argvstart, buf ); memset( _dict_argvstart+len, 0, _dict_argvlen-len ); } const char *dict_format_time( double t ) { static int current = 0; static char buf[10][128]; /* Rotate 10 buffers */ static char *this; long int s, m, h, d; this = buf[current]; if (++current >= 10) current = 0; if (t < 600) { snprintf( this, sizeof (buf [0]), "%0.3f", t ); } else { s = (long int)t; d = s / (3600*24); s -= d * 3600 * 24; h = s / 3600; s -= h * 3600; m = s / 60; s -= m * 60; if (d) snprintf( this, sizeof (buf [0]), "%ld+%02ld:%02ld:%02ld", d, h, m, s ); else if (h) snprintf( this, sizeof (buf [0]), "%02ld:%02ld:%02ld", h, m, s ); else snprintf( this, sizeof (buf [0]), "%02ld:%02ld", m, s ); } return this; } static int waitpid__exit_status (int status) { if (WIFEXITED(status)){ return WEXITSTATUS(status); }else if (WIFSIGNALED(status)){ return 128 + WTERMSIG(status); }else{ return -1; } } static void reaper( int dummy ) { #if 0 union wait status; #else int status; #endif pid_t pid; while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { ++_dict_reaps; if (flg_test(LOG_SERVER)) log_info( ":I: Reaped %d%s, exit status %i\n", pid, _dict_daemon ? " IN CHILD": "", waitpid__exit_status (status)); } } static int start_daemon( void ) { pid_t pid; ++_dict_forks; switch ((pid = fork())) { case 0: ++_dict_daemon; break; case -1: log_info( ":E: Unable to fork daemon\n" ); alarm(10); /* Can't use sleep() here */ pause(); break; default: if (flg_test(LOG_SERVER)) log_info( ":I: Forked %d\n", pid ); break; } return pid; } static const char * signal2name (int sig) { static char name [50]; switch (sig) { case SIGHUP: return "SIGHUP"; case SIGINT: return "SIGINT"; case SIGQUIT: return "SIGQUIT"; case SIGILL: return "SIGILL"; case SIGTRAP: return "SIGTRAP"; case SIGTERM: return "SIGTERM"; case SIGPIPE: return "SIGPIPE"; case SIGALRM: return "SIGALRM"; default: snprintf (name, sizeof (name), "Signal %d", sig); return name; } } static void log_sig_info (int sig) { log_info ( ":I: %s: c/f = %d/%d; %sr %su %ss\n", signal2name (sig), _dict_comparisons, _dict_forks, dict_format_time (tim_get_real ("dictd")), dict_format_time (tim_get_user ("dictd")), dict_format_time (tim_get_system ("dictd"))); } static void unload_databases (void) { dict_close_databases (DictConfig); DictConfig = NULL; } static void reload_config (void) { dict_close_databases (DictConfig); if (!access(configFile,R_OK)){ prs_file_pp (preprocessor, configFile); postprocess_filenames (DictConfig); } sanity (configFile); if (dbg_test (DBG_VERBOSE)) dict_config_print( NULL, DictConfig ); dict_init_databases (DictConfig); } static void handler( int sig ) { const char *name = NULL; time_t t; name = signal2name (sig); if (_dict_daemon) { daemon_terminate( sig, name ); } else { tim_stop( "dictd" ); switch (sig){ case SIGALRM: if (_dict_markTime > 0){ time(&t); log_info( ":T: %24.24s; %d/%d %sr %su %ss\n", ctime(&t), _dict_forks - _dict_reaps, _dict_forks, dict_format_time( tim_get_real( "dictd" ) ), dict_format_time( tim_get_user( "dictd" ) ), dict_format_time( tim_get_system( "dictd" ) ) ); alarm(_dict_markTime); return; } break; } log_sig_info (sig); } if (!dbg_test(DBG_NOFORK) || sig != SIGALRM) exit(sig+128); } static const char *postprocess_filename (const char *fn, const char *prefix) { char *new_fn; if (!fn) return NULL; if (fn [0] != '/' && fn [0] != '.'){ new_fn = xmalloc (2 + strlen (prefix) + strlen (fn)); strcpy (new_fn, prefix); strcat (new_fn, fn); return new_fn; }else{ return xstrdup (fn); } } const char *postprocess_plugin_filename (const char *fn) { return postprocess_filename (fn, DICT_PLUGIN_PATH); } const char *postprocess_dict_filename (const char *fn) { return postprocess_filename (fn, DICT_DICTIONARY_PATH); } static void postprocess_filenames (dictConfig *dc) { lst_Position p; dictDatabase *db; LST_ITERATE(dc -> dbl, p, db) { db -> dataFilename = postprocess_dict_filename (db -> dataFilename); db -> indexFilename = postprocess_dict_filename (db -> indexFilename); db -> indexsuffixFilename = postprocess_dict_filename (db -> indexsuffixFilename); db -> indexwordFilename = postprocess_dict_filename (db -> indexwordFilename); db -> pluginFilename = postprocess_plugin_filename (db -> pluginFilename); } site_info = postprocess_dict_filename (site_info); } static void handler_sighup (int sig) { log_sig_info (sig); need_reload_config = 1; } static void handler_sigusr1 (int sig) { log_sig_info (sig); need_unload_databases = 1; } static void setsig( int sig, void (*f)(int), int sa_flags ) { struct sigaction sa; sa.sa_handler = f; sigemptyset(&sa.sa_mask); sa.sa_flags = sa_flags; sigaction(sig, &sa, NULL); } struct access_print_struct { FILE *s; int offset; }; static int access_print( const void *datum, void *arg ) { dictAccess *a = (dictAccess *)datum; struct access_print_struct *aps = (struct access_print_struct *)arg; FILE *s = aps->s; int offset = aps->offset; int i; const char *desc; for (i = 0; i < offset; i++) fputc( ' ', s ); switch (a->type) { case DICT_DENY: desc = "deny"; break; case DICT_ALLOW: desc = "allow"; break; case DICT_AUTHONLY: desc = "authonly"; break; case DICT_USER: desc = "user"; break; case DICT_GROUP: desc = "group"; break; /* Not implemented. */ default: desc = "unknown"; break; } fprintf( s, "%s %s\n", desc, a->spec ); return 0; } static void acl_print( FILE *s, lst_List l, int offset) { struct access_print_struct aps; int i; aps.s = s; aps.offset = offset + 3; for (i = 0; i < offset; i++) fputc( ' ', s ); fprintf( s, "access {\n" ); lst_iterate_arg( l, access_print, &aps ); for (i = 0; i < offset; i++) fputc( ' ', s ); fprintf( s, "}\n" ); } static int user_print( const void *key, const void *datum, void *arg ) { const char *username = (const char *)key; const char *secret = (const char *)datum; FILE *s = (FILE *)arg; if (dbg_test(DBG_AUTH)) fprintf( s, "user %s %s\n", username, secret ); else fprintf( s, "user %s *\n", username ); return 0; } static int config_print( const void *datum, void *arg ) { dictDatabase *db = (dictDatabase *)datum; FILE *s = (FILE *)arg; fprintf( s, "database %s {\n", db->databaseName ); if (db->dataFilename) fprintf( s, " data %s\n", db->dataFilename ); if (db->indexFilename) fprintf( s, " index %s\n", db->indexFilename ); if (db->indexsuffixFilename) fprintf( s, " index_suffix %s\n", db->indexsuffixFilename ); if (db->indexwordFilename) fprintf( s, " index_word %s\n", db->indexwordFilename ); if (db->filter) fprintf( s, " filter %s\n", db->filter ); if (db->prefilter) fprintf( s, " prefilter %s\n", db->prefilter ); if (db->postfilter) fprintf( s, " postfilter %s\n", db->postfilter ); if (db->databaseShort) fprintf( s, " name %s\n", db->databaseShort ); if (db->acl) acl_print( s, db->acl, 3 ); fprintf( s, "}\n" ); return 0; } static void dict_config_print( FILE *stream, dictConfig *c ) { FILE *s = stream ? stream : stderr; if (c->acl) acl_print( s, c->acl, 0 ); lst_iterate_arg( c->dbl, config_print, s ); if (c->usl) hsh_iterate_arg( c->usl, user_print, s ); } static const char *get_entry_info( dictDatabase *db, const char *entryName ) { dictWord *dw; lst_List list = lst_create(); char *pt, *buf; if ( 0 >= dict_search ( list, entryName, db, DICT_STRAT_EXACT, 0, NULL, NULL, NULL )) { #ifdef USE_PLUGIN call_dictdb_free (DictConfig->dbl); #endif lst_destroy( list ); return NULL; } dw = lst_nth_get( list, 1 ); assert (dw -> database); buf = pt = dict_data_obtain( dw -> database, dw ); if (!strncmp (pt, "00database", 10) || !strncmp (pt, "00-database", 11)){ while (*pt != '\n') ++pt; ++pt; } while (*pt == ' ' || *pt == '\t') ++pt; pt[ strlen(pt) - 1 ] = '\0'; #ifdef USE_PLUGIN call_dictdb_free (DictConfig->dbl); #endif dict_destroy_list( list ); pt = xstrdup (pt); xfree (buf); return pt; } static dictDatabase *dbname2database (const char *dbname) { dictDatabase *db = NULL; lst_Position db_pos = lst_init_position (DictConfig->dbl); while (db_pos){ db = lst_get_position (db_pos); if (!strcmp (db -> databaseName, dbname)){ return db; } db_pos = lst_next_position (db_pos); } return NULL; } static lst_List string2virtual_db_list (char *s) { int len, i; lst_List virtual_db_list; char *p; dictDatabase *db = NULL; p = s; len = strlen (s); virtual_db_list = lst_create (); for (i = 0; i <= len; ++i){ if (s [i] == ',' || s [i] == '\n' || s [i] == '\0'){ s [i] = '\0'; if (*p){ db = dbname2database (p); if (db){ lst_append (virtual_db_list, db); }else{ log_info( ":E: Unknown database '%s'\n", p ); PRINTF(DBG_INIT, (":E: Unknown database '%s'\n", p)); exit (2); } } p = s + i + 1; } } return virtual_db_list; } static int init_virtual_db_list (const void *datum) { lst_List list; dictDatabase *db = (dictDatabase *)datum; dictWord *dw; char *buf; int ret; if (db -> database_list){ buf = xstrdup (db -> database_list); db -> virtual_db_list = string2virtual_db_list (buf); xfree (buf); }else{ if (!db -> index) return 0; list = lst_create(); ret = dict_search ( list, DICT_FLAG_VIRTUAL, db, DICT_STRAT_EXACT, 0, NULL, NULL, NULL); switch (ret){ case 1: case 2: dw = (dictWord *) lst_pop (list); buf = dict_data_obtain (db, dw); dict_destroy_datum (dw); db -> virtual_db_list = string2virtual_db_list (buf); xfree (buf); break; case 0: break; default: err_fatal ( __FUNCTION__, "index file contains more than one %s entry", DICT_FLAG_VIRTUAL); } dict_destroy_list (list); } return 0; } static int init_mime_db_list (const void *datum) { lst_List list; dictDatabase *db = (dictDatabase *)datum; dictWord *dw; char *buf; int ret; if (!db -> mime_db) return 0; /* MIME */ if (db -> mime_mimeDbname){ db -> mime_mimeDB = dbname2database (db -> mime_mimeDbname); if (!db -> mime_mimeDB){ err_fatal ( __FUNCTION__, "Incorrect database name '%s'\n", db -> mime_mimeDbname); } }else{ err_fatal ( __FUNCTION__, "MIME database '%s' has no mime_dbname keyword\n", db -> databaseName); } /* NO MIME */ if (db -> mime_nomimeDbname){ db -> mime_nomimeDB = dbname2database (db -> mime_nomimeDbname); if (!db -> mime_nomimeDB){ err_fatal ( __FUNCTION__, "Incorrect database name '%s'\n", db -> mime_nomimeDbname); } }else{ err_fatal ( __FUNCTION__, "MIME database '%s' has no nomime_dbname keyword\n", db -> databaseName); } return 0; } static int init_plugin( const void *datum ) { #ifdef USE_PLUGIN dictDatabase *db = (dictDatabase *)datum; dict_plugin_init (db); #endif return 0; } void dict_disable_strat (dictDatabase *db, const char* strategy) { int strat = -1; int array_size = get_max_strategy_num () + 1; assert (db); assert (strategy); if (!db -> strategy_disabled){ db -> strategy_disabled = xmalloc (array_size * sizeof (int)); memset (db -> strategy_disabled, 0, array_size * sizeof (int)); } strat = lookup_strategy_ex (strategy); assert (strat >= 0); db -> strategy_disabled [strat] = 1; } static void init_database_alphabet (dictDatabase *db) { int ret; lst_List l; const dictWord *dw; char *data; if (!db -> normal_db) return; l = lst_create (); ret = dict_search_database_ (l, DICT_FLAG_ALPHABET, db, DICT_STRAT_EXACT); if (ret){ dw = (const dictWord *) lst_top (l); data = dict_data_obtain (db, dw); db -> alphabet = data; data = strchr (db -> alphabet, '\n'); if (data) *data = 0; } dict_destroy_list (l); } static void init_database_default_strategy (dictDatabase *db) { int ret; lst_List l; const dictWord *dw; char *data; int def_strat = -1; char *p; if (!db -> normal_db) return; if (db -> default_strategy > 0){ /* already set by `default_strategy' directive*/ return; } l = lst_create (); ret = dict_search_database_ (l, DICT_FLAG_DEFAULT_STRAT, db, DICT_STRAT_EXACT); if (ret){ dw = (const dictWord *) lst_top (l); data = dict_data_obtain (db, dw); for (p=data; *p && isalpha ((unsigned char) *p); ++p){ } *p = '\0'; def_strat = lookup_strategy (data); if (-1 == def_strat){ PRINTF (DBG_INIT, (":I: `%s' is not supported by dictd\n", data)); }else{ db -> default_strategy = def_strat; } xfree (data); } dict_destroy_list (l); } static int init_database_mime_header (const void *datum) { dictDatabase *db = (dictDatabase *) datum; int ret; lst_List l; const dictWord *dw; char *data; if (!db -> normal_db) return 0; if (db -> mime_header){ /* already set by `mime_header' directive*/ return 0; } l = lst_create (); ret = dict_search_database_ (l, DICT_FLAG_MIME_HEADER, db, DICT_STRAT_EXACT); if (ret){ dw = (const dictWord *) lst_top (l); data = dict_data_obtain (db, dw); db -> mime_header = xstrdup (data); xfree (data); } dict_destroy_list (l); return 0; } static int init_database( const void *datum ) { dictDatabase *db = (dictDatabase *)datum; const char *strat_name = NULL; PRINTF (DBG_INIT, (":I: Initializing '%s'\n", db->databaseName)); if (db->indexFilename){ PRINTF (DBG_INIT, (":I: Opening indices\n")); } db->index = dict_index_open( db->indexFilename, 1, NULL ); if (db->indexFilename){ PRINTF (DBG_INIT, (":I: .index \n")); } if (db->index){ db->index_suffix = dict_index_open( db->indexsuffixFilename, 0, db->index); db->index_word = dict_index_open( db->indexwordFilename, 0, db->index); } if (db->index_suffix){ PRINTF (DBG_INIT, (":I: .indexsuffix \n")); db->index_suffix->flag_8bit = db->index->flag_8bit; db->index_suffix->flag_utf8 = db->index->flag_utf8; db->index_suffix->flag_allchars = db->index->flag_allchars; } if (db->index_word){ PRINTF (DBG_INIT, (":I: .indexword \n")); db->index_word->flag_utf8 = db->index->flag_utf8; db->index_word->flag_8bit = db->index->flag_8bit; db->index_word->flag_allchars = db->index->flag_allchars; } if (db->dataFilename){ PRINTF (DBG_INIT, (":I: Opening data\n")); } db->data = dict_data_open( db->dataFilename, 0 ); init_database_alphabet (db); if (db -> alphabet){ PRINTF (DBG_INIT, (":I: alphabet: %s\n", db -> alphabet)); }else{ PRINTF (DBG_INIT, (":I: alphabet: (NULL)\n")); } if (db -> default_strategy){ strat_name = get_strategy (db -> default_strategy) -> name; PRINTF (DBG_INIT, (":I: default_strategy (from conf file): %s\n", strat_name)); }else{ init_database_default_strategy (db); if (db -> default_strategy){ strat_name = get_strategy (db -> default_strategy) -> name; PRINTF (DBG_INIT, (":I: default_strategy (from db): %s\n", strat_name)); }else{ db -> default_strategy = default_strategy; } } if (db->dataFilename){ PRINTF(DBG_INIT, (":I: '%s' initialized\n", db->databaseName)); } return 0; } static int init_database_short (const void *datum) { char *NL; dictDatabase *db = (dictDatabase *) datum; if (!db->databaseShort){ db->databaseShort = get_entry_info( db, DICT_SHORT_ENTRY_NAME ); }else if (*db->databaseShort == '@'){ db->databaseShort = get_entry_info( db, db->databaseShort + 1 ); }else{ db->databaseShort = xstrdup (db->databaseShort); } if (db->databaseShort){ NL = strchr (db->databaseShort, '\n'); if (NL) *NL = 0; } if (!db->databaseShort) db->databaseShort = xstrdup (db->databaseName); return 0; } static int close_plugin (const void *datum) { #ifdef USE_PLUGIN dictDatabase *db = (dictDatabase *)datum; dict_plugin_destroy (db); #endif return 0; } static int close_database (const void *datum) { dictDatabase *db = (dictDatabase *)datum; dict_index_close (db->index); dict_index_close (db->index_suffix); dict_index_close (db->index_word); dict_data_close (db->data); if (db -> databaseShort) xfree ((void *) db -> databaseShort); if (db -> indexFilename) xfree ((void *) db -> indexFilename); if (db -> dataFilename) xfree ((void *) db -> dataFilename); if (db -> indexwordFilename) xfree ((void *) db -> indexwordFilename); if (db -> indexsuffixFilename) xfree ((void *) db -> indexsuffixFilename); if (db -> pluginFilename) xfree ((void *) db -> pluginFilename); if (db -> strategy_disabled) xfree ((void *) db -> strategy_disabled); if (db -> alphabet) xfree ((void *) db -> alphabet); if (db -> mime_header) xfree ((void *) db -> mime_header); return 0; } static int log_database_info( const void *datum ) { dictDatabase *db = (dictDatabase *)datum; const char *pt; unsigned long headwords = 0; if (db->index){ for (pt = db->index->start; pt < db->index->end; pt++) if (*pt == '\n') ++headwords; db->index->headwords = headwords; log_info( ":I: %-12.12s %12lu %12lu %12lu %12lu\n", db->databaseName, headwords, db->index->size, db->data->size, db->data->length ); } return 0; } static void dict_ltdl_init () { #if USE_PLUGIN && !HAVE_DLFCN_H if (lt_dlinit ()) err_fatal( __FUNCTION__, "Can not initialize 'ltdl' library\n" ); #endif } static void dict_ltdl_close () { #if USE_PLUGIN && !HAVE_DLFCN_H if (lt_dlexit ()) err_fatal( __FUNCTION__, "Can not deinitialize 'ltdl' library\n" ); #endif } /* Makes dictionary_exit db invisible if it is the last visible one */ static void make_dictexit_invisible (dictConfig *c) { lst_Position p; dictDatabase *db; dictDatabase *db_exit = NULL; LST_ITERATE(c -> dbl, p, db) { if (!db -> invisible){ if (db_exit) db_exit -> invisible = 0; db_exit = NULL; } if (db -> exit_db){ db_exit = db; db_exit -> invisible = 1; } } } static void dict_init_databases( dictConfig *c ) { make_dictexit_invisible (c); lst_iterate( c->dbl, init_database ); lst_iterate( c->dbl, init_plugin ); lst_iterate( c->dbl, init_virtual_db_list ); lst_iterate( c->dbl, init_mime_db_list ); lst_iterate( c->dbl, init_database_short ); lst_iterate( c->dbl, init_database_mime_header); lst_iterate( c->dbl, log_database_info ); } static void dict_close_databases (dictConfig *c) { dictDatabase *db; dictAccess *acl; if (!c) return; if (c -> dbl){ while (lst_length (c -> dbl) > 0){ db = (dictDatabase *) lst_pop (c -> dbl); if (db -> virtual_db_list) lst_destroy (db -> virtual_db_list); close_plugin (db); close_database (db); xfree (db); } lst_destroy (c -> dbl); } if (c -> acl){ while (lst_length (c -> acl) > 0){ acl = (dictAccess *) lst_pop (c->acl); xfree (acl); } lst_destroy (c -> acl); } if (site_info) xfree ((void *) site_info); xfree (c); } static const char *id_string (void) { static char buffer [BUFFERSIZE]; snprintf( buffer, BUFFERSIZE, "%s", DICT_VERSION ); return buffer; } const char *dict_get_banner( int shortFlag ) { static char *shortBuffer = NULL; static char *longBuffer = NULL; struct utsname uts; if (shortFlag && shortBuffer) return shortBuffer; if (!shortFlag && longBuffer) return longBuffer; uname( &uts ); shortBuffer = xmalloc(256); snprintf( shortBuffer, 256, "%s %s", err_program_name(), id_string () ); longBuffer = xmalloc(256); snprintf( longBuffer, 256, "%s %s/rf on %s %s", err_program_name(), id_string (), uts.sysname, uts.release ); if (shortFlag) return shortBuffer; return longBuffer; } static void banner( void ) { printf( "%s\n", dict_get_banner(0) ); printf( "Copyright 1997-2002 Rickard E. Faith (faith@dict.org)\n" ); printf( "Copyright 2002-2007 Aleksey Cheusov (vle@gmx.net)\n" ); printf( "\n" ); } static void license( void ) { static const char *license_msg[] = { "This program is free software; you can redistribute it and/or modify it", "under the terms of the GNU General Public License as published by the", "Free Software Foundation; either version 1, or (at your option) any", "later version.", "", "This program is distributed in the hope that it will be useful, but", "WITHOUT ANY WARRANTY; without even the implied warranty of", "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", "General Public License for more details.", "", "You should have received a copy of the GNU General Public License along", "with this program; if not, write to the Free Software Foundation, Inc.,", "675 Mass Ave, Cambridge, MA 02139, USA.", 0 }; const char **p = license_msg; banner(); while (*p) printf( " %s\n", *p++ ); } static void help( void ) { static const char *help_msg[] = { "Usage: dictd [options]", "Start the dictd daemon", "", "-h --help give this help", " --license display software license", "-v --verbose verbose mode", "-V --version display version number", "-p --port port number", " --delay client timeout in seconds", " --depth TCP/IP queue depth", " --limit maximum simultaneous children", "-c --config configuration file", "-l --log