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: 407874 $")
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 Oid insertid;
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 insertid = PQoidValue(result);
00998 ast_mutex_unlock(&pgsql_lock);
00999
01000 ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
01001
01002
01003
01004
01005
01006
01007
01008 if (insertid >= 0)
01009 return (int) insertid;
01010
01011 return -1;
01012 }
01013
01014 static int destroy_pgsql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
01015 {
01016 RAII_VAR(PGresult *, result, NULL, PQclear);
01017 int numrows = 0;
01018 int pgresult;
01019 struct ast_str *sql = ast_str_thread_get(&sql_buf, 256);
01020 struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60);
01021 const char *newparam, *newval;
01022
01023
01024
01025
01026
01027 database = dbname;
01028
01029 if (!table) {
01030 ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n");
01031 return -1;
01032 }
01033
01034
01035
01036
01037
01038 if (ast_strlen_zero(keyfield) || ast_strlen_zero(lookup)) {
01039 ast_log(LOG_WARNING,
01040 "PostgreSQL RealTime: Realtime destroy requires at least 1 parameter and 1 value to search on.\n");
01041 if (pgsqlConn) {
01042 PQfinish(pgsqlConn);
01043 pgsqlConn = NULL;
01044 };
01045 return -1;
01046 }
01047
01048
01049 ast_mutex_lock(&pgsql_lock);
01050 if (!pgsql_reconnect(database)) {
01051 ast_mutex_unlock(&pgsql_lock);
01052 return -1;
01053 }
01054
01055
01056
01057
01058
01059 ESCAPE_STRING(buf1, keyfield);
01060 ESCAPE_STRING(buf2, lookup);
01061 ast_str_set(&sql, 0, "DELETE FROM %s WHERE %s = '%s'", table, ast_str_buffer(buf1), ast_str_buffer(buf2));
01062 while ((newparam = va_arg(ap, const char *))) {
01063 newval = va_arg(ap, const char *);
01064 ESCAPE_STRING(buf1, newparam);
01065 ESCAPE_STRING(buf2, newval);
01066 ast_str_append(&sql, 0, " AND %s = '%s'", ast_str_buffer(buf1), ast_str_buffer(buf2));
01067 }
01068
01069 ast_debug(1, "PostgreSQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql));
01070
01071 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
01072 ast_mutex_unlock(&pgsql_lock);
01073 return -1;
01074 }
01075
01076 numrows = atoi(PQcmdTuples(result));
01077 ast_mutex_unlock(&pgsql_lock);
01078
01079 ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table);
01080
01081
01082
01083
01084
01085
01086
01087 if (numrows >= 0)
01088 return (int) numrows;
01089
01090 return -1;
01091 }
01092
01093
01094 static struct ast_config *config_pgsql(const char *database, const char *table,
01095 const char *file, struct ast_config *cfg,
01096 struct ast_flags flags, const char *suggested_incl, const char *who_asked)
01097 {
01098 RAII_VAR(PGresult *, result, NULL, PQclear);
01099 long num_rows;
01100 struct ast_variable *new_v;
01101 struct ast_category *cur_cat = NULL;
01102 struct ast_str *sql = ast_str_thread_get(&sql_buf, 100);
01103 char last[80];
01104 int last_cat_metric = 0;
01105
01106 last[0] = '\0';
01107
01108
01109
01110
01111
01112 database = dbname;
01113
01114 if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
01115 ast_log(LOG_WARNING, "PostgreSQL RealTime: Cannot configure myself.\n");
01116 return NULL;
01117 }
01118
01119 ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s "
01120 "WHERE filename='%s' and commented=0 "
01121 "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file);
01122
01123 ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", ast_str_buffer(sql));
01124
01125 ast_mutex_lock(&pgsql_lock);
01126
01127
01128 if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
01129 ast_mutex_unlock(&pgsql_lock);
01130 return NULL;
01131 }
01132
01133 if ((num_rows = PQntuples(result)) > 0) {
01134 int rowIndex = 0;
01135
01136 ast_debug(1, "PostgreSQL RealTime: Found %ld rows.\n", num_rows);
01137
01138 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
01139 char *field_category = PQgetvalue(result, rowIndex, 0);
01140 char *field_var_name = PQgetvalue(result, rowIndex, 1);
01141 char *field_var_val = PQgetvalue(result, rowIndex, 2);
01142 char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
01143 if (!strcmp(field_var_name, "#include")) {
01144 if (!ast_config_internal_load(field_var_val, cfg, flags, "", who_asked)) {
01145 ast_mutex_unlock(&pgsql_lock);
01146 return NULL;
01147 }
01148 continue;
01149 }
01150
01151 if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
01152 cur_cat = ast_category_new(field_category, "", 99999);
01153 if (!cur_cat)
01154 break;
01155 ast_copy_string(last, field_category, sizeof(last));
01156 last_cat_metric = atoi(field_cat_metric);
01157 ast_category_append(cfg, cur_cat);
01158 }
01159 new_v = ast_variable_new(field_var_name, field_var_val, "");
01160 ast_variable_append(cur_cat, new_v);
01161 }
01162 } else {
01163 ast_log(LOG_WARNING,
01164 "PostgreSQL RealTime: Could not find config '%s' in database.\n", file);
01165 }
01166
01167 ast_mutex_unlock(&pgsql_lock);
01168
01169 return cfg;
01170 }
01171
01172 static int require_pgsql(const char *database, const char *tablename, va_list ap)
01173 {
01174 struct columns *column;
01175 struct tables *table;
01176 char *elm;
01177 int type, size, res = 0;
01178
01179
01180
01181
01182
01183 database = dbname;
01184
01185 table = find_table(database, tablename);
01186 if (!table) {
01187 ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename);
01188 return -1;
01189 }
01190
01191 while ((elm = va_arg(ap, char *))) {
01192 type = va_arg(ap, require_type);
01193 size = va_arg(ap, int);
01194 AST_LIST_TRAVERSE(&table->columns, column, list) {
01195 if (strcmp(column->name, elm) == 0) {
01196
01197 if ((strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0 || strcmp(column->type, "bpchar") == 0)) {
01198 if ((size > column->len) && column->len != -1) {
01199 ast_log(LOG_WARNING, "Column '%s' should be at least %d long, but is only %d long.\n", column->name, size, column->len);
01200 res = -1;
01201 }
01202 } else if (strncmp(column->type, "int", 3) == 0) {
01203 int typesize = atoi(column->type + 3);
01204
01205 if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01206 type == RQ_INTEGER4 || type == RQ_UINTEGER4 ||
01207 type == RQ_INTEGER3 || type == RQ_UINTEGER3 ||
01208 type == RQ_UINTEGER2) && typesize == 2) {
01209 ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01210 res = -1;
01211 } else if ((type == RQ_INTEGER8 || type == RQ_UINTEGER8 ||
01212 type == RQ_UINTEGER4) && typesize == 4) {
01213 ast_log(LOG_WARNING, "Column '%s' may not be large enough for the required data length: %d\n", column->name, size);
01214 res = -1;
01215 } else if (type == RQ_CHAR || type == RQ_DATETIME || type == RQ_FLOAT || type == RQ_DATE) {
01216 ast_log(LOG_WARNING, "Column '%s' is of the incorrect type: (need %s(%d) but saw %s)\n",
01217 column->name,
01218 type == RQ_CHAR ? "char" :
01219 type == RQ_DATETIME ? "datetime" :
01220 type == RQ_DATE ? "date" :
01221 type == RQ_FLOAT ? "float" :
01222 "a rather stiff drink ",
01223 size, column->type);
01224 res = -1;
01225 }
01226 } else if (strncmp(column->type, "float", 5) == 0) {
01227 if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01228 ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01229 res = -1;
01230 }
01231 } else if (strncmp(column->type, "timestamp", 9) == 0) {
01232 if (type != RQ_DATETIME && type != RQ_DATE) {
01233 ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type);
01234 res = -1;
01235 }
01236 } else {
01237 ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name);
01238 res = -1;
01239 }
01240 break;
01241 }
01242 }
01243
01244 if (!column) {
01245 if (requirements == RQ_WARN) {
01246 ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size);
01247 } else {
01248 struct ast_str *sql = ast_str_create(100);
01249 char fieldtype[15];
01250 PGresult *result;
01251
01252 if (requirements == RQ_CREATECHAR || type == RQ_CHAR) {
01253
01254
01255
01256 snprintf(fieldtype, sizeof(fieldtype), "CHAR(%d)",
01257 size < 15 ? size * 2 :
01258 (size * 3 / 2 > 255) ? 255 : size * 3 / 2);
01259 } else if (type == RQ_INTEGER1 || type == RQ_UINTEGER1 || type == RQ_INTEGER2) {
01260 snprintf(fieldtype, sizeof(fieldtype), "INT2");
01261 } else if (type == RQ_UINTEGER2 || type == RQ_INTEGER3 || type == RQ_UINTEGER3 || type == RQ_INTEGER4) {
01262 snprintf(fieldtype, sizeof(fieldtype), "INT4");
01263 } else if (type == RQ_UINTEGER4 || type == RQ_INTEGER8) {
01264 snprintf(fieldtype, sizeof(fieldtype), "INT8");
01265 } else if (type == RQ_UINTEGER8) {
01266
01267 snprintf(fieldtype, sizeof(fieldtype), "CHAR(20)");
01268 } else if (type == RQ_FLOAT) {
01269 snprintf(fieldtype, sizeof(fieldtype), "FLOAT8");
01270 } else if (type == RQ_DATE) {
01271 snprintf(fieldtype, sizeof(fieldtype), "DATE");
01272 } else if (type == RQ_DATETIME) {
01273 snprintf(fieldtype, sizeof(fieldtype), "TIMESTAMP");
01274 } else {
01275 ast_log(LOG_ERROR, "Unrecognized request type %d\n", type);
01276 ast_free(sql);
01277 continue;
01278 }
01279 ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, fieldtype);
01280 ast_debug(1, "About to lock pgsql_lock (running alter on table '%s' to add column '%s')\n", tablename, elm);
01281
01282 ast_mutex_lock(&pgsql_lock);
01283 ast_debug(1, "About to run ALTER query on table '%s' to add column '%s'\n", tablename, elm);
01284
01285 if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
01286 ast_mutex_unlock(&pgsql_lock);
01287 return -1;
01288 }
01289
01290 ast_debug(1, "Finished running ALTER query on table '%s'\n", tablename);
01291 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
01292 ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql));
01293 }
01294 PQclear(result);
01295 ast_mutex_unlock(&pgsql_lock);
01296
01297 ast_free(sql);
01298 }
01299 }
01300 }
01301 release_table(table);
01302 return res;
01303 }
01304
01305 static int unload_pgsql(const char *database, const char *tablename)
01306 {
01307 struct tables *cur;
01308
01309
01310
01311
01312
01313 database = dbname;
01314
01315 ast_debug(2, "About to lock table cache list\n");
01316 AST_LIST_LOCK(&psql_tables);
01317 ast_debug(2, "About to traverse table cache list\n");
01318 AST_LIST_TRAVERSE_SAFE_BEGIN(&psql_tables, cur, list) {
01319 if (strcmp(cur->name, tablename) == 0) {
01320 ast_debug(2, "About to remove matching cache entry\n");
01321 AST_LIST_REMOVE_CURRENT(list);
01322 ast_debug(2, "About to destroy matching cache entry\n");
01323 destroy_table(cur);
01324 ast_debug(1, "Cache entry '%s@%s' destroyed\n", tablename, database);
01325 break;
01326 }
01327 }
01328 AST_LIST_TRAVERSE_SAFE_END
01329 AST_LIST_UNLOCK(&psql_tables);
01330 ast_debug(2, "About to return\n");
01331 return cur ? 0 : -1;
01332 }
01333
01334 static struct ast_config_engine pgsql_engine = {
01335 .name = "pgsql",
01336 .load_func = config_pgsql,
01337 .realtime_func = realtime_pgsql,
01338 .realtime_multi_func = realtime_multi_pgsql,
01339 .store_func = store_pgsql,
01340 .destroy_func = destroy_pgsql,
01341 .update_func = update_pgsql,
01342 .update2_func = update2_pgsql,
01343 .require_func = require_pgsql,
01344 .unload_func = unload_pgsql,
01345 };
01346
01347 static int load_module(void)
01348 {
01349 if(!parse_config(0))
01350 return AST_MODULE_LOAD_DECLINE;
01351
01352 ast_config_engine_register(&pgsql_engine);
01353 ast_verb(1, "PostgreSQL RealTime driver loaded.\n");
01354 ast_cli_register_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01355
01356 return 0;
01357 }
01358
01359 static int unload_module(void)
01360 {
01361 struct tables *table;
01362
01363 ast_mutex_lock(&pgsql_lock);
01364
01365 if (pgsqlConn) {
01366 PQfinish(pgsqlConn);
01367 pgsqlConn = NULL;
01368 }
01369 ast_cli_unregister_multiple(cli_realtime, ARRAY_LEN(cli_realtime));
01370 ast_config_engine_deregister(&pgsql_engine);
01371 ast_verb(1, "PostgreSQL RealTime unloaded.\n");
01372
01373
01374 AST_LIST_LOCK(&psql_tables);
01375 while ((table = AST_LIST_REMOVE_HEAD(&psql_tables, list))) {
01376 destroy_table(table);
01377 }
01378 AST_LIST_UNLOCK(&psql_tables);
01379
01380
01381 ast_mutex_unlock(&pgsql_lock);
01382
01383 return 0;
01384 }
01385
01386 static int reload(void)
01387 {
01388 parse_config(1);
01389
01390 return 0;
01391 }
01392
01393 static int parse_config(int is_reload)
01394 {
01395 struct ast_config *config;
01396 const char *s;
01397 struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01398
01399 config = ast_config_load(RES_CONFIG_PGSQL_CONF, config_flags);
01400 if (config == CONFIG_STATUS_FILEUNCHANGED) {
01401 return 0;
01402 }
01403
01404 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01405 ast_log(LOG_WARNING, "Unable to load config %s\n", RES_CONFIG_PGSQL_CONF);
01406 return 0;
01407 }
01408
01409 ast_mutex_lock(&pgsql_lock);
01410
01411 if (pgsqlConn) {
01412 PQfinish(pgsqlConn);
01413 pgsqlConn = NULL;
01414 }
01415
01416 if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
01417 ast_log(LOG_WARNING,
01418 "PostgreSQL RealTime: No database user found, using 'asterisk' as default.\n");
01419 strcpy(dbuser, "asterisk");
01420 } else {
01421 ast_copy_string(dbuser, s, sizeof(dbuser));
01422 }
01423
01424 if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
01425 ast_log(LOG_WARNING,
01426 "PostgreSQL RealTime: No database password found, using 'asterisk' as default.\n");
01427 strcpy(dbpass, "asterisk");
01428 } else {
01429 ast_copy_string(dbpass, s, sizeof(dbpass));
01430 }
01431
01432 if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
01433 ast_log(LOG_WARNING,
01434 "PostgreSQL RealTime: No database host found, using localhost via socket.\n");
01435 dbhost[0] = '\0';
01436 } else {
01437 ast_copy_string(dbhost, s, sizeof(dbhost));
01438 }
01439
01440 if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
01441 ast_log(LOG_WARNING,
01442 "PostgreSQL RealTime: No database name found, using 'asterisk' as default.\n");
01443 strcpy(dbname, "asterisk");
01444 } else {
01445 ast_copy_string(dbname, s, sizeof(dbname));
01446 }
01447
01448 if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
01449 ast_log(LOG_WARNING,
01450 "PostgreSQL RealTime: No database port found, using 5432 as default.\n");
01451 dbport = 5432;
01452 } else {
01453 dbport = atoi(s);
01454 }
01455
01456 if (!ast_strlen_zero(dbhost)) {
01457
01458 } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
01459 ast_log(LOG_WARNING,
01460 "PostgreSQL RealTime: No database socket found, using '/tmp/.s.PGSQL.%d' as default.\n", dbport);
01461 strcpy(dbsock, "/tmp");
01462 } else {
01463 ast_copy_string(dbsock, s, sizeof(dbsock));
01464 }
01465
01466 if (!(s = ast_variable_retrieve(config, "general", "requirements"))) {
01467 ast_log(LOG_WARNING,
01468 "PostgreSQL RealTime: no requirements setting found, using 'warn' as default.\n");
01469 requirements = RQ_WARN;
01470 } else if (!strcasecmp(s, "createclose")) {
01471 requirements = RQ_CREATECLOSE;
01472 } else if (!strcasecmp(s, "createchar")) {
01473 requirements = RQ_CREATECHAR;
01474 }
01475
01476 ast_config_destroy(config);
01477
01478 if (option_debug) {
01479 if (!ast_strlen_zero(dbhost)) {
01480 ast_debug(1, "PostgreSQL RealTime Host: %s\n", dbhost);
01481 ast_debug(1, "PostgreSQL RealTime Port: %i\n", dbport);
01482 } else {
01483 ast_debug(1, "PostgreSQL RealTime Socket: %s\n", dbsock);
01484 }
01485 ast_debug(1, "PostgreSQL RealTime User: %s\n", dbuser);
01486 ast_debug(1, "PostgreSQL RealTime Password: %s\n", dbpass);
01487 ast_debug(1, "PostgreSQL RealTime DBName: %s\n", dbname);
01488 }
01489
01490 if (!pgsql_reconnect(NULL)) {
01491 ast_log(LOG_WARNING,
01492 "PostgreSQL RealTime: Couldn't establish connection. Check debug.\n");
01493 ast_debug(1, "PostgreSQL RealTime: Cannot Connect: %s\n", PQerrorMessage(pgsqlConn));
01494 }
01495
01496 ast_verb(2, "PostgreSQL RealTime reloaded.\n");
01497
01498
01499 ast_mutex_unlock(&pgsql_lock);
01500
01501 return 1;
01502 }
01503
01504 static int pgsql_reconnect(const char *database)
01505 {
01506 char my_database[50];
01507
01508 ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
01509
01510
01511
01512 if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
01513 PQfinish(pgsqlConn);
01514 pgsqlConn = NULL;
01515 }
01516
01517
01518 if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
01519 struct ast_str *connInfo = ast_str_create(128);
01520
01521 ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
01522 S_OR(dbhost, dbsock), dbport, my_database, dbuser);
01523 if (!ast_strlen_zero(dbpass))
01524 ast_str_append(&connInfo, 0, " password=%s", dbpass);
01525
01526 ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01527 pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
01528 ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
01529 ast_free(connInfo);
01530 connInfo = NULL;
01531
01532 ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
01533 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01534 ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
01535 connect_time = time(NULL);
01536 version = PQserverVersion(pgsqlConn);
01537 return 1;
01538 } else {
01539 ast_log(LOG_ERROR,
01540 "PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
01541 my_database, dbhost, PQresultErrorMessage(NULL));
01542 return 0;
01543 }
01544 } else {
01545 ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
01546 return 1;
01547 }
01548 }
01549
01550 static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01551 {
01552 struct tables *cur;
01553 int l, which;
01554 char *ret = NULL;
01555
01556 switch (cmd) {
01557 case CLI_INIT:
01558 e->command = "realtime show pgsql cache";
01559 e->usage =
01560 "Usage: realtime show pgsql cache [<table>]\n"
01561 " Shows table cache for the PostgreSQL RealTime driver\n";
01562 return NULL;
01563 case CLI_GENERATE:
01564 if (a->argc != 4) {
01565 return NULL;
01566 }
01567 l = strlen(a->word);
01568 which = 0;
01569 AST_LIST_LOCK(&psql_tables);
01570 AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01571 if (!strncasecmp(a->word, cur->name, l) && ++which > a->n) {
01572 ret = ast_strdup(cur->name);
01573 break;
01574 }
01575 }
01576 AST_LIST_UNLOCK(&psql_tables);
01577 return ret;
01578 }
01579
01580 if (a->argc == 4) {
01581
01582 AST_LIST_LOCK(&psql_tables);
01583 AST_LIST_TRAVERSE(&psql_tables, cur, list) {
01584 ast_cli(a->fd, "%s\n", cur->name);
01585 }
01586 AST_LIST_UNLOCK(&psql_tables);
01587 } else if (a->argc == 5) {
01588
01589 if ((cur = find_table(NULL, a->argv[4]))) {
01590 struct columns *col;
01591 ast_cli(a->fd, "Columns for Table Cache '%s':\n", a->argv[4]);
01592 ast_cli(a->fd, "%-20.20s %-20.20s %-3.3s %-8.8s\n", "Name", "Type", "Len", "Nullable");
01593 AST_LIST_TRAVERSE(&cur->columns, col, list) {
01594 ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : "");
01595 }
01596 release_table(cur);
01597 } else {
01598 ast_cli(a->fd, "No such table '%s'\n", a->argv[4]);
01599 }
01600 }
01601 return 0;
01602 }
01603
01604 static char *handle_cli_realtime_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01605 {
01606 char status[256], credentials[100] = "";
01607 int ctimesec = time(NULL) - connect_time;
01608
01609 switch (cmd) {
01610 case CLI_INIT:
01611 e->command = "realtime show pgsql status";
01612 e->usage =
01613 "Usage: realtime show pgsql status\n"
01614 " Shows connection information for the PostgreSQL RealTime driver\n";
01615 return NULL;
01616 case CLI_GENERATE:
01617 return NULL;
01618 }
01619
01620 if (a->argc != 4)
01621 return CLI_SHOWUSAGE;
01622
01623 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
01624 if (!ast_strlen_zero(dbhost))
01625 snprintf(status, sizeof(status), "Connected to %s@%s, port %d", dbname, dbhost, dbport);
01626 else if (!ast_strlen_zero(dbsock))
01627 snprintf(status, sizeof(status), "Connected to %s on socket file %s", dbname, dbsock);
01628 else
01629 snprintf(status, sizeof(status), "Connected to %s@%s", dbname, dbhost);
01630
01631 if (!ast_strlen_zero(dbuser))
01632 snprintf(credentials, sizeof(credentials), " with username %s", dbuser);
01633
01634 if (ctimesec > 31536000)
01635 ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
01636 status, credentials, ctimesec / 31536000, (ctimesec % 31536000) / 86400,
01637 (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01638 else if (ctimesec > 86400)
01639 ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
01640 credentials, ctimesec / 86400, (ctimesec % 86400) / 3600, (ctimesec % 3600) / 60,
01641 ctimesec % 60);
01642 else if (ctimesec > 3600)
01643 ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, credentials,
01644 ctimesec / 3600, (ctimesec % 3600) / 60, ctimesec % 60);
01645 else if (ctimesec > 60)
01646 ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, credentials, ctimesec / 60,
01647 ctimesec % 60);
01648 else
01649 ast_cli(a->fd, "%s%s for %d seconds.\n", status, credentials, ctimesec);
01650
01651 return CLI_SUCCESS;
01652 } else {
01653 return CLI_FAILURE;
01654 }
01655 }
01656
01657
01658 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL RealTime Configuration Driver",
01659 .load = load_module,
01660 .unload = unload_module,
01661 .reload = reload,
01662 .load_pri = AST_MODPRI_REALTIME_DRIVER,
01663 );