00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413225 $")
00033
00034 #include <libpq-fe.h>
00035
00036 #include "asterisk/file.h"
00037 #include "asterisk/channel.h"
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/config.h"
00040 #include "asterisk/module.h"
00041 #include "asterisk/lock.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/cli.h"
00044
00045 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00046 AST_THREADSTORAGE(sql_buf);
00047 AST_THREADSTORAGE(findtable_buf);
00048 AST_THREADSTORAGE(where_buf);
00049 AST_THREADSTORAGE(escapebuf_buf);
00050 AST_THREADSTORAGE(semibuf_buf);
00051
00052 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
00053
00054 static PGconn *pgsqlConn = NULL;
00055 static int version;
00056 #define has_schema_support (version > 70300 ? 1 : 0)
00057
00058 #define MAX_DB_OPTION_SIZE 64
00059
00060 struct columns {
00061 char *name;
00062 char *type;
00063 int len;
00064 unsigned int notnull:1;
00065 unsigned int hasdefault:1;
00066 AST_LIST_ENTRY(columns) list;
00067 };
00068
00069 struct tables {
00070 ast_rwlock_t lock;
00071 AST_LIST_HEAD_NOLOCK(psql_columns, columns) columns;
00072 AST_LIST_ENTRY(tables) list;
00073 char name[0];
00074 };
00075
00076 static AST_LIST_HEAD_STATIC(psql_tables, tables);
00077
00078 static char dbhost[MAX_DB_OPTION_SIZE] = "";
00079 static char dbuser[MAX_DB_OPTION_SIZE] = "";
00080 static char dbpass[MAX_DB_OPTION_SIZE] = "";
00081 static char dbname[MAX_DB_OPTION_SIZE] = "";
00082 static char dbsock[MAX_DB_OPTION_SIZE] = "";
00083 static int dbport = 5432;
00084 static time_t connect_time = 0;
00085
00086 static int parse_config(int reload);
00087 static int pgsql_reconnect(const char *database);
00088 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00089 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00090
00091 static enum { RQ_WARN, RQ_CREATECLOSE, RQ_CREATECHAR } requirements;
00092
00093 static struct ast_cli_entry cli_realtime[] = {
00094 AST_CLI_DEFINE(handle_cli_realtime_pgsql_status, "Shows connection information for the PostgreSQL RealTime driver"),
00095 AST_CLI_DEFINE(handle_cli_realtime_pgsql_cache, "Shows cached tables within the PostgreSQL realtime driver"),
00096 };
00097
00098 #define ESCAPE_STRING(buffer, stringname) \
00099 do { \
00100 int len = strlen(stringname); \
00101 struct ast_str *semi = ast_str_thread_get(&semibuf_buf, len * 3 + 1); \
00102 const char *chunk = stringname; \
00103 ast_str_reset(semi); \
00104 for (; *chunk; chunk++) { \
00105 if (strchr(";^", *chunk)) { \
00106 ast_str_append(&semi, 0, "^%02hhX", *chunk); \
00107 } else { \
00108 ast_str_append(&semi, 0, "%c", *chunk); \
00109 } \
00110 } \
00111 if (ast_str_strlen(semi) > (ast_str_size(buffer) - 1) / 2) { \
00112 ast_str_make_space(&buffer, ast_str_strlen(semi) * 2 + 1); \
00113 } \
00114 PQescapeStringConn(pgsqlConn, ast_str_buffer(buffer), ast_str_buffer(semi), ast_str_size(buffer), &pgresult); \
00115 } while (0)
00116
00117 static void destroy_table(struct tables *table)
00118 {
00119 struct columns *column;
00120 ast_rwlock_wrlock(&table->lock);
00121 while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) {
00122 ast_free(column);
00123 }
00124 ast_rwlock_unlock(&table->lock);
00125 ast_rwlock_destroy(&table->lock);
00126 ast_free(table);
00127 }
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 static int _pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
00145 {
00146 ExecStatusType result_status;
00147
00148 if (!pgsqlConn) {
00149 ast_debug(1, "PostgreSQL connection not defined, connecting\n");
00150
00151 if (pgsql_reconnect(database) != 1) {
00152 ast_log(LOG_NOTICE, "reconnect failed\n");
00153 *result = NULL;
00154 return -1;
00155 }
00156
00157 ast_debug(1, "PostgreSQL connection successful\n");
00158 }
00159
00160 *result = PQexec(pgsqlConn, sql);
00161 result_status = PQresultStatus(*result);
00162 if (result_status != PGRES_COMMAND_OK
00163 && result_status != PGRES_TUPLES_OK
00164 && result_status != PGRES_NONFATAL_ERROR) {
00165
00166 ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to query '%s@%s'.\n", tablename, database);
00167 ast_log(LOG_ERROR, "PostgreSQL RealTime: Query Failed: %s\n", sql);
00168 ast_log(LOG_ERROR, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00169 PQresultErrorMessage(*result),
00170 PQresStatus(result_status));
00171
00172
00173
00174 if (PQstatus(pgsqlConn) != CONNECTION_OK) {
00175 PQfinish(pgsqlConn);
00176 pgsqlConn = NULL;
00177 return -2;
00178 }
00179
00180
00181 return -1;
00182 }
00183
00184 ast_debug(1, "PostgreSQL query successful: %s\n", sql);
00185 return 0;
00186 }
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 static int pgsql_exec(const char *database, const char *tablename, const char *sql, PGresult **result)
00219 {
00220 int attempts = 0;
00221 int res;
00222
00223
00224
00225
00226
00227 while (attempts++ < 2) {
00228 ast_debug(1, "PostgreSQL query attempt %d\n", attempts);
00229 res = _pgsql_exec(database, tablename, sql, result);
00230
00231 if (res == 0) {
00232 if (attempts > 1) {
00233 ast_log(LOG_NOTICE, "PostgreSQL RealTime: Query finally succeeded: %s\n", sql);
00234 }
00235
00236 return 0;
00237 }
00238
00239 if (res == -1) {
00240 return -1;
00241 }
00242
00243
00244 ast_debug(1, "PostgreSQL query attempt %d failed, trying again\n", attempts);
00245 }
00246
00247 return -1;
00248 }
00249
00250 static struct tables *find_table(const char *database, const char *orig_tablename)
00251 {
00252 struct columns *column;
00253 struct tables *table;
00254 struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330);
00255 RAII_VAR(PGresult *, result, NULL, PQclear);
00256 int exec_result;
00257 char *fname, *ftype, *flen, *fnotnull, *fdef;
00258 int i, rows;
00259
00260 AST_LIST_LOCK(&psql_tables);
00261 AST_LIST_TRAVERSE(&psql_tables, table, list) {
00262 if (!strcasecmp(table->name, orig_tablename)) {
00263 ast_debug(1, "Found table in cache; now locking\n");
00264 ast_rwlock_rdlock(&table->lock);
00265 ast_debug(1, "Lock cached table; now returning\n");
00266 AST_LIST_UNLOCK(&psql_tables);
00267 return table;
00268 }
00269 }
00270
00271 if (database == NULL) {
00272 return NULL;
00273 }
00274
00275 ast_debug(1, "Table '%s' not found in cache, querying now\n", orig_tablename);
00276
00277
00278 if (has_schema_support) {
00279 char *schemaname, *tablename;
00280 if (strchr(orig_tablename, '.')) {
00281 schemaname = ast_strdupa(orig_tablename);
00282 tablename = strchr(schemaname, '.');
00283 *tablename++ = '\0';
00284 } else {
00285 schemaname = "";
00286 tablename = ast_strdupa(orig_tablename);
00287 }
00288
00289
00290 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00291 char *tmp = schemaname, *ptr;
00292
00293 ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
00294 for (; *tmp; tmp++) {
00295 if (strchr("\\'", *tmp)) {
00296 *ptr++ = *tmp;
00297 }
00298 *ptr++ = *tmp;
00299 }
00300 *ptr = '\0';
00301 }
00302
00303 if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00304 char *tmp = tablename, *ptr;
00305
00306 ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
00307 for (; *tmp; tmp++) {
00308 if (strchr("\\'", *tmp)) {
00309 *ptr++ = *tmp;
00310 }
00311 *ptr++ = *tmp;
00312 }
00313 *ptr = '\0';
00314 }
00315
00316 ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
00317 tablename,
00318 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00319 } else {
00320
00321 if (strchr(orig_tablename, '\\') || strchr(orig_tablename, '\'')) {
00322 const char *tmp = orig_tablename;
00323 char *ptr;
00324
00325 orig_tablename = ptr = ast_alloca(strlen(tmp) * 2 + 1);
00326 for (; *tmp; tmp++) {
00327 if (strchr("\\'", *tmp)) {
00328 *ptr++ = *tmp;
00329 }
00330 *ptr++ = *tmp;
00331 }
00332 *ptr = '\0';
00333 }
00334
00335 ast_str_set(&sql, 0, "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", orig_tablename);
00336 }
00337
00338 exec_result = pgsql_exec(database, orig_tablename, ast_str_buffer(sql), &result);
00339 ast_debug(1, "Query of table structure complete. Now retrieving results.\n");
00340 if (exec_result != 0) {
00341 ast_log(LOG_ERROR, "Failed to query database columns for table %s\n", orig_tablename);
00342 AST_LIST_UNLOCK(&psql_tables);
00343 return NULL;
00344 }
00345
00346 if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
00347 ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
00348 AST_LIST_UNLOCK(&psql_tables);
00349 return NULL;
00350 }
00351 strcpy(table->name, orig_tablename);
00352 ast_rwlock_init(&table->lock);
00353 AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
00354
00355 rows = PQntuples(result);
00356 for (i = 0; i < rows; i++) {
00357 fname = PQgetvalue(result, i, 0);
00358 ftype = PQgetvalue(result, i, 1);
00359 flen = PQgetvalue(result, i, 2);
00360 fnotnull = PQgetvalue(result, i, 3);
00361 fdef = PQgetvalue(result, i, 4);
00362 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00363
00364 if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
00365 ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
00366 destroy_table(table);
00367 AST_LIST_UNLOCK(&psql_tables);
00368 return NULL;
00369 }
00370
00371 if (strcmp(flen, "-1") == 0) {
00372
00373 flen = PQgetvalue(result, i, 5);
00374 sscanf(flen, "%30d", &column->len);
00375 column->len -= 4;
00376 } else {
00377 sscanf(flen, "%30d", &column->len);
00378 }
00379 column->name = (char *)column + sizeof(*column);
00380 column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
00381 strcpy(column->name, fname);
00382 strcpy(column->type, ftype);
00383 if (*fnotnull == 't') {
00384 column->notnull = 1;
00385 } else {
00386 column->notnull = 0;
00387 }
00388 if (!ast_strlen_zero(fdef)) {
00389 column->hasdefault = 1;
00390 } else {
00391 column->hasdefault = 0;
00392 }
00393 AST_LIST_INSERT_TAIL(&table->columns, column, list);
00394 }
00395
00396 AST_LIST_INSERT_TAIL(&psql_tables, table, list);
00397 ast_rwlock_rdlock(&table->lock);
00398 AST_LIST_UNLOCK(&psql_tables);
00399 return table;
00400 }
00401
00402 #define release_table(table) ast_rwlock_unlock(&(table)->lock);
00403
00404 static struct columns *find_column(struct tables *t, const char *colname)
00405 {
00406 struct columns *column;
00407
00408
00409 AST_LIST_TRAVERSE(&t->columns, column, list) {
00410 if (strcmp(column->name, colname) == 0) {
00411 return column;
00412 }
00413 }
00414 return NULL;
00415 }
00416
00417 static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap)
00418 {
00419 RAII_VAR(PGresult *, result, NULL, PQclear);
00420 int num_rows = 0, pgresult;
00421 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00422 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00423 char *stringp;
00424 char *chunk;
00425 char *op;
00426 const char *newparam, *newval;
00427 struct ast_variable *var = NULL, *prev = NULL;
00428
00429
00430
00431
00432
00433 database = dbname;
00434
00435 if (!tablename) {
00436 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00437 return NULL;
00438 }
00439
00440
00441 newparam = va_arg(ap, const char *);
00442 newval = va_arg(ap, const char *);
00443 if (!newparam || !newval) {
00444 ast_log(LOG_WARNING,
00445 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00446 if (pgsqlConn) {
00447 PQfinish(pgsqlConn);
00448 pgsqlConn = NULL;
00449 }
00450 return NULL;
00451 }
00452
00453
00454
00455 op = strchr(newparam, ' ') ? "" : " =";
00456
00457 ESCAPE_STRING(escapebuf, newval);
00458 if (pgresult) {
00459 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00460 return NULL;
00461 }
00462
00463 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
00464 while ((newparam = va_arg(ap, const char *))) {
00465 newval = va_arg(ap, const char *);
00466 if (!strchr(newparam, ' '))
00467 op = " =";
00468 else
00469 op = "";
00470
00471 ESCAPE_STRING(escapebuf, newval);
00472 if (pgresult) {
00473 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00474 return NULL;
00475 }
00476
00477 ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00478 }
00479
00480
00481 ast_mutex_lock(&pgsql_lock);
00482
00483 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
00484 ast_mutex_unlock(&pgsql_lock);
00485 return NULL;
00486 }
00487
00488 ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00489
00490 if ((num_rows = PQntuples(result)) > 0) {
00491 int i = 0;
00492 int rowIndex = 0;
00493 int numFields = PQnfields(result);
00494 char **fieldnames = NULL;
00495
00496 ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00497
00498 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00499 ast_mutex_unlock(&pgsql_lock);
00500 return NULL;
00501 }
00502 for (i = 0; i < numFields; i++)
00503 fieldnames[i] = PQfname(result, i);
00504 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00505 for (i = 0; i < numFields; i++) {
00506 stringp = PQgetvalue(result, rowIndex, i);
00507 while (stringp) {
00508 chunk = strsep(&stringp, ";");
00509 if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00510 if (prev) {
00511 prev->next = ast_variable_new(fieldnames[i], chunk, "");
00512 if (prev->next) {
00513 prev = prev->next;
00514 }
00515 } else {
00516 prev = var = ast_variable_new(fieldnames[i], chunk, "");
00517 }
00518 }
00519 }
00520 }
00521 }
00522 ast_free(fieldnames);
00523 } else {
00524 ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
00525 }
00526
00527 ast_mutex_unlock(&pgsql_lock);
00528
00529 return var;
00530 }
00531
00532 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
00533 {
00534 RAII_VAR(PGresult *, result, NULL, PQclear);
00535 int num_rows = 0, pgresult;
00536 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00537 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00538 const char *initfield = NULL;
00539 char *stringp;
00540 char *chunk;
00541 char *op;
00542 const char *newparam, *newval;
00543 struct ast_variable *var = NULL;
00544 struct ast_config *cfg = NULL;
00545 struct ast_category *cat = NULL;
00546
00547
00548
00549
00550
00551 database = dbname;
00552
00553 if (!table) {
00554 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00555 return NULL;
00556 }
00557
00558 if (!(cfg = ast_config_new()))
00559 return NULL;
00560
00561
00562 newparam = va_arg(ap, const char *);
00563 newval = va_arg(ap, const char *);
00564 if (!newparam || !newval) {
00565 ast_log(LOG_WARNING,
00566 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00567 if (pgsqlConn) {
00568 PQfinish(pgsqlConn);
00569 pgsqlConn = NULL;
00570 }
00571 ast_config_destroy(cfg);
00572 return NULL;
00573 }
00574
00575 initfield = ast_strdupa(newparam);
00576 if ((op = strchr(initfield, ' '))) {
00577 *op = '\0';
00578 }
00579
00580
00581
00582
00583 if (!strchr(newparam, ' '))
00584 op = " =";
00585 else
00586 op = "";
00587
00588 ESCAPE_STRING(escapebuf, newval);
00589 if (pgresult) {
00590 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00591 ast_config_destroy(cfg);
00592 return NULL;
00593 }
00594
00595 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
00596 while ((newparam = va_arg(ap, const char *))) {
00597 newval = va_arg(ap, const char *);
00598 if (!strchr(newparam, ' '))
00599 op = " =";
00600 else
00601 op = "";
00602
00603 ESCAPE_STRING(escapebuf, newval);
00604 if (pgresult) {
00605 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00606 ast_config_destroy(cfg);
00607 return NULL;
00608 }
00609
00610 ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00611 }
00612
00613 if (initfield) {
00614 ast_str_append(&sql, 0, " ORDER BY %s", initfield);
00615 }
00616
00617
00618
00619 ast_mutex_lock(&pgsql_lock);
00620
00621 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
00622 ast_mutex_unlock(&pgsql_lock);
00623 ast_config_destroy(cfg);
00624 return NULL;
00625 } else {
00626 ExecStatusType result_status = PQresultStatus(result);
00627 if (result_status != PGRES_COMMAND_OK
00628 && result_status != PGRES_TUPLES_OK
00629 && result_status != PGRES_NONFATAL_ERROR) {
00630 ast_log(LOG_WARNING,
00631 "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00632 ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00633 ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00634 PQresultErrorMessage(result), PQresStatus(result_status));
00635 ast_mutex_unlock(&pgsql_lock);
00636 ast_config_destroy(cfg);
00637 return NULL;
00638 }
00639 }
00640
00641 ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00642
00643 if ((num_rows = PQntuples(result)) > 0) {
00644 int numFields = PQnfields(result);
00645 int i = 0;
00646 int rowIndex = 0;
00647 char **fieldnames = NULL;
00648
00649 ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00650
00651 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00652 ast_mutex_unlock(&pgsql_lock);
00653 ast_config_destroy(cfg);
00654 return NULL;
00655 }
00656 for (i = 0; i < numFields; i++)
00657 fieldnames[i] = PQfname(result, i);
00658
00659 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00660 var = NULL;
00661 if (!(cat = ast_category_new("","",99999)))
00662 continue;
00663 for (i = 0; i < numFields; i++) {
00664 stringp = PQgetvalue(result, rowIndex, i);
00665 while (stringp) {
00666 chunk = strsep(&stringp, ";");
00667 if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00668 if (initfield && !strcmp(initfield, fieldnames[i])) {
00669 ast_category_rename(cat, chunk);
00670 }
00671 var = ast_variable_new(fieldnames[i], chunk, "");
00672 ast_variable_append(cat, var);
00673 }
00674 }
00675 }
00676 ast_category_append(cfg, cat);
00677 }
00678 ast_free(fieldnames);
00679 } else {
00680 ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
00681 }
00682
00683 ast_mutex_unlock(&pgsql_lock);
00684
00685 return cfg;
00686 }
00687
00688 static int update_pgsql(const char *database, const char *tablename, const char *keyfield,
00689 const char *lookup, va_list ap)
00690 {
00691 RAII_VAR(PGresult *, result, NULL, PQclear);
00692 int numrows = 0, pgresult;
00693 const char *newparam, *newval;
00694 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00695 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00696 struct tables *table;
00697 struct columns *column = NULL;
00698
00699
00700
00701
00702
00703 database = dbname;
00704
00705 if (!tablename) {
00706 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00707 return -1;
00708 }
00709
00710 if (!(table = find_table(database, tablename))) {
00711 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00712 return -1;
00713 }
00714
00715
00716 newparam = va_arg(ap, const char *);
00717 newval = va_arg(ap, const char *);
00718 if (!newparam || !newval) {
00719 ast_log(LOG_WARNING,
00720 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00721 if (pgsqlConn) {
00722 PQfinish(pgsqlConn);
00723 pgsqlConn = NULL;
00724 }
00725 release_table(table);
00726 return -1;
00727 }
00728
00729
00730 AST_LIST_TRAVERSE(&table->columns, column, list) {
00731 if (strcmp(column->name, newparam) == 0) {
00732 break;
00733 }
00734 }
00735
00736 if (!column) {
00737 ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
00738 release_table(table);
00739 return -1;
00740 }
00741
00742
00743
00744
00745 ESCAPE_STRING(escapebuf, newval);
00746 if (pgresult) {
00747 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00748 release_table(table);
00749 return -1;
00750 }
00751 ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
00752
00753 while ((newparam = va_arg(ap, const char *))) {
00754 newval = va_arg(ap, const char *);
00755
00756 if (!find_column(table, newparam)) {
00757 ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
00758 continue;
00759 }
00760
00761 ESCAPE_STRING(escapebuf, newval);
00762 if (pgresult) {
00763 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00764 release_table(table);
00765 return -1;
00766 }
00767
00768 ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
00769 }
00770 release_table(table);
00771
00772 ESCAPE_STRING(escapebuf, lookup);
00773 if (pgresult) {
00774 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", lookup);
00775 return -1;
00776 }
00777
00778 ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
00779
00780 ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00781
00782
00783 ast_mutex_lock(&pgsql_lock);
00784
00785 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
00786 ast_mutex_unlock(&pgsql_lock);
00787 return -1;
00788 } else {
00789 ExecStatusType result_status = PQresultStatus(result);
00790 if (result_status != PGRES_COMMAND_OK
00791 && result_status != PGRES_TUPLES_OK
00792 && result_status != PGRES_NONFATAL_ERROR) {
00793 ast_log(LOG_WARNING,
00794 "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00795 ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00796 ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00797 PQresultErrorMessage(result), PQresStatus(result_status));
00798 ast_mutex_unlock(&pgsql_lock);
00799 return -1;
00800 }
00801 }
00802
00803 numrows = atoi(PQcmdTuples(result));
00804 ast_mutex_unlock(&pgsql_lock);
00805
00806 ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00807
00808
00809
00810
00811
00812
00813
00814 if (numrows >= 0)
00815 return (int) numrows;
00816
00817 return -1;
00818 }
00819
00820 static int update2_pgsql(const char *database, const char *tablename, va_list ap)
00821 {
00822 RAII_VAR(PGresult *, result, NULL, PQclear);
00823 int numrows = 0, pgresult, first = 1;
00824 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
00825 const char *newparam, *newval;
00826 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00827 struct ast_str *where = ast_str_thread_get(&where_buf, 100);
00828 struct tables *table;
00829
00830
00831
00832
00833
00834 database = dbname;
00835
00836 if (!tablename) {
00837 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00838 return -1;
00839 }
00840
00841 if (!escapebuf || !sql || !where) {
00842
00843 return -1;
00844 }
00845
00846 if (!(table = find_table(database, tablename))) {
00847 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00848 return -1;
00849 }
00850
00851 ast_str_set(&sql, 0, "UPDATE %s SET", tablename);
00852 ast_str_set(&where, 0, " WHERE");
00853
00854 while ((newparam = va_arg(ap, const char *))) {
00855 if (!find_column(table, newparam)) {
00856 ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
00857 release_table(table);
00858 return -1;
00859 }
00860
00861 newval = va_arg(ap, const char *);
00862 ESCAPE_STRING(escapebuf, newval);
00863 if (pgresult) {
00864 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00865 release_table(table);
00866 return -1;
00867 }
00868 ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
00869 first = 0;
00870 }
00871
00872 if (first) {
00873 ast_log(LOG_WARNING,
00874 "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
00875 if (pgsqlConn) {
00876 PQfinish(pgsqlConn);
00877 pgsqlConn = NULL;
00878 }
00879 release_table(table);
00880 return -1;
00881 }
00882
00883
00884 first = 1;
00885 while ((newparam = va_arg(ap, const char *))) {
00886 newval = va_arg(ap, const char *);
00887
00888
00889 if (!find_column(table, newparam)) {
00890 ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
00891 continue;
00892 }
00893
00894 ESCAPE_STRING(escapebuf, newval);
00895 if (pgresult) {
00896 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00897 release_table(table);
00898 return -1;
00899 }
00900
00901 ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
00902 first = 0;
00903 }
00904 release_table(table);
00905
00906 ast_str_append(&sql, 0, "%s", ast_str_buffer(where));
00907
00908 ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00909
00910
00911 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
00912 ast_mutex_unlock(&pgsql_lock);
00913 return -1;
00914 }
00915
00916 numrows = atoi(PQcmdTuples(result));
00917 ast_mutex_unlock(&pgsql_lock);
00918
00919 ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00920
00921
00922
00923
00924
00925
00926
00927 if (numrows >= 0) {
00928 return (int) numrows;
00929 }
00930
00931 return -1;
00932 }
00933
00934 static int store_pgsql(const char *database, const char *table, va_list ap)
00935 {
00936 RAII_VAR(PGresult *, result, NULL, PQclear);
00937 int numrows;
00938 struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
00939 struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
00940 struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
00941 int pgresult;
00942 const char *newparam, *newval;
00943
00944
00945
00946
00947
00948 database = dbname;
00949
00950 if (!table) {
00951 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00952 return -1;
00953 }
00954
00955
00956 newparam = va_arg(ap, const char *);
00957 newval = va_arg(ap, const char *);
00958 if (!newparam || !newval) {
00959 ast_log(LOG_WARNING,
00960 "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
00961 if (pgsqlConn) {
00962 PQfinish(pgsqlConn);
00963 pgsqlConn = NULL;
00964 }
00965 return -1;
00966 }
00967
00968
00969 ast_mutex_lock(&pgsql_lock);
00970 if (!pgsql_reconnect(database)) {
00971 ast_mutex_unlock(&pgsql_lock);
00972 return -1;
00973 }
00974
00975
00976
00977 ESCAPE_STRING(buf, newparam);
00978 ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
00979 ESCAPE_STRING(buf, newval);
00980 ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
00981 while ((newparam = va_arg(ap, const char *))) {
00982 newval = va_arg(ap, const char *);
00983 ESCAPE_STRING(buf, newparam);
00984 ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
00985 ESCAPE_STRING(buf, newval);
00986 ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
00987 }
00988 ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00989
00990 ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
00991
00992 if (pgsql_exec(database, table, ast_str_buffer(sql1), &result) != 0) {
00993 ast_mutex_unlock(&pgsql_lock);
00994 return -1;
00995 }
00996
00997 numrows = atoi(PQcmdTuples(result));
00998 ast_mutex_unlock(&pgsql_lock);
00999
01000 ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s.", table);
01001
01002
01003
01004
01005
01006
01007
01008 if (numrows >= 0) {
01009 return numrows;
01010 }
01011
01012 return -1;
01013 }
01014
01015 static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
01016 {
01017 RAII_VAR(PGresult *, result, NULL, PQclear);
01018 int numrows = 0;
01019 int pgresult;
01020 struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
01021 struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
01022 const char *newparam, *newval;
01023
01024
01025
01026
01027
01028 database = dbname;
01029
01030 if (!table) {
01031 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
01032 return -1;
01033 }
01034
01035
01036
01037
01038
01039 if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
01040 ast_log(LOG_WARNING,
01041 "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
01042 if (pgsqlConn) {
01043 PQfinish(pgsqlConn);
01044 pgsqlConn = NULL;
01045 };
01046 return -1;
01047 }
01048
01049
01050 ast_mutex_lock(&pgsql_lock);
01051 if (!pgsql_reconnect(database)) {
01052 ast_mutex_unlock(&pgsql_lock);
01053 return -1;
01054 }
01055
01056
01057
01058
01059
01060 ESCAPE_STRING(buf1, keyfield);
01061 ESCAPE_STRING(buf2, lookup);
01062 ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
01063 while ((newparam = va_arg(ap, const char *))) {
01064 newval = va_arg(ap, const char *);
01065 ESCAPE_STRING(buf1, newparam);
01066 ESCAPE_STRING(buf2, newval);
01067 ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
01068 }
01069
01070 ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
01071
01072 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
01073 ast_mutex_unlock(&pgsql_lock);
01074 return -1;
01075 }
01076
01077 numrows = atoi(PQcmdTuples(result));
01078 ast_mutex_unlock(&pgsql_lock);
01079
01080 ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01081
01082
01083
01084
01085
01086
01087
01088 if (numrows >= 0)
01089 return (int) numrows;
01090
01091 return -1;
01092 }
01093
01094
01095 static struct ast_config *config_pgsql(const char *database, const char *table,
01096 const char *file, struct ast_config *cfg,
01097 struct ast_flags flags, const char *suggested_incl, const char *who_asked)
01098 {
01099 RAII_VAR(PGresult *, result, NULL, PQclear);
01100 long num_rows;
01101 struct ast_variable *new_v;
01102 struct ast_category *cur_cat = NULL;
01103 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01104 char last[80];
01105 int last_cat_metric = 0;
01106
01107 last[0] = '\0';
01108
01109
01110
01111
01112
01113 database = dbname;
01114
01115 if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01116 ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01117 return NULL;
01118 }
01119
01120 ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01121 "WHERE filename='%s' and commented=0 "
01122 "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01123
01124 ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01125
01126 ast_mutex_lock(&pgsql_lock);
01127
01128
01129 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
01130 ast_mutex_unlock(&pgsql_lock);
01131 return NULL;
01132 }
01133
01134 if ((num_rows = PQntuples(result)) > 0) {
01135 int rowIndex = 0;
01136
01137 ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01138
01139 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01140 char *field_category = PQgetvalue(result, rowIndex, 0);
01141 char *field_var_name = PQgetvalue(result, rowIndex, 1);
01142 char *field_var_val = PQgetvalue(result, rowIndex, 2);
01143 char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01144 if (!strcmp(field_var_name, "#include")) {
01145 if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01146 ast_mutex_unlock(&pgsql_lock);
01147 return NULL;
01148 }
01149 continue;
01150 }
01151
01152 if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01153 cur_cat = ast_category_new(field_category, "", 99999);
01154 if (!cur_cat)
01155 break;
01156 ast_copy_string(last, field_category, sizeof(last));
01157 last_cat_metric = atoi(field_cat_metric);
01158 ast_category_append(cfg, cur_cat);
01159 }
01160 new_v = ast_variable_new(field_var_name, field_var_val, "");
01161 ast_variable_append(cur_cat, new_v);
01162 }
01163 } else {
01164 ast_log(LOG_WARNING,
01165 "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01166 }
01167
01168 ast_mutex_unlock(&pgsql_lock);
01169
01170 return cfg;
01171 }
01172
01173 static int require_pgsql(const char *database, const char *tablename, va_list ap)
01174 {
01175 struct columns *column;
01176 struct tables *table;
01177 char *elm;
01178 int type, size, res = 0;
01179
01180
01181
01182
01183
01184 database = dbname;
01185
01186 table = find_table(database, tablename);
01187 if (!table) {
01188 ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
01189 return -1;
01190 }
01191
01192 while ((elm = va_arg(ap, char *))) {
01193 type = va_arg(ap, require_type);
01194 size = va_arg(ap, int);
01195 AST_LIST_TRAVERSE(&table->columns, column, list) {
01196 if (strcmp(column->name, elm) == 0) {
01197
01198 if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01199 if ((size > column->len) && column->len != -1) {
01200 ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01201 res = -1;
01202 }
01203 } else if (strncmp(column->type, "int", 3) == 0) {
01204 int typesize = atoi(column->type + 3);
01205
01206 if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01207 type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01208 type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01209 type == RQ_UINTEGER2) && typesize == 2) {
01210 ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01211 res = -1;
01212 } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01213 type == RQ_UINTEGER4) && typesize == 4) {
01214 ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01215 res = -1;
01216 } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01217 ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01218 column->name,
01219 type == RQ_CHAR ? "char" :
01220 type == RQ_DATETIME ? "datetime" :
01221 type == RQ_DATE ? "date" :
01222 type == RQ_FLOAT ? "float" :
01223 "a rather stiff drink ",
01224 size, column->type);
01225 res = -1;
01226 }
01227 } else if (strncmp(column->type, "float", 5) == 0) {
01228 if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01229 ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01230 res = -1;
01231 }
01232 } else if (strncmp(column->type, "timestamp", 9) == 0) {
01233 if (type != RQ_DATETIME && type != RQ_DATE) {
01234 ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01235 res = -1;
01236 }
01237 } else {
01238 ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01239 res = -1;
01240 }
01241 break;
01242 }
01243 }
01244
01245 if (!column) {
01246 if (requirements == RQ_WARN) {
01247 ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01248 } else {
01249 struct ast_str *sql = ast_str_create(100);
01250 char fieldtype[15];
01251 PGresult *result;
01252
01253 if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01254
01255
01256
01257 snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01258 size < 15 ? size * 2 :
01259 (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01260 } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01261 snprintf(fieldtype, sizeof(fieldtype), "INT2");
01262 } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01263 snprintf(fieldtype, sizeof(fieldtype), "INT4");
01264 } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01265 snprintf(fieldtype, sizeof(fieldtype), "INT8");
01266 } else if (type == RQ_UINTEGER8) {
01267
01268 snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01269 } else if (type == RQ_FLOAT) {
01270 snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01271 } else if (type == RQ_DATE) {
01272 snprintf(fieldtype, sizeof(fieldtype), "DATE");
01273 } else if (type == RQ_DATETIME) {
01274 snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01275 } else {
01276 ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01277 ast_free(sql);
01278 continue;
01279 }
01280 ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01281 ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01282
01283 ast_mutex_lock(&pgsql_lock);
01284 ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01285
01286 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
01287 ast_mutex_unlock(&pgsql_lock);
01288 return -1;
01289 }
01290
01291 ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01292 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01293 ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01294 }
01295 PQclear(result);
01296 ast_mutex_unlock(&pgsql_lock);
01297
01298 ast_free(sql);
01299 }
01300 }
01301 }
01302 release_table(table);
01303 return res;
01304 }
01305
01306 static int unload_pgsql(const char *database, const char *tablename)
01307 {
01308 struct tables *cur;
01309
01310
01311
01312
01313
01314 database = dbname;
01315
01316 ast_debug(2, "About to lock table cache list\n");
01317 AST_LIST_LOCK(&psql_tables);
01318 ast_debug(2, "About to traverse table cache list\n");
01319 AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01320 if (strcmp(cur->name, tablename) == 0) {
01321 ast_debug(2, "About to remove matching cache entry\n");
01322 AST_LIST_REMOVE_CURRENT(list);
01323 ast_debug(2, "About to destroy matching cache entry\n");
01324 destroy_table(cur);
01325 ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01326 break;
01327 }
01328 }
01329 AST_LIST_TRAVERSE_SAFE_END
01330 AST_LIST_UNLOCK(&psql_tables);
01331 ast_debug(2, "About to return\n");
01332 return cur ? 0 : -1;
01333 }
01334
01335 static struct ast_config_engine pgsql_engine = {
01336 .name = "pgsql",
01337 .load_func = config_pgsql,
01338 .realtime_func = realtime_pgsql,
01339 .realtime_multi_func = realtime_multi_pgsql,
01340 .store_func = store_pgsql,
01341 .destroy_func = destroy_pgsql,
01342 .update_func = update_pgsql,
01343 .update2_func = update2_pgsql,
01344 .require_func = require_pgsql,
01345 .unload_func = unload_pgsql,
01346 };
01347
01348 static int load_module(void)
01349 {
01350 if(!parse_config(0))
01351 return AST_MODULE_LOAD_DECLINE;
01352
01353 ast_config_engine_register(&pgsql_engine);
01354 ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01355 ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01356
01357 return 0;
01358 }
01359
01360 static int unload_module(void)
01361 {
01362 struct tables *table;
01363
01364 ast_mutex_lock(&pgsql_lock);
01365
01366 if (pgsqlConn) {
01367 PQfinish(pgsqlConn);
01368 pgsqlConn = NULL;
01369 }
01370 ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01371 ast_config_engine_deregister(&pgsql_engine);
01372 ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01373
01374
01375 AST_LIST_LOCK(&psql_tables);
01376 while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01377 destroy_table(table);
01378 }
01379 AST_LIST_UNLOCK(&psql_tables);
01380
01381
01382 ast_mutex_unlock(&pgsql_lock);
01383
01384 return 0;
01385 }
01386
01387 static int reload(void)
01388 {
01389 parse_config(1);
01390
01391 return 0;
01392 }
01393
01394 static int parse_config(int is_reload)
01395 {
01396 struct ast_config *config;
01397 const char *s;
01398 struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01399
01400 config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01401 if (config == CONFIG_STATUS_FILEUNCHANGED) {
01402 return 0;
01403 }
01404
01405 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01406 ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01407 return 0;
01408 }
01409
01410 ast_mutex_lock(&pgsql_lock);
01411
01412 if (pgsqlConn) {
01413 PQfinish(pgsqlConn);
01414 pgsqlConn = NULL;
01415 }
01416
01417 if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01418 ast_log(LOG_WARNING,
01419 "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01420 strcpy(dbuser, "asterisk");
01421 } else {
01422 ast_copy_string(dbuser, s, sizeof(dbuser));
01423 }
01424
01425 if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01426 ast_log(LOG_WARNING,
01427 "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01428 strcpy(dbpass, "asterisk");
01429 } else {
01430 ast_copy_string(dbpass, s, sizeof(dbpass));
01431 }
01432
01433 if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01434 ast_log(LOG_WARNING,
01435 "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01436 dbhost[0] = '\0';
01437 } else {
01438 ast_copy_string(dbhost, s, sizeof(dbhost));
01439 }
01440
01441 if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01442 ast_log(LOG_WARNING,
01443 "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01444 strcpy(dbname, "asterisk");
01445 } else {
01446 ast_copy_string(dbname, s, sizeof(dbname));
01447 }
01448
01449 if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01450 ast_log(LOG_WARNING,
01451 "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01452 dbport = 5432;
01453 } else {
01454 dbport = atoi(s);
01455 }
01456
01457 if (!ast_strlen_zero(dbhost)) {
01458
01459 } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01460 ast_log(LOG_WARNING,
01461 "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
01462 strcpy(dbsock, "/tmp");
01463 } else {
01464 ast_copy_string(dbsock, s, sizeof(dbsock));
01465 }
01466
01467 if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01468 ast_log(LOG_WARNING,
01469 "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01470 requirements = RQ_WARN;
01471 } else if (!strcasecmp(s, "createclose")) {
01472 requirements = RQ_CREATECLOSE;
01473 } else if (!strcasecmp(s, "createchar")) {
01474 requirements = RQ_CREATECHAR;
01475 }
01476
01477 ast_config_destroy(config);
01478
01479 if (option_debug) {
01480 if (!ast_strlen_zero(dbhost)) {
01481 ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01482 ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01483 } else {
01484 ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01485 }
01486 ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01487 ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01488 ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01489 }
01490
01491 if (!pgsql_reconnect(NULL)) {
01492 ast_log(LOG_WARNING,
01493 "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01494 ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01495 }
01496
01497 ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01498
01499
01500 ast_mutex_unlock(&pgsql_lock);
01501
01502 return 1;
01503 }
01504
01505 static int pgsql_reconnect(const char *database)
01506 {
01507 char my_database[50];
01508
01509 ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01510
01511
01512
01513 if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01514 PQfinish(pgsqlConn);
01515 pgsqlConn = NULL;
01516 }
01517
01518
01519 if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01520 struct ast_str *connInfo = ast_str_create(128);
01521
01522 ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01523 S_OR(dbhost, dbsock), dbport, my_database, dbuser);
01524 if (!ast_strlen_zero(dbpass))
01525 ast_str_append(&connInfo, 0, " password=%s", dbpass);
01526
01527 ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01528 pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01529 ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01530 ast_free(connInfo);
01531 connInfo = NULL;
01532
01533 ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01534 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01535 ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01536 connect_time = time(NULL);
01537 version = PQserverVersion(pgsqlConn);
01538 return 1;
01539 } else {
01540 ast_log(LOG_ERROR,
01541 "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01542 my_database, dbhost, PQresultErrorMessage(NULL));
01543 return 0;
01544 }
01545 } else {
01546 ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01547 return 1;
01548 }
01549 }
01550
01551 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01552 {
01553 struct tables *cur;
01554 int l, which;
01555 char *ret = NULL;
01556
01557 switch (cmd) {
01558 case CLI_INIT:
01559 e->command = "realtime show pgsql cache";
01560 e->usage =
01561 "Usage: realtime show pgsql cache [<table>]\n"
01562 " Shows table cache for the PostgreSQL RealTime driver\n";
01563 return NULL;
01564 case CLI_GENERATE:
01565 if (a->argc != 4) {
01566 return NULL;
01567 }
01568 l = strlen(a->word);
01569 which = 0;
01570 AST_LIST_LOCK(&psql_tables);
01571 AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01572 if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01573 ret = ast_strdup(cur->name);
01574 break;
01575 }
01576 }
01577 AST_LIST_UNLOCK(&psql_tables);
01578 return ret;
01579 }
01580
01581 if (a->argc == 4) {
01582
01583 AST_LIST_LOCK(&psql_tables);
01584 AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01585 ast_cli(a->fd, "%s\n", cur->name);
01586 }
01587 AST_LIST_UNLOCK(&psql_tables);
01588 } else if (a->argc == 5) {
01589
01590 if ((cur = find_table(NULL, a->argv[4]))) {
01591 struct columns *col;
01592 ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01593 ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01594 AST_LIST_TRAVERSE(&cur->columns, col, list) {
01595 ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01596 }
01597 release_table(cur);
01598 } else {
01599 ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01600 }
01601 }
01602 return 0;
01603 }
01604
01605 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01606 {
01607 char status[256], credentials[100] = "";
01608 int ctimesec = time(NULL) - connect_time;
01609
01610 switch (cmd) {
01611 case CLI_INIT:
01612 e->command = "realtime show pgsql status";
01613 e->usage =
01614 "Usage: realtime show pgsql status\n"
01615 " Shows connection information for the PostgreSQL RealTime driver\n";
01616 return NULL;
01617 case CLI_GENERATE:
01618 return NULL;
01619 }
01620
01621 if (a->argc != 4)
01622 return CLI_SHOWUSAGE;
01623
01624 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01625 if (!ast_strlen_zero(dbhost))
01626 snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01627 else if (!ast_strlen_zero(dbsock))
01628 snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01629 else
01630 snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01631
01632 if (!ast_strlen_zero(dbuser))
01633 snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01634
01635 if (ctimesec > 31536000)
01636 ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01637 status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01638 (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01639 else if (ctimesec > 86400)
01640 ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01641 credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01642 ctimesec % 60);
01643 else if (ctimesec > 3600)
01644 ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01645 ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01646 else if (ctimesec > 60)
01647 ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01648 ctimesec % 60);
01649 else
01650 ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01651
01652 return CLI_SUCCESS;
01653 } else {
01654 return CLI_FAILURE;
01655 }
01656 }
01657
01658
01659 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
01660 .load = load_module,
01661 .unload = unload_module,
01662 .reload = reload,
01663 .load_pri = AST_MODPRI_REALTIME_DRIVER,
01664 );