~ubuntu-branches/ubuntu/hoary/postfix/hoary-security

« back to all changes in this revision

Viewing changes to src/global/dict_mysql.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-10-06 11:50:33 UTC
  • Revision ID: james.westby@ubuntu.com-20041006115033-ooo6yfg6kmoteu04
Tags: upstream-2.1.3
ImportĀ upstreamĀ versionĀ 2.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      dict_mysql 3
 
4
/* SUMMARY
 
5
/*      dictionary manager interface to MySQL databases
 
6
/* SYNOPSIS
 
7
/*      #include <dict_mysql.h>
 
8
/*
 
9
/*      DICT    *dict_mysql_open(name, open_flags, dict_flags)
 
10
/*      const char *name;
 
11
/*      int     open_flags;
 
12
/*      int     dict_flags;
 
13
/* DESCRIPTION
 
14
/*      dict_mysql_open() creates a dictionary of type 'mysql'.  This
 
15
/*      dictionary is an interface for the postfix key->value mappings
 
16
/*      to mysql.  The result is a pointer to the installed dictionary,
 
17
/*      or a null pointer in case of problems.
 
18
/*
 
19
/*      The mysql dictionary can manage multiple connections to different
 
20
/*      sql servers on different hosts.  It assumes that the underlying data
 
21
/*      on each host is identical (mirrored) and maintains one connection
 
22
/*      at any given time.  If any connection fails,  any other available
 
23
/*      ones will be opened and used.  The intent of this feature is to eliminate
 
24
/*      a single point of failure for mail systems that would otherwise rely
 
25
/*      on a single mysql server.
 
26
/* .PP
 
27
/*      Arguments:
 
28
/* .IP name
 
29
/*      Either the path to the MySQL configuration file (if it starts
 
30
/*      with '/' or '.'), or the prefix which will be used to obtain
 
31
/*      main.cf configuration parameters for this search.
 
32
/*
 
33
/*      In the first case, the configuration parameters below are
 
34
/*      specified in the file as \fIname\fR=\fBvalue\fR pairs.
 
35
/*
 
36
/*      In the second case, the configuration parameters are
 
37
/*      prefixed with the value of \fIname\fR and an underscore,
 
38
/*      and they are specified in main.cf.  For example, if this
 
39
/*      value is \fImysqlsource\fR, the parameters would look like
 
40
/*      \fImysqlsource_user\fR, \fImysqlsource_table\fR, and so on.
 
41
/*
 
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
/* .PP
 
49
/*      Configuration parameters:
 
50
/*
 
51
/*      The parameters encodes a number of pieces of information:
 
52
/*      username, password, databasename, table, select_field,
 
53
/*      where_field, and hosts:
 
54
/* .IP \fIuser\fR
 
55
/*      Username for connecting to the database.
 
56
/* .IP \fIpassword\fR
 
57
/*      Password for the above.
 
58
/* .IP \fIdbname\fR
 
59
/*      Name of the database.
 
60
/* .IP \fItable\fR
 
61
/*      Name of the table.
 
62
/* .IP \fIselect_field\fR
 
63
/*      Name of the result field.
 
64
/* .IP \fIwhere_field\fR
 
65
/*      Field used in the WHERE clause.
 
66
/* .IP \fIadditional_conditions\fR
 
67
/*      Additional conditions to the WHERE clause.
 
68
/* .IP \fIhosts\fR
 
69
/*      List of hosts to connect to.
 
70
/* .PP
 
71
/*      For example, if you want the map to reference databases of
 
72
/*      the name "your_db" and execute a query like this: select
 
73
/*      forw_addr from aliases where alias like '<some username>'
 
74
/*      against any database called "vmailer_info" located on hosts
 
75
/*      host1.some.domain and host2.some.domain, logging in as user
 
76
/*      "vmailer" and password "passwd" then the configuration file
 
77
/*      should read:
 
78
/* .PP
 
79
/*      \fIuser\fR = \fBvmailer\fR
 
80
/* .br
 
81
/*      \fIpassword\fR = \fBpasswd\fR
 
82
/* .br
 
83
/*      \fIdbname\fR = \fBvmailer_info\fR
 
84
/* .br
 
85
/*      \fItable\fR = \fBaliases\fR
 
86
/* .br
 
87
/*      \fIselect_field\fR = \fBforw_addr\fR
 
88
/* .br
 
89
/*      \fIwhere_field\fR = \fBalias\fR
 
90
/* .br
 
91
/*      \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR
 
92
/* .PP
 
93
/* SEE ALSO
 
94
/*      dict(3) generic dictionary manager
 
95
/* AUTHOR(S)
 
96
/*      Scott Cotton
 
97
/*      IC Group, Inc.
 
98
/*      scott@icgroup.com
 
99
/*
 
100
/*      Joshua Marcus
 
101
/*      IC Group, Inc.
 
102
/*      josh@icgroup.com
 
103
/*--*/
 
104
 
 
105
/* System library. */
 
106
#include "sys_defs.h"
 
107
 
 
108
#ifdef HAS_MYSQL
 
109
#include <sys/socket.h>
 
110
#include <netinet/in.h>
 
111
#include <arpa/inet.h>
 
112
#include <netdb.h>
 
113
#include <stdio.h>
 
114
#include <string.h>
 
115
#include <stdlib.h>
 
116
#include <syslog.h>
 
117
#include <time.h>
 
118
#include <mysql.h>
 
119
 
 
120
/* Utility library. */
 
121
 
 
122
#include "dict.h"
 
123
#include "msg.h"
 
124
#include "mymalloc.h"
 
125
#include "argv.h"
 
126
#include "vstring.h"
 
127
#include "split_at.h"
 
128
#include "find_inet.h"
 
129
#include "myrand.h"
 
130
#include "events.h"
 
131
 
 
132
/* Global library. */
 
133
 
 
134
#include "cfg_parser.h"
 
135
 
 
136
/* Application-specific. */
 
137
 
 
138
#include "dict_mysql.h"
 
139
 
 
140
/* need some structs to help organize things */
 
141
typedef struct {
 
142
    MYSQL  *db;
 
143
    char   *hostname;
 
144
    char   *name;
 
145
    unsigned port;
 
146
    unsigned type;                      /* TYPEUNIX | TYPEINET */
 
147
    unsigned stat;                      /* STATUNTRIED | STATFAIL | STATCUR */
 
148
    time_t  ts;                         /* used for attempting reconnection
 
149
                                         * every so often if a host is down */
 
150
} HOST;
 
151
 
 
152
typedef struct {
 
153
    int     len_hosts;                  /* number of hosts */
 
154
    HOST  **db_hosts;                   /* the hosts on which the databases
 
155
                                         * reside */
 
156
} PLMYSQL;
 
157
 
 
158
typedef struct {
 
159
    CFG_PARSER *parser;
 
160
    char   *username;
 
161
    char   *password;
 
162
    char   *dbname;
 
163
    char   *table;
 
164
    char   *select_field;
 
165
    char   *where_field;
 
166
    char   *additional_conditions;
 
167
    char  **hostnames;
 
168
    int     len_hosts;
 
169
} MYSQL_NAME;
 
170
 
 
171
typedef struct {
 
172
    DICT    dict;
 
173
    PLMYSQL *pldb;
 
174
    MYSQL_NAME *name;
 
175
} DICT_MYSQL;
 
176
 
 
177
#define STATACTIVE                      (1<<0)
 
178
#define STATFAIL                        (1<<1)
 
179
#define STATUNTRIED                     (1<<2)
 
180
 
 
181
#define TYPEUNIX                        (1<<0)
 
182
#define TYPEINET                        (1<<1)
 
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
/* internal function declarations */
 
189
static PLMYSQL *plmysql_init(char *hostnames[], int);
 
190
static MYSQL_RES *plmysql_query(PLMYSQL *, const char *, char *, char *, char *);
 
191
static void plmysql_dealloc(PLMYSQL *);
 
192
static void plmysql_close_host(HOST *);
 
193
static void plmysql_down_host(HOST *);
 
194
static void plmysql_connect_single(HOST *, char *, char *, char *);
 
195
static const char *dict_mysql_lookup(DICT *, const char *);
 
196
DICT   *dict_mysql_open(const char *, int, int);
 
197
static void dict_mysql_close(DICT *);
 
198
static MYSQL_NAME *mysqlname_parse(const char *);
 
199
static HOST *host_init(const char *);
 
200
 
 
201
 
 
202
 
 
203
/**********************************************************************
 
204
 * public interface dict_mysql_lookup
 
205
 * find database entry return 0 if no alias found, set dict_errno
 
206
 * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success
 
207
 *********************************************************************/
 
208
static const char *dict_mysql_lookup(DICT *dict, const char *name)
 
209
{
 
210
    MYSQL_RES *query_res;
 
211
    MYSQL_ROW row;
 
212
    DICT_MYSQL *dict_mysql;
 
213
    PLMYSQL *pldb;
 
214
    static VSTRING *result;
 
215
    static VSTRING *query = 0;
 
216
    int     i,
 
217
            j,
 
218
            numrows;
 
219
    char   *name_escaped = 0;
 
220
 
 
221
    dict_mysql = (DICT_MYSQL *) dict;
 
222
    pldb = dict_mysql->pldb;
 
223
    /* initialization  for query */
 
224
    query = vstring_alloc(24);
 
225
    vstring_strcpy(query, "");
 
226
    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
 
227
        msg_fatal("dict_mysql_lookup: out of memory.");
 
228
    }
 
229
    /* prepare the query */
 
230
    mysql_escape_string(name_escaped, name, (unsigned int) strlen(name));
 
231
    vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_mysql->name->select_field,
 
232
       dict_mysql->name->table, dict_mysql->name->where_field, name_escaped,
 
233
                    dict_mysql->name->additional_conditions);
 
234
    if (msg_verbose)
 
235
        msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
 
236
    /* free mem associated with preparing the query */
 
237
    myfree(name_escaped);
 
238
    /* do the query - set dict_errno & cleanup if there's an error */
 
239
    if ((query_res = plmysql_query(pldb,
 
240
                                   vstring_str(query),
 
241
                                   dict_mysql->name->dbname,
 
242
                                   dict_mysql->name->username,
 
243
                                   dict_mysql->name->password)) == 0) {
 
244
        dict_errno = DICT_ERR_RETRY;
 
245
        vstring_free(query);
 
246
        return 0;
 
247
    }
 
248
    dict_errno = 0;
 
249
    /* free the vstring query */
 
250
    vstring_free(query);
 
251
    numrows = mysql_num_rows(query_res);
 
252
    if (msg_verbose)
 
253
        msg_info("dict_mysql_lookup: retrieved %d rows", numrows);
 
254
    if (numrows == 0) {
 
255
        mysql_free_result(query_res);
 
256
        return 0;
 
257
    }
 
258
    if (result == 0)
 
259
        result = vstring_alloc(10);
 
260
    vstring_strcpy(result, "");
 
261
    for (i = 0; i < numrows; i++) {
 
262
        row = mysql_fetch_row(query_res);
 
263
        if (i > 0)
 
264
            vstring_strcat(result, ",");
 
265
        for (j = 0; j < mysql_num_fields(query_res); j++) {
 
266
            if (row[j] == 0) {
 
267
                if (msg_verbose > 1)
 
268
                    msg_info("dict_mysql_lookup: null field #%d row #%d", j, i);
 
269
                mysql_free_result(query_res);
 
270
                return (0);
 
271
            }
 
272
            if (j > 0)
 
273
                vstring_strcat(result, ",");
 
274
            vstring_strcat(result, row[j]);
 
275
            if (msg_verbose > 1)
 
276
                msg_info("dict_mysql_lookup: retrieved field: %d: %s", j, row[j]);
 
277
        }
 
278
    }
 
279
    mysql_free_result(query_res);
 
280
    return vstring_str(result);
 
281
}
 
282
 
 
283
/* dict_mysql_check_stat - check the status of a host */
 
284
 
 
285
static int dict_mysql_check_stat(HOST *host, unsigned stat, unsigned type,
 
286
                                 time_t t)
 
287
{
 
288
    if ((host->stat & stat) && (!type || host->type & type)) {
 
289
        /* try not to hammer the dead hosts too often */
 
290
        if (host->stat == STATFAIL && host->ts > 0 && host->ts >= t)
 
291
            return 0;
 
292
        return 1;
 
293
    }
 
294
    return 0;
 
295
}
 
296
 
 
297
/* dict_mysql_find_host - find a host with the given status */
 
298
 
 
299
static HOST *dict_mysql_find_host(PLMYSQL *PLDB, unsigned stat, unsigned type)
 
300
{
 
301
    time_t  t;
 
302
    int     count = 0;
 
303
    int     idx;
 
304
    int     i;
 
305
 
 
306
    t = time((time_t *) 0);
 
307
    for (i = 0; i < PLDB->len_hosts; i++) {
 
308
        if (dict_mysql_check_stat(PLDB->db_hosts[i], stat, type, t))
 
309
            count++;
 
310
    }
 
311
 
 
312
    if (count) {
 
313
        /*
 
314
         * Calling myrand() can deplete the random pool.
 
315
         * Don't rely on the optimizer to weed out the call
 
316
         * when count == 1.
 
317
         */
 
318
        idx = (count > 1) ? 1 + (count - 1) * (double) myrand() / RAND_MAX : 1;
 
319
 
 
320
        for (i = 0; i < PLDB->len_hosts; i++) {
 
321
            if (dict_mysql_check_stat(PLDB->db_hosts[i], stat, type, t) &&
 
322
                                      --idx == 0)
 
323
                return PLDB->db_hosts[i];
 
324
        }
 
325
    }
 
326
    return 0;
 
327
}
 
328
 
 
329
/* dict_mysql_get_active - get an active connection */
 
330
 
 
331
static HOST *dict_mysql_get_active(PLMYSQL *PLDB, char *dbname,
 
332
                                   char *username, char *password)
 
333
{
 
334
    const char *myname = "dict_mysql_get_active";
 
335
    HOST   *host;
 
336
    int     count = RETRY_CONN_MAX;
 
337
 
 
338
    /* Try the active connections first; prefer the ones to UNIX sockets. */
 
339
    if ((host = dict_mysql_find_host(PLDB, STATACTIVE, TYPEUNIX)) != NULL ||
 
340
        (host = dict_mysql_find_host(PLDB, STATACTIVE, TYPEINET)) != NULL) {
 
341
        if (msg_verbose)
 
342
            msg_info("%s: found active connection to host %s", myname,
 
343
                     host->hostname);
 
344
        return host;
 
345
    }
 
346
 
 
347
    /*
 
348
     * Try the remaining hosts.
 
349
     * "count" is a safety net, in case the loop takes more than
 
350
     * RETRY_CONN_INTV and the dead hosts are no longer skipped.
 
351
     */
 
352
    while (--count > 0 &&
 
353
           ((host = dict_mysql_find_host(PLDB, STATUNTRIED | STATFAIL,
 
354
                                         TYPEUNIX)) != NULL ||
 
355
           (host = dict_mysql_find_host(PLDB, STATUNTRIED | STATFAIL,
 
356
                                        TYPEINET)) != NULL)) {
 
357
        if (msg_verbose)
 
358
            msg_info("%s: attempting to connect to host %s", myname,
 
359
                     host->hostname);
 
360
        plmysql_connect_single(host, dbname, username, password);
 
361
        if (host->stat == STATACTIVE)
 
362
            return host;
 
363
    }
 
364
 
 
365
    /* bad news... */
 
366
    return 0;
 
367
}
 
368
 
 
369
/* dict_mysql_event - callback: close idle connections */
 
370
 
 
371
static void dict_mysql_event(int unused_event, char *context)
 
372
{
 
373
    HOST   *host = (HOST *) context;
 
374
 
 
375
    if (host->db)
 
376
        plmysql_close_host(host);
 
377
}
 
378
 
 
379
/*
 
380
 * plmysql_query - process a MySQL query.  Return MYSQL_RES* on success.
 
381
 *                      On failure, log failure and try other db instances.
 
382
 *                      on failure of all db instances, return 0;
 
383
 *                      close unnecessary active connections
 
384
 */
 
385
 
 
386
static MYSQL_RES *plmysql_query(PLMYSQL *PLDB,
 
387
                                        const char *query,
 
388
                                        char *dbname,
 
389
                                        char *username,
 
390
                                        char *password)
 
391
{
 
392
    HOST   *host;
 
393
    MYSQL_RES *res = 0;
 
394
 
 
395
    while ((host = dict_mysql_get_active(PLDB, dbname, username, password)) != NULL) {
 
396
        if (!(mysql_query(host->db, query))) {
 
397
            if ((res = mysql_store_result(host->db)) == 0) {
 
398
                msg_warn("mysql query failed: %s", mysql_error(host->db));
 
399
                plmysql_down_host(host);
 
400
            } else {
 
401
                if (msg_verbose)
 
402
                    msg_info("dict_mysql: successful query from host %s", host->hostname);
 
403
                event_request_timer(dict_mysql_event, (char *) host, IDLE_CONN_INTV);
 
404
                break;
 
405
            }
 
406
        } else {
 
407
            msg_warn("mysql query failed: %s", mysql_error(host->db));
 
408
            plmysql_down_host(host);
 
409
        }
 
410
    }
 
411
 
 
412
    return res;
 
413
}
 
414
 
 
415
/*
 
416
 * plmysql_connect_single -
 
417
 * used to reconnect to a single database when one is down or none is
 
418
 * connected yet. Log all errors and set the stat field of host accordingly
 
419
 */
 
420
static void plmysql_connect_single(HOST *host, char *dbname, char *username, char *password)
 
421
{
 
422
    if ((host->db = mysql_init(NULL)) == NULL)
 
423
        msg_fatal("dict_mysql: insufficient memory");
 
424
    if (mysql_real_connect(host->db,
 
425
                           (host->type == TYPEINET ? host->name : 0),
 
426
                           username,
 
427
                           password,
 
428
                           dbname,
 
429
                           host->port,
 
430
                           (host->type == TYPEUNIX ? host->name : 0),
 
431
                           0)) {
 
432
        if (msg_verbose)
 
433
            msg_info("dict_mysql: successful connection to host %s",
 
434
                     host->hostname);
 
435
        host->stat = STATACTIVE;
 
436
    } else {
 
437
        msg_warn("connect to mysql server %s: %s",
 
438
                 host->hostname, mysql_error(host->db));
 
439
        plmysql_down_host(host);
 
440
    }
 
441
}
 
442
 
 
443
/* plmysql_close_host - close an established MySQL connection */
 
444
static void plmysql_close_host(HOST *host)
 
445
{
 
446
    mysql_close(host->db);
 
447
    host->db = 0;
 
448
    host->stat = STATUNTRIED;
 
449
}
 
450
 
 
451
/*
 
452
 * plmysql_down_host - close a failed connection AND set a "stay away from
 
453
 * this host" timer
 
454
 */
 
455
static void plmysql_down_host(HOST *host)
 
456
{
 
457
    mysql_close(host->db);
 
458
    host->db = 0;
 
459
    host->ts = time((time_t *) 0) + RETRY_CONN_INTV;
 
460
    host->stat = STATFAIL;
 
461
    event_cancel_timer(dict_mysql_event, (char *) host);
 
462
}
 
463
 
 
464
/**********************************************************************
 
465
 * public interface dict_mysql_open
 
466
 *    create association with database with appropriate values
 
467
 *    parse the map's config file
 
468
 *    allocate memory
 
469
 **********************************************************************/
 
470
DICT   *dict_mysql_open(const char *name, int open_flags, int dict_flags)
 
471
{
 
472
    DICT_MYSQL *dict_mysql;
 
473
 
 
474
    /*
 
475
     * Sanity checks.
 
476
     */
 
477
    if (open_flags != O_RDONLY)
 
478
        msg_fatal("%s:%s map requires O_RDONLY access mode",
 
479
                  DICT_TYPE_MYSQL, name);
 
480
 
 
481
    dict_mysql = (DICT_MYSQL *) dict_alloc(DICT_TYPE_MYSQL, name,
 
482
                                           sizeof(DICT_MYSQL));
 
483
    dict_mysql->dict.lookup = dict_mysql_lookup;
 
484
    dict_mysql->dict.close = dict_mysql_close;
 
485
    dict_mysql->dict.flags = dict_flags | DICT_FLAG_FIXED;
 
486
    dict_mysql->name = mysqlname_parse(name);
 
487
    dict_mysql->pldb = plmysql_init(dict_mysql->name->hostnames,
 
488
                                    dict_mysql->name->len_hosts);
 
489
    if (dict_mysql->pldb == NULL)
 
490
        msg_fatal("couldn't intialize pldb!\n");
 
491
    return (DICT_DEBUG (&dict_mysql->dict));
 
492
}
 
493
 
 
494
/* mysqlname_parse - parse mysql configuration file */
 
495
static MYSQL_NAME *mysqlname_parse(const char *mysqlcf)
 
496
{
 
497
    const char *myname = "mysqlname_parse";
 
498
    int     i;
 
499
    char   *hosts;
 
500
    MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
 
501
    ARGV   *hosts_argv;
 
502
 
 
503
    /* parser */
 
504
    name->parser = cfg_parser_alloc(mysqlcf);
 
505
 
 
506
    /* username */
 
507
    name->username = cfg_get_str(name->parser, "user", "", 0, 0);
 
508
 
 
509
    /* password */
 
510
    name->password = cfg_get_str(name->parser, "password", "", 0, 0);
 
511
 
 
512
    /* database name */
 
513
    name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);
 
514
 
 
515
    /* table name */
 
516
    name->table = cfg_get_str(name->parser, "table", "", 1, 0);
 
517
 
 
518
    /* select field */
 
519
    name->select_field = cfg_get_str(name->parser, "select_field", "", 1, 0);
 
520
 
 
521
    /* where field */
 
522
    name->where_field = cfg_get_str(name->parser, "where_field", "", 1, 0);
 
523
 
 
524
    /* additional conditions */
 
525
    name->additional_conditions = cfg_get_str(name->parser,
 
526
                                              "additional_conditions",
 
527
                                              "", 0, 0);
 
528
 
 
529
    /* mysql server hosts */
 
530
    hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);
 
531
 
 
532
    /* coo argv interface */
 
533
    hosts_argv = argv_split(hosts, " ,\t\r\n");
 
534
    if (hosts_argv->argc == 0) {                /* no hosts specified,
 
535
                                                 * default to 'localhost' */
 
536
        if (msg_verbose)
 
537
            msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
 
538
                     myname, mysqlcf);
 
539
        argv_add(hosts_argv, "localhost", ARGV_END);
 
540
        argv_terminate(hosts_argv);
 
541
    }
 
542
    name->len_hosts = hosts_argv->argc;
 
543
    name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
 
544
    i = 0;
 
545
    for (i = 0; hosts_argv->argv[i] != NULL; i++) {
 
546
        name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
 
547
        if (msg_verbose)
 
548
            msg_info("%s: %s: adding host '%s' to list of mysql server hosts",
 
549
                     myname, mysqlcf, name->hostnames[i]);
 
550
    }
 
551
    myfree(hosts);
 
552
    argv_free(hosts_argv);
 
553
    return name;
 
554
}
 
555
 
 
556
 
 
557
/*
 
558
 * plmysql_init - initalize a MYSQL database.
 
559
 *                  Return NULL on failure, or a PLMYSQL * on success.
 
560
 */
 
561
static PLMYSQL *plmysql_init(char *hostnames[], int len_hosts)
 
562
{
 
563
    PLMYSQL *PLDB;
 
564
    int     i;
 
565
 
 
566
    if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) {
 
567
        msg_fatal("mymalloc of pldb failed");
 
568
    }
 
569
    PLDB->len_hosts = len_hosts;
 
570
    if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * len_hosts)) == NULL)
 
571
        return NULL;
 
572
    for (i = 0; i < len_hosts; i++) {
 
573
        PLDB->db_hosts[i] = host_init(hostnames[i]);
 
574
    }
 
575
    return PLDB;
 
576
}
 
577
 
 
578
 
 
579
/* host_init - initialize HOST structure */
 
580
static HOST *host_init(const char *hostname)
 
581
{
 
582
    const char *myname = "mysql host_init";
 
583
    HOST   *host = (HOST *) mymalloc(sizeof(HOST));
 
584
    const char *d = hostname;
 
585
    char   *s;
 
586
 
 
587
    host->db = 0;
 
588
    host->hostname = mystrdup(hostname);
 
589
    host->port = 0;
 
590
    host->stat = STATUNTRIED;
 
591
    host->ts = 0;
 
592
 
 
593
    /*
 
594
     * Ad-hoc parsing code. Expect "unix:pathname" or "inet:host:port", where
 
595
     * both "inet:" and ":port" are optional.
 
596
     */
 
597
    if (strncmp(d, "unix:", 5) == 0) {
 
598
        d += 5;
 
599
        host->type = TYPEUNIX;
 
600
    } else {
 
601
        if (strncmp(d, "inet:", 5) == 0)
 
602
            d += 5;
 
603
        host->type = TYPEINET;
 
604
    }
 
605
    host->name = mystrdup(d);
 
606
    if ((s = split_at_right(host->name, ':')) != 0)
 
607
        host->port = ntohs(find_inet_port(s, "tcp"));
 
608
    if (strcasecmp(host->name, "localhost") == 0) {
 
609
        /* The MySQL way: this will actually connect over the UNIX socket */
 
610
        myfree(host->name);
 
611
        host->name = 0;
 
612
        host->type = TYPEUNIX;
 
613
    }
 
614
 
 
615
    if (msg_verbose > 1)
 
616
        msg_info("%s: host=%s, port=%d, type=%s", myname,
 
617
                 host->name ? host->name : "localhost",
 
618
                 host->port, host->type == TYPEUNIX ? "unix" : "inet");
 
619
    return host;
 
620
}
 
621
 
 
622
/**********************************************************************
 
623
 * public interface dict_mysql_close
 
624
 * unregister, disassociate from database, freeing appropriate memory
 
625
 **********************************************************************/
 
626
static void dict_mysql_close(DICT *dict)
 
627
{
 
628
    int     i;
 
629
    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
 
630
 
 
631
    plmysql_dealloc(dict_mysql->pldb);
 
632
    cfg_parser_free(dict_mysql->name->parser);
 
633
    myfree(dict_mysql->name->username);
 
634
    myfree(dict_mysql->name->password);
 
635
    myfree(dict_mysql->name->dbname);
 
636
    myfree(dict_mysql->name->table);
 
637
    myfree(dict_mysql->name->select_field);
 
638
    myfree(dict_mysql->name->where_field);
 
639
    myfree(dict_mysql->name->additional_conditions);
 
640
    for (i = 0; i < dict_mysql->name->len_hosts; i++) {
 
641
        myfree(dict_mysql->name->hostnames[i]);
 
642
    }
 
643
    myfree((char *) dict_mysql->name->hostnames);
 
644
    myfree((char *) dict_mysql->name);
 
645
    dict_free(dict);
 
646
}
 
647
 
 
648
/* plmysql_dealloc - free memory associated with PLMYSQL close databases */
 
649
static void plmysql_dealloc(PLMYSQL *PLDB)
 
650
{
 
651
    int     i;
 
652
 
 
653
    for (i = 0; i < PLDB->len_hosts; i++) {
 
654
        event_cancel_timer(dict_mysql_event, (char *) (PLDB->db_hosts[i]));
 
655
        if (PLDB->db_hosts[i]->db)
 
656
            mysql_close(PLDB->db_hosts[i]->db);
 
657
        myfree(PLDB->db_hosts[i]->hostname);
 
658
        if (PLDB->db_hosts[i]->name)
 
659
            myfree(PLDB->db_hosts[i]->name);
 
660
        myfree((char *) PLDB->db_hosts[i]);
 
661
    }
 
662
    myfree((char *) PLDB->db_hosts);
 
663
    myfree((char *) (PLDB));
 
664
}
 
665
 
 
666
#endif