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: 332831 $")
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 PGresult *result;
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 = 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 = 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 = 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 PQclear(result);
00343 AST_LIST_UNLOCK(&psql_tables);
00344 return NULL;
00345 }
00346
00347 if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
00348 ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
00349 PQclear(result);
00350 AST_LIST_UNLOCK(&psql_tables);
00351 return NULL;
00352 }
00353 strcpy(table->name, orig_tablename);
00354 ast_rwlock_init(&table->lock);
00355 AST_LIST_HEAD_INIT_NOLOCK(&table->columns);
00356
00357 rows = PQntuples(result);
00358 for (i = 0; i < rows; i++) {
00359 fname = PQgetvalue(result, i, 0);
00360 ftype = PQgetvalue(result, i, 1);
00361 flen = PQgetvalue(result, i, 2);
00362 fnotnull = PQgetvalue(result, i, 3);
00363 fdef = PQgetvalue(result, i, 4);
00364 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00365
00366 if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
00367 ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
00368 PQclear(result);
00369 destroy_table(table);
00370 AST_LIST_UNLOCK(&psql_tables);
00371 return NULL;
00372 }
00373
00374 if (strcmp(flen, "-1") == 0) {
00375
00376 flen = PQgetvalue(result, i, 5);
00377 sscanf(flen, "%30d", &column->len);
00378 column->len -= 4;
00379 } else {
00380 sscanf(flen, "%30d", &column->len);
00381 }
00382 column->name = (char *)column + sizeof(*column);
00383 column->type = (char *)column + sizeof(*column) + strlen(fname) + 1;
00384 strcpy(column->name, fname);
00385 strcpy(column->type, ftype);
00386 if (*fnotnull == 't') {
00387 column->notnull = 1;
00388 } else {
00389 column->notnull = 0;
00390 }
00391 if (!ast_strlen_zero(fdef)) {
00392 column->hasdefault = 1;
00393 } else {
00394 column->hasdefault = 0;
00395 }
00396 AST_LIST_INSERT_TAIL(&table->columns, column, list);
00397 }
00398 PQclear(result);
00399
00400 AST_LIST_INSERT_TAIL(&psql_tables, table, list);
00401 ast_rwlock_rdlock(&table->lock);
00402 AST_LIST_UNLOCK(&psql_tables);
00403 return table;
00404 }
00405
00406 #define release_table(table) ast_rwlock_unlock(&(table)->lock);
00407
00408 static struct columns *find_column(struct tables *t, const char *colname)
00409 {
00410 struct columns *column;
00411
00412
00413 AST_LIST_TRAVERSE(&t->columns, column, list) {
00414 if (strcmp(column->name, colname) == 0) {
00415 return column;
00416 }
00417 }
00418 return NULL;
00419 }
00420
00421 static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap)
00422 {
00423 PGresult *result = NULL;
00424 int num_rows = 0, pgresult;
00425 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00426 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00427 char *stringp;
00428 char *chunk;
00429 char *op;
00430 const char *newparam, *newval;
00431 struct ast_variable *var = NULL, *prev = NULL;
00432
00433 if (!tablename) {
00434 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00435 return NULL;
00436 }
00437
00438
00439 newparam = va_arg(ap, const char *);
00440 newval = va_arg(ap, const char *);
00441 if (!newparam || !newval) {
00442 ast_log(LOG_WARNING,
00443 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00444 if (pgsqlConn) {
00445 PQfinish(pgsqlConn);
00446 pgsqlConn = NULL;
00447 }
00448 return NULL;
00449 }
00450
00451
00452
00453 op = strchr(newparam, ' ') ? "" : " =";
00454
00455 ESCAPE_STRING(escapebuf, newval);
00456 if (pgresult) {
00457 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00458 va_end(ap);
00459 return NULL;
00460 }
00461
00462 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, ast_str_buffer(escapebuf));
00463 while ((newparam = va_arg(ap, const char *))) {
00464 newval = va_arg(ap, const char *);
00465 if (!strchr(newparam, ' '))
00466 op = " =";
00467 else
00468 op = "";
00469
00470 ESCAPE_STRING(escapebuf, newval);
00471 if (pgresult) {
00472 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00473 va_end(ap);
00474 return NULL;
00475 }
00476
00477 ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(escapebuf));
00478 }
00479 va_end(ap);
00480
00481
00482 ast_mutex_lock(&pgsql_lock);
00483
00484 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
00485 PQclear(result);
00486 ast_mutex_unlock(&pgsql_lock);
00487 return NULL;
00488 }
00489
00490 ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00491
00492 if ((num_rows = PQntuples(result)) > 0) {
00493 int i = 0;
00494 int rowIndex = 0;
00495 int numFields = PQnfields(result);
00496 char **fieldnames = NULL;
00497
00498 ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00499
00500 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00501 PQclear(result);
00502 ast_mutex_unlock(&pgsql_lock);
00503 return NULL;
00504 }
00505 for (i = 0; i < numFields; i++)
00506 fieldnames[i] = PQfname(result, i);
00507 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00508 for (i = 0; i < numFields; i++) {
00509 stringp = PQgetvalue(result, rowIndex, i);
00510 while (stringp) {
00511 chunk = strsep(&stringp, ";");
00512 if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00513 if (prev) {
00514 prev->next = ast_variable_new(fieldnames[i], chunk, "");
00515 if (prev->next) {
00516 prev = prev->next;
00517 }
00518 } else {
00519 prev = var = ast_variable_new(fieldnames[i], chunk, "");
00520 }
00521 }
00522 }
00523 }
00524 }
00525 ast_free(fieldnames);
00526 } else {
00527 ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
00528 }
00529
00530 PQclear(result);
00531 ast_mutex_unlock(&pgsql_lock);
00532
00533 return var;
00534 }
00535
00536 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
00537 {
00538 PGresult *result = NULL;
00539 int num_rows = 0, pgresult;
00540 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00541 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00542 const char *initfield = NULL;
00543 char *stringp;
00544 char *chunk;
00545 char *op;
00546 const char *newparam, *newval;
00547 struct ast_variable *var = NULL;
00548 struct ast_config *cfg = NULL;
00549 struct ast_category *cat = NULL;
00550
00551 if (!table) {
00552 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00553 return NULL;
00554 }
00555
00556 if (!(cfg = ast_config_new()))
00557 return NULL;
00558
00559
00560 newparam = va_arg(ap, const char *);
00561 newval = va_arg(ap, const char *);
00562 if (!newparam || !newval) {
00563 ast_log(LOG_WARNING,
00564 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00565 if (pgsqlConn) {
00566 PQfinish(pgsqlConn);
00567 pgsqlConn = NULL;
00568 }
00569 ast_config_destroy(cfg);
00570 return NULL;
00571 }
00572
00573 initfield = ast_strdupa(newparam);
00574 if ((op = strchr(initfield, ' '))) {
00575 *op = '\0';
00576 }
00577
00578
00579
00580
00581 if (!strchr(newparam, ' '))
00582 op = " =";
00583 else
00584 op = "";
00585
00586 ESCAPE_STRING(escapebuf, newval);
00587 if (pgresult) {
00588 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00589 va_end(ap);
00590 ast_config_destroy(cfg);
00591 return NULL;
00592 }
00593
00594 ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(escapebuf));
00595 while ((newparam = va_arg(ap, const char *))) {
00596 newval = va_arg(ap, const char *);
00597 if (!strchr(newparam, ' '))
00598 op = " =";
00599 else
00600 op = "";
00601
00602 ESCAPE_STRING(escapebuf, newval);
00603 if (pgresult) {
00604 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00605 va_end(ap);
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 va_end(ap);
00618
00619
00620 ast_mutex_lock(&pgsql_lock);
00621
00622 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
00623 ast_mutex_unlock(&pgsql_lock);
00624 ast_config_destroy(cfg);
00625 return NULL;
00626 } else {
00627 ExecStatusType result_status = PQresultStatus(result);
00628 if (result_status != PGRES_COMMAND_OK
00629 && result_status != PGRES_TUPLES_OK
00630 && result_status != PGRES_NONFATAL_ERROR) {
00631 ast_log(LOG_WARNING,
00632 "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
00633 ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00634 ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00635 PQresultErrorMessage(result), PQresStatus(result_status));
00636 PQclear(result);
00637 ast_mutex_unlock(&pgsql_lock);
00638 ast_config_destroy(cfg);
00639 return NULL;
00640 }
00641 }
00642
00643 ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
00644
00645 if ((num_rows = PQntuples(result)) > 0) {
00646 int numFields = PQnfields(result);
00647 int i = 0;
00648 int rowIndex = 0;
00649 char **fieldnames = NULL;
00650
00651 ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
00652
00653 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00654 PQclear(result);
00655 ast_mutex_unlock(&pgsql_lock);
00656 ast_config_destroy(cfg);
00657 return NULL;
00658 }
00659 for (i = 0; i < numFields; i++)
00660 fieldnames[i] = PQfname(result, i);
00661
00662 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00663 var = NULL;
00664 if (!(cat = ast_category_new("","",99999)))
00665 continue;
00666 for (i = 0; i < numFields; i++) {
00667 stringp = PQgetvalue(result, rowIndex, i);
00668 while (stringp) {
00669 chunk = strsep(&stringp, ";");
00670 if (chunk && !ast_strlen_zero(ast_realtime_decode_chunk(ast_strip(chunk)))) {
00671 if (initfield && !strcmp(initfield, fieldnames[i])) {
00672 ast_category_rename(cat, chunk);
00673 }
00674 var = ast_variable_new(fieldnames[i], chunk, "");
00675 ast_variable_append(cat, var);
00676 }
00677 }
00678 }
00679 ast_category_append(cfg, cat);
00680 }
00681 ast_free(fieldnames);
00682 } else {
00683 ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
00684 }
00685
00686 PQclear(result);
00687 ast_mutex_unlock(&pgsql_lock);
00688
00689 return cfg;
00690 }
00691
00692 static int update_pgsql(const char *database, const char *tablename, const char *keyfield,
00693 const char *lookup, va_list ap)
00694 {
00695 PGresult *result = NULL;
00696 int numrows = 0, pgresult;
00697 const char *newparam, *newval;
00698 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00699 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100);
00700 struct tables *table;
00701 struct columns *column = NULL;
00702
00703 if (!tablename) {
00704 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00705 return -1;
00706 }
00707
00708 if (!(table = find_table(database, tablename))) {
00709 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00710 return -1;
00711 }
00712
00713
00714 newparam = va_arg(ap, const char *);
00715 newval = va_arg(ap, const char *);
00716 if (!newparam || !newval) {
00717 ast_log(LOG_WARNING,
00718 "PostgreSQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00719 if (pgsqlConn) {
00720 PQfinish(pgsqlConn);
00721 pgsqlConn = NULL;
00722 }
00723 release_table(table);
00724 return -1;
00725 }
00726
00727
00728 AST_LIST_TRAVERSE(&table->columns, column, list) {
00729 if (strcmp(column->name, newparam) == 0) {
00730 break;
00731 }
00732 }
00733
00734 if (!column) {
00735 ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename);
00736 release_table(table);
00737 return -1;
00738 }
00739
00740
00741
00742
00743 ESCAPE_STRING(escapebuf, newval);
00744 if (pgresult) {
00745 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00746 va_end(ap);
00747 release_table(table);
00748 return -1;
00749 }
00750 ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, ast_str_buffer(escapebuf));
00751
00752 while ((newparam = va_arg(ap, const char *))) {
00753 newval = va_arg(ap, const char *);
00754
00755 if (!find_column(table, newparam)) {
00756 ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename);
00757 continue;
00758 }
00759
00760 ESCAPE_STRING(escapebuf, newval);
00761 if (pgresult) {
00762 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00763 va_end(ap);
00764 release_table(table);
00765 return -1;
00766 }
00767
00768 ast_str_append(&sql, 0, ", %s = '%s'", newparam, ast_str_buffer(escapebuf));
00769 }
00770 va_end(ap);
00771 release_table(table);
00772
00773 ESCAPE_STRING(escapebuf, lookup);
00774 if (pgresult) {
00775 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", lookup);
00776 va_end(ap);
00777 return -1;
00778 }
00779
00780 ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, ast_str_buffer(escapebuf));
00781
00782 ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00783
00784
00785 ast_mutex_lock(&pgsql_lock);
00786
00787 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
00788 ast_mutex_unlock(&pgsql_lock);
00789 return -1;
00790 } else {
00791 ExecStatusType result_status = PQresultStatus(result);
00792 if (result_status != PGRES_COMMAND_OK
00793 && result_status != PGRES_TUPLES_OK
00794 && result_status != PGRES_NONFATAL_ERROR) {
00795 ast_log(LOG_WARNING,
00796 "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
00797 ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
00798 ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
00799 PQresultErrorMessage(result), PQresStatus(result_status));
00800 PQclear(result);
00801 ast_mutex_unlock(&pgsql_lock);
00802 return -1;
00803 }
00804 }
00805
00806 numrows = atoi(PQcmdTuples(result));
00807 ast_mutex_unlock(&pgsql_lock);
00808
00809 ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00810
00811
00812
00813
00814
00815
00816
00817 if (numrows >= 0)
00818 return (int) numrows;
00819
00820 return -1;
00821 }
00822
00823 static int update2_pgsql(const char *database, const char *tablename, va_list ap)
00824 {
00825 PGresult *result = NULL;
00826 int numrows = 0, pgresult, first = 1;
00827 struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16);
00828 const char *newparam, *newval;
00829 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
00830 struct ast_str *where = ast_str_thread_get(&where_buf, 100);
00831 struct tables *table;
00832
00833 if (!tablename) {
00834 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00835 return -1;
00836 }
00837
00838 if (!escapebuf || !sql || !where) {
00839
00840 return -1;
00841 }
00842
00843 if (!(table = find_table(database, tablename))) {
00844 ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename);
00845 return -1;
00846 }
00847
00848 ast_str_set(&sql, 0, "UPDATE %s SET ", tablename);
00849 ast_str_set(&where, 0, "WHERE");
00850
00851 while ((newparam = va_arg(ap, const char *))) {
00852 if (!find_column(table, newparam)) {
00853 ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database);
00854 release_table(table);
00855 return -1;
00856 }
00857
00858 newval = va_arg(ap, const char *);
00859 ESCAPE_STRING(escapebuf, newval);
00860 if (pgresult) {
00861 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00862 release_table(table);
00863 ast_free(sql);
00864 return -1;
00865 }
00866 ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, ast_str_buffer(escapebuf));
00867 first = 0;
00868 }
00869
00870 if (first) {
00871 ast_log(LOG_WARNING,
00872 "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n");
00873 if (pgsqlConn) {
00874 PQfinish(pgsqlConn);
00875 pgsqlConn = NULL;
00876 }
00877 release_table(table);
00878 return -1;
00879 }
00880
00881
00882 first = 1;
00883 while ((newparam = va_arg(ap, const char *))) {
00884 newval = va_arg(ap, const char *);
00885
00886
00887 if (!find_column(table, newparam)) {
00888 ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database);
00889 continue;
00890 }
00891
00892 ESCAPE_STRING(escapebuf, newval);
00893 if (pgresult) {
00894 ast_log(LOG_ERROR, "PostgreSQL RealTime: detected invalid input: '%s'\n", newval);
00895 release_table(table);
00896 ast_free(sql);
00897 return -1;
00898 }
00899
00900 ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, ast_str_buffer(escapebuf));
00901 }
00902 release_table(table);
00903
00904 ast_str_append(&sql, 0, " %s", ast_str_buffer(where));
00905
00906 ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", ast_str_buffer(sql));
00907
00908
00909 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
00910 ast_mutex_unlock(&pgsql_lock);
00911 return -1;
00912 }
00913
00914 numrows = atoi(PQcmdTuples(result));
00915 ast_mutex_unlock(&pgsql_lock);
00916
00917 ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename);
00918
00919
00920
00921
00922
00923
00924
00925 if (numrows >= 0) {
00926 return (int) numrows;
00927 }
00928
00929 return -1;
00930 }
00931
00932 static int store_pgsql(const char *database, const char *table, va_list ap)
00933 {
00934 PGresult *result = NULL;
00935 Oid insertid;
00936 struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256);
00937 struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256);
00938 struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256);
00939 int pgresult;
00940 const char *newparam, *newval;
00941
00942 if (!table) {
00943 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
00944 return -1;
00945 }
00946
00947
00948 newparam = va_arg(ap, const char *);
00949 newval = va_arg(ap, const char *);
00950 if (!newparam || !newval) {
00951 ast_log(LOG_WARNING,
00952 "PostgreSQL RealTime: Realtime storage requires at least 1 parameter and 1 value to store.\n");
00953 if (pgsqlConn) {
00954 PQfinish(pgsqlConn);
00955 pgsqlConn = NULL;
00956 }
00957 return -1;
00958 }
00959
00960
00961 ast_mutex_lock(&pgsql_lock);
00962 if (!pgsql_reconnect(database)) {
00963 ast_mutex_unlock(&pgsql_lock);
00964 return -1;
00965 }
00966
00967
00968
00969 ESCAPE_STRING(buf, newparam);
00970 ast_str_set(&sql1, 0, "INSERT INTO %s (%s", table, ast_str_buffer(buf));
00971 ESCAPE_STRING(buf, newval);
00972 ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf));
00973 while ((newparam = va_arg(ap, const char *))) {
00974 newval = va_arg(ap, const char *);
00975 ESCAPE_STRING(buf, newparam);
00976 ast_str_append(&sql1, 0, ", %s", ast_str_buffer(buf));
00977 ESCAPE_STRING(buf, newval);
00978 ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf));
00979 }
00980 va_end(ap);
00981 ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
00982
00983 ast_debug(1, "PostgreSQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql1));
00984
00985 if (pgsql_exec(database, table, ast_str_buffer(sql1), &result) != 0) {
00986 ast_mutex_unlock(&pgsql_lock);
00987 return -1;
00988 }
00989
00990 insertid = PQoidValue(result);
00991 PQclear(result);
00992 ast_mutex_unlock(&pgsql_lock);
00993
00994 ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
00995
00996
00997
00998
00999
01000
01001
01002 if (insertid >= 0)
01003 return (int) insertid;
01004
01005 return -1;
01006 }
01007
01008 static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
01009 {
01010 PGresult *result = NULL;
01011 int numrows = 0;
01012 int pgresult;
01013 struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
01014 struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
01015 const char *newparam, *newval;
01016
01017 if (!table) {
01018 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
01019 return -1;
01020 }
01021
01022
01023
01024
01025
01026 if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
01027 ast_log(LOG_WARNING,
01028 "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
01029 if (pgsqlConn) {
01030 PQfinish(pgsqlConn);
01031 pgsqlConn = NULL;
01032 };
01033 return -1;
01034 }
01035
01036
01037 ast_mutex_lock(&pgsql_lock);
01038 if (!pgsql_reconnect(database)) {
01039 ast_mutex_unlock(&pgsql_lock);
01040 return -1;
01041 }
01042
01043
01044
01045
01046
01047 ESCAPE_STRING(buf1, keyfield);
01048 ESCAPE_STRING(buf2, lookup);
01049 ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
01050 while ((newparam = va_arg(ap, const char *))) {
01051 newval = va_arg(ap, const char *);
01052 ESCAPE_STRING(buf1, newparam);
01053 ESCAPE_STRING(buf2, newval);
01054 ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
01055 }
01056 va_end(ap);
01057
01058 ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
01059
01060 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
01061 ast_mutex_unlock(&pgsql_lock);
01062 return -1;
01063 }
01064
01065 numrows = atoi(PQcmdTuples(result));
01066 ast_mutex_unlock(&pgsql_lock);
01067
01068 ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01069
01070
01071
01072
01073
01074
01075
01076 if (numrows >= 0)
01077 return (int) numrows;
01078
01079 return -1;
01080 }
01081
01082
01083 static struct ast_config *config_pgsql(const char *database, const char *table,
01084 const char *file, struct ast_config *cfg,
01085 struct ast_flags flags, const char *suggested_incl, const char *who_asked)
01086 {
01087 PGresult *result = NULL;
01088 long num_rows;
01089 struct ast_variable *new_v;
01090 struct ast_category *cur_cat = NULL;
01091 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01092 char last[80] = "";
01093 int last_cat_metric = 0;
01094
01095 last[0] = '\0';
01096
01097 if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01098 ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01099 return NULL;
01100 }
01101
01102 ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01103 "WHERE filename='%s' and commented=0 "
01104 "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01105
01106 ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01107
01108 ast_mutex_lock(&pgsql_lock);
01109
01110
01111 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
01112 ast_mutex_unlock(&pgsql_lock);
01113 return NULL;
01114 }
01115
01116 if ((num_rows = PQntuples(result)) > 0) {
01117 int rowIndex = 0;
01118
01119 ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01120
01121 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01122 char *field_category = PQgetvalue(result, rowIndex, 0);
01123 char *field_var_name = PQgetvalue(result, rowIndex, 1);
01124 char *field_var_val = PQgetvalue(result, rowIndex, 2);
01125 char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01126 if (!strcmp(field_var_name, "#include")) {
01127 if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01128 PQclear(result);
01129 ast_mutex_unlock(&pgsql_lock);
01130 return NULL;
01131 }
01132 continue;
01133 }
01134
01135 if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01136 cur_cat = ast_category_new(field_category, "", 99999);
01137 if (!cur_cat)
01138 break;
01139 strcpy(last, field_category);
01140 last_cat_metric = atoi(field_cat_metric);
01141 ast_category_append(cfg, cur_cat);
01142 }
01143 new_v = ast_variable_new(field_var_name, field_var_val, "");
01144 ast_variable_append(cur_cat, new_v);
01145 }
01146 } else {
01147 ast_log(LOG_WARNING,
01148 "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01149 }
01150
01151 PQclear(result);
01152 ast_mutex_unlock(&pgsql_lock);
01153
01154 return cfg;
01155 }
01156
01157 static int require_pgsql(const char *database, const char *tablename, va_list ap)
01158 {
01159 struct columns *column;
01160 struct tables *table = find_table(database, tablename);
01161 char *elm;
01162 int type, size, res = 0;
01163
01164 if (!table) {
01165 ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
01166 return -1;
01167 }
01168
01169 while ((elm = va_arg(ap, char *))) {
01170 type = va_arg(ap, require_type);
01171 size = va_arg(ap, int);
01172 AST_LIST_TRAVERSE(&table->columns, column, list) {
01173 if (strcmp(column->name, elm) == 0) {
01174
01175 if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01176 if ((size > column->len) && column->len != -1) {
01177 ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01178 res = -1;
01179 }
01180 } else if (strncmp(column->type, "int", 3) == 0) {
01181 int typesize = atoi(column->type + 3);
01182
01183 if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01184 type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01185 type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01186 type == RQ_UINTEGER2) && typesize == 2) {
01187 ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01188 res = -1;
01189 } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01190 type == RQ_UINTEGER4) && typesize == 4) {
01191 ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01192 res = -1;
01193 } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01194 ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01195 column->name,
01196 type == RQ_CHAR ? "char" :
01197 type == RQ_DATETIME ? "datetime" :
01198 type == RQ_DATE ? "date" :
01199 type == RQ_FLOAT ? "float" :
01200 "a rather stiff drink ",
01201 size, column->type);
01202 res = -1;
01203 }
01204 } else if (strncmp(column->type, "float", 5) == 0) {
01205 if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01206 ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01207 res = -1;
01208 }
01209 } else if (strncmp(column->type, "timestamp", 9) == 0) {
01210 if (type != RQ_DATETIME && type != RQ_DATE) {
01211 ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01212 res = -1;
01213 }
01214 } else {
01215 ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01216 res = -1;
01217 }
01218 break;
01219 }
01220 }
01221
01222 if (!column) {
01223 if (requirements == RQ_WARN) {
01224 ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01225 } else {
01226 struct ast_str *sql = ast_str_create(100);
01227 char fieldtype[15];
01228 PGresult *result;
01229
01230 if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01231
01232
01233
01234 snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01235 size < 15 ? size * 2 :
01236 (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01237 } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01238 snprintf(fieldtype, sizeof(fieldtype), "INT2");
01239 } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01240 snprintf(fieldtype, sizeof(fieldtype), "INT4");
01241 } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01242 snprintf(fieldtype, sizeof(fieldtype), "INT8");
01243 } else if (type == RQ_UINTEGER8) {
01244
01245 snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01246 } else if (type == RQ_FLOAT) {
01247 snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01248 } else if (type == RQ_DATE) {
01249 snprintf(fieldtype, sizeof(fieldtype), "DATE");
01250 } else if (type == RQ_DATETIME) {
01251 snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01252 } else {
01253 ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01254 ast_free(sql);
01255 continue;
01256 }
01257 ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01258 ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01259
01260 ast_mutex_lock(&pgsql_lock);
01261 ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01262
01263 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
01264 ast_mutex_unlock(&pgsql_lock);
01265 return -1;
01266 }
01267
01268 ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01269 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01270 ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01271 }
01272 PQclear(result);
01273 ast_mutex_unlock(&pgsql_lock);
01274
01275 ast_free(sql);
01276 }
01277 }
01278 }
01279 release_table(table);
01280 return res;
01281 }
01282
01283 static int unload_pgsql(const char *database, const char *tablename)
01284 {
01285 struct tables *cur;
01286 ast_debug(2, "About to lock table cache list\n");
01287 AST_LIST_LOCK(&psql_tables);
01288 ast_debug(2, "About to traverse table cache list\n");
01289 AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01290 if (strcmp(cur->name, tablename) == 0) {
01291 ast_debug(2, "About to remove matching cache entry\n");
01292 AST_LIST_REMOVE_CURRENT(list);
01293 ast_debug(2, "About to destroy matching cache entry\n");
01294 destroy_table(cur);
01295 ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01296 break;
01297 }
01298 }
01299 AST_LIST_TRAVERSE_SAFE_END
01300 AST_LIST_UNLOCK(&psql_tables);
01301 ast_debug(2, "About to return\n");
01302 return cur ? 0 : -1;
01303 }
01304
01305 static struct ast_config_engine pgsql_engine = {
01306 .name = "pgsql",
01307 .load_func = config_pgsql,
01308 .realtime_func = realtime_pgsql,
01309 .realtime_multi_func = realtime_multi_pgsql,
01310 .store_func = store_pgsql,
01311 .destroy_func = destroy_pgsql,
01312 .update_func = update_pgsql,
01313 .update2_func = update2_pgsql,
01314 .require_func = require_pgsql,
01315 .unload_func = unload_pgsql,
01316 };
01317
01318 static int load_module(void)
01319 {
01320 if(!parse_config(0))
01321 return AST_MODULE_LOAD_DECLINE;
01322
01323 ast_config_engine_register(&pgsql_engine);
01324 ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01325 ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01326
01327 return 0;
01328 }
01329
01330 static int unload_module(void)
01331 {
01332 struct tables *table;
01333
01334 ast_mutex_lock(&pgsql_lock);
01335
01336 if (pgsqlConn) {
01337 PQfinish(pgsqlConn);
01338 pgsqlConn = NULL;
01339 }
01340 ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01341 ast_config_engine_deregister(&pgsql_engine);
01342 ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01343
01344
01345 AST_LIST_LOCK(&psql_tables);
01346 while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01347 destroy_table(table);
01348 }
01349 AST_LIST_UNLOCK(&psql_tables);
01350
01351
01352 ast_mutex_unlock(&pgsql_lock);
01353
01354 return 0;
01355 }
01356
01357 static int reload(void)
01358 {
01359 parse_config(1);
01360
01361 return 0;
01362 }
01363
01364 static int parse_config(int is_reload)
01365 {
01366 struct ast_config *config;
01367 const char *s;
01368 struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01369
01370 config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01371 if (config == CONFIG_STATUS_FILEUNCHANGED) {
01372 return 0;
01373 }
01374
01375 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01376 ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01377 return 0;
01378 }
01379
01380 ast_mutex_lock(&pgsql_lock);
01381
01382 if (pgsqlConn) {
01383 PQfinish(pgsqlConn);
01384 pgsqlConn = NULL;
01385 }
01386
01387 if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01388 ast_log(LOG_WARNING,
01389 "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01390 strcpy(dbuser, "asterisk");
01391 } else {
01392 ast_copy_string(dbuser, s, sizeof(dbuser));
01393 }
01394
01395 if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01396 ast_log(LOG_WARNING,
01397 "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01398 strcpy(dbpass, "asterisk");
01399 } else {
01400 ast_copy_string(dbpass, s, sizeof(dbpass));
01401 }
01402
01403 if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01404 ast_log(LOG_WARNING,
01405 "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01406 dbhost[0] = '\0';
01407 } else {
01408 ast_copy_string(dbhost, s, sizeof(dbhost));
01409 }
01410
01411 if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01412 ast_log(LOG_WARNING,
01413 "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01414 strcpy(dbname, "asterisk");
01415 } else {
01416 ast_copy_string(dbname, s, sizeof(dbname));
01417 }
01418
01419 if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01420 ast_log(LOG_WARNING,
01421 "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01422 dbport = 5432;
01423 } else {
01424 dbport = atoi(s);
01425 }
01426
01427 if (!ast_strlen_zero(dbhost)) {
01428
01429 } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01430 ast_log(LOG_WARNING,
01431 "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
01432 strcpy(dbsock, "/tmp");
01433 } else {
01434 ast_copy_string(dbsock, s, sizeof(dbsock));
01435 }
01436
01437 if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01438 ast_log(LOG_WARNING,
01439 "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01440 requirements = RQ_WARN;
01441 } else if (!strcasecmp(s, "createclose")) {
01442 requirements = RQ_CREATECLOSE;
01443 } else if (!strcasecmp(s, "createchar")) {
01444 requirements = RQ_CREATECHAR;
01445 }
01446
01447 ast_config_destroy(config);
01448
01449 if (option_debug) {
01450 if (!ast_strlen_zero(dbhost)) {
01451 ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01452 ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01453 } else {
01454 ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01455 }
01456 ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01457 ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01458 ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01459 }
01460
01461 if (!pgsql_reconnect(NULL)) {
01462 ast_log(LOG_WARNING,
01463 "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01464 ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01465 }
01466
01467 ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01468
01469
01470 ast_mutex_unlock(&pgsql_lock);
01471
01472 return 1;
01473 }
01474
01475 static int pgsql_reconnect(const char *database)
01476 {
01477 char my_database[50];
01478
01479 ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01480
01481
01482
01483 if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01484 PQfinish(pgsqlConn);
01485 pgsqlConn = NULL;
01486 }
01487
01488
01489 if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01490 struct ast_str *connInfo = ast_str_create(32);
01491
01492 ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01493 S_OR(dbhost, dbsock), dbport, my_database, dbuser);
01494 if (!ast_strlen_zero(dbpass))
01495 ast_str_append(&connInfo, 0, " password=%s", dbpass);
01496
01497 ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01498 pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01499 ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01500 ast_free(connInfo);
01501 connInfo = NULL;
01502
01503 ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01504 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01505 ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01506 connect_time = time(NULL);
01507 version = PQserverVersion(pgsqlConn);
01508 return 1;
01509 } else {
01510 ast_log(LOG_ERROR,
01511 "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01512 dbname, dbhost, PQresultErrorMessage(NULL));
01513 return 0;
01514 }
01515 } else {
01516 ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01517 return 1;
01518 }
01519 }
01520
01521 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01522 {
01523 struct tables *cur;
01524 int l, which;
01525 char *ret = NULL;
01526
01527 switch (cmd) {
01528 case CLI_INIT:
01529 e->command = "realtime show pgsql cache";
01530 e->usage =
01531 "Usage: realtime show pgsql cache [<table>]\n"
01532 " Shows table cache for the PostgreSQL RealTime driver\n";
01533 return NULL;
01534 case CLI_GENERATE:
01535 if (a->argc != 4) {
01536 return NULL;
01537 }
01538 l = strlen(a->word);
01539 which = 0;
01540 AST_LIST_LOCK(&psql_tables);
01541 AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01542 if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01543 ret = ast_strdup(cur->name);
01544 break;
01545 }
01546 }
01547 AST_LIST_UNLOCK(&psql_tables);
01548 return ret;
01549 }
01550
01551 if (a->argc == 4) {
01552
01553 AST_LIST_LOCK(&psql_tables);
01554 AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01555 ast_cli(a->fd, "%s\n", cur->name);
01556 }
01557 AST_LIST_UNLOCK(&psql_tables);
01558 } else if (a->argc == 5) {
01559
01560 if ((cur = find_table(NULL, a->argv[4]))) {
01561 struct columns *col;
01562 ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01563 ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01564 AST_LIST_TRAVERSE(&cur->columns, col, list) {
01565 ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01566 }
01567 release_table(cur);
01568 } else {
01569 ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01570 }
01571 }
01572 return 0;
01573 }
01574
01575 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01576 {
01577 char status[256], credentials[100] = "";
01578 int ctimesec = time(NULL) - connect_time;
01579
01580 switch (cmd) {
01581 case CLI_INIT:
01582 e->command = "realtime show pgsql status";
01583 e->usage =
01584 "Usage: realtime show pgsql status\n"
01585 " Shows connection information for the PostgreSQL RealTime driver\n";
01586 return NULL;
01587 case CLI_GENERATE:
01588 return NULL;
01589 }
01590
01591 if (a->argc != 4)
01592 return CLI_SHOWUSAGE;
01593
01594 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01595 if (!ast_strlen_zero(dbhost))
01596 snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01597 else if (!ast_strlen_zero(dbsock))
01598 snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01599 else
01600 snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01601
01602 if (!ast_strlen_zero(dbuser))
01603 snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01604
01605 if (ctimesec > 31536000)
01606 ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01607 status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01608 (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01609 else if (ctimesec > 86400)
01610 ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01611 credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01612 ctimesec % 60);
01613 else if (ctimesec > 3600)
01614 ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01615 ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01616 else if (ctimesec > 60)
01617 ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01618 ctimesec % 60);
01619 else
01620 ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01621
01622 return CLI_SUCCESS;
01623 } else {
01624 return CLI_FAILURE;
01625 }
01626 }
01627
01628
01629 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
01630 .load = load_module,
01631 .unload = unload_module,
01632 .reload = reload,
01633 .load_pri = AST_MODPRI_REALTIME_DRIVER,
01634 );