]>
Commit | Line | Data |
---|---|---|
f512ae96 JB |
1 | diff -rU3 --new-file postfix-1.1.3-vanilla/PGSQL_README postfix-1.1.3/PGSQL_README |
2 | --- postfix-1.1.3-vanilla/PGSQL_README Thu Jan 1 01:00:00 1970 | |
3 | +++ postfix-1.1.3/PGSQL_README Thu Mar 7 11:48:40 2002 | |
4 | @@ -0,0 +1,88 @@ | |
5 | +[Code contributed by Mathieu Arnold] | |
6 | + | |
7 | +We've written code to add a pgsql map type. It utilizes the pgsql | |
8 | +client library, which can be obtained from: | |
9 | + | |
10 | + http://www.postgresql.org/ | |
11 | + | |
12 | +In order to build postfix with pgsql map support, you will need to add | |
13 | +-DHAS_PGSQL and -I for the directory containing the postgres headers, and | |
14 | +the libpq library (and libcrypt) to AUXLIBS, for example: | |
15 | + | |
16 | +make -f Makefile.init makefiles \ | |
17 | + 'CCARGS=-DHAS_PGSQL -I/some/where/include/postgresql' \ | |
18 | + 'AUXLIBS=/some/where/lib/postgres/libpq.a -lcrypt' | |
19 | + | |
20 | +then, just run 'make'. | |
21 | + | |
22 | +Postfix installations which may benefit from using pgsql map types | |
23 | +include sites that have a need for instantaneous updates of | |
24 | +forwarding, and sites that may benefit from having mail exchangers | |
25 | +reference a networked database, possibly working in conjunction with a | |
26 | +customer database of sorts. | |
27 | + | |
28 | +Once postfix is built with pgsql support, you can specify a map type | |
29 | +in main.cf like this: | |
30 | + | |
31 | +alias_maps = pgsql:/etc/postfix/pgsql-aliases.cf | |
32 | + | |
33 | +The file /etc/postfix/pgsql-aliases.cf specifies lots of information | |
34 | +telling postfix how to reference the postgresql database. An example | |
35 | +postgresql map config file follows: | |
36 | + | |
37 | +# | |
38 | +# postgresql config file for alias lookups on postfix | |
39 | +# comments are ok. | |
40 | +# | |
41 | + | |
42 | +# the user name and password to log into the pgsql server | |
43 | +user = someone | |
44 | +password = some_passwordd | |
45 | + | |
46 | +# the database name on the servers | |
47 | +dbname = customer_database | |
48 | + | |
49 | +# the table name | |
50 | +table = mxaliases | |
51 | + | |
52 | +# | |
53 | +select_field = forw_addr | |
54 | +where_field = alias | |
55 | + | |
56 | +# you may specify additional_conditions here | |
57 | +additional_conditions = and status = 'paid' | |
58 | + | |
59 | +# the above variables will result in a query of | |
60 | +# the form: | |
61 | +# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid' | |
62 | +# ($lookup is escaped so if it contains single quotes or other odd | |
63 | +# characters, it will not cause a parse error in the sql). | |
64 | +# | |
65 | +# the hosts that postfix will try to connect to | |
66 | +# and query from (in the order listed) | |
67 | +hosts = host1.some.domain host2.some.domain | |
68 | + | |
69 | +# end postgresql config file | |
70 | + | |
71 | +Some notes: | |
72 | + | |
73 | +This configuration interface setup allows for multiple postgresql | |
74 | +databases: you can use one for a virtual table, one for an access | |
75 | +table, and one for an aliases table if you want. | |
76 | + | |
77 | +Since sites that have a need for multiple mail exchangers may enjoy | |
78 | +the convenience of using a networked mailer database, but do not want | |
79 | +to introduce a single point of failure to their system, we've included | |
80 | +the ability to have postfix reference multiple hosts for access to a | |
81 | +single pgsql map. This will work if sites set up mirrored pgsql | |
82 | +databases on two or more hosts. Whenever queries fail with an error | |
83 | +at one host, the rest of the hosts will be tried in order. Each host | |
84 | +that is in an error state will undergo a reconnection attempt every so | |
85 | +often, and if no pgsql server hosts are reachable, then mail will be | |
86 | +deferred until atleast one of those hosts is reachable. | |
87 | + | |
88 | +Performance of postfix with pgsql has not been thoroughly tested, | |
89 | +however, we have found it to be stable. Busy mail servers using pgsql | |
90 | +maps will generate lots of concurrent pgsql clients, so the pgsql | |
91 | +server(s) should be run with this fact in mind. Any further | |
92 | +performance information, in addition to any feedback is most welcome. | |
93 | diff -rU3 --new-file postfix-1.1.3-vanilla/src/util/Makefile.in postfix-1.1.3/src/util/Makefile.in | |
94 | --- postfix-1.1.3-vanilla/src/util/Makefile.in Thu Mar 7 11:47:44 2002 | |
95 | +++ postfix-1.1.3/src/util/Makefile.in Thu Mar 7 12:10:56 2002 | |
96 | @@ -4,7 +4,7 @@ | |
97 | chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \ | |
98 | dict.c dict_alloc.c dict_db.c dict_dbm.c dict_debug.c dict_env.c \ | |
99 | dict_ht.c dict_mysql.c dict_ni.c dict_nis.c \ | |
100 | - dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c dict_static.c \ | |
101 | + dict_nisplus.c dict_open.c dict_pcre.c dict_pgsql.c dict_regexp.c dict_static.c \ | |
102 | dict_tcp.c dict_unix.c dir_forest.c doze.c duplex_pipe.c \ | |
103 | environ.c events.c exec_command.c fifo_listen.c fifo_trigger.c \ | |
104 | file_limit.c find_inet.c fsspace.c fullname.c get_domainname.c \ | |
105 | @@ -58,7 +58,7 @@ | |
106 | HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \ | |
107 | connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \ | |
108 | dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \ | |
109 | - dict_nisplus.h dict_pcre.h dict_regexp.h dict_static.h dict_tcp.h \ | |
110 | + dict_nisplus.h dict_pcre.h dict_pgsql.h dict_regexp.h dict_static.h dict_tcp.h \ | |
111 | dict_unix.h dir_forest.h events.h exec_command.h find_inet.h \ | |
112 | fsspace.h fullname.h get_domainname.h get_hostname.h get_port.h hex_quote.h \ | |
113 | htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \ | |
114 | @@ -84,6 +84,7 @@ | |
115 | INCL = | |
116 | PCRESO = dict_pcre.so | |
117 | MYSQLSO = dict_mysql.so | |
118 | +PGSQLSO = dict_pgsql.so | |
119 | LIB = libutil.a | |
120 | TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ | |
121 | fifo_rdonly_bug fifo_rdwr_bug fifo_trigger fsspace fullname \ | |
122 | @@ -96,7 +97,7 @@ | |
123 | ||
124 | LIB_DIR = ../../lib | |
125 | INC_DIR = ../../include | |
126 | -LIBS = $(LIB_DIR)/$(LIB) $(LIB_DIR)/$(PCRESO) $(LIB_DIR)/$(MYSQLSO) | |
127 | +LIBS = $(LIB_DIR)/$(LIB) $(LIB_DIR)/$(PCRESO) $(LIB_DIR)/$(MYSQLSO) $(LIB_DIR)/$(PGSQLSO) | |
128 | ||
129 | .c.o:; $(CC) -fPIC $(CFLAGS) -c $*.c | |
130 | ||
131 | @@ -113,6 +114,9 @@ | |
132 | $(MYSQLSO): dict_mysql.o | |
133 | gcc -shared -Wl,-soname,dict_mysql.so -o $@ $? -lmysqlclient -L. -lutil | |
134 | ||
135 | +$(PGSQLSO): dict_pgsql.o | |
136 | + gcc -shared -Wl,-soname,dict_pgsql.so -o $@ $? -lpq -lcrypt -L. -lutil | |
137 | + | |
138 | $(LIB): $(OBJS) | |
139 | gcc -shared -Wl,-soname,libpostfix-util.so.1 -o $(LIB) $(OBJS) -ldl | |
140 | ||
141 | @@ -125,6 +129,9 @@ | |
142 | $(LIB_DIR)/$(MYSQLSO): $(MYSQLSO) | |
143 | cp $(MYSQLSO) $(LIB_DIR) | |
144 | ||
145 | +$(LIB_DIR)/$(PGSQLSO): $(PGSQLSO) | |
146 | + cp $(PGSQLSO) $(LIB_DIR) | |
147 | + | |
148 | update: $(LIBS) $(HDRS) | |
149 | -for i in $(HDRS); \ | |
150 | do \ | |
151 | @@ -147,7 +154,7 @@ | |
152 | lint $(SRCS) | |
153 | ||
154 | clean: | |
155 | - rm -f *.o $(LIB) $(PCRESO) $(MYSQLSO) *core $(TESTPROG) \ | |
156 | + rm -f *.o $(LIB) $(PCRESO) $(MYSQLSO) $(PGSQLSO) *core $(TESTPROG) \ | |
157 | junk $(MAKES) *.tmp | |
158 | rm -rf printfck | |
159 | ||
160 | @@ -571,6 +578,8 @@ | |
161 | dict_ldap.o: sys_defs.h | |
162 | dict_mysql.o: dict_mysql.c | |
163 | dict_mysql.o: sys_defs.h | |
164 | +dict_pgsql.o: dict_pgsql.c | |
165 | +dict_pgsql.o: sys_defs.h | |
166 | dict_ni.o: dict_ni.c | |
167 | dict_ni.o: sys_defs.h | |
168 | dict_nis.o: dict_nis.c | |
169 | @@ -612,6 +621,7 @@ | |
170 | dict_open.o: dict_ni.h | |
171 | dict_open.o: dict_ldap.h | |
172 | dict_open.o: dict_mysql.h | |
173 | +dict_open.o: dict_pgsql.h | |
174 | dict_open.o: dict_pcre.h | |
175 | dict_open.o: dict_regexp.h | |
176 | dict_open.o: dict_static.h | |
177 | diff -rU3 --new-file postfix-1.1.3-vanilla/src/util/dict_open.c postfix-1.1.3/src/util/dict_open.c | |
178 | --- postfix-1.1.3-vanilla/src/util/dict_open.c Thu Mar 7 11:47:44 2002 | |
179 | +++ postfix-1.1.3/src/util/dict_open.c Thu Mar 7 11:48:40 2002 | |
180 | @@ -170,6 +170,7 @@ | |
181 | #include <dict_ni.h> | |
182 | #include <dict_ldap.h> | |
183 | #include <dict_mysql.h> | |
184 | +#include <dict_pgsql.h> | |
185 | #include <dict_pcre.h> | |
186 | #include <dict_regexp.h> | |
187 | #include <dict_static.h> | |
188 | @@ -223,6 +224,9 @@ | |
189 | #ifdef HAS_MYSQL | |
190 | DICT_TYPE_MYSQL, dict_mysql_open, | |
191 | #endif | |
192 | +#ifdef HAS_PGSQL | |
193 | + DICT_TYPE_PGSQL, dict_pgsql_open, | |
194 | +#endif | |
195 | #ifdef HAS_PCRE | |
196 | DICT_TYPE_PCRE, dict_pcre_open, | |
197 | #endif | |
198 | diff -rU3 --new-file postfix-1.1.3-vanilla/src/util/dict_pgsql.c postfix-1.1.3/src/util/dict_pgsql.c | |
199 | --- postfix-1.1.3-vanilla/src/util/dict_pgsql.c Thu Jan 1 01:00:00 1970 | |
200 | +++ postfix-1.1.3/src/util/dict_pgsql.c Thu Mar 7 11:48:41 2002 | |
201 | @@ -0,0 +1,675 @@ | |
202 | + | |
203 | +/*++ | |
204 | +/* NAME | |
205 | +/* dict_pgsql 3 | |
206 | +/* SUMMARY | |
207 | +/* dictionary manager interface to db files | |
208 | +/* SYNOPSIS | |
209 | +/* #include <dict.h> | |
210 | +/* #include <dict_pgsql.h> | |
211 | +/* | |
212 | +/* DICT *dict_pgsql_open(name, dummy, unused_dict_flags) | |
213 | +/* const char *name; | |
214 | +/* int dummy; | |
215 | +/* int unused_dict_flags; | |
216 | +/* DESCRIPTION | |
217 | +/* dict_pgsql_open() creates a dictionary of type 'pg'. This | |
218 | +/* dictionary is an interface for the postfix key->value mappings | |
219 | +/* to pgsql. The result is a pointer to the installed dictionary, | |
220 | +/* or a null pointer in case of problems. | |
221 | +/* | |
222 | +/* The pgsql dictionary can manage multiple connections to different | |
223 | +/* sql servers on different hosts. It assumes that the underlying data | |
224 | +/* on each host is identical (mirrored) and maintains one connection | |
225 | +/* at any given time. If any connection fails, any other available | |
226 | +/* ones will be opened and used. The intent of this feature is to eliminate | |
227 | +/* a single point of failure for mail systems that would otherwise rely | |
228 | +/* on a single pgsql server. | |
229 | +/* | |
230 | +/* Arguments: | |
231 | +/* .IP name | |
232 | +/* The path of the PostgreSQL configuration file. The file encodes a number of | |
233 | +/* pieces of information: username, password, databasename, table, | |
234 | +/* select_field, where_field, and hosts. For example, if you want the map to | |
235 | +/* reference databases of the name "your_db" and execute a query like this: | |
236 | +/* select forw_addr from aliases where alias like '<some username>' against | |
237 | +/* any database called "vmailer_info" located on hosts host1.some.domain and | |
238 | +/* host2.some.domain, logging in as user "vmailer" and password "passwd" then | |
239 | +/* the configuration file should read: | |
240 | +/* | |
241 | +/* user = vmailer | |
242 | +/* password = passwd | |
243 | +/* DBname = vmailer_info | |
244 | +/* table = aliases | |
245 | +/* select_field = forw_addr | |
246 | +/* where_field = alias | |
247 | +/* hosts = host1.some.domain host2.some.domain | |
248 | +/* | |
249 | +/* .IP other_name | |
250 | +/* reference for outside use. | |
251 | +/* .IP unusued_flags | |
252 | +/* unused flags | |
253 | +/* SEE ALSO | |
254 | +/* dict(3) generic dictionary manager | |
255 | +/* AUTHOR(S) | |
256 | +/* Mathieu Arnold | |
257 | +/* Absolight | |
258 | +/* mat@absolight.com | |
259 | +/* | |
260 | +/* based on dict_mysql by | |
261 | +/* | |
262 | +/* Scott Cotton | |
263 | +/* IC Group, Inc. | |
264 | +/* scott@icgroup.com | |
265 | +/* | |
266 | +/* Joshua Marcus | |
267 | +/* IC Group, Inc. | |
268 | +/* josh@icgroup.com | |
269 | +/*--*/ | |
270 | + | |
271 | +/* System library. */ | |
272 | +#include "sys_defs.h" | |
273 | + | |
274 | +#ifdef HAS_PGSQL | |
275 | +#include <sys/socket.h> | |
276 | +#include <netinet/in.h> | |
277 | +#include <arpa/inet.h> | |
278 | +#include <netdb.h> | |
279 | +#include <stdio.h> | |
280 | +#include <string.h> | |
281 | +#include <stdlib.h> | |
282 | +#include <syslog.h> | |
283 | +#include <time.h> | |
284 | +#include <libpq-fe.h> | |
285 | + | |
286 | +/* Utility library. */ | |
287 | +#include "dict.h" | |
288 | +#include "msg.h" | |
289 | +#include "mymalloc.h" | |
290 | +#include "dict_pgsql.h" | |
291 | +#include "argv.h" | |
292 | +#include "vstring.h" | |
293 | +#include "split_at.h" | |
294 | +#include "find_inet.h" | |
295 | + | |
296 | +/* need some structs to help organize things */ | |
297 | +typedef struct | |
298 | +{ | |
299 | + PGconn *db; | |
300 | + char *hostname; | |
301 | + int stat; /* STATUNTRIED | STATFAIL | STATCUR */ | |
302 | + time_t ts; /* used for attempting reconnection | |
303 | + * every so often if a host is down */ | |
304 | +} HOST; | |
305 | + | |
306 | +typedef struct | |
307 | +{ | |
308 | + int len_hosts; /* number of hosts */ | |
309 | + HOST *db_hosts; /* the hosts on which the databases | |
310 | + * reside */ | |
311 | +} PLPGSQL; | |
312 | + | |
313 | +typedef struct | |
314 | +{ | |
315 | + char *username; | |
316 | + char *password; | |
317 | + char *dbname; | |
318 | + char *table; | |
319 | + char *select_field; | |
320 | + char *where_field; | |
321 | + char *additional_conditions; | |
322 | + char **hostnames; | |
323 | + int len_hosts; | |
324 | +} PGSQL_NAME; | |
325 | + | |
326 | +typedef struct | |
327 | +{ | |
328 | + DICT dict; | |
329 | + PLPGSQL *pldb; | |
330 | + PGSQL_NAME *name; | |
331 | +} DICT_PGSQL; | |
332 | + | |
333 | +#define STATACTIVE 0 | |
334 | +#define STATFAIL 1 | |
335 | +#define STATUNTRIED 2 | |
336 | +#define RETRY_CONN_INTV 60 /* 1 minute */ | |
337 | + | |
338 | +/* internal function declarations */ | |
339 | +static PLPGSQL *plpgsql_init (char *hostnames[], int); | |
340 | +static PGresult *plpgsql_query (PLPGSQL *, const char *, char *, char *, | |
341 | + | |
342 | + char *); | |
343 | +static void plpgsql_dealloc (PLPGSQL *); | |
344 | +static void plpgsql_down_host (HOST *); | |
345 | +static void plpgsql_connect_single (HOST *, char *, char *, char *); | |
346 | +static int plpgsql_ready_reconn (HOST *); | |
347 | +static const char *dict_pgsql_lookup (DICT *, const char *); | |
348 | +DICT *dict_pgsql_open (const char *, int, int); | |
349 | +static void dict_pgsql_close (DICT *); | |
350 | +static PGSQL_NAME *pgsqlname_parse (const char *); | |
351 | +static HOST host_init (char *); | |
352 | +void pgsql_escape_string (char *escaped, const char *name, int len); | |
353 | + | |
354 | + | |
355 | + | |
356 | +/********************************************************************** | |
357 | + * public interface dict_pgsql_lookup | |
358 | + * find database entry return 0 if no alias found, set dict_errno | |
359 | + * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success | |
360 | + *********************************************************************/ | |
361 | +static const char *dict_pgsql_lookup (DICT *dict, const char *name) | |
362 | +{ | |
363 | + PGresult *query_res; | |
364 | + char *field; | |
365 | + DICT_PGSQL *dict_pgsql; | |
366 | + PLPGSQL *pldb; | |
367 | + static VSTRING *result; | |
368 | + static VSTRING *query = 0; | |
369 | + int i, | |
370 | + j, | |
371 | + numrows; | |
372 | + char *name_escaped = 0; | |
373 | + | |
374 | + dict_pgsql = (DICT_PGSQL *) dict; | |
375 | + pldb = dict_pgsql->pldb; | |
376 | + /* initialization for query */ | |
377 | + query = vstring_alloc (24); | |
378 | + vstring_strcpy (query, ""); | |
379 | + if ( | |
380 | + (name_escaped = | |
381 | + (char *) mymalloc ((sizeof (char) * (strlen (name) * 2) + 1))) == | |
382 | + NULL) | |
383 | + { | |
384 | + msg_fatal ("dict_pgsql_lookup: out of memory."); | |
385 | + } | |
386 | + /* prepare the query */ | |
387 | + pgsql_escape_string (name_escaped, name, (unsigned int) strlen (name)); | |
388 | + vstring_sprintf (query, "select %s from %s where %s = '%s' %s", | |
389 | + dict_pgsql->name->select_field, dict_pgsql->name->table, | |
390 | + dict_pgsql->name->where_field, name_escaped, | |
391 | + dict_pgsql->name->additional_conditions); | |
392 | + if (msg_verbose) | |
393 | + msg_info ("dict_pgsql_lookup using sql query: %s", | |
394 | + vstring_str (query)); | |
395 | + /* free mem associated with preparing the query */ | |
396 | + myfree (name_escaped); | |
397 | + /* do the query - set dict_errno & cleanup if there's an error */ | |
398 | + if ((query_res = plpgsql_query (pldb, | |
399 | + vstring_str (query), | |
400 | + dict_pgsql->name->dbname, | |
401 | + dict_pgsql->name->username, | |
402 | + dict_pgsql->name->password)) == 0) | |
403 | + { | |
404 | + dict_errno = DICT_ERR_RETRY; | |
405 | + vstring_free (query); | |
406 | + return 0; | |
407 | + } | |
408 | + dict_errno = 0; | |
409 | + /* free the vstring query */ | |
410 | + vstring_free (query); | |
411 | + numrows = PQntuples (query_res); | |
412 | + if (msg_verbose) | |
413 | + msg_info ("dict_pgsql_lookup: retrieved %d rows", numrows); | |
414 | + if (numrows == 0) | |
415 | + { | |
416 | + PQclear (query_res); | |
417 | + return 0; | |
418 | + } | |
419 | + if (result == 0) | |
420 | + result = vstring_alloc (10); | |
421 | + vstring_strcpy (result, ""); | |
422 | + for (i = 0; i < numrows; i++) | |
423 | + { | |
424 | + if (i > 0) | |
425 | + vstring_strcat (result, ","); | |
426 | + for (j = 0; j < PQnfields (query_res); j++) | |
427 | + { | |
428 | + if (j > 0) | |
429 | + vstring_strcat (result, ","); | |
430 | + field = PQgetvalue (query_res, i, j); | |
431 | + vstring_strcat (result, field); | |
432 | + if (msg_verbose > 1) | |
433 | + msg_info ("dict_pgsql_lookup: retrieved field: %d: %s", j, | |
434 | + field); | |
435 | + } | |
436 | + } | |
437 | + PQclear (query_res); | |
438 | + return vstring_str (result); | |
439 | +} | |
440 | + | |
441 | +/* | |
442 | + * plpgsql_query - process a PGSQL query. Return PGresult* on success. | |
443 | + * On failure, log failure and try other db instances. | |
444 | + * on failure of all db instances, return 0; | |
445 | + * close unnecessary active connections | |
446 | + */ | |
447 | + | |
448 | +static PGresult *plpgsql_query (PLPGSQL * PLDB, | |
449 | + const char *query, | |
450 | + char *dbname, char *username, char *password) | |
451 | +{ | |
452 | + int i; | |
453 | + HOST *host; | |
454 | + PGresult *res = 0; | |
455 | + ExecStatusType status; | |
456 | + | |
457 | + for (i = 0; i < PLDB->len_hosts; i++) | |
458 | + { | |
459 | + /* can't deal with typing or reading PLDB->db_hosts[i] over & over */ | |
460 | + host = &(PLDB->db_hosts[i]); | |
461 | + if (msg_verbose > 1) | |
462 | + msg_info ("dict_pgsql: trying host %s stat %d, last res %p", | |
463 | + host->hostname, host->stat, res); | |
464 | + | |
465 | + /* answer already found */ | |
466 | + if (res != 0 && host->stat == STATACTIVE) | |
467 | + { | |
468 | + if (msg_verbose) | |
469 | + msg_info | |
470 | + ("dict_pgsql: closing unnessary connection to %s", | |
471 | + host->hostname); | |
472 | + plpgsql_down_host (host); | |
473 | + } | |
474 | + /* try to connect for the first time if we don't have a result yet */ | |
475 | + if (res == 0 && host->stat == STATUNTRIED) | |
476 | + { | |
477 | + if (msg_verbose) | |
478 | + msg_info ("dict_pgsql: attempting to connect to host %s", | |
479 | + host->hostname); | |
480 | + plpgsql_connect_single (host, dbname, username, password); | |
481 | + } | |
482 | + | |
483 | + /* | |
484 | + * try to reconnect if we don't have an answer and the host had a | |
485 | + * prob in the past and it's time for it to reconnect | |
486 | + */ | |
487 | + if (res == 0 && host->stat == STATFAIL | |
488 | + && host->ts < time ((time_t *) 0)) | |
489 | + { | |
490 | + if (msg_verbose) | |
491 | + msg_info | |
492 | + ("dict_pgsql: attempting to reconnect to host %s", | |
493 | + host->hostname); | |
494 | + plpgsql_connect_single (host, dbname, username, password); | |
495 | + } | |
496 | + | |
497 | + /* | |
498 | + * if we don't have a result and the current host is marked active, | |
499 | + * try the query. If the query fails, mark the host STATFAIL | |
500 | + */ | |
501 | + if (res == 0 && host->stat == STATACTIVE) | |
502 | + { | |
503 | + res = PQexec (host->db, query); | |
504 | + status = PQresultStatus (res); | |
505 | + if (res | |
506 | + && (status = PGRES_COMMAND_OK | |
507 | + || status == PGRES_TUPLES_OK)) | |
508 | + { | |
509 | + if (msg_verbose) | |
510 | + msg_info | |
511 | + ("dict_pgsql: successful query from host %s", | |
512 | + host->hostname); | |
513 | + } | |
514 | + else | |
515 | + { | |
516 | + msg_warn ("%s", PQerrorMessage (host->db)); | |
517 | + plpgsql_down_host (host); | |
518 | + } | |
519 | + } | |
520 | + } | |
521 | + return res; | |
522 | +} | |
523 | + | |
524 | +/* | |
525 | + * plpgsql_connect_single - | |
526 | + * used to reconnect to a single database when one is down or none is | |
527 | + * connected yet. Log all errors and set the stat field of host accordingly | |
528 | + */ | |
529 | +static void | |
530 | +plpgsql_connect_single (HOST *host, char *dbname, char *username, | |
531 | + char *password) | |
532 | +{ | |
533 | + char *destination = host->hostname; | |
534 | + char *hostname = 0; | |
535 | + char *service; | |
536 | + VSTRING *conninfo = vstring_alloc (100); | |
537 | + char *conn; | |
538 | + unsigned port = 0; | |
539 | + | |
540 | + /* | |
541 | + * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where | |
542 | + * both "inet:" and ":port" are optional. | |
543 | + */ | |
544 | + if (strncmp (destination, "unix:", 5) == 0) | |
545 | + { | |
546 | + vstring_sprintf_append (conninfo, "host=%s ", destination + 5); | |
547 | + } | |
548 | + else | |
549 | + { | |
550 | + if (strncmp (destination, "inet:", 5) == 0) | |
551 | + destination += 5; | |
552 | + hostname = mystrdup (destination); | |
553 | + if ((service = split_at (hostname, ':')) != 0) | |
554 | + { | |
555 | + port = ntohs (find_inet_port (service, "tcp")); | |
556 | + vstring_sprintf_append (conninfo, "host='%s' port='%d'", | |
557 | + hostname, port); | |
558 | + } | |
559 | + else | |
560 | + { | |
561 | + vstring_sprintf_append (conninfo, "host='%s'", hostname); | |
562 | + } | |
563 | + } | |
564 | + | |
565 | + vstring_sprintf_append (conninfo, " dbname='%s' user='%s' password='%s'", dbname, | |
566 | + username, password); | |
567 | + conn = vstring_export (conninfo); | |
568 | + | |
569 | + host->db = PQconnectdb (conn); | |
570 | + | |
571 | + if ((host->db != NULL) && (PQstatus (host->db) == CONNECTION_OK)) | |
572 | + { | |
573 | + if (msg_verbose) | |
574 | + msg_info ("dict_pgsql: successful connection to host %s", | |
575 | + host->hostname); | |
576 | + host->stat = STATACTIVE; | |
577 | + } | |
578 | + else | |
579 | + { | |
580 | + msg_warn ("%s", PQerrorMessage (host->db)); | |
581 | + plpgsql_down_host (host); | |
582 | + } | |
583 | + if (hostname) | |
584 | + myfree (hostname); | |
585 | + myfree (conn); | |
586 | +} | |
587 | + | |
588 | +/* | |
589 | + * plpgsql_down_host - mark a HOST down update ts if marked down | |
590 | + * for the first time so that we'll know when to retry the connection | |
591 | + */ | |
592 | +static void plpgsql_down_host (HOST *host) | |
593 | +{ | |
594 | + if (host->stat != STATFAIL) | |
595 | + { | |
596 | + host->ts = time ((time_t *) 0) + RETRY_CONN_INTV; | |
597 | + host->stat = STATFAIL; | |
598 | + } | |
599 | + PQfinish (host->db); | |
600 | + host->db = 0; | |
601 | +} | |
602 | + | |
603 | +/********************************************************************** | |
604 | + * public interface dict_pgsql_open | |
605 | + * create association with database with appropriate values | |
606 | + * parse the map's config file | |
607 | + * allocate memory | |
608 | + **********************************************************************/ | |
609 | +DICT *dict_pgsql_open (const char *name, int unused_open_flags, | |
610 | + int dict_flags) | |
611 | +{ | |
612 | + DICT_PGSQL *dict_pgsql; | |
613 | + int connections; | |
614 | + | |
615 | + dict_pgsql = (DICT_PGSQL *) dict_alloc (DICT_TYPE_PGSQL, name, | |
616 | + sizeof (DICT_PGSQL)); | |
617 | + dict_pgsql->dict.lookup = dict_pgsql_lookup; | |
618 | + dict_pgsql->dict.close = dict_pgsql_close; | |
619 | + dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED; | |
620 | + dict_pgsql->name = pgsqlname_parse (name); | |
621 | + dict_pgsql->pldb = plpgsql_init (dict_pgsql->name->hostnames, | |
622 | + dict_pgsql->name->len_hosts); | |
623 | + if (dict_pgsql->pldb == NULL) | |
624 | + msg_fatal ("couldn't intialize pldb!\n"); | |
625 | + dict_register (name, (DICT *) dict_pgsql); | |
626 | + return (DICT_DEBUG (&dict_pgsql->dict)); | |
627 | +} | |
628 | + | |
629 | +/* pgsqlname_parse - parse pgsql configuration file */ | |
630 | +static PGSQL_NAME *pgsqlname_parse (const char *pgsqlcf_path) | |
631 | +{ | |
632 | + int i; | |
633 | + char *nameval; | |
634 | + char *hosts; | |
635 | + PGSQL_NAME *name = (PGSQL_NAME *) mymalloc (sizeof (PGSQL_NAME)); | |
636 | + ARGV *hosts_argv; | |
637 | + VSTRING *opt_dict_name; | |
638 | + | |
639 | + /* | |
640 | + * setup a dict containing info in the pgsql cf file. the dict has a | |
641 | + * name, and a path. The name must be distinct from the path, or the | |
642 | + * dict interface gets confused. The name must be distinct for two | |
643 | + * different paths, or the configuration info will cache across different | |
644 | + * pgsql maps, which can be confusing. | |
645 | + */ | |
646 | + opt_dict_name = vstring_alloc (64); | |
647 | + vstring_sprintf (opt_dict_name, "pgsql opt dict %s", pgsqlcf_path); | |
648 | + dict_load_file (vstring_str (opt_dict_name), pgsqlcf_path); | |
649 | + /* pgsql username lookup */ | |
650 | + if ( | |
651 | + (nameval = | |
652 | + (char *) dict_lookup (vstring_str (opt_dict_name), "user")) == NULL) | |
653 | + name->username = mystrdup (""); | |
654 | + else | |
655 | + name->username = mystrdup (nameval); | |
656 | + if (msg_verbose) | |
657 | + msg_info ("pgsqlname_parse(): set username to '%s'", name->username); | |
658 | + /* password lookup */ | |
659 | + if ( | |
660 | + (nameval = | |
661 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
662 | + "password")) == NULL) | |
663 | + name->password = mystrdup (""); | |
664 | + else | |
665 | + name->password = mystrdup (nameval); | |
666 | + if (msg_verbose) | |
667 | + msg_info ("pgsqlname_parse(): set password to '%s'", name->password); | |
668 | + | |
669 | + /* database name lookup */ | |
670 | + if ( | |
671 | + (nameval = | |
672 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
673 | + "dbname")) == NULL) | |
674 | + | |
675 | + | |
676 | + | |
677 | + msg_fatal ("%s: pgsql options file does not include database name", | |
678 | + pgsqlcf_path); | |
679 | + else | |
680 | + name->dbname = mystrdup (nameval); | |
681 | + if (msg_verbose) | |
682 | + msg_info ("pgsqlname_parse(): set database name to '%s'", | |
683 | + name->dbname); | |
684 | + | |
685 | + /* table lookup */ | |
686 | + if ( | |
687 | + (nameval = | |
688 | + (char *) dict_lookup (vstring_str (opt_dict_name), "table")) == NULL) | |
689 | + msg_fatal ("%s: pgsql options file does not include table name", | |
690 | + pgsqlcf_path); | |
691 | + else | |
692 | + name->table = mystrdup (nameval); | |
693 | + if (msg_verbose) | |
694 | + msg_info ("pgsqlname_parse(): set table name to '%s'", name->table); | |
695 | + | |
696 | + /* select field lookup */ | |
697 | + if ( | |
698 | + (nameval = | |
699 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
700 | + "select_field")) == NULL) | |
701 | + | |
702 | + | |
703 | + | |
704 | + msg_fatal ("%s: pgsql options file does not include select field", | |
705 | + pgsqlcf_path); | |
706 | + else | |
707 | + name->select_field = mystrdup (nameval); | |
708 | + if (msg_verbose) | |
709 | + msg_info ("pgsqlname_parse(): set select_field to '%s'", | |
710 | + name->select_field); | |
711 | + | |
712 | + /* where field lookup */ | |
713 | + if ( | |
714 | + (nameval = | |
715 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
716 | + "where_field")) == NULL) | |
717 | + msg_fatal ("%s: pgsql options file does not include where field", | |
718 | + pgsqlcf_path); | |
719 | + else | |
720 | + name->where_field = mystrdup (nameval); | |
721 | + if (msg_verbose) | |
722 | + msg_info ("pgsqlname_parse(): set where_field to '%s'", | |
723 | + name->where_field); | |
724 | + | |
725 | + /* additional conditions */ | |
726 | + if ( | |
727 | + (nameval = | |
728 | + (char *) dict_lookup (vstring_str (opt_dict_name), | |
729 | + "additional_conditions")) == NULL) | |
730 | + name->additional_conditions = mystrdup (""); | |
731 | + else | |
732 | + name->additional_conditions = mystrdup (nameval); | |
733 | + if (msg_verbose) | |
734 | + msg_info ("pgsqlname_parse(): set additional_conditions to '%s'", | |
735 | + name->additional_conditions); | |
736 | + | |
737 | + /* pgsql server hosts */ | |
738 | + if ( | |
739 | + (nameval = | |
740 | + (char *) dict_lookup (vstring_str (opt_dict_name), "hosts")) == NULL) | |
741 | + hosts = mystrdup (""); | |
742 | + else | |
743 | + hosts = mystrdup (nameval); | |
744 | + /* coo argv interface */ | |
745 | + hosts_argv = argv_split (hosts, " ,\t\r\n"); | |
746 | + | |
747 | + if (hosts_argv->argc == 0) | |
748 | + { /* no hosts specified, | |
749 | + * default to 'localhost' */ | |
750 | + if (msg_verbose) | |
751 | + msg_info | |
752 | + ("pgsqlname_parse(): no hostnames specified, defaulting to 'localhost'"); | |
753 | + argv_add (hosts_argv, "localhost", ARGV_END); | |
754 | + argv_terminate (hosts_argv); | |
755 | + } | |
756 | + name->len_hosts = hosts_argv->argc; | |
757 | + name->hostnames = | |
758 | + | |
759 | + (char **) mymalloc ((sizeof (char *)) * name->len_hosts); | |
760 | + i = 0; | |
761 | + for (i = 0; hosts_argv->argv[i] != NULL; i++) | |
762 | + { | |
763 | + name->hostnames[i] = mystrdup (hosts_argv->argv[i]); | |
764 | + if (msg_verbose) | |
765 | + msg_info | |
766 | + ("pgsqlname_parse(): adding host '%s' to list of pgsql server hosts", | |
767 | + name->hostnames[i]); | |
768 | + } | |
769 | + myfree (hosts); | |
770 | + vstring_free (opt_dict_name); | |
771 | + argv_free (hosts_argv); | |
772 | + return name; | |
773 | +} | |
774 | + | |
775 | + | |
776 | +/* | |
777 | + * plpgsql_init - initalize a PGSQL database. | |
778 | + * Return NULL on failure, or a PLPGSQL * on success. | |
779 | + */ | |
780 | +static PLPGSQL *plpgsql_init (char *hostnames[], int len_hosts) | |
781 | +{ | |
782 | + PLPGSQL *PLDB; | |
783 | + int i; | |
784 | + HOST host; | |
785 | + | |
786 | + if ((PLDB = (PLPGSQL *) mymalloc (sizeof (PLPGSQL))) == NULL) | |
787 | + { | |
788 | + msg_fatal ("mymalloc of pldb failed"); | |
789 | + } | |
790 | + PLDB->len_hosts = len_hosts; | |
791 | + if ((PLDB->db_hosts = (HOST *) mymalloc (sizeof (HOST) * len_hosts)) | |
792 | + == NULL) | |
793 | + return NULL; | |
794 | + | |
795 | + for (i = 0; i < len_hosts; i++) | |
796 | + { | |
797 | + PLDB->db_hosts[i] = host_init (hostnames[i]); | |
798 | + } | |
799 | + return PLDB; | |
800 | +} | |
801 | + | |
802 | + | |
803 | +/* host_init - initialize HOST structure */ | |
804 | +static HOST host_init (char *hostname) | |
805 | +{ | |
806 | + HOST host; | |
807 | + | |
808 | + host.stat = STATUNTRIED; | |
809 | + host.hostname = mystrdup (hostname); | |
810 | + host.db = 0; | |
811 | + host.ts = 0; | |
812 | + return host; | |
813 | +} | |
814 | + | |
815 | +/********************************************************************** | |
816 | + * public interface dict_pgsql_close | |
817 | + * unregister, disassociate from database, freeing appropriate memory | |
818 | + **********************************************************************/ | |
819 | +static void dict_pgsql_close (DICT *dict) | |
820 | +{ | |
821 | + int i; | |
822 | + DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict; | |
823 | + | |
824 | + plpgsql_dealloc (dict_pgsql->pldb); | |
825 | + myfree (dict_pgsql->name->username); | |
826 | + myfree (dict_pgsql->name->password); | |
827 | + myfree (dict_pgsql->name->dbname); | |
828 | + myfree (dict_pgsql->name->table); | |
829 | + myfree (dict_pgsql->name->select_field); | |
830 | + myfree (dict_pgsql->name->where_field); | |
831 | + myfree (dict_pgsql->name->additional_conditions); | |
832 | + for (i = 0; i < dict_pgsql->name->len_hosts; i++) | |
833 | + { | |
834 | + myfree (dict_pgsql->name->hostnames[i]); | |
835 | + } | |
836 | + myfree ((char *) dict_pgsql->name->hostnames); | |
837 | + myfree ((char *) dict_pgsql->name); | |
838 | + dict_free (dict); | |
839 | +} | |
840 | + | |
841 | +/* plpgsql_dealloc - free memory associated with PLPGSQL close databases */ | |
842 | +static void plpgsql_dealloc (PLPGSQL * PLDB) | |
843 | +{ | |
844 | + int i; | |
845 | + | |
846 | + for (i = 0; i < PLDB->len_hosts; i++) | |
847 | + { | |
848 | + if (PLDB->db_hosts[i].db) | |
849 | + PQfinish (PLDB->db_hosts[i].db); | |
850 | + myfree (PLDB->db_hosts[i].hostname); | |
851 | + } | |
852 | + myfree ((char *) PLDB->db_hosts); | |
853 | + myfree ((char *) (PLDB)); | |
854 | +} | |
855 | + | |
856 | +/* pgsql_escape_string - replace mysql_escape_string */ | |
857 | +void pgsql_escape_string (char *escaped, const char *name, int len) | |
858 | +{ | |
859 | + int i, | |
860 | + j; | |
861 | + | |
862 | + for (i = 0, j = 0; i <= len; i++, j++) | |
863 | + { | |
864 | + if ((name[i] == '\'') || (name[i] == '\\')) | |
865 | + { | |
866 | + escaped[j] = '\\'; | |
867 | + j++; | |
868 | + } | |
869 | + escaped[j] = name[i]; | |
870 | + } | |
871 | +} | |
872 | + | |
873 | + | |
874 | + | |
875 | +#endif | |
876 | + | |
877 | diff -rU3 --new-file postfix-1.1.3-vanilla/src/util/dict_pgsql.h postfix-1.1.3/src/util/dict_pgsql.h | |
878 | --- postfix-1.1.3-vanilla/src/util/dict_pgsql.h Thu Jan 1 01:00:00 1970 | |
879 | +++ postfix-1.1.3/src/util/dict_pgsql.h Thu Mar 7 11:48:41 2002 | |
880 | @@ -0,0 +1,41 @@ | |
881 | +#ifndef _DICT_PGSQL_H_INCLUDED_ | |
882 | +#define _DICT_PGSQL_H_INCLUDED_ | |
883 | + | |
884 | +/*++ | |
885 | +/* NAME | |
886 | +/* dict_pgsql 3h | |
887 | +/* SUMMARY | |
888 | +/* dictionary manager interface to pgsql databases | |
889 | +/* SYNOPSIS | |
890 | +/* #include <dict_pgsql.h> | |
891 | +/* DESCRIPTION | |
892 | +/* .nf | |
893 | + | |
894 | + /* | |
895 | + * Utility library. | |
896 | + */ | |
897 | +#include <dict.h> | |
898 | + | |
899 | + /* | |
900 | + * External interface. | |
901 | + */ | |
902 | +#define DICT_TYPE_PGSQL "pgsql" | |
903 | + | |
904 | +extern DICT *dict_pgsql_open (const char *, int, int); | |
905 | + | |
906 | +/* LICENSE | |
907 | +/* .ad | |
908 | +/* .fi | |
909 | +/* The Secure Mailer license must be distributed with this software. | |
910 | +/* AUTHOR(S) | |
911 | +/* Scott Cotton | |
912 | +/* IC Group, Inc. | |
913 | +/* scott@icgroup.com | |
914 | +/* | |
915 | +/* Joshua Marcus | |
916 | +/* IC Group, Inc. | |
917 | +/* josh@icgroup.com | |
918 | +/*--*/ | |
919 | + | |
920 | +#endif | |
921 | + |