Index: conf/dynamicmaps.cf =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ conf/dynamicmaps.cf 2007-08-06 00:02:19.119436818 +0200 @@ -0,0 +1,7 @@ +# Postfix dynamic maps configuration file. +# +# The first match found is the one that is used. +# Wildcards are not supported. +# +#type location of .so file open function (mkmap func) +#==== ================================ ============= ============ Index: conf/postfix-files =================================================================== --- conf/postfix-files.orig 2007-03-25 22:08:21.000000000 +0200 +++ conf/postfix-files 2007-08-06 00:02:19.135437796 +0200 @@ -63,6 +63,11 @@ $queue_directory/saved:d:$mail_owner:-:7 $queue_directory/trace:d:$mail_owner:-:700:ucr $daemon_directory/anvil:f:root:-:755 $daemon_directory/bounce:f:root:-:755 +$daemon_directory/dict_ldap.so:f:root:-:755 +$daemon_directory/dict_pcre.so:f:root:-:755 +$daemon_directory/dict_tcp.so:f:root:-:755 +$daemon_directory/dict_mysql.so:f:root:-:755 +$daemon_directory/dict_pgsql.so:f:root:-:755 $daemon_directory/cleanup:f:root:-:755 $daemon_directory/discard:f:root:-:755 $daemon_directory/error:f:root:-:755 @@ -85,6 +90,11 @@ $daemon_directory/tlsmgr:f:root:-:755 $daemon_directory/trivial-rewrite:f:root:-:755 $daemon_directory/verify:f:root:-:755 $daemon_directory/virtual:f:root:-:755 +/usr/lib/libpostfix-dns.so.1:f:root:-:755 +/usr/lib/libpostfix-global.so.1:f:root:-:755 +/usr/lib/libpostfix-tls.so.1:f:root:-:755 +/usr/lib/libpostfix-master.so.1:f:root:-:755 +/usr/lib/libpostfix-util.so.1:f:root:-:755 $daemon_directory/nqmgr:h:$daemon_directory/qmgr $daemon_directory/lmtp:h:$daemon_directory/smtp $command_directory/postalias:f:root:-:755 @@ -107,6 +117,7 @@ $config_directory/access:f:root:-:644:p $config_directory/aliases:f:root:-:644:p $config_directory/bounce.cf.default:f:root:-:644 $config_directory/canonical:f:root:-:644:p +$config_directory/dynamicmaps.cf:f:root:-:644:p $config_directory/cidr_table:f:root:-:644:o $config_directory/generic:f:root:-:644:p $config_directory/generics:f:root:-:644:o Index: src/dns/Makefile.in =================================================================== --- src/dns/Makefile.in.orig 2007-03-17 18:51:37.000000000 +0100 +++ src/dns/Makefile.in 2007-08-06 00:02:19.251444890 +0200 @@ -14,7 +14,7 @@ LIBS = ../../lib/libutil.a LIB_DIR = ../../lib INC_DIR = ../../include -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c all: $(LIB) @@ -31,12 +31,10 @@ tests: test dns_rr_to_pa_test dns_rr_to_ root_tests: $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-dns.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) update: $(LIB_DIR)/$(LIB) $(HDRS) -for i in $(HDRS); \ Index: src/global/Makefile.in =================================================================== --- src/global/Makefile.in.orig 2007-03-17 18:53:42.000000000 +0100 +++ src/global/Makefile.in 2007-08-06 00:02:19.351451006 +0200 @@ -32,7 +32,7 @@ OBJS = abounce.o anvil_clnt.o been_here. canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ defer.o deliver_completed.o deliver_flock.o deliver_pass.o \ - deliver_request.o dict_ldap.o dict_mysql.o dict_pgsql.o \ + deliver_request.o \ dict_proxy.o domain_list.o dot_lockfile.o dot_lockfile_as.o \ dsb_scan.o dsn.o dsn_buf.o dsn_mask.o dsn_print.o dsn_util.o \ ehlo_mask.o ext_prop.o file_id.o flush_clnt.o header_opts.o \ @@ -97,10 +97,13 @@ LIBS = ../../lib/libutil.a LIB_DIR = ../../lib INC_DIR = ../../include MAKES = +LDAPSO = dict_ldap.so +MYSQLSO = dict_mysql.so +PGSQLSO = dict_pgsql.so -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c -all: $(LIB) +all: $(LIB) $(LDAPSO) $(MYSQLSO) $(PGSQLSO) $(OBJS): ../../conf/makedefs.out @@ -110,14 +113,30 @@ Makefile: Makefile.in test: $(TESTPROG) $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-global.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) + +$(LDAPSO): dict_ldap.o + gcc -shared -Wl,-soname,dict_ldap.so -o $@ $? -lldap -llber -L../../lib -lutil -L. -lglobal + +$(MYSQLSO): dict_mysql.o + gcc -shared -Wl,-soname,dict_mysql.so -o $@ $? -L/usr/lib/mysql -lmysqlclient -L. -lutil -lglobal + +$(PGSQLSO): dict_pgsql.o + gcc -shared -Wl,-soname,dict_pgsql.so -o $@ $? -lpq -L. -lutil -lglobal $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) -update: $(LIB_DIR)/$(LIB) $(HDRS) +$(LIB_DIR)/$(LDAPSO): $(LDAPSO) + cp $(LDAPSO) $(LIB_DIR) + +$(LIB_DIR)/$(MYSQLSO): $(MYSQLSO) + cp $(MYSQLSO) $(LIB_DIR) + +$(LIB_DIR)/$(PGSQLSO): $(PGSQLSO) + cp $(PGSQLSO) $(LIB_DIR) + +update: $(LIB_DIR)/$(LIB) $(LIB_DIR)/${LDAPSO} $(LIB_DIR)/${MYSQLSO} $(LIB_DIR)/${PGSQLSO} $(HDRS) -for i in $(HDRS); \ do \ cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ @@ -425,7 +444,7 @@ lint: lint $(DEFS) $(SRCS) $(LINTFIX) clean: - rm -f *.o $(LIB) *core $(TESTPROG) junk + rm -f *.o $(LIB) $(LDAPSO) $(MYSQLSO) $(PGSQLSO) *core $(TESTPROG) junk rm -rf printfck tidy: clean Index: src/global/mail_conf.c =================================================================== --- src/global/mail_conf.c.orig 2004-04-10 16:52:51.000000000 +0200 +++ src/global/mail_conf.c 2007-08-06 00:02:19.451457122 +0200 @@ -175,6 +175,13 @@ void mail_conf_suck(void) path = concatenate(var_config_dir, "/", "main.cf", (char *) 0); dict_load_file(CONFIG_DICT, path); myfree(path); + +#ifndef NO_DYNAMIC_MAPS + path = concatenate(var_config_dir, "/", "dynamicmaps.cf", (char *) 0); + dict_open_dlinfo(path); + myfree(path); +#endif + } /* mail_conf_eval - expand macros in string */ Index: src/global/mail_dict.c =================================================================== --- src/global/mail_dict.c.orig 2004-01-04 19:53:27.000000000 +0100 +++ src/global/mail_dict.c 2007-08-06 00:02:19.499460057 +0200 @@ -45,6 +45,7 @@ typedef struct { static DICT_OPEN_INFO dict_open_info[] = { DICT_TYPE_PROXY, dict_proxy_open, +#ifndef MAX_DYNAMIC_MAPS #ifdef HAS_LDAP DICT_TYPE_LDAP, dict_ldap_open, #endif @@ -54,6 +55,7 @@ static DICT_OPEN_INFO dict_open_info[] = #ifdef HAS_PGSQL DICT_TYPE_PGSQL, dict_pgsql_open, #endif +#endif /* MAX_DYNAMIC_MAPS */ 0, }; Index: src/global/mail_params.c =================================================================== --- src/global/mail_params.c.orig 2007-07-10 19:27:12.000000000 +0200 +++ src/global/mail_params.c 2007-08-06 00:02:19.515461036 +0200 @@ -77,6 +77,7 @@ /* char *var_export_environ; /* char *var_debug_peer_list; /* int var_debug_peer_level; +/* int var_command_maxtime; /* int var_in_flow_delay; /* int var_fault_inj_code; /* char *var_bounce_service; @@ -248,6 +249,7 @@ char *var_import_environ; char *var_export_environ; char *var_debug_peer_list; int var_debug_peer_level; +int var_command_maxtime; int var_fault_inj_code; char *var_bounce_service; char *var_cleanup_service; @@ -259,6 +261,7 @@ char *var_showq_service; char *var_error_service; char *var_flush_service; char *var_verify_service; +char *var_scache_service; char *var_trace_service; int var_db_create_buf; int var_db_read_buf; Index: src/global/mkmap_open.c =================================================================== --- src/global/mkmap_open.c.orig 2004-12-15 01:24:22.000000000 +0100 +++ src/global/mkmap_open.c 2007-08-06 00:02:19.547462993 +0200 @@ -78,7 +78,7 @@ * types that exist as files. Network-based maps are not of interest. */ typedef struct { - char *type; + const char *type; MKMAP *(*before_open) (const char *); } MKMAP_OPEN_INFO; @@ -152,7 +152,16 @@ MKMAP *mkmap_open(const char *type, con */ for (mp = mkmap_types; /* void */ ; mp++) { if (mp->type == 0) +#ifndef NO_DYNAMIC_MAPS + { + static MKMAP_OPEN_INFO oi; + oi.before_open=(MKMAP*(*)(const char*))dict_mkmap_func(type); + oi.type=type; + mp=&oi; + } +#else msg_fatal("unsupported map type: %s", type); +#endif if (strcmp(type, mp->type) == 0) break; } Index: src/master/Makefile.in =================================================================== --- src/master/Makefile.in.orig 2007-03-17 18:51:39.000000000 +0100 +++ src/master/Makefile.in 2007-08-06 00:02:19.563463971 +0200 @@ -20,7 +20,7 @@ LIB_DIR = ../../lib INC_DIR = ../../include BIN_DIR = ../../libexec -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) `for i in $(LIB_OBJ); do [ $$i = $@ ] && echo -fPIC; done` $(CFLAGS) -c $*.c all: $(PROG) $(LIB) @@ -39,12 +39,10 @@ tests: root_tests: $(LIB): $(LIB_OBJ) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-master.so.1 -o $(LIB) $(LIB_OBJ) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR)/$(LIB) - $(RANLIB) $(LIB_DIR)/$(LIB) $(BIN_DIR)/$(PROG): $(PROG) cp $(PROG) $(BIN_DIR) Index: src/postconf/postconf.c =================================================================== --- src/postconf/postconf.c.orig 2007-04-26 21:34:57.000000000 +0200 +++ src/postconf/postconf.c 2007-08-06 00:02:19.687471554 +0200 @@ -893,6 +893,16 @@ static void show_maps(void) { ARGV *maps_argv; int i; +#ifndef NO_DYNAMIC_MAPS + char *path; + char *config_dir; + + var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ? + config_dir : DEF_CONFIG_DIR); /* XXX */ + path = concatenate(var_config_dir, "/", "dynamicmaps.cf", (char *) 0); + dict_open_dlinfo(path); + myfree(path); +#endif maps_argv = dict_mapnames(); for (i = 0; i < maps_argv->argc; i++) Index: src/postmap/postmap.c =================================================================== --- src/postmap/postmap.c.orig 2007-03-27 02:09:16.000000000 +0200 +++ src/postmap/postmap.c 2007-08-06 00:02:19.707472778 +0200 @@ -5,7 +5,7 @@ /* Postfix lookup table management /* SYNOPSIS /* .fi -/* \fBpostmap\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR] +/* \fBpostmap\fR [\fB-Nfinoprsuvw\fR] [\fB-c \fIconfig_dir\fR] /* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR] /* [\fIfile_type\fR:]\fIfile_name\fR ... /* DESCRIPTION @@ -115,6 +115,8 @@ /* as the original input order. /* This feature is available in Postfix version 2.2 and later, /* and is not available for all database types. +/* .IP \fB-u\fR +/* Upgrade the database to the current version. /* .IP \fB-v\fR /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR /* options make the software increasingly verbose. @@ -538,6 +540,18 @@ static void postmap_seq(const char *map_ dict_close(dict); } +/* postmap_upgrade - upgrade a map */ + +static int postmap_upgrade(const char *map_type, const char *map_name) +{ + DICT *dict; + + dict = dict_open3(map_type, map_name, O_RDWR, + DICT_FLAG_LOCK|DICT_FLAG_UPGRADE); + dict_close(dict); + return (dict != 0); +} + /* usage - explain */ static NORETURN usage(char *myname) @@ -558,6 +572,7 @@ int main(int argc, char **argv) int postmap_flags = POSTMAP_FLAG_AS_OWNER | POSTMAP_FLAG_SAVE_PERM; int open_flags = O_RDWR | O_CREAT | O_TRUNC; int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX; + int upgrade = 0; char *query = 0; char *delkey = 0; int sequence = 0; @@ -602,7 +617,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsvw")) > 0) { + while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsuvw")) > 0) { switch (ch) { default: usage(argv[0]); @@ -616,8 +631,8 @@ int main(int argc, char **argv) msg_fatal("out of memory"); break; case 'd': - if (sequence || query || delkey) - msg_fatal("specify only one of -s -q or -d"); + if (sequence || query || delkey || upgrade) + msg_fatal("specify only one of -s -q -u or -d"); delkey = optarg; break; case 'f': @@ -637,8 +652,8 @@ int main(int argc, char **argv) postmap_flags &= ~POSTMAP_FLAG_SAVE_PERM; break; case 'q': - if (sequence || query || delkey) - msg_fatal("specify only one of -s -q or -d"); + if (sequence || query || delkey || upgrade) + msg_fatal("specify only one of -s -q -u or -d"); query = optarg; break; case 'r': @@ -646,10 +661,15 @@ int main(int argc, char **argv) dict_flags |= DICT_FLAG_DUP_REPLACE; break; case 's': - if (query || delkey) - msg_fatal("specify only one of -s or -q or -d"); + if (query || delkey || upgrade) + msg_fatal("specify only one of -s or -q -u or -d"); sequence = 1; break; + case 'u': + if (sequence || query || delkey || upgrade) + msg_fatal("specify only one of -s -q -u or -d"); + upgrade=1; + break; case 'v': msg_verbose++; break; @@ -717,6 +737,21 @@ int main(int argc, char **argv) exit(0); } exit(1); + } else if (upgrade) { /* Upgrade the map(s) */ + int success = 1; + if (optind + 1 > argc) + usage(argv[0]); + while (optind < argc) { + if ((path_name = split_at(argv[optind], ':')) != 0) { + success &= postmap_upgrade(argv[optind], path_name); + } else { + success &= postmap_upgrade(var_db_type, path_name); + } + if (!success) + exit(1); + optind++; + } + exit(0); } else { /* create/update map(s) */ if (optind + 1 > argc) usage(argv[0]); Index: src/tls/Makefile.in =================================================================== --- src/tls/Makefile.in.orig 2007-03-17 18:51:38.000000000 +0100 +++ src/tls/Makefile.in 2007-08-06 00:02:19.731474245 +0200 @@ -22,7 +22,7 @@ LIB_DIR = ../../lib INC_DIR = ../../include MAKES = -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c all: $(LIB) @@ -38,12 +38,10 @@ tests: root_tests: $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-tls.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) update: $(LIB_DIR)/$(LIB) $(HDRS) -for i in $(HDRS); \ Index: src/util/Makefile.in =================================================================== --- src/util/Makefile.in.orig 2007-07-29 18:02:35.000000000 +0200 +++ src/util/Makefile.in 2007-08-06 00:04:47.140488589 +0200 @@ -30,21 +30,21 @@ SRCS = alldig.c allprint.c argv.c argv_s username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \ vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \ write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \ - allascii.c load_file.c killme_after.c vstream_tweak.c + allascii.c load_file.c killme_after.c vstream_tweak.c load_lib.c sdbm.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ chroot_uid.o cidr_match.o clean_env.o close_on_exec.o concatenate.o \ ctable.o dict.o dict_alloc.o dict_cdb.o dict_cidr.o dict_db.o \ dict_dbm.o dict_debug.o dict_env.o dict_ht.o dict_ni.o dict_nis.o \ - dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o dict_sdbm.o \ - dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o dummy_read.o \ + dict_nisplus.o dict_open.o dict_regexp.o dict_sdbm.o \ + dict_static.o dict_unix.o dir_forest.o doze.o dummy_read.o \ dummy_write.o duplex_pipe.o environ.o events.o exec_command.o \ fifo_listen.o fifo_trigger.o file_limit.o find_inet.o fsspace.o \ fullname.o get_domainname.o get_hostname.o hex_code.o hex_quote.o \ host_port.o htable.o inet_addr_host.o inet_addr_list.o \ inet_addr_local.o inet_connect.o inet_listen.o inet_proto.o \ - inet_trigger.o line_wrap.o lowercase.o lstat_as.o mac_expand.o \ + inet_trigger.o line_wrap.o lowercase.o lstat_as.o mac_expand.o load_lib.o sdbm.o \ mac_parse.o make_dirs.o mask_addr.o match_list.o match_ops.o msg.o \ msg_output.o msg_syslog.o msg_vstream.o mvect.o myaddrinfo.o myflock.o \ mymalloc.o myrand.o mystrtok.o name_code.o name_mask.o netstring.o \ @@ -76,7 +76,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_cl msg_output.h msg_syslog.h msg_vstream.h mvect.h myaddrinfo.h myflock.h \ mymalloc.h myrand.h name_code.h name_mask.h netstring.h nvtable.h \ open_as.h open_lock.h percentm.h posix_signals.h readlline.h ring.h \ - safe.h safe_open.h sane_accept.h sane_connect.h sane_fsops.h \ + safe.h safe_open.h sane_accept.h sane_connect.h sane_fsops.h sdbm.h load_lib.h \ sane_socketpair.h sane_time.h scan_dir.h set_eugid.h set_ugid.h \ sigdelay.h sock_addr.h spawn_command.h split_at.h stat_as.h \ stringops.h sys_defs.h timed_connect.h timed_wait.h trigger.h \ @@ -88,6 +88,8 @@ DEFS = -I. -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) FILES = Makefile $(SRCS) $(HDRS) INCL = +PCRESO = dict_pcre.so +TCPSO = dict_tcp.so LIB = libutil.a TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ fifo_rdonly_bug fifo_rdwr_bug fifo_trigger fsspace fullname \ @@ -102,10 +104,11 @@ TESTPROG= dict_open dup2_pass_on_exec ev LIB_DIR = ../../lib INC_DIR = ../../include +LIBS = $(LIB_DIR)/$(LIB) $(LIB_DIR)/$(PCRESO) $(LIB_DIR)/$(TCPSO) -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c -all: $(LIB) +all: $(LIB) $(PCRESO) $(TCPSO) $(OBJS): ../../conf/makedefs.out @@ -114,15 +117,25 @@ Makefile: Makefile.in test: $(TESTPROG) +$(PCRESO): dict_pcre.o + gcc -shared -Wl,-soname,dict_pcre.so -o $@ $? -lpcre -L. -lutil + +$(TCPSO): dict_tcp.o + gcc -shared -Wl,-soname,dict_tcp.so -o $@ $? -L. -lutil + $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-util.so.1 -o $(LIB) $(OBJS) -ldl $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) -update: $(LIB_DIR)/$(LIB) $(HDRS) +$(LIB_DIR)/$(PCRESO): $(PCRESO) + cp $(PCRESO) $(LIB_DIR) + +$(LIB_DIR)/$(TCPSO): $(TCPSO) + cp $(TCPSO) $(LIB_DIR) + +update: $(LIBS) $(HDRS) -for i in $(HDRS); \ do \ cmp -s $$i $(INC_DIR)/$$i 2>/dev/null || cp $$i $(INC_DIR); \ @@ -144,7 +157,8 @@ lint: lint $(SRCS) clean: - rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp + rm -f *.o $(LIB) $(PCRESO) $(TCPSO) *core $(TESTPROG) \ + junk $(MAKES) *.tmp rm -rf printfck tidy: clean Index: src/util/dict.h =================================================================== --- src/util/dict.h.orig 2007-01-05 01:48:48.000000000 +0100 +++ src/util/dict.h 2007-08-06 00:02:19.799478404 +0200 @@ -66,6 +66,7 @@ extern DICT *dict_debug(DICT *); #define DICT_FLAG_NO_UNAUTH (1<<13) /* disallow unauthenticated data */ #define DICT_FLAG_FOLD_FIX (1<<14) /* case-fold key with fixed-case map */ #define DICT_FLAG_FOLD_MUL (1<<15) /* case-fold key with multi-case map */ +#define DICT_FLAG_UPGRADE (1<<30) /* Upgrade the db */ #define DICT_FLAG_FOLD_ANY (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL) /* IMPORTANT: Update the dict_mask[] table when the above changes */ @@ -136,6 +137,11 @@ extern const char *dict_eval(const char extern DICT *dict_open(const char *, int, int); extern DICT *dict_open3(const char *, const char *, int, int); extern void dict_open_register(const char *, DICT *(*) (const char *, int, int)); +#ifndef NO_DYNAMIC_MAPS +extern void dict_open_dlinfo(const char *path); +typedef void* (*dict_mkmap_func_t)(const char *); +dict_mkmap_func_t dict_mkmap_func(const char *dict_type); +#endif #define dict_get(dp, key) (dp)->lookup((dp), (key)) #define dict_put(dp, key, val) (dp)->update((dp), (key), (val)) Index: src/util/dict_db.c =================================================================== --- src/util/dict_db.c.orig 2007-01-04 15:06:07.000000000 +0100 +++ src/util/dict_db.c 2007-08-06 00:02:19.843481095 +0200 @@ -664,6 +664,12 @@ static DICT *dict_db_open(const char *cl msg_fatal("set DB cache size %d: %m", dict_db_cache_size); if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0) msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM); + if (dict_flags & DICT_FLAG_UPGRADE) { + if (msg_verbose) + msg_info("upgrading database %s",db_path); + if ((errno = db->upgrade(db,db_path,0)) != 0) + msg_fatal("upgrade of database %s: %m",db_path); + } #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0) if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0) msg_fatal("open database %s: %m", db_path); Index: src/util/dict_dbm.c =================================================================== --- src/util/dict_dbm.c.orig 2007-01-04 15:07:36.000000000 +0100 +++ src/util/dict_dbm.c 2007-08-06 00:02:19.867482562 +0200 @@ -407,6 +407,10 @@ DICT *dict_dbm_open(const char *path, char *dbm_path; int lock_fd; +#ifdef HAVE_GDBM + msg_fatal("%s: gdbm maps use locking that is incompatible with postfix. Use a hash map instead.", + path); +#endif /* * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in * the time domain) locking while accessing individual database records. Index: src/util/dict_open.c =================================================================== --- src/util/dict_open.c.orig 2006-06-26 14:59:19.000000000 +0200 +++ src/util/dict_open.c 2007-08-06 00:02:19.907485009 +0200 @@ -44,6 +44,8 @@ /* DICT *(*open) (const char *, int, int); /* /* ARGV *dict_mapnames() +/* +/* void (*)() dict_mkmap_func(const char *dict_type) /* DESCRIPTION /* This module implements a low-level interface to multiple /* physical dictionary types. @@ -156,6 +158,9 @@ /* /* dict_mapnames() returns a sorted list with the names of all available /* dictionary types. +/* +/* dict_mkmap_func() returns a pointer to the mkmap setup function +/* for the given map type, as given in /etc/dynamicmaps.cf /* DIAGNOSTICS /* Fatal error: open error, unsupported dictionary type, attempt to /* update non-writable dictionary. @@ -180,6 +185,9 @@ #include #endif +#include +#include + /* Utility library. */ #include @@ -204,6 +212,27 @@ #include #include +#ifndef NO_DYNAMIC_MAPS +#include +#include +#include +#include +#include + + /* + * Interface for dynamic map loading. + */ +typedef struct { + const char *pattern; + const char *soname; + const char *openfunc; + const char *mkmapfunc; +} DLINFO; + +static DLINFO *dict_dlinfo; +static DLINFO *dict_open_dlfind(const char *type); +#endif + /* * lookup table for available map types. */ @@ -218,9 +247,11 @@ static DICT_OPEN_INFO dict_open_info[] = #endif DICT_TYPE_ENVIRON, dict_env_open, DICT_TYPE_UNIX, dict_unix_open, +#ifndef MAX_DYNAMIC_MAPS #ifdef SNAPSHOT DICT_TYPE_TCP, dict_tcp_open, #endif +#endif #ifdef HAS_SDBM DICT_TYPE_SDBM, dict_sdbm_open, #endif @@ -240,9 +271,11 @@ static DICT_OPEN_INFO dict_open_info[] = #ifdef HAS_NETINFO DICT_TYPE_NETINFO, dict_ni_open, #endif +#ifndef MAX_DYNAMIC_MAPS #ifdef HAS_PCRE DICT_TYPE_PCRE, dict_pcre_open, #endif +#endif /* MAX_DYNAMIC_MAPS */ #ifdef HAS_POSIX_REGEXP DICT_TYPE_REGEXP, dict_regexp_open, #endif @@ -300,8 +333,31 @@ DICT *dict_open3(const char *dict_type dict_type, dict_name); if (dict_open_hash == 0) dict_open_init(); - if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) - msg_fatal("unsupported dictionary type: %s", dict_type); + if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) { +#ifdef NO_DYNAMIC_MAPS + msg_fatal("%s: unsupported dictionary type: %s", myname, dict_type); +#else + struct stat st; + LIB_FN fn[2]; + DICT *(*open) (const char *, int, int); + DLINFO *dl=dict_open_dlfind(dict_type); + if (!dl) + msg_fatal("%s: unsupported dictionary type: %s: Is the postfix-%s package installed?", myname, dict_type, dict_type); + if (stat(dl->soname,&st) < 0) { + msg_fatal("%s: unsupported dictionary type: %s (%s not found. Is the postfix-%s package installed?)", + myname, dict_type, dl->soname, dict_type); + } + fn[0].name = dl->openfunc; + fn[0].ptr = (void**)&open; + fn[1].name = NULL; + load_library_symbols(dl->soname, fn, NULL); + dict_open_register(dict_type, open); + dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type); +#endif + } + if (msg_verbose>1) { + msg_info("%s: calling %s open routine",myname,dict_type); + } if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0) msg_fatal("opening %s:%s %m", dict_type, dict_name); if (msg_verbose) @@ -309,6 +365,36 @@ DICT *dict_open3(const char *dict_type return (dict); } +dict_mkmap_func_t dict_mkmap_func(const char *dict_type) +{ + char *myname="dict_mkmap_func"; + struct stat st; + LIB_FN fn[2]; + dict_mkmap_func_t mkmap; + DLINFO *dl; +#ifndef NO_DYNAMIC_MAPS + if (!dict_dlinfo) + msg_fatal("dlinfo==NULL"); + dl=dict_open_dlfind(dict_type); + if (!dl) + msg_fatal("%s: unsupported dictionary type: %s: Is the postfix-%s package installed?", myname, dict_type, dict_type); + if (stat(dl->soname,&st) < 0) { + msg_fatal("%s: unsupported dictionary type: %s (%s not found. Is the postfix-%s package installed?)", + myname, dict_type, dl->soname, dict_type); + } + if (!dl->mkmapfunc) + msg_fatal("%s: unsupported dictionary type: %s does not allow map creation.", myname, dict_type); + + fn[0].name = dl->mkmapfunc; + fn[0].ptr = (void**)&mkmap; + fn[1].name = NULL; + load_library_symbols(dl->soname, fn, NULL); + return mkmap; +#else + return (void(*)())NULL; +#endif +} + /* dict_open_register - register dictionary type */ void dict_open_register(const char *type, @@ -342,6 +428,9 @@ ARGV *dict_mapnames() HTABLE_INFO **ht; DICT_OPEN_INFO *dp; ARGV *mapnames; +#ifndef NO_DYNAMIC_MAPS + DLINFO *dlp; +#endif if (dict_open_hash == 0) dict_open_init(); @@ -350,6 +439,13 @@ ARGV *dict_mapnames() dp = (DICT_OPEN_INFO *) ht[0]->value; argv_add(mapnames, dp->type, ARGV_END); } +#ifndef NO_DYNAMIC_MAPS + if (!dict_dlinfo) + msg_fatal("dlinfo==NULL"); + for (dlp=dict_dlinfo; dlp->pattern; dlp++) { + argv_add(mapnames, dlp->pattern, ARGV_END); + } +#endif qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]), dict_sort_alpha_cpp); myfree((char *) ht_info); @@ -357,6 +453,87 @@ ARGV *dict_mapnames() return mapnames; } +#ifndef NO_DYNAMIC_MAPS +#define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0)) + +void dict_open_dlinfo(const char *path) +{ + char *myname="dict_open_dlinfo"; + VSTREAM *conf_fp=vstream_fopen(path,O_RDONLY,0); + VSTRING *buf = vstring_alloc(100); + char *cp; + ARGV *argv; + MVECT vector; + int nelm=0; + int linenum=0; + + dict_dlinfo=(DLINFO*)mvect_alloc(&vector,sizeof(DLINFO),3,NULL,NULL); + + if (!conf_fp) { + msg_warn("%s: cannot open %s. No dynamic maps will be allowed.", + myname, path); + } else { + while (vstring_get_nonl(buf,conf_fp) != VSTREAM_EOF) { + cp = vstring_str(buf); + linenum++; + if (*cp == '#' || *cp == '\0') + continue; + argv = argv_split(cp, " \t"); + if (argv->argc != 3 && argv->argc != 4) { + msg_fatal("%s: Expected \"pattern .so-name open-function [mkmap-function]\" at line %d", + myname, linenum); + } + if (STREQ(argv->argv[0],"*")) { + msg_warn("%s: wildcard dynamic map entry no longer supported.", + myname); + continue; + } + if (argv->argv[1][0] != '/') { + msg_fatal("%s: .so name must begin with a \"/\" at line %d", + myname, linenum); + } + if (nelm >= vector.nelm) { + dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+3); + } + dict_dlinfo[nelm].pattern = mystrdup(argv->argv[0]); + dict_dlinfo[nelm].soname = mystrdup(argv->argv[1]); + dict_dlinfo[nelm].openfunc = mystrdup(argv->argv[2]); + if (argv->argc==4) + dict_dlinfo[nelm].mkmapfunc = mystrdup(argv->argv[3]); + else + dict_dlinfo[nelm].mkmapfunc = NULL; + nelm++; + argv_free(argv); + } + } + if (nelm >= vector.nelm) { + dict_dlinfo=(DLINFO*)mvect_realloc(&vector,vector.nelm+1); + } + dict_dlinfo[nelm].pattern = NULL; + dict_dlinfo[nelm].soname = NULL; + dict_dlinfo[nelm].openfunc = NULL; + dict_dlinfo[nelm].mkmapfunc = NULL; + if (conf_fp) + vstream_fclose(conf_fp); + vstring_free(buf); +} + +static DLINFO *dict_open_dlfind(const char *type) +{ + DLINFO *dp; + + if (!dict_dlinfo) + return NULL; + + for (dp=dict_dlinfo; dp->pattern; dp++) { + if (STREQ(dp->pattern,type)) + return dp; + } + return NULL; +} + +#endif /* !NO_DYNAMIC_MAPS */ + #ifdef TEST /* Index: src/util/load_lib.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/util/load_lib.c 2007-08-06 00:02:19.939486966 +0200 @@ -0,0 +1,135 @@ +/*++ +/* NAME +/* load_lib 3 +/* SUMMARY +/* library loading wrappers +/* SYNOPSIS +/* #include +/* +/* extern int load_library_symbols(const char *, LIB_FN *, LIB_FN *); +/* const char *libname; +/* LIB_FN *libfuncs; +/* LIB_FN *libdata; +/* +/* DESCRIPTION +/* This module loads functions from libraries, returnine pointers +/* to the named functions. +/* +/* load_library_symbols() loads all of the desired functions, and +/* returns zero for success, or exits via msg_fatal(). +/* +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Problems are reported via the msg(3) diagnostics routines: +/* library not found, symbols not found, other fatal errors. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* LaMont Jones +/* Hewlett-Packard Company +/* 3404 Harmony Road +/* Fort Collins, CO 80528, USA +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System libraries. */ + +#include "sys_defs.h" +#include +#include +#include +#if defined(HAS_DLOPEN) +#include +#elif defined(HAS_SHL_LOAD) +#include +#endif + +/* Application-specific. */ + +#include "msg.h" +#include "load_lib.h" + +extern int load_library_symbols(const char * libname, LIB_FN * libfuncs, LIB_FN * libdata) +{ + char *myname = "load_library_symbols"; + LIB_FN *fn; + +#if defined(HAS_DLOPEN) + void *handle; + char *emsg; + + handle=dlopen(libname,RTLD_NOW); + emsg=dlerror(); + if (emsg) { + msg_fatal("%s: dlopen failure loading %s: %s", myname, libname, emsg); + } + + if (libfuncs) { + for (fn=libfuncs; fn->name; fn++) { + *(fn->ptr) = dlsym(handle,fn->name); + emsg=dlerror(); + if (emsg) { + msg_fatal("%s: dlsym failure looking up %s in %s: %s", myname, + fn->name, libname, emsg); + } + if (msg_verbose>1) { + msg_info("loaded %s = %lx",fn->name, *((long*)(fn->ptr))); + } + } + } + + if (libdata) { + for (fn=libdata; fn->name; fn++) { + *(fn->ptr) = dlsym(handle,fn->name); + emsg=dlerror(); + if (emsg) { + msg_fatal("%s: dlsym failure looking up %s in %s: %s", myname, + fn->name, libname, emsg); + } + if (msg_verbose>1) { + msg_info("loaded %s = %lx",fn->name, *((long*)(fn->ptr))); + } + } + } +#elif defined(HAS_SHL_LOAD) + shl_t handle; + + handle = shl_load(libname,BIND_IMMEDIATE,0); + + if (libfuncs) { + for (fn=libfuncs; fn->name; fn++) { + if (shl_findsym(&handle,fn->name,TYPE_PROCEDURE,fn->ptr) != 0) { + msg_fatal("%s: shl_findsym failure looking up %s in %s: %m", + myname, fn->name, libname); + } + if (msg_verbose>1) { + msg_info("loaded %s = %x",fn->name, *((long*)(fn->ptr))); + } + } + } + + if (libdata) { + for (fn=libdata; fn->name; fn++) { + if (shl_findsym(&handle,fn->name,TYPE_DATA,fn->ptr) != 0) { + msg_fatal("%s: shl_findsym failure looking up %s in %s: %m", + myname, fn->name, libname); + } + if (msg_verbose>1) { + msg_info("loaded %s = %x",fn->name, *((long*)(fn->ptr))); + } + } + } + +#else + msg_fatal("%s: need dlopen or shl_load support for dynamic libraries", + myname); +#endif + return 0; +} Index: src/util/load_lib.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/util/load_lib.h 2007-08-06 00:02:19.971488923 +0200 @@ -0,0 +1,41 @@ +#ifndef _LOAD_LIB_H_INCLUDED_ +#define _LOAD_LIB_H_INCLUDED_ + +/*++ +/* NAME +/* load_lib 3h +/* SUMMARY +/* library loading wrappers +/* SYNOPSIS +/* #include "load_lib.h" +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +/* NULL name terminates list */ +typedef struct LIB_FN { + const char *name; + void **ptr; +} LIB_FN; + +extern int load_library_symbols(const char *, LIB_FN *, LIB_FN *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* LaMont Jones +/* Hewlett-Packard Company +/* 3404 Harmony Road +/* Fort Collins, CO 80528, USA +/* +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif Index: src/util/sdbm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/util/sdbm.c 2007-08-06 00:02:19.995490390 +0200 @@ -0,0 +1,972 @@ +/*++ +/* NAME +/* sdbm 3h +/* SUMMARY +/* SDBM Simple DBM: ndbm work-alike hashed database library +/* SYNOPSIS +/* include "sdbm.h" +/* DESCRIPTION +/* This file includes the public domain SDBM (ndbm work-alike hashed +/* database library), based on Per-Aake Larson's Dynamic Hashing +/* algorithms. BIT 18 (1978). +/* author: oz@nexus.yorku.ca +/* status: public domain +/* The file has been patched following the advice of Uwe Ohse +/* : +/* -------------------------------------------------------------- +/* this patch fixes a problem with sdbms .dir file, which arrises when +/* a second .dir block is needed for the first time. read() returns 0 +/* in that case, and the library forgot to initialize that new block. +/* +/* A related problem is that the calculation of db->maxbno is wrong. +/* It just appends 4096*BYTESIZ bits, which is not enough except for +/* small databases (.dir basically doubles everytime it's too small). +/* -------------------------------------------------------------- +/* According to Uwe Ohse, the patch has also been submitted to the +/* author of SDBM. (The 4096*BYTESIZ bits comment may apply with a +/* different size for Postfix/TLS, as the patch was sent against the +/* original SDBM distributiona and for Postfix/TLS I have changed the +/* default sizes. +/* .nf +/*--*/ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + * + * core routines + */ + +#include +#include +#ifdef WIN32 +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#ifdef __STDC__ +#include +#endif +#include + +#include + +/* + * useful macros + */ +#define bad(x) ((x).dptr == NULL || (x).dsize <= 0) +#define exhash(item) sdbm_hash((item).dptr, (item).dsize) +#define ioerr(db) ((db)->flags |= DBM_IOERR) + +#define OFF_PAG(off) (long) (off) * PBLKSIZ +#define OFF_DIR(off) (long) (off) * DBLKSIZ + +static long masks[] = +{ + 000000000000, 000000000001, 000000000003, 000000000007, + 000000000017, 000000000037, 000000000077, 000000000177, + 000000000377, 000000000777, 000000001777, 000000003777, + 000000007777, 000000017777, 000000037777, 000000077777, + 000000177777, 000000377777, 000000777777, 000001777777, + 000003777777, 000007777777, 000017777777, 000037777777, + 000077777777, 000177777777, 000377777777, 000777777777, + 001777777777, 003777777777, 007777777777, 017777777777 +}; + +datum nullitem = +{NULL, 0}; + +typedef struct +{ + int dirf; /* directory file descriptor */ + int pagf; /* page file descriptor */ + int flags; /* status/error flags, see below */ + long maxbno; /* size of dirfile in bits */ + long curbit; /* current bit number */ + long hmask; /* current hash mask */ + long blkptr; /* current block for nextkey */ + int keyptr; /* current key for nextkey */ + long blkno; /* current page to read/write */ + long pagbno; /* current page in pagbuf */ + char *pagbuf; /* page file block buffer */ + long dirbno; /* current block in dirbuf */ + char *dirbuf; /* directory file block buffer */ +} DBM; + + +/* ************************* */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. keep it that way. + * + * hashing routine + */ + +/* + * polynomial conversion ignoring overflows + * [this seems to work remarkably well, in fact better + * then the ndbm hash function. Replace at your own risk] + * use: 65599 nice. + * 65587 even better. + */ +static long sdbm_hash (char *str, int len) +{ + unsigned long n = 0; + +#ifdef DUFF +#define HASHC n = *str++ + 65599 * n + if (len > 0) + { + int loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) + { + case 0: + do + { + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } + while (--loop); + } + + } +#else + while (len--) + n = *str++ + 65599 * n; +#endif + return n; +} + +/* + * check page sanity: + * number of entries should be something + * reasonable, and all offsets in the index should be in order. + * this could be made more rigorous. + */ +static int chkpage (char *pag) +{ + int n; + int off; + short *ino = (short *) pag; + + if ((n = ino[0]) < 0 || n > PBLKSIZ / sizeof (short)) + return 0; + + if (n > 0) + { + off = PBLKSIZ; + for (ino++; n > 0; ino += 2) + { + if (ino[0] > off || ino[1] > off || + ino[1] > ino[0]) + return 0; + off = ino[1]; + n -= 2; + } + } + return 1; +} + +/* + * search for the key in the page. + * return offset index in the range 0 < i < n. + * return 0 if not found. + */ +static int seepair (char *pag, int n, char *key, int siz) +{ + int i; + int off = PBLKSIZ; + short *ino = (short *) pag; + + for (i = 1; i < n; i += 2) + { + if (siz == off - ino[i] && + memcmp (key, pag + ino[i], siz) == 0) + return i; + off = ino[i + 1]; + } + return 0; +} + +#ifdef SEEDUPS +static int duppair (char *pag, datum key) +{ + short *ino = (short *) pag; + + return ino[0] > 0 && seepair (pag, ino[0], key.dptr, key.dsize) > 0; +} + +#endif + +/* ************************* */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + * + * page-level routines + */ + +/* + * page format: + * +------------------------------+ + * ino | n | keyoff | datoff | keyoff | + * +------------+--------+--------+ + * | datoff | - - - ----> | + * +--------+---------------------+ + * | F R E E A R E A | + * +--------------+---------------+ + * | <---- - - - | data | + * +--------+-----+----+----------+ + * | key | data | key | + * +--------+----------+----------+ + * + * calculating the offsets for free area: if the number + * of entries (ino[0]) is zero, the offset to the END of + * the free area is the block size. Otherwise, it is the + * nth (ino[ino[0]]) entry's offset. + */ + +static int fitpair (char *pag, int need) +{ + int n; + int off; + int avail; + short *ino = (short *) pag; + + off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; + avail = off - (n + 1) * sizeof (short); + need += 2 * sizeof (short); + + return need <= avail; +} + +static void putpair (char *pag, datum key, datum val) +{ + int n; + int off; + short *ino = (short *) pag; + + off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; +/* + * enter the key first + */ + off -= key.dsize; + (void) memcpy (pag + off, key.dptr, key.dsize); + ino[n + 1] = off; +/* + * now the data + */ + off -= val.dsize; + (void) memcpy (pag + off, val.dptr, val.dsize); + ino[n + 2] = off; +/* + * adjust item count + */ + ino[0] += 2; +} + +static datum getpair (char *pag, datum key) +{ + int i; + int n; + datum val; + short *ino = (short *) pag; + + if ((n = ino[0]) == 0) + return nullitem; + + if ((i = seepair (pag, n, key.dptr, key.dsize)) == 0) + return nullitem; + + val.dptr = pag + ino[i + 1]; + val.dsize = ino[i] - ino[i + 1]; + return val; +} + +static datum getnkey (char *pag, int num) +{ + datum key; + int off; + short *ino = (short *) pag; + + num = num * 2 - 1; + if (ino[0] == 0 || num > ino[0]) + return nullitem; + + off = (num > 1) ? ino[num - 1] : PBLKSIZ; + + key.dptr = pag + ino[num]; + key.dsize = off - ino[num]; + + return key; +} + +static int delpair (char *pag, datum key) +{ + int n; + int i; + short *ino = (short *) pag; + + if ((n = ino[0]) == 0) + return 0; + + if ((i = seepair (pag, n, key.dptr, key.dsize)) == 0) + return 0; +/* + * found the key. if it is the last entry + * [i.e. i == n - 1] we just adjust the entry count. + * hard case: move all data down onto the deleted pair, + * shift offsets onto deleted offsets, and adjust them. + * [note: 0 < i < n] + */ + if (i < n - 1) + { + int m; + char *dst = pag + (i == 1 ? PBLKSIZ : ino[i - 1]); + char *src = pag + ino[i + 1]; + int zoo = dst - src; + +/* + * shift data/keys down + */ + m = ino[i + 1] - ino[n]; +#ifdef DUFF +#define MOVB *--dst = *--src + if (m > 0) + { + int loop = (m + 8 - 1) >> 3; + + switch (m & (8 - 1)) + { + case 0: + do + { + MOVB; + case 7: + MOVB; + case 6: + MOVB; + case 5: + MOVB; + case 4: + MOVB; + case 3: + MOVB; + case 2: + MOVB; + case 1: + MOVB; + } + while (--loop); + } + } +#else + dst -= m; + src -= m; + memmove (dst, src, m); +#endif +/* + * adjust offset index up + */ + while (i < n - 1) + { + ino[i] = ino[i + 2] + zoo; + i++; + } + } + ino[0] -= 2; + return 1; +} + +static void splpage (char *pag, char *new, long sbit) +{ + datum key; + datum val; + + int n; + int off = PBLKSIZ; + char cur[PBLKSIZ]; + short *ino = (short *) cur; + + (void) memcpy (cur, pag, PBLKSIZ); + (void) memset (pag, 0, PBLKSIZ); + (void) memset (new, 0, PBLKSIZ); + + n = ino[0]; + for (ino++; n > 0; ino += 2) + { + key.dptr = cur + ino[0]; + key.dsize = off - ino[0]; + val.dptr = cur + ino[1]; + val.dsize = ino[0] - ino[1]; +/* + * select the page pointer (by looking at sbit) and insert + */ + (void) putpair ((exhash (key) & sbit) ? new : pag, key, val); + + off = ino[1]; + n -= 2; + } +} + +static int getdbit (DBM * db, long dbit) +{ + long c; + long dirb; + + c = dbit / BYTESIZ; + dirb = c / DBLKSIZ; + + if (dirb != db->dirbno) + { + int got; + if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 + || (got = read(db->dirf, db->dirbuf, DBLKSIZ)) < 0) + return 0; + if (got==0) + memset(db->dirbuf,0,DBLKSIZ); + db->dirbno = dirb; + } + + return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ); +} + +static int setdbit (DBM * db, long dbit) +{ + long c; + long dirb; + + c = dbit / BYTESIZ; + dirb = c / DBLKSIZ; + + if (dirb != db->dirbno) + { + int got; + if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 + || (got = read(db->dirf, db->dirbuf, DBLKSIZ)) < 0) + return 0; + if (got==0) + memset(db->dirbuf,0,DBLKSIZ); + db->dirbno = dirb; + } + + db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ); + +#if 0 + if (dbit >= db->maxbno) + db->maxbno += DBLKSIZ * BYTESIZ; +#else + if (OFF_DIR((dirb+1))*BYTESIZ > db->maxbno) + db->maxbno=OFF_DIR((dirb+1))*BYTESIZ; +#endif + + if (lseek (db->dirf, OFF_DIR (dirb), SEEK_SET) < 0 + || write (db->dirf, db->dirbuf, DBLKSIZ) < 0) + return 0; + + return 1; +} + +/* + * getnext - get the next key in the page, and if done with + * the page, try the next page in sequence + */ +static datum getnext (DBM * db) +{ + datum key; + + for (;;) + { + db->keyptr++; + key = getnkey (db->pagbuf, db->keyptr); + if (key.dptr != NULL) + return key; +/* + * we either run out, or there is nothing on this page.. + * try the next one... If we lost our position on the + * file, we will have to seek. + */ + db->keyptr = 0; + if (db->pagbno != db->blkptr++) + if (lseek (db->pagf, OFF_PAG (db->blkptr), SEEK_SET) < 0) + break; + db->pagbno = db->blkptr; + if (read (db->pagf, db->pagbuf, PBLKSIZ) <= 0) + break; + if (!chkpage (db->pagbuf)) + break; + } + + return ioerr (db), nullitem; +} + +/* + * all important binary trie traversal + */ +static int getpage (DBM * db, long hash) +{ + int hbit; + long dbit; + long pagb; + + dbit = 0; + hbit = 0; + while (dbit < db->maxbno && getdbit (db, dbit)) + dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1); + + db->curbit = dbit; + db->hmask = masks[hbit]; + + pagb = hash & db->hmask; +/* + * see if the block we need is already in memory. + * note: this lookaside cache has about 10% hit rate. + */ + if (pagb != db->pagbno) + { +/* + * note: here, we assume a "hole" is read as 0s. + * if not, must zero pagbuf first. + */ + if (lseek (db->pagf, OFF_PAG (pagb), SEEK_SET) < 0 + || read (db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + if (!chkpage (db->pagbuf)) + return 0; + db->pagbno = pagb; + } + return 1; +} + +/* + * makroom - make room by splitting the overfull page + * this routine will attempt to make room for SPLTMAX times before + * giving up. + */ +static int makroom (DBM * db, long hash, int need) +{ + long newp; + char twin[PBLKSIZ]; + char *pag = db->pagbuf; + char *new = twin; + int smax = SPLTMAX; + + do + { +/* + * split the current page + */ + (void) splpage (pag, new, db->hmask + 1); +/* + * address of the new page + */ + newp = (hash & db->hmask) | (db->hmask + 1); + +/* + * write delay, read avoidence/cache shuffle: + * select the page for incoming pair: if key is to go to the new page, + * write out the previous one, and copy the new one over, thus making + * it the current page. If not, simply write the new page, and we are + * still looking at the page of interest. current page is not updated + * here, as sdbm_store will do so, after it inserts the incoming pair. + */ + if (hash & (db->hmask + 1)) + { + if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + db->pagbno = newp; + (void) memcpy (pag, new, PBLKSIZ); + } + else if (lseek (db->pagf, OFF_PAG (newp), SEEK_SET) < 0 + || write (db->pagf, new, PBLKSIZ) < 0) + return 0; + + if (!setdbit (db, db->curbit)) + return 0; +/* + * see if we have enough room now + */ + if (fitpair (pag, need)) + return 1; +/* + * try again... update curbit and hmask as getpage would have + * done. because of our update of the current page, we do not + * need to read in anything. BUT we have to write the current + * [deferred] page out, as the window of failure is too great. + */ + db->curbit = 2 * db->curbit + + ((hash & (db->hmask + 1)) ? 2 : 1); + db->hmask |= db->hmask + 1; + + if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + + } + while (--smax); +/* + * if we are here, this is real bad news. After SPLTMAX splits, + * we still cannot fit the key. say goodnight. + */ +#ifdef BADMESS + (void) write (2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44); +#endif + return 0; + +} + +static SDBM *sdbm_prep (char *dirname, char *pagname, int flags, int mode) +{ + SDBM *db; + struct stat dstat; + + if ((db = (SDBM *) mymalloc (sizeof (SDBM))) == NULL) + return errno = ENOMEM, (SDBM *) NULL; + + db->flags = 0; + db->blkptr = 0; + db->keyptr = 0; +/* + * adjust user flags so that WRONLY becomes RDWR, + * as required by this package. Also set our internal + * flag for RDONLY if needed. + */ + if (flags & O_WRONLY) + flags = (flags & ~O_WRONLY) | O_RDWR; + else if ((flags & 03) == O_RDONLY) + db->flags = DBM_RDONLY; +#if defined(OS2) || defined(MSDOS) || defined(WIN32) + flags |= O_BINARY; +#endif + +/* + * Make sure to ignore the O_EXCL option, as the file might exist due + * to the locking. + */ + flags &= ~O_EXCL; + +/* + * open the files in sequence, and stat the dirfile. + * If we fail anywhere, undo everything, return NULL. + */ + + if ((db->pagf = open (pagname, flags, mode)) > -1) + { + if ((db->dirf = open (dirname, flags, mode)) > -1) + { +/* + * need the dirfile size to establish max bit number. + */ + if (fstat (db->dirf, &dstat) == 0) + { + /* + * success + */ + return db; + } + msg_info ("closing dirf"); + (void) close (db->dirf); + } + msg_info ("closing pagf"); + (void) close (db->pagf); + } + myfree ((char *) db); + return (SDBM *) NULL; +} + +static DBM *sdbm_internal_open (SDBM * sdbm) +{ + DBM *db; + struct stat dstat; + + if ((db = (DBM *) mymalloc (sizeof (DBM))) == NULL) + return errno = ENOMEM, (DBM *) NULL; + + db->flags = sdbm->flags; + db->hmask = 0; + db->blkptr = sdbm->blkptr; + db->keyptr = sdbm->keyptr; + db->pagf = sdbm->pagf; + db->dirf = sdbm->dirf; + db->pagbuf = sdbm->pagbuf; + db->dirbuf = sdbm->dirbuf; + +/* + * need the dirfile size to establish max bit number. + */ + if (fstat (db->dirf, &dstat) == 0) + { +/* + * zero size: either a fresh database, or one with a single, + * unsplit data page: dirpage is all zeros. + */ + db->dirbno = (!dstat.st_size) ? 0 : -1; + db->pagbno = -1; + db->maxbno = dstat.st_size * BYTESIZ; + + (void) memset (db->pagbuf, 0, PBLKSIZ); + (void) memset (db->dirbuf, 0, DBLKSIZ); + return db; + } + myfree ((char *) db); + return (DBM *) NULL; +} + +static void sdbm_internal_close (DBM * db) +{ + if (db == NULL) + errno = EINVAL; + else + { + myfree ((char *) db); + } +} + +datum sdbm_fetch (SDBM * sdb, datum key) +{ + datum retval; + DBM *db; + + if (sdb == NULL || bad (key)) + return errno = EINVAL, nullitem; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, nullitem; + + if (getpage (db, exhash (key))) + { + retval = getpair (db->pagbuf, key); + sdbm_internal_close (db); + return retval; + } + + sdbm_internal_close (db); + + return ioerr (sdb), nullitem; +} + +int sdbm_delete (SDBM * sdb, datum key) +{ + int retval; + DBM *db; + + if (sdb == NULL || bad (key)) + return errno = EINVAL, -1; + if (sdbm_rdonly (sdb)) + return errno = EPERM, -1; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, -1; + + if (getpage (db, exhash (key))) + { + if (!delpair (db->pagbuf, key)) + retval = -1; +/* + * update the page file + */ + else if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + retval = ioerr (sdb), -1; + else + retval = 0; + } + else + retval = ioerr (sdb), -1; + + sdbm_internal_close (db); + + return retval; +} + +int sdbm_store (SDBM * sdb, datum key, datum val, int flags) +{ + int need; + int retval; + long hash; + DBM *db; + + if (sdb == NULL || bad (key)) + return errno = EINVAL, -1; + if (sdbm_rdonly (sdb)) + return errno = EPERM, -1; + + need = key.dsize + val.dsize; +/* + * is the pair too big (or too small) for this database ?? + */ + if (need < 0 || need > PAIRMAX) + return errno = EINVAL, -1; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, -1; + + if (getpage (db, (hash = exhash (key)))) + { +/* + * if we need to replace, delete the key/data pair + * first. If it is not there, ignore. + */ + if (flags == DBM_REPLACE) + (void) delpair (db->pagbuf, key); +#ifdef SEEDUPS + else if (duppair (db->pagbuf, key)) + { + sdbm_internal_close (db); + return 1; + } +#endif +/* + * if we do not have enough room, we have to split. + */ + if (!fitpair (db->pagbuf, need)) + if (!makroom (db, hash, need)) + { + sdbm_internal_close (db); + return ioerr (db), -1; + } +/* + * we have enough room or split is successful. insert the key, + * and update the page file. + */ + (void) putpair (db->pagbuf, key, val); + + if (lseek (db->pagf, OFF_PAG (db->pagbno), SEEK_SET) < 0 + || write (db->pagf, db->pagbuf, PBLKSIZ) < 0) + { + sdbm_internal_close (db); + return ioerr (db), -1; + } + /* + * success + */ + sdbm_internal_close (db); + return 0; + } + + sdbm_internal_close (db); + return ioerr (sdb), -1; +} + +/* + * the following two routines will break if + * deletions aren't taken into account. (ndbm bug) + */ +datum sdbm_firstkey (SDBM * sdb) +{ + datum retval; + DBM *db; + + if (sdb == NULL) + return errno = EINVAL, nullitem; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, nullitem; + +/* + * start at page 0 + */ + if (lseek (db->pagf, OFF_PAG (0), SEEK_SET) < 0 + || read (db->pagf, db->pagbuf, PBLKSIZ) < 0) + { + sdbm_internal_close (db); + return ioerr (sdb), nullitem; + } + db->pagbno = 0; + db->blkptr = 0; + db->keyptr = 0; + + retval = getnext (db); + sdb->blkptr = db->blkptr; + sdb->keyptr = db->keyptr; + sdbm_internal_close (db); + return retval; +} + +datum sdbm_nextkey (SDBM * sdb) +{ + datum retval; + DBM *db; + + if (sdb == NULL) + return errno = EINVAL, nullitem; + + if (!(db = sdbm_internal_open (sdb))) + return errno = EINVAL, nullitem; + + retval = getnext (db); + sdb->blkptr = db->blkptr; + sdb->keyptr = db->keyptr; + sdbm_internal_close (db); + return retval; +} + +void sdbm_close (SDBM * db) +{ + if (db == NULL) + errno = EINVAL; + else + { + (void) close (db->dirf); + (void) close (db->pagf); + myfree ((char *) db); + } +} + +SDBM *sdbm_open (char *file, int flags, int mode) +{ + SDBM *db; + char *dirname; + char *pagname; + int n; + + if (file == NULL || !*file) + return errno = EINVAL, (SDBM *) NULL; +/* + * need space for two seperate filenames + */ + n = strlen (file) * 2 + strlen (DIRFEXT) + strlen (PAGFEXT) + 2; + + if ((dirname = (char *) mymalloc ((unsigned) n)) == NULL) + return errno = ENOMEM, (SDBM *) NULL; +/* + * build the file names + */ + dirname = strcat (strcpy (dirname, file), DIRFEXT); + pagname = strcpy (dirname + strlen (dirname) + 1, file); + pagname = strcat (pagname, PAGFEXT); + + db = sdbm_prep (dirname, pagname, flags, mode); + myfree ((char *) dirname); + return db; +} + Index: src/util/sdbm.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/util/sdbm.h 2007-08-06 00:02:20.023492103 +0200 @@ -0,0 +1,97 @@ +/*++ +/* NAME +/* sdbm 3h +/* SUMMARY +/* SDBM Simple DBM: ndbm work-alike hashed database library +/* SYNOPSIS +/* include "sdbm.h" +/* DESCRIPTION +/* .nf +/*--*/ + +#ifndef UTIL_SDBM_H +#define UTIL_SDBM_H + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Ake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + */ + +#define DUFF /* go ahead and use the loop-unrolled version */ + +#include + +#define DBLKSIZ 16384 /* SSL cert chains require more */ +#define PBLKSIZ 8192 /* SSL cert chains require more */ +#define PAIRMAX 8008 /* arbitrary on PBLKSIZ-N */ +#define SPLTMAX 10 /* maximum allowed splits */ + /* for a single insertion */ +#define DIRFEXT ".dir" +#define PAGFEXT ".pag" + +typedef struct { + int dirf; /* directory file descriptor */ + int pagf; /* page file descriptor */ + int flags; /* status/error flags, see below */ + long blkptr; /* current block for nextkey */ + int keyptr; /* current key for nextkey */ + char pagbuf[PBLKSIZ]; /* page file block buffer */ + char dirbuf[DBLKSIZ]; /* directory file block buffer */ +} SDBM; + +#define DBM_RDONLY 0x1 /* data base open read-only */ +#define DBM_IOERR 0x2 /* data base I/O error */ + +/* + * utility macros + */ +#define sdbm_rdonly(db) ((db)->flags & DBM_RDONLY) +#define sdbm_error(db) ((db)->flags & DBM_IOERR) + +#define sdbm_clearerr(db) ((db)->flags &= ~DBM_IOERR) /* ouch */ + +#define sdbm_dirfno(db) ((db)->dirf) +#define sdbm_pagfno(db) ((db)->pagf) + +typedef struct { + char *dptr; + int dsize; +} datum; + +extern datum nullitem; + +/* + * flags to sdbm_store + */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * ndbm interface + */ +extern SDBM *sdbm_open(char *, int, int); +extern void sdbm_close(SDBM *); +extern datum sdbm_fetch(SDBM *, datum); +extern int sdbm_delete(SDBM *, datum); +extern int sdbm_store(SDBM *, datum, datum, int); +extern datum sdbm_firstkey(SDBM *); +extern datum sdbm_nextkey(SDBM *); + +/* + * sdbm - ndbm work-alike hashed database library + * tuning and portability constructs [not nearly enough] + * author: oz@nexus.yorku.ca + */ + +#define BYTESIZ 8 + +/* + * important tuning parms (hah) + */ + +#define SEEDUPS /* always detect duplicates */ +#define BADMESS /* generate a message for worst case: + cannot make room after SPLTMAX splits */ +#endif /* UTIL_SDBM_H */ Index: src/util/sys_defs.h =================================================================== --- src/util/sys_defs.h.orig 2007-03-24 23:45:33.000000000 +0100 +++ src/util/sys_defs.h 2007-08-06 00:02:20.039493081 +0200 @@ -689,6 +689,7 @@ extern int initgroups(const char *, int) #define INTERNAL_LOCK MYFLOCK_STYLE_FLOCK #define DEF_MAILBOX_LOCK "fcntl, dotlock" /* RedHat >= 4.x */ #define HAS_FSYNC +#define HAS_SDBM #define HAS_DB #define DEF_DB_TYPE "hash" #define ALIAS_DB_MAP "hash:/etc/aliases" @@ -700,11 +701,25 @@ extern int initgroups(const char *, int) #define STATFS_IN_SYS_VFS_H #define PREPEND_PLUS_TO_OPTSTRING #define HAS_POSIX_REGEXP +#define HAS_DLOPEN #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" #define NATIVE_COMMAND_DIR "/usr/sbin" +#ifdef DEBIAN +#define NATIVE_DAEMON_DIR "/usr/lib/postfix" +#ifndef DEF_MANPAGE_DIR +#define DEF_MANPAGE_DIR "/usr/share/man" +#endif +#ifndef DEF_SAMPLE_DIR +#define DEF_SAMPLE_DIR "/usr/share/doc/postfix/examples" +#endif +#ifndef DEF_README_DIR +#define DEF_README_DIR "/usr/share/doc/postfix" +#endif +#else #define NATIVE_DAEMON_DIR "/usr/libexec/postfix" +#endif #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 #define SOCKADDR_SIZE socklen_t #define SOCKOPT_SIZE socklen_t @@ -859,6 +874,7 @@ extern int h_errno; /* impor #define USE_STATFS #define STATFS_IN_SYS_VFS_H #define HAS_POSIX_REGEXP +#define HAS_DLOPEN #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" @@ -896,6 +912,7 @@ extern int h_errno; /* impor #define USE_STATFS #define STATFS_IN_SYS_VFS_H #define HAS_POSIX_REGEXP +#define HAS_SHL_LOAD #define NATIVE_SENDMAIL_PATH "/usr/sbin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" @@ -935,6 +952,7 @@ extern int h_errno; #define USE_STATFS #define STATFS_IN_SYS_VFS_H #define HAS_POSIX_REGEXP +#define HAS_SHL_LOAD #define NATIVE_SENDMAIL_PATH "/usr/bin/sendmail" #define NATIVE_MAILQ_PATH "/usr/bin/mailq" #define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases" Index: src/tls/tls_client.c =================================================================== --- src/tls/tls_client.c.orig 2007-02-22 01:08:45.000000000 +0100 +++ src/tls/tls_client.c 2007-08-06 00:02:20.079495528 +0200 @@ -147,6 +147,8 @@ #define STR vstring_str #define LEN VSTRING_LEN +int var_tls_daemon_rand_bytes; + /* load_clnt_session - load session from client cache (non-callback) */ static SSL_SESSION *load_clnt_session(TLScontext_t *TLScontext) Index: src/tls/tls_server.c =================================================================== --- src/tls/tls_server.c.orig 2007-02-22 01:08:45.000000000 +0100 +++ src/tls/tls_server.c 2007-08-06 00:02:20.095496506 +0200 @@ -120,6 +120,8 @@ #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) +int var_tls_daemon_rand_bytes; + /* Application-specific. */ /* We must keep some of the info available */ Index: src/milter/Makefile.in =================================================================== --- src/milter/Makefile.in.orig 2007-03-17 18:51:39.000000000 +0100 +++ src/milter/Makefile.in 2007-08-06 00:02:20.211503600 +0200 @@ -14,7 +14,7 @@ LIB_DIR = ../../lib INC_DIR = ../../include MAKES = -.c.o:; $(CC) $(CFLAGS) -c $*.c +.c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c all: $(LIB) @@ -30,12 +30,10 @@ tests: root_tests: $(LIB): $(OBJS) - $(AR) $(ARFL) $(LIB) $? - $(RANLIB) $(LIB) + gcc -shared -Wl,-soname,libpostfix-milter.so.1 -o $(LIB) $(OBJS) $(LIBS) $(SYSLIBS) $(LIB_DIR)/$(LIB): $(LIB) cp $(LIB) $(LIB_DIR) - $(RANLIB) $(LIB_DIR)/$(LIB) update: $(LIB_DIR)/$(LIB) $(HDRS) -for i in $(HDRS); \