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: 354275 $")
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 struct ast_variable *var;
00452 char *pgerror;
00453 struct columns *cur;
00454 PGresult *result;
00455 const char *tmp;
00456 struct ast_config *cfg;
00457 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00458
00459 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00460 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00461 return -1;
00462 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00463 return 0;
00464 }
00465
00466 ast_mutex_lock(&pgsql_lock);
00467
00468 if (!(var = ast_variable_browse(cfg, "global"))) {
00469 ast_config_destroy(cfg);
00470 ast_mutex_unlock(&pgsql_lock);
00471 ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
00472 reload ? "reload" : "load");
00473 return -1;
00474 }
00475
00476 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00477 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
00478 tmp = "";
00479 }
00480
00481 ast_free(pghostname);
00482 if (!(pghostname = ast_strdup(tmp))) {
00483 ast_config_destroy(cfg);
00484 ast_mutex_unlock(&pgsql_lock);
00485 return -1;
00486 }
00487
00488 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00489 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
00490 tmp = "asteriskcdrdb";
00491 }
00492
00493 ast_free(pgdbname);
00494 if (!(pgdbname = ast_strdup(tmp))) {
00495 ast_config_destroy(cfg);
00496 ast_mutex_unlock(&pgsql_lock);
00497 return -1;
00498 }
00499
00500 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00501 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
00502 tmp = "asterisk";
00503 }
00504
00505 ast_free(pgdbuser);
00506 if (!(pgdbuser = ast_strdup(tmp))) {
00507 ast_config_destroy(cfg);
00508 ast_mutex_unlock(&pgsql_lock);
00509 return -1;
00510 }
00511
00512 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00513 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
00514 tmp = "";
00515 }
00516
00517 ast_free(pgpassword);
00518 if (!(pgpassword = ast_strdup(tmp))) {
00519 ast_config_destroy(cfg);
00520 ast_mutex_unlock(&pgsql_lock);
00521 return -1;
00522 }
00523
00524 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00525 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
00526 tmp = "5432";
00527 }
00528
00529 ast_free(pgdbport);
00530 if (!(pgdbport = ast_strdup(tmp))) {
00531 ast_config_destroy(cfg);
00532 ast_mutex_unlock(&pgsql_lock);
00533 return -1;
00534 }
00535
00536 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00537 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
00538 tmp = "cdr";
00539 }
00540
00541 ast_free(table);
00542 if (!(table = ast_strdup(tmp))) {
00543 ast_config_destroy(cfg);
00544 ast_mutex_unlock(&pgsql_lock);
00545 return -1;
00546 }
00547
00548 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00549 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n");
00550 tmp = "LATIN9";
00551 }
00552
00553 ast_free(encoding);
00554 if (!(encoding = ast_strdup(tmp))) {
00555 ast_config_destroy(cfg);
00556 ast_mutex_unlock(&pgsql_lock);
00557 return -1;
00558 }
00559
00560 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
00561 tmp = "";
00562 }
00563
00564 ast_free(tz);
00565 tz = NULL;
00566
00567 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
00568 ast_config_destroy(cfg);
00569 ast_mutex_unlock(&pgsql_lock);
00570 return -1;
00571 }
00572
00573 if (option_debug) {
00574 if (ast_strlen_zero(pghostname)) {
00575 ast_debug(1, "using default unix socket\n");
00576 } else {
00577 ast_debug(1, "got hostname of %s\n", pghostname);
00578 }
00579 ast_debug(1, "got port of %s\n", pgdbport);
00580 ast_debug(1, "got user of %s\n", pgdbuser);
00581 ast_debug(1, "got dbname of %s\n", pgdbname);
00582 ast_debug(1, "got password of %s\n", pgpassword);
00583 ast_debug(1, "got sql table name of %s\n", table);
00584 ast_debug(1, "got encoding of %s\n", encoding);
00585 ast_debug(1, "got timezone of %s\n", tz);
00586 }
00587
00588 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00589 if (PQstatus(conn) != CONNECTION_BAD) {
00590 char sqlcmd[768];
00591 char *fname, *ftype, *flen, *fnotnull, *fdef;
00592 int i, rows, version;
00593 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00594 connected = 1;
00595 connect_time = time(NULL);
00596 records = 0;
00597 if (PQsetClientEncoding(conn, encoding)) {
00598 #ifdef HAVE_PGSQL_pg_encoding_to_char
00599 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00600 #else
00601 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00602 #endif
00603 }
00604 version = PQserverVersion(conn);
00605
00606 if (version >= 70300) {
00607 char *schemaname, *tablename;
00608 if (strchr(table, '.')) {
00609 schemaname = ast_strdupa(table);
00610 tablename = strchr(schemaname, '.');
00611 *tablename++ = '\0';
00612 } else {
00613 schemaname = "";
00614 tablename = table;
00615 }
00616
00617
00618 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00619 char *tmp = schemaname, *ptr;
00620
00621 ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00622 for (; *tmp; tmp++) {
00623 if (strchr("\\'", *tmp)) {
00624 *ptr++ = *tmp;
00625 }
00626 *ptr++ = *tmp;
00627 }
00628 *ptr = '\0';
00629 }
00630
00631 if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00632 char *tmp = tablename, *ptr;
00633
00634 ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00635 for (; *tmp; tmp++) {
00636 if (strchr("\\'", *tmp)) {
00637 *ptr++ = *tmp;
00638 }
00639 *ptr++ = *tmp;
00640 }
00641 *ptr = '\0';
00642 }
00643
00644 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",
00645 tablename,
00646 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00647 } else {
00648 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);
00649 }
00650
00651 result = PQexec(conn, sqlcmd);
00652 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00653 pgerror = PQresultErrorMessage(result);
00654 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00655 PQclear(result);
00656 unload_module();
00657 ast_mutex_unlock(&pgsql_lock);
00658 return AST_MODULE_LOAD_DECLINE;
00659 }
00660
00661 rows = PQntuples(result);
00662 if (rows == 0) {
00663 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00664 PQclear(result);
00665 unload_module();
00666 ast_mutex_unlock(&pgsql_lock);
00667 return AST_MODULE_LOAD_DECLINE;
00668 }
00669
00670
00671 empty_columns();
00672
00673 for (i = 0; i < rows; i++) {
00674 fname = PQgetvalue(result, i, 0);
00675 ftype = PQgetvalue(result, i, 1);
00676 flen = PQgetvalue(result, i, 2);
00677 fnotnull = PQgetvalue(result, i, 3);
00678 fdef = PQgetvalue(result, i, 4);
00679 if (atoi(flen) == -1) {
00680
00681 flen = PQgetvalue(result, i, 5);
00682 }
00683 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00684 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00685 if (cur) {
00686 sscanf(flen, "%30d", &cur->len);
00687 cur->name = (char *)cur + sizeof(*cur);
00688 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00689 strcpy(cur->name, fname);
00690 strcpy(cur->type, ftype);
00691 if (*fnotnull == 't') {
00692 cur->notnull = 1;
00693 } else {
00694 cur->notnull = 0;
00695 }
00696 if (!ast_strlen_zero(fdef)) {
00697 cur->hasdefault = 1;
00698 } else {
00699 cur->hasdefault = 0;
00700 }
00701 AST_RWLIST_WRLOCK(&psql_columns);
00702 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00703 AST_RWLIST_UNLOCK(&psql_columns);
00704 }
00705 }
00706 PQclear(result);
00707 } else {
00708 pgerror = PQerrorMessage(conn);
00709 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00710 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00711 connected = 0;
00712 }
00713
00714 ast_config_destroy(cfg);
00715
00716 ast_mutex_unlock(&pgsql_lock);
00717 return 0;
00718 }
00719
00720 static int load_module(void)
00721 {
00722 ast_cli_register_multiple(cdr_pgsql_status_cli, sizeof(cdr_pgsql_status_cli) / sizeof(struct ast_cli_entry));
00723 if (config_module(0)) {
00724 return AST_MODULE_LOAD_DECLINE;
00725 }
00726 return ast_cdr_register(name, ast_module_info->description, pgsql_log)
00727 ? AST_MODULE_LOAD_DECLINE : 0;
00728 }
00729
00730 static int reload(void)
00731 {
00732 return config_module(1);
00733 }
00734
00735 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL CDR Backend",
00736 .load = load_module,
00737 .unload = unload_module,
00738 .reload = reload,
00739 .load_pri = AST_MODPRI_CDR_DRIVER,
00740 );