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
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "asterisk.h"
00042
00043 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $")
00044
00045 #include <libpq-fe.h>
00046
00047 #include "asterisk/config.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/cdr.h"
00050 #include "asterisk/cli.h"
00051 #include "asterisk/module.h"
00052
00053 #define DATE_FORMAT "'%Y-%m-%d %T'"
00054
00055 static const char name[] = "pgsql";
00056 static const char config[] = "cdr_pgsql.conf";
00057 static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL, *encoding = NULL, *tz = NULL;
00058 static int connected = 0;
00059 static int maxsize = 512, maxsize2 = 512;
00060 static time_t connect_time = 0;
00061 static int totalrecords = 0;
00062 static int records;
00063
00064 static char *handle_cdr_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00065 static struct ast_cli_entry cdr_pgsql_status_cli[] = {
00066 AST_CLI_DEFINE(handle_cdr_pgsql_status, "Show connection status of the PostgreSQL CDR driver (cdr_pgsql)"),
00067 };
00068
00069 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00070
00071 static PGconn *conn = NULL;
00072
00073 struct columns {
00074 char *name;
00075 char *type;
00076 int len;
00077 unsigned int notnull:1;
00078 unsigned int hasdefault:1;
00079 AST_RWLIST_ENTRY(columns) list;
00080 };
00081
00082 static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
00083
00084 #define LENGTHEN_BUF1(size) \
00085 do { \
00086 \
00087 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
00088 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
00089 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
00090 ast_free(sql); \
00091 ast_free(sql2); \
00092 AST_RWLIST_UNLOCK(&psql_columns); \
00093 ast_mutex_unlock(&pgsql_lock); \
00094 return -1; \
00095 } \
00096 } \
00097 } while (0)
00098
00099 #define LENGTHEN_BUF2(size) \
00100 do { \
00101 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
00102 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
00103 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
00104 ast_free(sql); \
00105 ast_free(sql2); \
00106 AST_RWLIST_UNLOCK(&psql_columns); \
00107 ast_mutex_unlock(&pgsql_lock); \
00108 return -1; \
00109 } \
00110 } \
00111 } while (0)
00112
00113
00114 static char *handle_cdr_pgsql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00115 {
00116 switch (cmd) {
00117 case CLI_INIT:
00118 e->command = "cdr show pgsql status";
00119 e->usage =
00120 "Usage: cdr show pgsql status\n"
00121 " Shows current connection status for cdr_pgsql\n";
00122 return NULL;
00123 case CLI_GENERATE:
00124 return NULL;
00125 }
00126
00127 if (a->argc != 3)
00128 return CLI_SHOWUSAGE;
00129
00130 if (connected) {
00131 char status[256], status2[100] = "";
00132 int ctime = time(NULL) - connect_time;
00133
00134 if (pgdbport) {
00135 snprintf(status, 255, "Connected to %s@%s, port %s", pgdbname, pghostname, pgdbport);
00136 } else {
00137 snprintf(status, 255, "Connected to %s@%s", pgdbname, pghostname);
00138 }
00139
00140 if (pgdbuser && *pgdbuser) {
00141 snprintf(status2, 99, " with username %s", pgdbuser);
00142 }
00143 if (table && *table) {
00144 snprintf(status2, 99, " using table %s", table);
00145 }
00146 if (ctime > 31536000) {
00147 ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00148 } else if (ctime > 86400) {
00149 ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
00150 } else if (ctime > 3600) {
00151 ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
00152 } else if (ctime > 60) {
00153 ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
00154 } else {
00155 ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
00156 }
00157 if (records == totalrecords) {
00158 ast_cli(a->fd, " Wrote %d records since last restart.\n", totalrecords);
00159 } else {
00160 ast_cli(a->fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
00161 }
00162 } else {
00163 ast_cli(a->fd, "Not currently connected to a PgSQL server.\n");
00164 }
00165 return CLI_SUCCESS;
00166 }
00167
00168 static int pgsql_log(struct ast_cdr *cdr)
00169 {
00170 struct ast_tm tm;
00171 char *pgerror;
00172 PGresult *result;
00173
00174 ast_mutex_lock(&pgsql_lock);
00175
00176 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00177 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00178 if (PQstatus(conn) != CONNECTION_BAD) {
00179 connected = 1;
00180 connect_time = time(NULL);
00181 records = 0;
00182 if (PQsetClientEncoding(conn, encoding)) {
00183 #ifdef HAVE_PGSQL_pg_encoding_to_char
00184 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00185 #else
00186 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00187 #endif
00188 }
00189 } else {
00190 pgerror = PQerrorMessage(conn);
00191 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
00192 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00193 PQfinish(conn);
00194 conn = NULL;
00195 }
00196 }
00197
00198 if (connected) {
00199 struct columns *cur;
00200 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00201 char buf[257], escapebuf[513], *value;
00202 int first = 1;
00203
00204 if (!sql || !sql2) {
00205 ast_free(sql);
00206 ast_free(sql2);
00207 return -1;
00208 }
00209
00210 ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00211 ast_str_set(&sql2, 0, " VALUES (");
00212
00213 AST_RWLIST_RDLOCK(&psql_columns);
00214 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00215
00216 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00217 if (strcmp(cur->name, "calldate") == 0 && !value) {
00218 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00219 }
00220 if (!value) {
00221 if (cur->notnull && !cur->hasdefault) {
00222
00223 LENGTHEN_BUF1(strlen(cur->name) + 2);
00224 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00225 LENGTHEN_BUF2(3);
00226 ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00227 first = 0;
00228 }
00229 continue;
00230 }
00231
00232 LENGTHEN_BUF1(strlen(cur->name) + 2);
00233 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00234
00235 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00236 if (strncmp(cur->type, "int", 3) == 0) {
00237 LENGTHEN_BUF2(13);
00238 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00239 } else if (strncmp(cur->type, "float", 5) == 0) {
00240 LENGTHEN_BUF2(31);
00241 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00242 } else {
00243
00244 LENGTHEN_BUF2(31);
00245 ast_localtime(&cdr->start, &tm, tz);
00246 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00247 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00248 }
00249 } else if (strcmp(cur->name, "answer") == 0) {
00250 if (strncmp(cur->type, "int", 3) == 0) {
00251 LENGTHEN_BUF2(13);
00252 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00253 } else if (strncmp(cur->type, "float", 5) == 0) {
00254 LENGTHEN_BUF2(31);
00255 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00256 } else {
00257
00258 LENGTHEN_BUF2(31);
00259 ast_localtime(&cdr->answer, &tm, tz);
00260 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00261 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00262 }
00263 } else if (strcmp(cur->name, "end") == 0) {
00264 if (strncmp(cur->type, "int", 3) == 0) {
00265 LENGTHEN_BUF2(13);
00266 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00267 } else if (strncmp(cur->type, "float", 5) == 0) {
00268 LENGTHEN_BUF2(31);
00269 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00270 } else {
00271
00272 LENGTHEN_BUF2(31);
00273 ast_localtime(&cdr->end, &tm, tz);
00274 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00275 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00276 }
00277 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00278 if (cur->type[0] == 'i') {
00279
00280 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00281 LENGTHEN_BUF2(13);
00282 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00283 } else if (strncmp(cur->type, "float", 5) == 0) {
00284 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00285 LENGTHEN_BUF2(31);
00286 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00287 } else {
00288
00289 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00290 LENGTHEN_BUF2(31);
00291 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00292 }
00293 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00294 if (strncmp(cur->type, "int", 3) == 0) {
00295
00296 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00297 LENGTHEN_BUF2(13);
00298 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00299 } else {
00300
00301 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00302 LENGTHEN_BUF2(31);
00303 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00304 }
00305 } else {
00306
00307 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00308 if (strncmp(cur->type, "int", 3) == 0) {
00309 long long whatever;
00310 if (value && sscanf(value, "%30lld", &whatever) == 1) {
00311 LENGTHEN_BUF2(26);
00312 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00313 } else {
00314 LENGTHEN_BUF2(2);
00315 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00316 }
00317 } else if (strncmp(cur->type, "float", 5) == 0) {
00318 long double whatever;
00319 if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00320 LENGTHEN_BUF2(51);
00321 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00322 } else {
00323 LENGTHEN_BUF2(2);
00324 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00325 }
00326
00327 } else {
00328 if (value)
00329 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00330 else
00331 escapebuf[0] = '\0';
00332 LENGTHEN_BUF2(strlen(escapebuf) + 3);
00333 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00334 }
00335 }
00336 first = 0;
00337 }
00338
00339 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00340 AST_RWLIST_UNLOCK(&psql_columns);
00341 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
00342 ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00343
00344 ast_debug(2, "inserting a CDR record.\n");
00345
00346
00347
00348
00349 if (PQstatus(conn) == CONNECTION_OK) {
00350 connected = 1;
00351 } else {
00352 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
00353 PQreset(conn);
00354 if (PQstatus(conn) == CONNECTION_OK) {
00355 ast_log(LOG_ERROR, "Connection reestablished.\n");
00356 connected = 1;
00357 connect_time = time(NULL);
00358 records = 0;
00359 } else {
00360 pgerror = PQerrorMessage(conn);
00361 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00362 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00363 PQfinish(conn);
00364 conn = NULL;
00365 connected = 0;
00366 ast_mutex_unlock(&pgsql_lock);
00367 ast_free(sql);
00368 ast_free(sql2);
00369 return -1;
00370 }
00371 }
00372 result = PQexec(conn, ast_str_buffer(sql));
00373 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00374 pgerror = PQresultErrorMessage(result);
00375 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
00376 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00377 ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
00378 PQreset(conn);
00379 if (PQstatus(conn) == CONNECTION_OK) {
00380 ast_log(LOG_ERROR, "Connection reestablished.\n");
00381 connected = 1;
00382 connect_time = time(NULL);
00383 records = 0;
00384 PQclear(result);
00385 result = PQexec(conn, ast_str_buffer(sql));
00386 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00387 pgerror = PQresultErrorMessage(result);
00388 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
00389 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00390 } else {
00391
00392 totalrecords++;
00393 records++;
00394 ast_mutex_unlock(&pgsql_lock);
00395 PQclear(result);
00396 return 0;
00397 }
00398 }
00399 ast_mutex_unlock(&pgsql_lock);
00400 PQclear(result);
00401 ast_free(sql);
00402 ast_free(sql2);
00403 return -1;
00404 } else {
00405 totalrecords++;
00406 records++;
00407 }
00408 PQclear(result);
00409 ast_free(sql);
00410 ast_free(sql2);
00411 }
00412 ast_mutex_unlock(&pgsql_lock);
00413 return 0;
00414 }
00415
00416
00417 static void empty_columns(void)
00418 {
00419 struct columns *current;
00420 AST_RWLIST_WRLOCK(&psql_columns);
00421 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00422 ast_free(current);
00423 }
00424 AST_RWLIST_UNLOCK(&psql_columns);
00425
00426 }
00427
00428 static int unload_module(void)
00429 {
00430 ast_cdr_unregister(name);
00431 ast_cli_unregister_multiple(cdr_pgsql_status_cli, ARRAY_LEN(cdr_pgsql_status_cli));
00432
00433 PQfinish(conn);
00434
00435 ast_free(pghostname);
00436 ast_free(pgdbname);
00437 ast_free(pgdbuser);
00438 ast_free(pgpassword);
00439 ast_free(pgdbport);
00440 ast_free(table);
00441 ast_free(encoding);
00442 ast_free(tz);
00443
00444 empty_columns();
00445
00446 return 0;
00447 }
00448
00449 static int config_module(int reload)
00450 {
00451 char *pgerror;
00452 struct columns *cur;
00453 PGresult *result;
00454 const char *tmp;
00455 struct ast_config *cfg;
00456 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00457
00458 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00459 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00460 return -1;
00461 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00462 return 0;
00463 }
00464
00465 ast_mutex_lock(&pgsql_lock);
00466
00467 if (!ast_variable_browse(cfg, "global")) {
00468 ast_config_destroy(cfg);
00469 ast_mutex_unlock(&pgsql_lock);
00470 ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
00471 reload ? "reload" : "load");
00472 return -1;
00473 }
00474
00475 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00476 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
00477 tmp = "";
00478 }
00479
00480 ast_free(pghostname);
00481 if (!(pghostname = ast_strdup(tmp))) {
00482 ast_config_destroy(cfg);
00483 ast_mutex_unlock(&pgsql_lock);
00484 return -1;
00485 }
00486
00487 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00488 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
00489 tmp = "asteriskcdrdb";
00490 }
00491
00492 ast_free(pgdbname);
00493 if (!(pgdbname = ast_strdup(tmp))) {
00494 ast_config_destroy(cfg);
00495 ast_mutex_unlock(&pgsql_lock);
00496 return -1;
00497 }
00498
00499 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00500 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
00501 tmp = "asterisk";
00502 }
00503
00504 ast_free(pgdbuser);
00505 if (!(pgdbuser = ast_strdup(tmp))) {
00506 ast_config_destroy(cfg);
00507 ast_mutex_unlock(&pgsql_lock);
00508 return -1;
00509 }
00510
00511 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00512 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
00513 tmp = "";
00514 }
00515
00516 ast_free(pgpassword);
00517 if (!(pgpassword = ast_strdup(tmp))) {
00518 ast_config_destroy(cfg);
00519 ast_mutex_unlock(&pgsql_lock);
00520 return -1;
00521 }
00522
00523 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00524 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
00525 tmp = "5432";
00526 }
00527
00528 ast_free(pgdbport);
00529 if (!(pgdbport = ast_strdup(tmp))) {
00530 ast_config_destroy(cfg);
00531 ast_mutex_unlock(&pgsql_lock);
00532 return -1;
00533 }
00534
00535 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00536 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
00537 tmp = "cdr";
00538 }
00539
00540 ast_free(table);
00541 if (!(table = ast_strdup(tmp))) {
00542 ast_config_destroy(cfg);
00543 ast_mutex_unlock(&pgsql_lock);
00544 return -1;
00545 }
00546
00547 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00548 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n");
00549 tmp = "LATIN9";
00550 }
00551
00552 ast_free(encoding);
00553 if (!(encoding = ast_strdup(tmp))) {
00554 ast_config_destroy(cfg);
00555 ast_mutex_unlock(&pgsql_lock);
00556 return -1;
00557 }
00558
00559 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
00560 tmp = "";
00561 }
00562
00563 ast_free(tz);
00564 tz = NULL;
00565
00566 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
00567 ast_config_destroy(cfg);
00568 ast_mutex_unlock(&pgsql_lock);
00569 return -1;
00570 }
00571
00572 if (option_debug) {
00573 if (ast_strlen_zero(pghostname)) {
00574 ast_debug(1, "using default unix socket\n");
00575 } else {
00576 ast_debug(1, "got hostname of %s\n", pghostname);
00577 }
00578 ast_debug(1, "got port of %s\n", pgdbport);
00579 ast_debug(1, "got user of %s\n", pgdbuser);
00580 ast_debug(1, "got dbname of %s\n", pgdbname);
00581 ast_debug(1, "got password of %s\n", pgpassword);
00582 ast_debug(1, "got sql table name of %s\n", table);
00583 ast_debug(1, "got encoding of %s\n", encoding);
00584 ast_debug(1, "got timezone of %s\n", tz);
00585 }
00586
00587 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00588 if (PQstatus(conn) != CONNECTION_BAD) {
00589 char sqlcmd[768];
00590 char *fname, *ftype, *flen, *fnotnull, *fdef;
00591 int i, rows, version;
00592 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00593 connected = 1;
00594 connect_time = time(NULL);
00595 records = 0;
00596 if (PQsetClientEncoding(conn, encoding)) {
00597 #ifdef HAVE_PGSQL_pg_encoding_to_char
00598 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00599 #else
00600 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00601 #endif
00602 }
00603 version = PQserverVersion(conn);
00604
00605 if (version >= 70300) {
00606 char *schemaname, *tablename;
00607 if (strchr(table, '.')) {
00608 schemaname = ast_strdupa(table);
00609 tablename = strchr(schemaname, '.');
00610 *tablename++ = '\0';
00611 } else {
00612 schemaname = "";
00613 tablename = table;
00614 }
00615
00616
00617 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00618 char *tmp = schemaname, *ptr;
00619
00620 ptr = schemaname = ast_alloca(strlen(tmp) * 2 + 1);
00621 for (; *tmp; tmp++) {
00622 if (strchr("\\'", *tmp)) {
00623 *ptr++ = *tmp;
00624 }
00625 *ptr++ = *tmp;
00626 }
00627 *ptr = '\0';
00628 }
00629
00630 if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00631 char *tmp = tablename, *ptr;
00632
00633 ptr = tablename = ast_alloca(strlen(tmp) * 2 + 1);
00634 for (; *tmp; tmp++) {
00635 if (strchr("\\'", *tmp)) {
00636 *ptr++ = *tmp;
00637 }
00638 *ptr++ = *tmp;
00639 }
00640 *ptr = '\0';
00641 }
00642
00643 snprintf(sqlcmd, sizeof(sqlcmd), "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",
00644 tablename,
00645 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00646 } else {
00647 snprintf(sqlcmd, sizeof(sqlcmd), "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", table);
00648 }
00649
00650 result = PQexec(conn, sqlcmd);
00651 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00652 pgerror = PQresultErrorMessage(result);
00653 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00654 PQclear(result);
00655 unload_module();
00656 ast_mutex_unlock(&pgsql_lock);
00657 return AST_MODULE_LOAD_DECLINE;
00658 }
00659
00660 rows = PQntuples(result);
00661 if (rows == 0) {
00662 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00663 PQclear(result);
00664 unload_module();
00665 ast_mutex_unlock(&pgsql_lock);
00666 return AST_MODULE_LOAD_DECLINE;
00667 }
00668
00669
00670 empty_columns();
00671
00672 for (i = 0; i < rows; i++) {
00673 fname = PQgetvalue(result, i, 0);
00674 ftype = PQgetvalue(result, i, 1);
00675 flen = PQgetvalue(result, i, 2);
00676 fnotnull = PQgetvalue(result, i, 3);
00677 fdef = PQgetvalue(result, i, 4);
00678 if (atoi(flen) == -1) {
00679
00680 flen = PQgetvalue(result, i, 5);
00681 }
00682 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00683 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00684 if (cur) {
00685 sscanf(flen, "%30d", &cur->len);
00686 cur->name = (char *)cur + sizeof(*cur);
00687 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00688 strcpy(cur->name, fname);
00689 strcpy(cur->type, ftype);
00690 if (*fnotnull == 't') {
00691 cur->notnull = 1;
00692 } else {
00693 cur->notnull = 0;
00694 }
00695 if (!ast_strlen_zero(fdef)) {
00696 cur->hasdefault = 1;
00697 } else {
00698 cur->hasdefault = 0;
00699 }
00700 AST_RWLIST_WRLOCK(&psql_columns);
00701 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00702 AST_RWLIST_UNLOCK(&psql_columns);
00703 }
00704 }
00705 PQclear(result);
00706 } else {
00707 pgerror = PQerrorMessage(conn);
00708 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00709 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00710 connected = 0;
00711 }
00712
00713 ast_config_destroy(cfg);
00714
00715 ast_mutex_unlock(&pgsql_lock);
00716 return 0;
00717 }
00718
00719 static int load_module(void)
00720 {
00721 ast_cli_register_multiple(cdr_pgsql_status_cli, sizeof(cdr_pgsql_status_cli) / sizeof(struct ast_cli_entry));
00722 if (config_module(0)) {
00723 return AST_MODULE_LOAD_DECLINE;
00724 }
00725 return ast_cdr_register(name, ast_module_info->description, pgsql_log)
00726 ? AST_MODULE_LOAD_DECLINE : 0;
00727 }
00728
00729 static int reload(void)
00730 {
00731 return config_module(1);
00732 }
00733
00734 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL CDR Backend",
00735 .load = load_module,
00736 .unload = unload_module,
00737 .reload = reload,
00738 .load_pri = AST_MODPRI_CDR_DRIVER,
00739 );