Postfix3.3.1
dict_pgsql.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_pgsql 3
4 /* SUMMARY
5 /* dictionary manager interface to PostgreSQL databases
6 /* SYNOPSIS
7 /* #include <dict_pgsql.h>
8 /*
9 /* DICT *dict_pgsql_open(name, open_flags, dict_flags)
10 /* const char *name;
11 /* int open_flags;
12 /* int dict_flags;
13 /* DESCRIPTION
14 /* dict_pgsql_open() creates a dictionary of type 'pgsql'. This
15 /* dictionary is an interface for the postfix key->value mappings
16 /* to pgsql. The result is a pointer to the installed dictionary,
17 /* or a null pointer in case of problems.
18 /*
19 /* The pgsql dictionary can manage multiple connections to
20 /* different sql servers for the same database. It assumes that
21 /* the underlying data on each server is identical (mirrored) and
22 /* maintains one connection at any given time. If any connection
23 /* fails, any other available ones will be opened and used.
24 /* The intent of this feature is to eliminate a single point of
25 /* failure for mail systems that would otherwise rely on a single
26 /* pgsql server.
27 /* .PP
28 /* Arguments:
29 /* .IP name
30 /* Either the path to the PostgreSQL configuration file (if it
31 /* starts with '/' or '.'), or the prefix which will be used to
32 /* obtain main.cf configuration parameters for this search.
33 /*
34 /* In the first case, the configuration parameters below are
35 /* specified in the file as \fIname\fR=\fIvalue\fR pairs.
36 /*
37 /* In the second case, the configuration parameters are
38 /* prefixed with the value of \fIname\fR and an underscore,
39 /* and they are specified in main.cf. For example, if this
40 /* value is \fIpgsqlsource\fR, the parameters would look like
41 /* \fIpgsqlsource_user\fR, \fIpgsqlsource_table\fR, and so on.
42 /* .IP other_name
43 /* reference for outside use.
44 /* .IP open_flags
45 /* Must be O_RDONLY.
46 /* .IP dict_flags
47 /* See dict_open(3).
48 /*
49 /* .PP
50 /* Configuration parameters:
51 /* .IP user
52 /* Username for connecting to the database.
53 /* .IP password
54 /* Password for the above.
55 /* .IP dbname
56 /* Name of the database.
57 /* .IP query
58 /* Query template. If not defined a default query template is constructed
59 /* from the legacy \fIselect_function\fR or failing that the \fItable\fR,
60 /* \fIselect_field\fR, \fIwhere_field\fR, and \fIadditional_conditions\fR
61 /* parameters. Before the query is issues, variable substitutions are
62 /* performed. See pgsql_table(5).
63 /* .IP domain
64 /* List of domains the queries should be restricted to. If
65 /* specified, only FQDN addresses whose domain parts matching this
66 /* list will be queried against the SQL database. Lookups for
67 /* partial addresses are also suppressed. This can significantly
68 /* reduce the query load on the server.
69 /* .IP result_format
70 /* The format used to expand results from queries. Substitutions
71 /* are performed as described in pgsql_table(5). Defaults to returning
72 /* the lookup result unchanged.
73 /* .IP expansion_limit
74 /* Limit (if any) on the total number of lookup result values. Lookups which
75 /* exceed the limit fail with dict->error=DICT_ERR_RETRY. Note that each
76 /* non-empty (and non-NULL) column of a multi-column result row counts as
77 /* one result.
78 /* .IP select_function
79 /* When \fIquery\fR is not defined, the function to be used instead of
80 /* the default query based on the legacy \fItable\fR, \fIselect_field\fR,
81 /* \fIwhere_field\fR, and \fIadditional_conditions\fR parameters.
82 /* .IP table
83 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
84 /* FROM table used to construct the default query template, see pgsql_table(5).
85 /* .IP select_field
86 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
87 /* SELECT field used to construct the default query template, see pgsql_table(5).
88 /* .IP where_field
89 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
90 /* WHERE field used to construct the default query template, see pgsql_table(5).
91 /* .IP additional_conditions
92 /* When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
93 /* additional text to add to the WHERE field in the default query template (this
94 /* usually begins with "and") see pgsql_table(5).
95 /* .IP hosts
96 /* List of hosts to connect to.
97 /* .PP
98 /* For example, if you want the map to reference databases of
99 /* the name "your_db" and execute a query like this: select
100 /* forw_addr from aliases where alias like '<some username>'
101 /* against any database called "postfix_info" located on hosts
102 /* host1.some.domain and host2.some.domain, logging in as user
103 /* "postfix" and password "passwd" then the configuration file
104 /* should read:
105 /* .PP
106 /* user = postfix
107 /* .br
108 /* password = passwd
109 /* .br
110 /* dbname = postfix_info
111 /* .br
112 /* table = aliases
113 /* .br
114 /* select_field = forw_addr
115 /* .br
116 /* where_field = alias
117 /* .br
118 /* hosts = host1.some.domain host2.some.domain
119 /* .PP
120 /* SEE ALSO
121 /* dict(3) generic dictionary manager
122 /* AUTHOR(S)
123 /* Aaron Sethman
124 /* androsyn@ratbox.org
125 /*
126 /* Based upon dict_mysql.c by
127 /*
128 /* Scott Cotton
129 /* IC Group, Inc.
130 /* scott@icgroup.com
131 /*
132 /* Joshua Marcus
133 /* IC Group, Inc.
134 /* josh@icgroup.com
135 /*--*/
136 
137 /* System library. */
138 
139 #include "sys_defs.h"
140 
141 #ifdef HAS_PGSQL
142 #include <sys/socket.h>
143 #include <netinet/in.h>
144 #include <arpa/inet.h>
145 #include <netdb.h>
146 #include <stdio.h>
147 #include <string.h>
148 #include <stdlib.h>
149 #include <syslog.h>
150 #include <time.h>
151 
152 #include <postgres_ext.h>
153 #include <libpq-fe.h>
154 
155 /* Utility library. */
156 
157 #include "dict.h"
158 #include "msg.h"
159 #include "mymalloc.h"
160 #include "argv.h"
161 #include "vstring.h"
162 #include "split_at.h"
163 #include "myrand.h"
164 #include "events.h"
165 #include "stringops.h"
166 
167 /* Global library. */
168 
169 #include "cfg_parser.h"
170 #include "db_common.h"
171 
172 /* Application-specific. */
173 
174 #include "dict_pgsql.h"
175 
176 #define STATACTIVE (1<<0)
177 #define STATFAIL (1<<1)
178 #define STATUNTRIED (1<<2)
179 
180 #define TYPEUNIX (1<<0)
181 #define TYPEINET (1<<1)
182 #define TYPECONNSTRING (1<<2)
183 
184 #define RETRY_CONN_MAX 100
185 #define RETRY_CONN_INTV 60 /* 1 minute */
186 #define IDLE_CONN_INTV 60 /* 1 minute */
187 
188 typedef struct {
189  PGconn *db;
190  char *hostname;
191  char *name;
192  char *port;
193  unsigned type; /* TYPEUNIX | TYPEINET | TYPECONNSTRING*/
194  unsigned stat; /* STATUNTRIED | STATFAIL | STATCUR */
195  time_t ts; /* used for attempting reconnection */
196 } HOST;
197 
198 typedef struct {
199  int len_hosts; /* number of hosts */
200  HOST **db_hosts; /* hosts on which databases reside */
201 } PLPGSQL;
202 
203 typedef struct {
204  DICT dict;
205  CFG_PARSER *parser;
206  char *query;
207  char *result_format;
208  void *ctx;
209  int expansion_limit;
210  char *username;
211  char *password;
212  char *dbname;
213  char *table;
214  ARGV *hosts;
215  PLPGSQL *pldb;
216  HOST *active_host;
217 } DICT_PGSQL;
218 
219 
220 /* Just makes things a little easier for me.. */
221 #define PGSQL_RES PGresult
222 
223 /* internal function declarations */
224 static PLPGSQL *plpgsql_init(ARGV *);
225 static PGSQL_RES *plpgsql_query(DICT_PGSQL *, const char *, VSTRING *, char *,
226  char *, char *);
227 static void plpgsql_dealloc(PLPGSQL *);
228 static void plpgsql_close_host(HOST *);
229 static void plpgsql_down_host(HOST *);
230 static void plpgsql_connect_single(HOST *, char *, char *, char *);
231 static const char *dict_pgsql_lookup(DICT *, const char *);
232 DICT *dict_pgsql_open(const char *, int, int);
233 static void dict_pgsql_close(DICT *);
234 static HOST *host_init(const char *);
235 
236 /* dict_pgsql_quote - escape SQL metacharacters in input string */
237 
238 static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
239 {
240  DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
241  HOST *active_host = dict_pgsql->active_host;
242  char *myname = "dict_pgsql_quote";
243  size_t len = strlen(name);
244  size_t buflen;
245  int err = 1;
246 
247  if (active_host == 0)
248  msg_panic("%s: bogus dict_pgsql->active_host", myname);
249 
250  /*
251  * We won't get arithmetic overflows in 2*len + 1, because Postfix input
252  * keys have reasonable size limits, better safe than sorry.
253  */
254  if (len > (SSIZE_T_MAX - VSTRING_LEN(result) - 1) / 2)
255  msg_panic("%s: arithmetic overflow in %lu+2*%lu+1",
256  myname, (unsigned long) VSTRING_LEN(result),
257  (unsigned long) len);
258  buflen = 2 * len + 1;
259 
260  /*
261  * XXX Workaround: stop further processing when PQescapeStringConn()
262  * (below) fails. A more proper fix requires invasive changes, not
263  * suitable for a stable release.
264  */
265  if (active_host->stat == STATFAIL)
266  return;
267 
268  /*
269  * Escape the input string, using PQescapeStringConn(), because the older
270  * PQescapeString() is not safe anymore, as stated by the documentation.
271  *
272  * From current libpq (8.1.4) documentation:
273  *
274  * PQescapeStringConn writes an escaped version of the from string to the to
275  * buffer, escaping special characters so that they cannot cause any
276  * harm, and adding a terminating zero byte.
277  *
278  * ...
279  *
280  * The parameter from points to the first character of the string that is to
281  * be escaped, and the length parameter gives the number of bytes in this
282  * string. A terminating zero byte is not required, and should not be
283  * counted in length.
284  *
285  * ...
286  *
287  * (The parameter) to shall point to a buffer that is able to hold at least
288  * one more byte than twice the value of length, otherwise the behavior
289  * is undefined.
290  *
291  * ...
292  *
293  * If the error parameter is not NULL, then *error is set to zero on
294  * success, nonzero on error ... The output string is still generated on
295  * error, but it can be expected that the server will reject it as
296  * malformed. On error, a suitable message is stored in the conn object,
297  * whether or not error is NULL.
298  */
299  VSTRING_SPACE(result, buflen);
300  PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err);
301  if (err == 0) {
302  VSTRING_SKIP(result);
303  } else {
304 
305  /*
306  * PQescapeStringConn() failed. According to the docs, we still have
307  * a valid, null-terminated output string, but we need not rely on
308  * this behavior.
309  */
310  msg_warn("dict pgsql: (host %s) cannot escape input string: %s",
311  active_host->hostname, PQerrorMessage(active_host->db));
312  active_host->stat = STATFAIL;
313  VSTRING_TERMINATE(result);
314  }
315 }
316 
317 /* dict_pgsql_lookup - find database entry */
318 
319 static const char *dict_pgsql_lookup(DICT *dict, const char *name)
320 {
321  const char *myname = "dict_pgsql_lookup";
322  PGSQL_RES *query_res;
323  DICT_PGSQL *dict_pgsql;
324  static VSTRING *query;
325  static VSTRING *result;
326  int i;
327  int j;
328  int numrows;
329  int numcols;
330  int expansion;
331  const char *r;
332  int domain_rc;
333 
334  dict_pgsql = (DICT_PGSQL *) dict;
335 
336 #define INIT_VSTR(buf, len) do { \
337  if (buf == 0) \
338  buf = vstring_alloc(len); \
339  VSTRING_RESET(buf); \
340  VSTRING_TERMINATE(buf); \
341  } while (0)
342 
343  INIT_VSTR(query, 10);
344  INIT_VSTR(result, 10);
345 
346  dict->error = 0;
347 
348  /*
349  * Optionally fold the key.
350  */
351  if (dict->flags & DICT_FLAG_FOLD_FIX) {
352  if (dict->fold_buf == 0)
353  dict->fold_buf = vstring_alloc(10);
354  vstring_strcpy(dict->fold_buf, name);
355  name = lowercase(vstring_str(dict->fold_buf));
356  }
357 
358  /*
359  * If there is a domain list for this map, then only search for addresses
360  * in domains on the list. This can significantly reduce the load on the
361  * server.
362  */
363  if ((domain_rc = db_common_check_domain(dict_pgsql->ctx, name)) == 0) {
364  if (msg_verbose)
365  msg_info("%s: Skipping lookup of '%s'", myname, name);
366  return (0);
367  }
368  if (domain_rc < 0)
369  DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);
370 
371  /*
372  * Suppress the actual lookup if the expansion is empty.
373  *
374  * This initial expansion is outside the context of any specific host
375  * connection, we just want to check the key pre-requisites, so when
376  * quoting happens separately for each connection, we don't bother with
377  * quoting...
378  */
379  if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
380  name, 0, query, 0))
381  return (0);
382 
383  /* do the query - set dict->error & cleanup if there's an error */
384  if ((query_res = plpgsql_query(dict_pgsql, name, query,
385  dict_pgsql->dbname,
386  dict_pgsql->username,
387  dict_pgsql->password)) == 0) {
388  dict->error = DICT_ERR_RETRY;
389  return 0;
390  }
391  numrows = PQntuples(query_res);
392  if (msg_verbose)
393  msg_info("%s: retrieved %d rows", myname, numrows);
394  if (numrows == 0) {
395  PQclear(query_res);
396  return 0;
397  }
398  numcols = PQnfields(query_res);
399 
400  for (expansion = i = 0; i < numrows && dict->error == 0; i++) {
401  for (j = 0; j < numcols; j++) {
402  r = PQgetvalue(query_res, i, j);
403  if (db_common_expand(dict_pgsql->ctx, dict_pgsql->result_format,
404  r, name, result, 0)
405  && dict_pgsql->expansion_limit > 0
406  && ++expansion > dict_pgsql->expansion_limit) {
407  msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
408  myname, dict_pgsql->parser->name, name);
409  dict->error = DICT_ERR_RETRY;
410  break;
411  }
412  }
413  }
414  PQclear(query_res);
415  r = vstring_str(result);
416  return ((dict->error == 0 && *r) ? r : 0);
417 }
418 
419 /* dict_pgsql_check_stat - check the status of a host */
420 
421 static int dict_pgsql_check_stat(HOST *host, unsigned stat, unsigned type,
422  time_t t)
423 {
424  if ((host->stat & stat) && (!type || host->type & type)) {
425  /* try not to hammer the dead hosts too often */
426  if (host->stat == STATFAIL && host->ts > 0 && host->ts >= t)
427  return 0;
428  return 1;
429  }
430  return 0;
431 }
432 
433 /* dict_pgsql_find_host - find a host with the given status */
434 
435 static HOST *dict_pgsql_find_host(PLPGSQL *PLDB, unsigned stat, unsigned type)
436 {
437  time_t t;
438  int count = 0;
439  int idx;
440  int i;
441 
442  t = time((time_t *) 0);
443  for (i = 0; i < PLDB->len_hosts; i++) {
444  if (dict_pgsql_check_stat(PLDB->db_hosts[i], stat, type, t))
445  count++;
446  }
447 
448  if (count) {
449  idx = (count > 1) ?
450  1 + count * (double) myrand() / (1.0 + RAND_MAX) : 1;
451 
452  for (i = 0; i < PLDB->len_hosts; i++) {
453  if (dict_pgsql_check_stat(PLDB->db_hosts[i], stat, type, t) &&
454  --idx == 0)
455  return PLDB->db_hosts[i];
456  }
457  }
458  return 0;
459 }
460 
461 /* dict_pgsql_get_active - get an active connection */
462 
463 static HOST *dict_pgsql_get_active(PLPGSQL *PLDB, char *dbname,
464  char *username, char *password)
465 {
466  const char *myname = "dict_pgsql_get_active";
467  HOST *host;
468  int count = RETRY_CONN_MAX;
469 
470  /* try the active connections first; prefer the ones to UNIX sockets */
471  if ((host = dict_pgsql_find_host(PLDB, STATACTIVE, TYPEUNIX)) != NULL ||
472  (host = dict_pgsql_find_host(PLDB, STATACTIVE, TYPEINET)) != NULL ||
473  (host = dict_pgsql_find_host(PLDB, STATACTIVE, TYPECONNSTRING)) != NULL) {
474  if (msg_verbose)
475  msg_info("%s: found active connection to host %s", myname,
476  host->hostname);
477  return host;
478  }
479 
480  /*
481  * Try the remaining hosts. "count" is a safety net, in case the loop
482  * takes more than RETRY_CONN_INTV and the dead hosts are no longer
483  * skipped.
484  */
485  while (--count > 0 &&
486  ((host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
487  TYPEUNIX)) != NULL ||
488  (host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
489  TYPEINET)) != NULL ||
490  (host = dict_pgsql_find_host(PLDB, STATUNTRIED | STATFAIL,
491  TYPECONNSTRING)) != NULL)) {
492  if (msg_verbose)
493  msg_info("%s: attempting to connect to host %s", myname,
494  host->hostname);
495  plpgsql_connect_single(host, dbname, username, password);
496  if (host->stat == STATACTIVE)
497  return host;
498  }
499 
500  /* bad news... */
501  return 0;
502 }
503 
504 /* dict_pgsql_event - callback: close idle connections */
505 
506 static void dict_pgsql_event(int unused_event, void *context)
507 {
508  HOST *host = (HOST *) context;
509 
510  if (host->db)
511  plpgsql_close_host(host);
512 }
513 
514 /*
515  * plpgsql_query - process a PostgreSQL query. Return PGSQL_RES* on success.
516  * On failure, log failure and try other db instances.
517  * on failure of all db instances, return 0;
518  * close unnecessary active connections
519  */
520 
521 static PGSQL_RES *plpgsql_query(DICT_PGSQL *dict_pgsql,
522  const char *name,
523  VSTRING *query,
524  char *dbname,
525  char *username,
526  char *password)
527 {
528  PLPGSQL *PLDB = dict_pgsql->pldb;
529  HOST *host;
530  PGSQL_RES *res = 0;
531  ExecStatusType status;
532 
533  while ((host = dict_pgsql_get_active(PLDB, dbname, username, password)) != NULL) {
534 
535  /*
536  * The active host is used to escape strings in the context of the
537  * active connection's character encoding.
538  */
539  dict_pgsql->active_host = host;
540  VSTRING_RESET(query);
541  VSTRING_TERMINATE(query);
542  db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
543  name, 0, query, dict_pgsql_quote);
544  dict_pgsql->active_host = 0;
545 
546  /* Check for potential dict_pgsql_quote() failure. */
547  if (host->stat == STATFAIL) {
548  plpgsql_down_host(host);
549  continue;
550  }
551 
552  /*
553  * Submit a command to the server. Be paranoid when processing the
554  * result set: try to enumerate every successful case, and reject
555  * everything else.
556  *
557  * From PostgreSQL 8.1.4 docs: (PQexec) returns a PGresult pointer or
558  * possibly a null pointer. A non-null pointer will generally be
559  * returned except in out-of-memory conditions or serious errors such
560  * as inability to send the command to the server.
561  */
562  if ((res = PQexec(host->db, vstring_str(query))) != 0) {
563 
564  /*
565  * XXX Because non-null result pointer does not imply success, we
566  * need to check the command's result status.
567  *
568  * Section 28.3.1: A result of status PGRES_NONFATAL_ERROR will
569  * never be returned directly by PQexec or other query execution
570  * functions; results of this kind are instead passed to the
571  * notice processor.
572  *
573  * PGRES_EMPTY_QUERY is being sent by the server when the query
574  * string is empty. The sanity-checking done by the Postfix
575  * infrastructure makes this case impossible, so we need not
576  * handle this situation explicitly.
577  */
578  switch ((status = PQresultStatus(res))) {
579  case PGRES_TUPLES_OK:
580  case PGRES_COMMAND_OK:
581  /* Success. */
582  if (msg_verbose)
583  msg_info("dict_pgsql: successful query from host %s",
584  host->hostname);
585  event_request_timer(dict_pgsql_event, (void *) host,
586  IDLE_CONN_INTV);
587  return (res);
588  case PGRES_FATAL_ERROR:
589  msg_warn("pgsql query failed: fatal error from host %s: %s",
590  host->hostname, PQresultErrorMessage(res));
591  break;
592  case PGRES_BAD_RESPONSE:
593  msg_warn("pgsql query failed: protocol error, host %s",
594  host->hostname);
595  break;
596  default:
597  msg_warn("pgsql query failed: unknown code 0x%lx from host %s",
598  (unsigned long) status, host->hostname);
599  break;
600  }
601  } else {
602 
603  /*
604  * This driver treats null pointers like fatal, non-null result
605  * pointer errors, as suggested by the PostgreSQL 8.1.4
606  * documentation.
607  */
608  msg_warn("pgsql query failed: fatal error from host %s: %s",
609  host->hostname, PQerrorMessage(host->db));
610  }
611 
612  /*
613  * XXX An error occurred. Clean up memory and skip this connection.
614  */
615  if (res != 0)
616  PQclear(res);
617  plpgsql_down_host(host);
618  }
619 
620  return (0);
621 }
622 
623 /*
624  * plpgsql_connect_single -
625  * used to reconnect to a single database when one is down or none is
626  * connected yet. Log all errors and set the stat field of host accordingly
627  */
628 static void plpgsql_connect_single(HOST *host, char *dbname, char *username, char *password)
629 {
630  if (host->type == TYPECONNSTRING) {
631  host->db = PQconnectdb(host->name);
632  } else {
633  host->db = PQsetdbLogin(host->name, host->port, NULL, NULL,
634  dbname, username, password);
635  }
636  if (host->db == NULL || PQstatus(host->db) != CONNECTION_OK) {
637  msg_warn("connect to pgsql server %s: %s",
638  host->hostname, PQerrorMessage(host->db));
639  plpgsql_down_host(host);
640  return;
641  }
642  if (msg_verbose)
643  msg_info("dict_pgsql: successful connection to host %s",
644  host->hostname);
645 
646  /*
647  * XXX Postfix does not send multi-byte characters. The following piece
648  * of code is an explicit statement of this fact, and the database server
649  * should not accept multi-byte information after this point.
650  */
651  if (PQsetClientEncoding(host->db, "LATIN1") != 0) {
652  msg_warn("dict_pgsql: cannot set the encoding to LATIN1, skipping %s",
653  host->hostname);
654  plpgsql_down_host(host);
655  return;
656  }
657  /* Success. */
658  host->stat = STATACTIVE;
659 }
660 
661 /* plpgsql_close_host - close an established PostgreSQL connection */
662 
663 static void plpgsql_close_host(HOST *host)
664 {
665  if (host->db)
666  PQfinish(host->db);
667  host->db = 0;
668  host->stat = STATUNTRIED;
669 }
670 
671 /*
672  * plpgsql_down_host - close a failed connection AND set a "stay away from
673  * this host" timer.
674  */
675 static void plpgsql_down_host(HOST *host)
676 {
677  if (host->db)
678  PQfinish(host->db);
679  host->db = 0;
680  host->ts = time((time_t *) 0) + RETRY_CONN_INTV;
681  host->stat = STATFAIL;
682  event_cancel_timer(dict_pgsql_event, (void *) host);
683 }
684 
685 /* pgsql_parse_config - parse pgsql configuration file */
686 
687 static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
688 {
689  const char *myname = "pgsql_parse_config";
690  CFG_PARSER *p = dict_pgsql->parser;
691  char *hosts;
692  VSTRING *query;
693  char *select_function;
694 
695  dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
696  dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
697  dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
698  dict_pgsql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
699 
700  /*
701  * XXX: The default should be non-zero for safety, but that is not
702  * backwards compatible.
703  */
704  dict_pgsql->expansion_limit = cfg_get_int(dict_pgsql->parser,
705  "expansion_limit", 0, 0, 0);
706 
707  if ((dict_pgsql->query = cfg_get_str(p, "query", 0, 0, 0)) == 0) {
708 
709  /*
710  * No query specified -- fallback to building it from components (
711  * old style "select %s from %s where %s" )
712  */
713  query = vstring_alloc(64);
714  select_function = cfg_get_str(p, "select_function", 0, 0, 0);
715  if (select_function != 0) {
716  vstring_sprintf(query, "SELECT %s('%%s')", select_function);
717  myfree(select_function);
718  } else
719  db_common_sql_build_query(query, p);
720  dict_pgsql->query = vstring_export(query);
721  }
722 
723  /*
724  * Must parse all templates before we can use db_common_expand()
725  */
726  dict_pgsql->ctx = 0;
727  (void) db_common_parse(&dict_pgsql->dict, &dict_pgsql->ctx,
728  dict_pgsql->query, 1);
729  (void) db_common_parse(0, &dict_pgsql->ctx, dict_pgsql->result_format, 0);
730  db_common_parse_domain(p, dict_pgsql->ctx);
731 
732  /*
733  * Maps that use substring keys should only be used with the full input
734  * key.
735  */
736  if (db_common_dict_partial(dict_pgsql->ctx))
737  dict_pgsql->dict.flags |= DICT_FLAG_PATTERN;
738  else
739  dict_pgsql->dict.flags |= DICT_FLAG_FIXED;
740  if (dict_pgsql->dict.flags & DICT_FLAG_FOLD_FIX)
741  dict_pgsql->dict.fold_buf = vstring_alloc(10);
742 
743  hosts = cfg_get_str(p, "hosts", "", 0, 0);
744 
745  dict_pgsql->hosts = argv_split(hosts, CHARS_COMMA_SP);
746  if (dict_pgsql->hosts->argc == 0) {
747  argv_add(dict_pgsql->hosts, "localhost", ARGV_END);
748  argv_terminate(dict_pgsql->hosts);
749  if (msg_verbose)
750  msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
751  myname, pgsqlcf, dict_pgsql->hosts->argv[0]);
752  }
753  myfree(hosts);
754 }
755 
756 /* dict_pgsql_open - open PGSQL data base */
757 
758 DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
759 {
760  DICT_PGSQL *dict_pgsql;
761  CFG_PARSER *parser;
762 
763  /*
764  * Sanity check.
765  */
766  if (open_flags != O_RDONLY)
767  return (dict_surrogate(DICT_TYPE_PGSQL, name, open_flags, dict_flags,
768  "%s:%s map requires O_RDONLY access mode",
769  DICT_TYPE_PGSQL, name));
770 
771  /*
772  * Open the configuration file.
773  */
774  if ((parser = cfg_parser_alloc(name)) == 0)
775  return (dict_surrogate(DICT_TYPE_PGSQL, name, open_flags, dict_flags,
776  "open %s: %m", name));
777 
778  dict_pgsql = (DICT_PGSQL *) dict_alloc(DICT_TYPE_PGSQL, name,
779  sizeof(DICT_PGSQL));
780  dict_pgsql->dict.lookup = dict_pgsql_lookup;
781  dict_pgsql->dict.close = dict_pgsql_close;
782  dict_pgsql->dict.flags = dict_flags;
783  dict_pgsql->parser = parser;
784  pgsql_parse_config(dict_pgsql, name);
785  dict_pgsql->active_host = 0;
786  dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
787  if (dict_pgsql->pldb == NULL)
788  msg_fatal("couldn't initialize pldb!\n");
789  dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser);
790  return (DICT_DEBUG (&dict_pgsql->dict));
791 }
792 
793 /* plpgsql_init - initialize a PGSQL database */
794 
795 static PLPGSQL *plpgsql_init(ARGV *hosts)
796 {
797  PLPGSQL *PLDB;
798  int i;
799 
800  PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL));
801  PLDB->len_hosts = hosts->argc;
802  PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc);
803  for (i = 0; i < hosts->argc; i++)
804  PLDB->db_hosts[i] = host_init(hosts->argv[i]);
805 
806  return PLDB;
807 }
808 
809 
810 /* host_init - initialize HOST structure */
811 
812 static HOST *host_init(const char *hostname)
813 {
814  const char *myname = "pgsql host_init";
815  HOST *host = (HOST *) mymalloc(sizeof(HOST));
816  const char *d = hostname;
817 
818  host->db = 0;
819  host->hostname = mystrdup(hostname);
820  host->stat = STATUNTRIED;
821  host->ts = 0;
822 
823  /*
824  * Modern syntax: "postgresql://connection-info".
825  */
826  if (strncmp(d, "postgresql:", 11) == 0) {
827  host->type = TYPECONNSTRING;
828  host->name = mystrdup(d);
829  host->port = 0;
830  }
831 
832  /*
833  * Historical syntax: "unix:/pathname" and "inet:host:port". Strip the
834  * "unix:" and "inet:" prefixes. Look at the first character, which is
835  * how PgSQL historically distinguishes between UNIX and INET.
836  */
837  else {
838  if (strncmp(d, "unix:", 5) == 0 || strncmp(d, "inet:", 5) == 0)
839  d += 5;
840  host->name = mystrdup(d);
841  if (host->name[0] && host->name[0] != '/') {
842  host->type = TYPEINET;
843  host->port = split_at_right(host->name, ':');
844  } else {
845  host->type = TYPEUNIX;
846  host->port = 0;
847  }
848  }
849  if (msg_verbose > 1)
850  msg_info("%s: host=%s, port=%s, type=%s", myname, host->name,
851  host->port ? host->port : "",
852  host->type == TYPEUNIX ? "unix" :
853  host->type == TYPEINET ? "inet" :
854  "uri");
855  return host;
856 }
857 
858 /* dict_pgsql_close - close PGSQL data base */
859 
860 static void dict_pgsql_close(DICT *dict)
861 {
862  DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
863 
864  plpgsql_dealloc(dict_pgsql->pldb);
865  cfg_parser_free(dict_pgsql->parser);
866  myfree(dict_pgsql->username);
867  myfree(dict_pgsql->password);
868  myfree(dict_pgsql->dbname);
869  myfree(dict_pgsql->query);
870  myfree(dict_pgsql->result_format);
871  if (dict_pgsql->hosts)
872  argv_free(dict_pgsql->hosts);
873  if (dict_pgsql->ctx)
874  db_common_free_ctx(dict_pgsql->ctx);
875  if (dict->fold_buf)
876  vstring_free(dict->fold_buf);
877  dict_free(dict);
878 }
879 
880 /* plpgsql_dealloc - free memory associated with PLPGSQL close databases */
881 
882 static void plpgsql_dealloc(PLPGSQL *PLDB)
883 {
884  int i;
885 
886  for (i = 0; i < PLDB->len_hosts; i++) {
887  event_cancel_timer(dict_pgsql_event, (void *) (PLDB->db_hosts[i]));
888  if (PLDB->db_hosts[i]->db)
889  PQfinish(PLDB->db_hosts[i]->db);
890  myfree(PLDB->db_hosts[i]->hostname);
891  myfree(PLDB->db_hosts[i]->name);
892  myfree((void *) PLDB->db_hosts[i]);
893  }
894  myfree((void *) PLDB->db_hosts);
895  myfree((void *) (PLDB));
896 }
897 
898 #endif
int msg_verbose
Definition: msg.c:177
void myfree(void *ptr)
Definition: mymalloc.c:207
#define ARGV_END
Definition: argv.h:52
char * mystrdup(const char *str)
Definition: mymalloc.c:225
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
Definition: argv.h:17
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
Definition: db_common.c:539
#define vstring_str(vp)
Definition: vstring.h:71
#define stat(p, s)
Definition: warn_stat.h:18
#define DICT_FLAG_FIXED
Definition: dict.h:114
#define RAND_MAX
Definition: myrand.h:18
int flags
Definition: dict.h:81
#define DICT_ERR_RETRY
Definition: dict.h:178
char ** argv
Definition: argv.h:20
void argv_add(ARGV *argvp,...)
Definition: argv.c:197
int cfg_get_int(const CFG_PARSER *parser, const char *name, int defval, int min, int max)
Definition: cfg_parser.c:281
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
void db_common_free_ctx(void *ctxPtr)
Definition: db_common.c:288
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
#define vstring_end(vp)
Definition: vstring.h:73
int db_common_dict_partial(void *ctxPtr)
Definition: db_common.c:276
Definition: dict.h:78
void db_common_parse_domain(CFG_PARSER *parser, void *ctxPtr)
Definition: db_common.c:251
int db_common_expand(void *ctxArg, const char *format, const char *value, const char *key, VSTRING *result, db_quote_callback_t quote_func)
Definition: db_common.c:299
#define VSTRING_SKIP(vp)
Definition: vstring.h:82
#define VSTRING_RESET(vp)
Definition: vstring.h:77
void msg_warn(const char *fmt,...)
Definition: msg.c:215
#define DICT_TYPE_PGSQL
Definition: dict_pgsql.h:22
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
const char * username(void)
Definition: username.c:38
DICT * dict_pgsql_open(const char *name, int unused_flags, int dict_flags)
int error
Definition: dict.h:94
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
int db_common_check_domain(void *ctxPtr, const char *addr)
Definition: db_common.c:521
char * lowercase(char *string)
Definition: lowercase.c:34
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define DICT_ERR_VAL_RETURN(dict, err, val)
Definition: dict.h:192
char * cfg_get_str(const CFG_PARSER *parser, const char *name, const char *defval, int min, int max)
Definition: cfg_parser.c:261
#define CHARS_COMMA_SP
Definition: sys_defs.h:1761
#define DICT_FLAG_PATTERN
Definition: dict.h:115
void dict_free(DICT *)
Definition: dict_alloc.c:163
ARGV * argv_split(const char *, const char *)
Definition: argv_split.c:63
CFG_PARSER * cfg_parser_free(CFG_PARSER *parser)
Definition: cfg_parser.c:309
#define VSTRING_SPACE(vp, len)
Definition: vstring.h:70
#define SSIZE_T_MAX
Definition: sys_defs.h:1687
int myrand(void)
Definition: myrand.c:58
CFG_PARSER * cfg_parser_alloc(const char *pname)
Definition: cfg_parser.c:227
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
Definition: events.c:894
int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
Definition: db_common.c:185
ssize_t argc
Definition: argv.h:19
DICT * dict_alloc(const char *, const char *, ssize_t)
Definition: dict_alloc.c:135
VSTRING * fold_buf
Definition: dict.h:92
int event_cancel_timer(EVENT_NOTIFY_TIME_FN callback, void *context)
Definition: events.c:965
char * split_at_right(char *string, int delimiter)
Definition: split_at.c:64
#define cfg_get_owner(cfg)
Definition: cfg_parser.h:38
char * vstring_export(VSTRING *vp)
Definition: vstring.c:569
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void argv_terminate(ARGV *argvp)
Definition: argv.c:242
void msg_info(const char *fmt,...)
Definition: msg.c:199