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 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 283880 $")
00032
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include <string.h>
00036 #include <libpq-fe.h>
00037
00038 #include "asterisk/file.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/options.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/cli.h"
00048
00049 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00050
00051 #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf"
00052
00053 PGconn *pgsqlConn = NULL;
00054
00055 #define MAX_DB_OPTION_SIZE 64
00056
00057 static char dbhost[MAX_DB_OPTION_SIZE] = "";
00058 static char dbuser[MAX_DB_OPTION_SIZE] = "";
00059 static char dbpass[MAX_DB_OPTION_SIZE] = "";
00060 static char dbname[MAX_DB_OPTION_SIZE] = "";
00061 static char dbsock[MAX_DB_OPTION_SIZE] = "";
00062 static int dbport = 5432;
00063 static time_t connect_time = 0;
00064
00065 static int parse_config(void);
00066 static int pgsql_reconnect(const char *database);
00067 static int realtime_pgsql_status(int fd, int argc, char **argv);
00068
00069 static char cli_realtime_pgsql_status_usage[] =
00070 "Usage: realtime pgsql status\n"
00071 " Shows connection information for the Postgresql RealTime driver\n";
00072
00073 static struct ast_cli_entry cli_realtime[] = {
00074 { { "realtime", "pgsql", "status", NULL },
00075 realtime_pgsql_status, "Shows connection information for the Postgresql RealTime driver",
00076 cli_realtime_pgsql_status_usage },
00077 };
00078
00079 static char *encode_chunk(const char *chunk, char *buf, size_t len)
00080 {
00081 char *cptr = buf;
00082 for (; *chunk && cptr < buf + len; chunk++) {
00083 if (strchr(";^", *chunk)) {
00084 snprintf(cptr, buf + len - cptr, "^%02hhX", *chunk);
00085 cptr += 3;
00086 } else {
00087 *cptr++ = *chunk;
00088 }
00089 }
00090 if (cptr < buf + len) {
00091 *cptr = '\0';
00092 } else {
00093 buf[len - 1] = '\0';
00094 }
00095 return buf;
00096 }
00097
00098 static char *decode_chunk(char *chunk)
00099 {
00100 char *orig = chunk;
00101 for (; *chunk; chunk++) {
00102 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
00103 sscanf(chunk + 1, "%02hhX", chunk);
00104 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
00105 }
00106 }
00107 return orig;
00108 }
00109
00110 static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap)
00111 {
00112 PGresult *result = NULL;
00113 int num_rows = 0, pgerror;
00114 char sql[256], escapebuf[2049], semibuf[1024];
00115 char *stringp;
00116 char *chunk;
00117 char *op;
00118 const char *newparam, *newval;
00119 struct ast_variable *var = NULL, *prev = NULL;
00120
00121 if (!table) {
00122 ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
00123 return NULL;
00124 }
00125
00126
00127 newparam = va_arg(ap, const char *);
00128 newval = va_arg(ap, const char *);
00129 if (!newparam || !newval) {
00130 ast_log(LOG_WARNING,
00131 "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00132 if (pgsqlConn) {
00133 PQfinish(pgsqlConn);
00134 pgsqlConn = NULL;
00135 };
00136 return NULL;
00137 }
00138
00139
00140
00141 op = strchr(newparam, ' ') ? "" : " =";
00142
00143 PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00144 if (pgerror) {
00145 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00146 va_end(ap);
00147 return NULL;
00148 }
00149
00150 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
00151 escapebuf);
00152 while ((newparam = va_arg(ap, const char *))) {
00153 newval = va_arg(ap, const char *);
00154 if (!strchr(newparam, ' '))
00155 op = " =";
00156 else
00157 op = "";
00158
00159 PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00160 if (pgerror) {
00161 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00162 va_end(ap);
00163 return NULL;
00164 }
00165
00166 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
00167 op, escapebuf);
00168 }
00169 va_end(ap);
00170
00171
00172 ast_mutex_lock(&pgsql_lock);
00173 if (!pgsql_reconnect(database)) {
00174 ast_mutex_unlock(&pgsql_lock);
00175 return NULL;
00176 }
00177
00178 if (!(result = PQexec(pgsqlConn, sql))) {
00179 ast_log(LOG_WARNING,
00180 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00181 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00182 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00183 PQerrorMessage(pgsqlConn));
00184 ast_mutex_unlock(&pgsql_lock);
00185 return NULL;
00186 } else {
00187 ExecStatusType result_status = PQresultStatus(result);
00188 if (result_status != PGRES_COMMAND_OK
00189 && result_status != PGRES_TUPLES_OK
00190 && result_status != PGRES_NONFATAL_ERROR) {
00191 ast_log(LOG_WARNING,
00192 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00193 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00194 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00195 PQresultErrorMessage(result), PQresStatus(result_status));
00196 ast_mutex_unlock(&pgsql_lock);
00197 return NULL;
00198 }
00199 }
00200
00201 ast_log(LOG_DEBUG, "Postgresql RealTime: Result=%p Query: %s\n", result, sql);
00202
00203 if ((num_rows = PQntuples(result)) > 0) {
00204 int i = 0;
00205 int rowIndex = 0;
00206 int numFields = PQnfields(result);
00207 char **fieldnames = NULL;
00208
00209 ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows);
00210
00211 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00212 ast_mutex_unlock(&pgsql_lock);
00213 PQclear(result);
00214 return NULL;
00215 }
00216 for (i = 0; i < numFields; i++)
00217 fieldnames[i] = PQfname(result, i);
00218 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00219 for (i = 0; i < numFields; i++) {
00220 stringp = PQgetvalue(result, rowIndex, i);
00221 while (stringp) {
00222 chunk = strsep(&stringp, ";");
00223 if (chunk && !ast_strlen_zero(decode_chunk(ast_strip(chunk)))) {
00224 if (prev) {
00225 prev->next = ast_variable_new(fieldnames[i], chunk);
00226 if (prev->next) {
00227 prev = prev->next;
00228 }
00229 } else {
00230 prev = var = ast_variable_new(fieldnames[i], chunk);
00231 }
00232 }
00233 }
00234 }
00235 }
00236 ast_free(fieldnames);
00237 } else {
00238 ast_log(LOG_DEBUG, "Postgresql RealTime: Could not find any rows in table %s.\n", table);
00239 }
00240
00241 ast_mutex_unlock(&pgsql_lock);
00242 PQclear(result);
00243
00244 return var;
00245 }
00246
00247 static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap)
00248 {
00249 PGresult *result = NULL;
00250 int num_rows = 0, pgerror;
00251 char sql[256], escapebuf[2049], semibuf[1024];
00252 const char *initfield = NULL;
00253 char *stringp;
00254 char *chunk;
00255 char *op;
00256 const char *newparam, *newval;
00257 struct ast_realloca ra;
00258 struct ast_variable *var = NULL;
00259 struct ast_config *cfg = NULL;
00260 struct ast_category *cat = NULL;
00261
00262 if (!table) {
00263 ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
00264 return NULL;
00265 }
00266
00267 memset(&ra, 0, sizeof(ra));
00268
00269 if (!(cfg = ast_config_new()))
00270 return NULL;
00271
00272
00273 newparam = va_arg(ap, const char *);
00274 newval = va_arg(ap, const char *);
00275 if (!newparam || !newval) {
00276 ast_log(LOG_WARNING,
00277 "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00278 if (pgsqlConn) {
00279 PQfinish(pgsqlConn);
00280 pgsqlConn = NULL;
00281 };
00282 return NULL;
00283 }
00284
00285 initfield = ast_strdupa(newparam);
00286 if ((op = strchr(initfield, ' '))) {
00287 *op = '\0';
00288 }
00289
00290
00291
00292
00293 if (!strchr(newparam, ' '))
00294 op = " =";
00295 else
00296 op = "";
00297
00298 PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00299 if (pgerror) {
00300 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00301 va_end(ap);
00302 return NULL;
00303 }
00304
00305 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op,
00306 escapebuf);
00307 while ((newparam = va_arg(ap, const char *))) {
00308 newval = va_arg(ap, const char *);
00309 if (!strchr(newparam, ' '))
00310 op = " =";
00311 else
00312 op = "";
00313
00314 PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00315 if (pgerror) {
00316 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00317 va_end(ap);
00318 return NULL;
00319 }
00320
00321 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam,
00322 op, escapebuf);
00323 }
00324
00325 if (initfield) {
00326 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00327 }
00328
00329 va_end(ap);
00330
00331
00332 ast_mutex_lock(&pgsql_lock);
00333 if (!pgsql_reconnect(database)) {
00334 ast_mutex_unlock(&pgsql_lock);
00335 return NULL;
00336 }
00337
00338 if (!(result = PQexec(pgsqlConn, sql))) {
00339 ast_log(LOG_WARNING,
00340 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00341 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00342 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00343 PQerrorMessage(pgsqlConn));
00344 ast_mutex_unlock(&pgsql_lock);
00345 return NULL;
00346 } else {
00347 ExecStatusType result_status = PQresultStatus(result);
00348 if (result_status != PGRES_COMMAND_OK
00349 && result_status != PGRES_TUPLES_OK
00350 && result_status != PGRES_NONFATAL_ERROR) {
00351 ast_log(LOG_WARNING,
00352 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00353 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00354 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00355 PQresultErrorMessage(result), PQresStatus(result_status));
00356 ast_mutex_unlock(&pgsql_lock);
00357 return NULL;
00358 }
00359 }
00360
00361 ast_log(LOG_DEBUG, "2Postgresql RealTime: Result=%p Query: %s\n", result, sql);
00362
00363 if ((num_rows = PQntuples(result)) > 0) {
00364 int numFields = PQnfields(result);
00365 int i = 0;
00366 int rowIndex = 0;
00367 char **fieldnames = NULL;
00368
00369 ast_log(LOG_DEBUG, "Postgresql RealTime: Found %d rows.\n", num_rows);
00370
00371 if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
00372 ast_mutex_unlock(&pgsql_lock);
00373 PQclear(result);
00374 return NULL;
00375 }
00376 for (i = 0; i < numFields; i++)
00377 fieldnames[i] = PQfname(result, i);
00378
00379 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00380 var = NULL;
00381 if (!(cat = ast_category_new("")))
00382 continue;
00383 for (i = 0; i < numFields; i++) {
00384 stringp = PQgetvalue(result, rowIndex, i);
00385 while (stringp) {
00386 chunk = strsep(&stringp, ";");
00387 if (chunk && !ast_strlen_zero(decode_chunk(ast_strip(chunk)))) {
00388 if (initfield && !strcmp(initfield, fieldnames[i])) {
00389 ast_category_rename(cat, chunk);
00390 }
00391 var = ast_variable_new(fieldnames[i], chunk);
00392 ast_variable_append(cat, var);
00393 }
00394 }
00395 }
00396 ast_category_append(cfg, cat);
00397 }
00398 ast_free(fieldnames);
00399 } else {
00400 ast_log(LOG_DEBUG,
00401 "Postgresql RealTime: Could not find any rows in table %s.\n", table);
00402 }
00403
00404 ast_mutex_unlock(&pgsql_lock);
00405 PQclear(result);
00406
00407 return cfg;
00408 }
00409
00410 static int update_pgsql(const char *database, const char *table, const char *keyfield,
00411 const char *lookup, va_list ap)
00412 {
00413 PGresult *result = NULL;
00414 int numrows = 0, pgerror;
00415 char sql[256], escapebuf[2049], semibuf[1024];
00416 const char *newparam, *newval;
00417
00418 if (!table) {
00419 ast_log(LOG_WARNING, "Postgresql RealTime: No table specified.\n");
00420 return -1;
00421 }
00422
00423
00424 newparam = va_arg(ap, const char *);
00425 newval = va_arg(ap, const char *);
00426 if (!newparam || !newval) {
00427 ast_log(LOG_WARNING,
00428 "Postgresql RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
00429 if (pgsqlConn) {
00430 PQfinish(pgsqlConn);
00431 pgsqlConn = NULL;
00432 };
00433 return -1;
00434 }
00435
00436
00437
00438
00439 PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00440 if (pgerror) {
00441 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00442 va_end(ap);
00443 return -1;
00444 }
00445 snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, escapebuf);
00446
00447 while ((newparam = va_arg(ap, const char *))) {
00448 newval = va_arg(ap, const char *);
00449
00450 PQescapeStringConn(pgsqlConn, escapebuf, encode_chunk(newval, semibuf, sizeof(semibuf)), (sizeof(escapebuf) - 1) / 2, &pgerror);
00451 if (pgerror) {
00452 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval);
00453 va_end(ap);
00454 return -1;
00455 }
00456
00457 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam,
00458 escapebuf);
00459 }
00460 va_end(ap);
00461
00462 PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror);
00463 if (pgerror) {
00464 ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup);
00465 va_end(ap);
00466 return -1;
00467 }
00468
00469 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield,
00470 escapebuf);
00471
00472 ast_log(LOG_DEBUG, "Postgresql RealTime: Update SQL: %s\n", sql);
00473
00474
00475 ast_mutex_lock(&pgsql_lock);
00476 if (!pgsql_reconnect(database)) {
00477 ast_mutex_unlock(&pgsql_lock);
00478 return -1;
00479 }
00480
00481 if (!(result = PQexec(pgsqlConn, sql))) {
00482 ast_log(LOG_WARNING,
00483 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00484 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00485 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00486 PQerrorMessage(pgsqlConn));
00487 ast_mutex_unlock(&pgsql_lock);
00488 return -1;
00489 } else {
00490 ExecStatusType result_status = PQresultStatus(result);
00491 if (result_status != PGRES_COMMAND_OK
00492 && result_status != PGRES_TUPLES_OK
00493 && result_status != PGRES_NONFATAL_ERROR) {
00494 ast_log(LOG_WARNING,
00495 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00496 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00497 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00498 PQresultErrorMessage(result), PQresStatus(result_status));
00499 ast_mutex_unlock(&pgsql_lock);
00500 return -1;
00501 }
00502 }
00503
00504 numrows = atoi(PQcmdTuples(result));
00505 ast_mutex_unlock(&pgsql_lock);
00506
00507 ast_log(LOG_DEBUG, "Postgresql RealTime: Updated %d rows on table: %s\n", numrows,
00508 table);
00509
00510
00511
00512
00513
00514
00515
00516 if (numrows >= 0)
00517 return (int) numrows;
00518
00519 return -1;
00520 }
00521
00522 static struct ast_config *config_pgsql(const char *database, const char *table,
00523 const char *file, struct ast_config *cfg,
00524 int withcomments)
00525 {
00526 PGresult *result = NULL;
00527 long num_rows;
00528 struct ast_variable *new_v;
00529 struct ast_category *cur_cat = NULL;
00530 char sqlbuf[1024] = "";
00531 char *sql = sqlbuf;
00532 size_t sqlleft = sizeof(sqlbuf);
00533 char last[80] = "";
00534 int last_cat_metric = 0;
00535
00536 last[0] = '\0';
00537
00538 if (!file || !strcmp(file, RES_CONFIG_PGSQL_CONF)) {
00539 ast_log(LOG_WARNING, "Postgresql RealTime: Cannot configure myself.\n");
00540 return NULL;
00541 }
00542
00543 ast_build_string(&sql, &sqlleft, "SELECT category, var_name, var_val, cat_metric FROM %s ", table);
00544 ast_build_string(&sql, &sqlleft, "WHERE filename='%s' and commented=0", file);
00545 ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
00546
00547 ast_log(LOG_DEBUG, "Postgresql RealTime: Static SQL: %s\n", sqlbuf);
00548
00549
00550 ast_mutex_lock(&pgsql_lock);
00551 if (!pgsql_reconnect(database)) {
00552 ast_mutex_unlock(&pgsql_lock);
00553 return NULL;
00554 }
00555
00556 if (!(result = PQexec(pgsqlConn, sqlbuf))) {
00557 ast_log(LOG_WARNING,
00558 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00559 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00560 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s\n",
00561 PQerrorMessage(pgsqlConn));
00562 ast_mutex_unlock(&pgsql_lock);
00563 return NULL;
00564 } else {
00565 ExecStatusType result_status = PQresultStatus(result);
00566 if (result_status != PGRES_COMMAND_OK
00567 && result_status != PGRES_TUPLES_OK
00568 && result_status != PGRES_NONFATAL_ERROR) {
00569 ast_log(LOG_WARNING,
00570 "Postgresql RealTime: Failed to query database. Check debug for more info.\n");
00571 ast_log(LOG_DEBUG, "Postgresql RealTime: Query: %s\n", sql);
00572 ast_log(LOG_DEBUG, "Postgresql RealTime: Query Failed because: %s (%s)\n",
00573 PQresultErrorMessage(result), PQresStatus(result_status));
00574 ast_mutex_unlock(&pgsql_lock);
00575 return NULL;
00576 }
00577 }
00578
00579 if ((num_rows = PQntuples(result)) > 0) {
00580 int rowIndex = 0;
00581
00582 ast_log(LOG_DEBUG, "Postgresql RealTime: Found %ld rows.\n", num_rows);
00583
00584 for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
00585 char *field_category = PQgetvalue(result, rowIndex, 0);
00586 char *field_var_name = PQgetvalue(result, rowIndex, 1);
00587 char *field_var_val = PQgetvalue(result, rowIndex, 2);
00588 char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
00589 if (!strcmp(field_var_name, "#include")) {
00590 if (!ast_config_internal_load(field_var_val, cfg, 0)) {
00591 PQclear(result);
00592 ast_mutex_unlock(&pgsql_lock);
00593 return NULL;
00594 }
00595 continue;
00596 }
00597
00598 if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
00599 cur_cat = ast_category_new(field_category);
00600 if (!cur_cat)
00601 break;
00602 strcpy(last, field_category);
00603 last_cat_metric = atoi(field_cat_metric);
00604 ast_category_append(cfg, cur_cat);
00605 }
00606 new_v = ast_variable_new(field_var_name, field_var_val);
00607 ast_variable_append(cur_cat, new_v);
00608 }
00609 } else {
00610 ast_log(LOG_WARNING,
00611 "Postgresql RealTime: Could not find config '%s' in database.\n", file);
00612 }
00613
00614 PQclear(result);
00615 ast_mutex_unlock(&pgsql_lock);
00616
00617 return cfg;
00618 }
00619
00620 static struct ast_config_engine pgsql_engine = {
00621 .name = "pgsql",
00622 .load_func = config_pgsql,
00623 .realtime_func = realtime_pgsql,
00624 .realtime_multi_func = realtime_multi_pgsql,
00625 .update_func = update_pgsql
00626 };
00627
00628 static int load_module(void)
00629 {
00630 if(!parse_config())
00631 return AST_MODULE_LOAD_DECLINE;
00632
00633 ast_mutex_lock(&pgsql_lock);
00634
00635 if (!pgsql_reconnect(NULL)) {
00636 ast_log(LOG_WARNING,
00637 "Postgresql RealTime: Couldn't establish connection. Check debug.\n");
00638 ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n",
00639 PQerrorMessage(pgsqlConn));
00640 }
00641
00642 ast_config_engine_register(&pgsql_engine);
00643 if (option_verbose) {
00644 ast_verbose("Postgresql RealTime driver loaded.\n");
00645 }
00646 ast_cli_register_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
00647
00648 ast_mutex_unlock(&pgsql_lock);
00649
00650 return 0;
00651 }
00652
00653 static int unload_module(void)
00654 {
00655
00656 ast_mutex_lock(&pgsql_lock);
00657
00658 if (pgsqlConn) {
00659 PQfinish(pgsqlConn);
00660 pgsqlConn = NULL;
00661 };
00662 ast_cli_unregister_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
00663 ast_config_engine_deregister(&pgsql_engine);
00664 if (option_verbose) {
00665 ast_verbose("Postgresql RealTime unloaded.\n");
00666 }
00667
00668 ast_module_user_hangup_all();
00669
00670
00671 ast_mutex_unlock(&pgsql_lock);
00672
00673 return 0;
00674 }
00675
00676 static int reload(void)
00677 {
00678
00679 ast_mutex_lock(&pgsql_lock);
00680
00681 if (pgsqlConn) {
00682 PQfinish(pgsqlConn);
00683 pgsqlConn = NULL;
00684 };
00685 parse_config();
00686
00687 if (!pgsql_reconnect(NULL)) {
00688 ast_log(LOG_WARNING,
00689 "Postgresql RealTime: Couldn't establish connection. Check debug.\n");
00690 ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n",
00691 PQerrorMessage(pgsqlConn));
00692 }
00693
00694 ast_verbose(VERBOSE_PREFIX_2 "Postgresql RealTime reloaded.\n");
00695
00696
00697 ast_mutex_unlock(&pgsql_lock);
00698
00699 return 0;
00700 }
00701
00702 static int parse_config(void)
00703 {
00704 struct ast_config *config;
00705 const char *s;
00706
00707 config = ast_config_load(RES_CONFIG_PGSQL_CONF);
00708
00709 if (!config) {
00710 ast_log(LOG_WARNING, "Unable to load config %s\n",RES_CONFIG_PGSQL_CONF);
00711 return 0;
00712 }
00713 if (!(s = ast_variable_retrieve(config, "general", "dbuser"))) {
00714 ast_log(LOG_WARNING,
00715 "Postgresql RealTime: No database user found, using 'asterisk' as default.\n");
00716 strcpy(dbuser, "asterisk");
00717 } else {
00718 ast_copy_string(dbuser, s, sizeof(dbuser));
00719 }
00720
00721 if (!(s = ast_variable_retrieve(config, "general", "dbpass"))) {
00722 ast_log(LOG_WARNING,
00723 "Postgresql RealTime: No database password found, using 'asterisk' as default.\n");
00724 strcpy(dbpass, "asterisk");
00725 } else {
00726 ast_copy_string(dbpass, s, sizeof(dbpass));
00727 }
00728
00729 if (!(s = ast_variable_retrieve(config, "general", "dbhost"))) {
00730 ast_log(LOG_WARNING,
00731 "Postgresql RealTime: No database host found, using localhost via socket.\n");
00732 dbhost[0] = '\0';
00733 } else {
00734 ast_copy_string(dbhost, s, sizeof(dbhost));
00735 }
00736
00737 if (!(s = ast_variable_retrieve(config, "general", "dbname"))) {
00738 ast_log(LOG_WARNING,
00739 "Postgresql RealTime: No database name found, using 'asterisk' as default.\n");
00740 strcpy(dbname, "asterisk");
00741 } else {
00742 ast_copy_string(dbname, s, sizeof(dbname));
00743 }
00744
00745 if (!(s = ast_variable_retrieve(config, "general", "dbport"))) {
00746 ast_log(LOG_WARNING,
00747 "Postgresql RealTime: No database port found, using 5432 as default.\n");
00748 dbport = 5432;
00749 } else {
00750 dbport = atoi(s);
00751 }
00752
00753 if (!ast_strlen_zero(dbhost)) {
00754
00755 } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
00756 ast_log(LOG_WARNING,
00757 "Postgresql RealTime: No database socket found, using '/tmp/pgsql.sock' as default.\n");
00758 strcpy(dbsock, "/tmp/pgsql.sock");
00759 } else {
00760 ast_copy_string(dbsock, s, sizeof(dbsock));
00761 }
00762 ast_config_destroy(config);
00763
00764 if (!ast_strlen_zero(dbhost)) {
00765 ast_log(LOG_DEBUG, "Postgresql RealTime Host: %s\n", dbhost);
00766 ast_log(LOG_DEBUG, "Postgresql RealTime Port: %i\n", dbport);
00767 } else {
00768 ast_log(LOG_DEBUG, "Postgresql RealTime Socket: %s\n", dbsock);
00769 }
00770 ast_log(LOG_DEBUG, "Postgresql RealTime User: %s\n", dbuser);
00771 ast_log(LOG_DEBUG, "Postgresql RealTime Password: %s\n", dbpass);
00772 ast_log(LOG_DEBUG, "Postgresql RealTime DBName: %s\n", dbname);
00773
00774 return 1;
00775 }
00776
00777 static int pgsql_reconnect(const char *database)
00778 {
00779 char my_database[50];
00780
00781 ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));
00782
00783
00784
00785 if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
00786 PQfinish(pgsqlConn);
00787 pgsqlConn = NULL;
00788 }
00789
00790 if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(dbpass) && !ast_strlen_zero(my_database)) {
00791 char *connInfo = NULL;
00792 unsigned int size = 100 + strlen(dbhost)
00793 + strlen(dbuser)
00794 + strlen(dbpass)
00795 + strlen(my_database);
00796
00797 if (!(connInfo = ast_malloc(size)))
00798 return 0;
00799
00800 sprintf(connInfo, "host=%s port=%d dbname=%s user=%s password=%s",
00801 dbhost, dbport, my_database, dbuser, dbpass);
00802 ast_log(LOG_DEBUG, "%u connInfo=%s\n", size, connInfo);
00803 pgsqlConn = PQconnectdb(connInfo);
00804 ast_log(LOG_DEBUG, "%u connInfo=%s\n", size, connInfo);
00805 ast_free(connInfo);
00806 connInfo = NULL;
00807 ast_log(LOG_DEBUG, "pgsqlConn=%p\n", pgsqlConn);
00808 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
00809 ast_log(LOG_DEBUG, "Postgresql RealTime: Successfully connected to database.\n");
00810 connect_time = time(NULL);
00811 return 1;
00812 } else {
00813 ast_log(LOG_ERROR,
00814 "Postgresql RealTime: Failed to connect database server %s on %s. Check debug for more info.\n",
00815 dbname, dbhost);
00816 ast_log(LOG_DEBUG, "Postgresql RealTime: Cannot Connect: %s\n",
00817 PQresultErrorMessage(NULL));
00818 return 0;
00819 }
00820 } else {
00821 ast_log(LOG_DEBUG, "Postgresql RealTime: Everything is fine.\n");
00822 return 1;
00823 }
00824 }
00825
00826 static int realtime_pgsql_status(int fd, int argc, char **argv)
00827 {
00828 char status[256], status2[100] = "";
00829 int ctime = time(NULL) - connect_time;
00830
00831 if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
00832 if (!ast_strlen_zero(dbhost)) {
00833 snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
00834 } else if (!ast_strlen_zero(dbsock)) {
00835 snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
00836 } else {
00837 snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
00838 }
00839
00840 if (!ast_strlen_zero(dbuser)) {
00841 snprintf(status2, 99, " with username %s", dbuser);
00842 }
00843
00844 if (ctime > 31536000) {
00845 ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
00846 status, status2, ctime / 31536000, (ctime % 31536000) / 86400,
00847 (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00848 } else if (ctime > 86400) {
00849 ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status,
00850 status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60,
00851 ctime % 60);
00852 } else if (ctime > 3600) {
00853 ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2,
00854 ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00855 } else if (ctime > 60) {
00856 ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60,
00857 ctime % 60);
00858 } else {
00859 ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
00860 }
00861
00862 return RESULT_SUCCESS;
00863 } else {
00864 return RESULT_FAILURE;
00865 }
00866 }
00867
00868
00869 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL RealTime Configuration Driver",
00870 .load = load_module,
00871 .unload = unload_module,
00872 .reload = reload
00873 );