Index: release.1/util/Makefile.in --- release.1/util/Makefile.in Sat, 15 May 1999 21:18:10 +0000 thomas (Postfix/h/15_Makefile.i 1.1.1.1 644) +++ thomas.5/util/Makefile.in Sat, 15 May 1999 21:21:50 +0000 thomas (Postfix/h/15_Makefile.i 1.1.1.2 644) @@ -2,7 +2,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ close_on_exec.c concatenate.c dict.c dict_db.c dict_dbm.c \ dict_env.c dict_ht.c dict_ldap.c dict_ni.c dict_nis.c \ - dict_nisplus.c dict_open.c dir_forest.c doze.c environ.c \ + dict_nisplus.c dict_open.c dict_prog.c dir_forest.c doze.c environ.c \ events.c exec_command.c fifo_listen.c fifo_trigger.c file_limit.c \ find_inet.c fsspace.c fullname.c get_domainname.c get_hostname.c \ htable.c inet_addr_host.c inet_addr_list.c inet_addr_local.c \ @@ -24,7 +24,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \ dict_env.o dict_ht.o dict_ldap.o dict_ni.o dict_nis.o \ - dict_nisplus.o dict_open.o dir_forest.o doze.o environ.o \ + dict_nisplus.o dict_open.o dict_prog.o dir_forest.o doze.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 \ htable.o inet_addr_host.o inet_addr_list.o inet_addr_local.o \ @@ -45,7 +45,7 @@ clean_env.o HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_ni.h dict_nis.h \ - dict_nisplus.h dir_forest.h events.h exec_command.h find_inet.h \ + dict_nisplus.h dict_prog.h dir_forest.h events.h exec_command.h find_inet.h \ fsspace.h fullname.h get_domainname.h get_hostname.h htable.h \ inet_addr_host.h inet_addr_list.h inet_addr_local.h inet_util.h \ iostuff.h line_wrap.h listen.h lstat_as.h mac_parse.h make_dirs.h \ Index: release.1/util/dict_open.c --- release.1/util/dict_open.c Sat, 15 May 1999 21:18:10 +0000 thomas (Postfix/i/4_dict_open. 1.1.1.1 644) +++ thomas.5/util/dict_open.c Tue, 18 May 1999 16:39:15 +0000 thomas (Postfix/i/4_dict_open. 1.1.1.3 644) @@ -100,6 +100,8 @@ /* PERL-compatible regular expressions. /* .IP regexp /* POSIX-compatible regular expressions. +/* .IP prog +/* Generic external program query. /* .PP /* dict_open3() takes separate arguments for dictionary type and /* name, but otherwise performs the same functions as dict_open(). @@ -153,6 +155,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +179,7 @@ static DICT_OPEN_INFO dict_open_info[] = { "environ", dict_env_open, "unix", dict_unix_open, + "prog", dict_prog_open, #ifdef HAS_DBM "dbm", dict_dbm_open, #endif Index: release.1/util/dict_prog.c --- release.1/util/dict_prog.c Fri, 18 Jun 1999 15:56:43 +0000 thomas () +++ thomas.5/util/dict_prog.c Tue, 18 May 1999 16:56:52 +0000 thomas (Postfix/m/50_dict_prog. 1.3 644) @@ -0,0 +1,323 @@ +/*++ +/* NAME +/* dict_prog 3 +/* SUMMARY +/* dictionary manager interface to generic external program map +/* SYNOPSIS +/* #include +/* +/* DICT *dict_prog_open (attribute, dummy, dict_flags) +/* const char *attribute; +/* int dummy; +/* int dict_flags; +/* DESCRIPTION +/* dict_prog_open() allows any external program to be used for +/* the generic dictionary operations described in dict_open(3). +/* +/* Arguments: +/* .IP progname +/* The prefix which will be used to obtain configuration parameters +/* for this search. If it's 'progone', the configuration variables below +/* would look like 'progone_command', 'progone_timeout', and so +/* on in main.cf. +/* .IP dummy +/* Not used; this argument exists only for compatibility with +/* the dict_open(3) interface. +/* .PP +/* Configuration parameters: +/* .IP \fIprogname_\fRcommand +/* The command line to be run. +/* .IP \fIprogname_\fRtimeout +/* Deadline for open() and search() . +/* BUGS +/* Of course not! :) +/* SEE ALSO +/* dict(3) generic dictionary manager +/* DIAGNOSTICS +/* Warnings: no process, query failed +/* Fatals: unable to popen, query failed with error, signal +/* AUTHOR(S) +/* Thomas Quinot +/* thomas@cuivre.fr.eu.org +/* +/*--*/ + +/* System library. */ + +#include "sys_defs.h" + +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include "msg.h" +#include "mymalloc.h" +#include "vstring.h" +#include "dict.h" +#include "dict_prog.h" + + /* + * Grr.. this module should sit in the global library, because it interacts + * with application-specific configuration parameters. I will have to + * generalize the manner in which new dictionary types can register + * themselves, including their configuration file parameters. + */ + +/* + * structure containing all the configuration parameters for a given + * prog source, plus its file handle + */ +typedef struct { + DICT dict; /* generic member */ + char *progname; + char *command; + int timeout; + VSTREAM *fh; +} DICT_PROG; + + /* + * Program timeout support. + */ +static jmp_buf env; + +static void dict_prog_timeout (int unused_sig) +{ + longjmp (env, 1); +} + +/* prog_open - open pipe to external program */ + +static VSTREAM *prog_open (const char *command) { + char *myname = "prog_open"; + + VSTREAM *fh; + + fh = vstream_popen (command, O_RDWR); + if (fh == NULL) + msg_fatal ("%s: unable to popen: %m", myname); + + return fh; +} + +/* dict_prog_lookup - find database entry */ + +static const char *dict_prog_lookup(DICT *dict, const char *name) +{ + char *myname = "dict_prog_lookup"; + DICT_PROG *dict_prog = (DICT_PROG *) dict; + static char val_buf[VSTREAM_BUFSIZE + 1]; + long i = 0; + long val_len = 0; + int rc = 0; + void (*saved_alarm) (int); + + dict_errno = 0; + + /* + * Initialize. + */ + + val_buf[0] = '\0'; + + if (msg_verbose) + msg_info("%s: In dict_prog_lookup", myname); + + if (dict_prog->fh == NULL) { + msg_warn("%s: no existing process for command %s, reopening", + myname, dict_prog->command); + if (msg_verbose) + msg_info("%s: spawning %s", myname, + dict_prog->command); + + if ((saved_alarm = signal(SIGALRM, dict_prog_timeout)) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + alarm(dict_prog->timeout); + if (setjmp(env) == 0) + dict_prog->fh = prog_open(dict_prog->command); + alarm(0); + + if (signal(SIGALRM, saved_alarm) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + if (msg_verbose) + msg_info("%s: after prog_open", myname); + } + + /* + * Ask program for entry + */ + + val_len = 0; + + if ((saved_alarm = signal(SIGALRM, dict_prog_timeout)) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + if (msg_verbose) + msg_info("%s: trying lookup: %s", myname, name); + + alarm(dict_prog->timeout); + if (setjmp(env) == 0) { + char ch; + int res; + + if ((vstream_fputs (name, dict_prog->fh) == VSTREAM_EOF) || + (VSTREAM_PUTC ('\n', dict_prog->fh) == VSTREAM_EOF) || + ((ch = VSTREAM_GETC (dict_prog->fh)) == VSTREAM_EOF)) { + res = vstream_pclose (dict_prog->fh); + dict_prog->fh = NULL; + + if (res == 0) { + msg_warn ("%s: query to %s for %s failed", + myname, dict_prog->progname, name); + rc = DICT_ERR_RETRY; + val_len = 0; + } else + msg_fatal ("%s: query to %s for %s failed with an error: %s", + myname, dict_prog->progname, name, strerror (res)); + } else { + vstream_ungetc (dict_prog->fh, ch); + val_len = vstream_peek (dict_prog->fh); + if (val_len > sizeof val_buf - 1) + val_len = sizeof val_buf - 1; + + val_len = vstream_fread (dict_prog->fh, val_buf, val_len); + } + } else { + vstream_pclose (dict_prog->fh); + dict_prog->fh = NULL; + msg_warn ("%s: query to %s for %s timed out", + myname, dict_prog->progname, name); + rc = DICT_ERR_RETRY; + val_len = 0; + } + alarm(0); + + if (signal(SIGALRM, saved_alarm) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + if (rc == 0) { + for (i = 0 ; i < val_len ; i++) { + if (val_buf[i] == '\n') + break; + } + val_buf[i] = '\0'; + + if (msg_verbose) + if (i > 0) + msg_info("%s: lookup succeeded: %s", myname, val_buf); + else + msg_info("%s: not found", myname); + } + + /* + * Cleanup. Always return with dict_errno set when we were unable to + * perform the query. + */ + dict_errno = rc; + return (i > 0 ? val_buf : 0); +} + +/* dict_prog_update - add or update database entry */ + +static void dict_prog_update(DICT *dict, const char *unused_name, + const char *unused_value) +{ + msg_fatal("dict_prog_update: operation not implemented"); +} + +/* dict_prog_close - disassociate from data base */ + +static void dict_prog_close(DICT *dict) +{ + char *myname = "dict_prog_close"; + DICT_PROG *dict_prog = (DICT_PROG *) dict; + + if (dict_prog->fh != NULL) + vstream_pclose (dict_prog->fh); + + myfree(dict_prog->progname); + myfree(dict_prog->command); + myfree((char *) dict_prog); +} + +/* dict_prog_open - create association with data base */ + +DICT *dict_prog_open(const char *progname, int dummy, int dict_flags) +{ + char *myname = "dict_prog_open"; + DICT_PROG *dict_prog; + VSTRING *config_param; + void (*saved_alarm) (int); + + dict_prog = (DICT_PROG *) mymalloc(sizeof(*dict_prog)); + dict_prog->dict.lookup = dict_prog_lookup; + dict_prog->dict.update = dict_prog_update; + dict_prog->dict.close = dict_prog_close; + dict_prog->fh = NULL; + dict_prog->dict.flags = dict_flags | DICT_FLAG_FIXED; + + if (msg_verbose) + msg_info("%s: using prog map %s", myname, progname); + + dict_prog->progname = mystrdup(progname); + + /* get configured value of "progname_command"; no default */ + config_param = vstring_alloc(15); + vstring_sprintf(config_param, "%s_command", progname); + +#ifndef TEST + dict_prog->command = + mystrdup((char *) get_mail_conf_str(vstring_str(config_param), + "", 0, 0)); +#else + dict_prog->command = "/tmp/toto"; +#endif + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_prog->command); + + /* get configured value of "progname_timeout"; default to 10 */ + vstring_sprintf(config_param, "%s_timeout", progname); +#ifndef TEST + dict_prog->timeout = get_mail_conf_int(config_param, 10, 0, 0); +#else + dict_prog->timeout = 10; +#endif + if (msg_verbose) + msg_info("%s: %s is %d", myname, vstring_str(config_param), + dict_prog->timeout); + + /* + * launch the map program. + */ + + if (msg_verbose) + msg_info("%s: spawning %s", myname, + dict_prog->command); + + if ((saved_alarm = signal(SIGALRM, dict_prog_timeout)) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + alarm(dict_prog->timeout); + if (setjmp(env) == 0) + dict_prog->fh = prog_open(dict_prog->command); + alarm(0); + + if (signal(SIGALRM, saved_alarm) == SIG_ERR) + msg_fatal("%s: signal: %m", myname); + + if (msg_verbose) + msg_info("%s: after prog_open", myname); + + if (dict_prog->fh == NULL) { + msg_fatal("%s: unable to spawn %s", + myname, dict_prog->command); + } + + return (&dict_prog->dict); +} Index: release.1/util/dict_prog.h --- release.1/util/dict_prog.h Fri, 18 Jun 1999 15:56:43 +0000 thomas () +++ thomas.5/util/dict_prog.h Sat, 15 May 1999 21:16:47 +0000 thomas (Postfix/m/51_dict_prog. 1.1 644) @@ -0,0 +1,29 @@ +#ifndef _DICT_PROG_H_INCLUDED_ +#define _DICT_PROG_H_INCLUDED_ + +/*++ +/* NAME +/* dict_prog 3h +/* SUMMARY +/* dictionary manager interface to generic external program maps +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern DICT *dict_prog_open(const char *, int, int); + +/* AUTHOR(S) +/* Thomas Quinot +/* thomas@cuivre.fr.eu.org +/*--*/ + +#endif Index: release.1/conf/sample-prog.cf --- release.1/conf/sample-prog.cf Fri, 18 Jun 1999 15:56:43 +0000 thomas () +++ thomas.5/conf/sample-prog.cf Tue, 18 May 1999 16:56:52 +0000 thomas (Postfix/n/4_sample-pro 1.1 644) @@ -0,0 +1,44 @@ +# This file contains example settings of Postfix configuration +# parameters that control external program lookups. + +# If you have a map "prog:myprogram"... + +# The myprogram_timeout parameter specifies the timeout for external +# program lookups. +# +myprogram_timeout = 10 + +# The myprogram_command parameter specifies the program to query. +# +myprogram_command = + +## Example external program for querying a PostgreSQL database. + +## #! /bin/sh +## +## PSQL=/opt/postgres/bin/psql +## DB=mybase +## HOST=localhost +## VALCOL=email +## TAB=users +## KEYCOL=nom +## +## while read KEY +## do +## ${PSQL} -d ${DB} -h ${HOST} -t -q -c \ +## "SELECT ${VALCOL} FROM ${TAB} WHERE ${KEYCOL} ~* '${KEY}'" +## if [ $? != 0 ] +## then +## exit 1 +## fi +## done + +# The external program shall read keys on its standard input, +# and output one single line for the associated value. +# This line may be empty (a single \n) if there is no value associated +# to the key. + +# A temporary failure to make the query shall result in the program +# exiting with status 0. + +# A permanent failure shall result in an exit with a non-zero status.