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 #include "asterisk.h"
00041
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 253620 $")
00043
00044 #include <time.h>
00045
00046 #include <libpq-fe.h>
00047
00048 #include "asterisk/config.h"
00049 #include "asterisk/channel.h"
00050 #include "asterisk/cdr.h"
00051 #include "asterisk/module.h"
00052
00053 #define DATE_FORMAT "'%Y-%m-%d %T'"
00054
00055 static char *name = "pgsql";
00056 static char *config = "cdr_pgsql.conf";
00057 static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL;
00058 static int connected = 0;
00059 static int maxsize = 512, maxsize2 = 512;
00060
00061 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00062
00063 static PGconn *conn = NULL;
00064
00065 struct columns {
00066 char *name;
00067 char *type;
00068 int len;
00069 unsigned int notnull:1;
00070 unsigned int hasdefault:1;
00071 AST_RWLIST_ENTRY(columns) list;
00072 };
00073
00074 static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
00075
00076 #define LENGTHEN_BUF1(size) \
00077 do { \
00078 \
00079 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
00080 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
00081 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
00082 ast_free(sql); \
00083 ast_free(sql2); \
00084 AST_RWLIST_UNLOCK(&psql_columns); \
00085 return -1; \
00086 } \
00087 } \
00088 } while (0)
00089
00090 #define LENGTHEN_BUF2(size) \
00091 do { \
00092 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
00093 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
00094 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
00095 ast_free(sql); \
00096 ast_free(sql2); \
00097 AST_RWLIST_UNLOCK(&psql_columns); \
00098 return -1; \
00099 } \
00100 } \
00101 } while (0)
00102
00103 static int pgsql_log(struct ast_cdr *cdr)
00104 {
00105 struct ast_tm tm;
00106 char *pgerror;
00107 PGresult *result;
00108
00109 ast_mutex_lock(&pgsql_lock);
00110
00111 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00112 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00113 if (PQstatus(conn) != CONNECTION_BAD) {
00114 connected = 1;
00115 } else {
00116 pgerror = PQerrorMessage(conn);
00117 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
00118 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00119 PQfinish(conn);
00120 conn = NULL;
00121 }
00122 }
00123
00124 if (connected) {
00125 struct columns *cur;
00126 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00127 char buf[257], escapebuf[513], *value;
00128 int first = 1;
00129
00130 if (!sql || !sql2) {
00131 if (sql) {
00132 ast_free(sql);
00133 }
00134 if (sql2) {
00135 ast_free(sql2);
00136 }
00137 return -1;
00138 }
00139
00140 ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00141 ast_str_set(&sql2, 0, " VALUES (");
00142
00143 AST_RWLIST_RDLOCK(&psql_columns);
00144 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00145
00146 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00147 if (strcmp(cur->name, "calldate") == 0 && !value) {
00148 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00149 }
00150 if (!value) {
00151 if (cur->notnull && !cur->hasdefault) {
00152
00153 LENGTHEN_BUF1(strlen(cur->name) + 2);
00154 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00155 LENGTHEN_BUF2(3);
00156 ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00157 first = 0;
00158 }
00159 continue;
00160 }
00161
00162 LENGTHEN_BUF1(strlen(cur->name) + 2);
00163 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00164
00165 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00166 if (strncmp(cur->type, "int", 3) == 0) {
00167 LENGTHEN_BUF2(13);
00168 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00169 } else if (strncmp(cur->type, "float", 5) == 0) {
00170 LENGTHEN_BUF2(31);
00171 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00172 } else {
00173
00174 LENGTHEN_BUF2(31);
00175 ast_localtime(&cdr->start, &tm, NULL);
00176 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00177 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00178 }
00179 } else if (strcmp(cur->name, "answer") == 0) {
00180 if (strncmp(cur->type, "int", 3) == 0) {
00181 LENGTHEN_BUF2(13);
00182 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00183 } else if (strncmp(cur->type, "float", 5) == 0) {
00184 LENGTHEN_BUF2(31);
00185 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00186 } else {
00187
00188 LENGTHEN_BUF2(31);
00189 ast_localtime(&cdr->start, &tm, NULL);
00190 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00191 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00192 }
00193 } else if (strcmp(cur->name, "end") == 0) {
00194 if (strncmp(cur->type, "int", 3) == 0) {
00195 LENGTHEN_BUF2(13);
00196 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00197 } else if (strncmp(cur->type, "float", 5) == 0) {
00198 LENGTHEN_BUF2(31);
00199 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00200 } else {
00201
00202 LENGTHEN_BUF2(31);
00203 ast_localtime(&cdr->end, &tm, NULL);
00204 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00205 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00206 }
00207 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00208 if (cur->type[0] == 'i') {
00209
00210 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00211 LENGTHEN_BUF2(13);
00212 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00213 } else if (strncmp(cur->type, "float", 5) == 0) {
00214 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
00215 LENGTHEN_BUF2(31);
00216 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
00217 } else {
00218
00219 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
00220 LENGTHEN_BUF2(31);
00221 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
00222 }
00223 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00224 if (strncmp(cur->type, "int", 3) == 0) {
00225
00226 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00227 LENGTHEN_BUF2(13);
00228 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00229 } else {
00230
00231 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00232 LENGTHEN_BUF2(31);
00233 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00234 }
00235 } else {
00236
00237 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00238 if (strncmp(cur->type, "int", 3) == 0) {
00239 long long whatever;
00240 if (value && sscanf(value, "%30lld", &whatever) == 1) {
00241 LENGTHEN_BUF2(26);
00242 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00243 } else {
00244 LENGTHEN_BUF2(2);
00245 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00246 }
00247 } else if (strncmp(cur->type, "float", 5) == 0) {
00248 long double whatever;
00249 if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00250 LENGTHEN_BUF2(51);
00251 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00252 } else {
00253 LENGTHEN_BUF2(2);
00254 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00255 }
00256
00257 } else {
00258 if (value)
00259 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00260 else
00261 escapebuf[0] = '\0';
00262 LENGTHEN_BUF2(strlen(escapebuf) + 3);
00263 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00264 }
00265 }
00266 first = 0;
00267 }
00268 AST_RWLIST_UNLOCK(&psql_columns);
00269 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00270 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
00271 ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00272
00273 ast_debug(2, "inserting a CDR record.\n");
00274
00275
00276
00277
00278 if (PQstatus(conn) == CONNECTION_OK) {
00279 connected = 1;
00280 } else {
00281 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
00282 PQreset(conn);
00283 if (PQstatus(conn) == CONNECTION_OK) {
00284 ast_log(LOG_ERROR, "Connection reestablished.\n");
00285 connected = 1;
00286 } else {
00287 pgerror = PQerrorMessage(conn);
00288 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00289 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00290 PQfinish(conn);
00291 conn = NULL;
00292 connected = 0;
00293 ast_mutex_unlock(&pgsql_lock);
00294 ast_free(sql);
00295 ast_free(sql2);
00296 return -1;
00297 }
00298 }
00299 result = PQexec(conn, ast_str_buffer(sql));
00300 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00301 pgerror = PQresultErrorMessage(result);
00302 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
00303 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00304 ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
00305 PQreset(conn);
00306 if (PQstatus(conn) == CONNECTION_OK) {
00307 ast_log(LOG_ERROR, "Connection reestablished.\n");
00308 connected = 1;
00309 PQclear(result);
00310 result = PQexec(conn, ast_str_buffer(sql));
00311 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00312 pgerror = PQresultErrorMessage(result);
00313 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
00314 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00315 }
00316 }
00317 ast_mutex_unlock(&pgsql_lock);
00318 PQclear(result);
00319 ast_free(sql);
00320 ast_free(sql2);
00321 return -1;
00322 }
00323 PQclear(result);
00324 ast_free(sql);
00325 ast_free(sql2);
00326 }
00327 ast_mutex_unlock(&pgsql_lock);
00328 return 0;
00329 }
00330
00331 static int unload_module(void)
00332 {
00333 struct columns *current;
00334 ast_cdr_unregister(name);
00335
00336
00337 usleep(1);
00338 PQfinish(conn);
00339
00340 if (pghostname)
00341 ast_free(pghostname);
00342 if (pgdbname)
00343 ast_free(pgdbname);
00344 if (pgdbuser)
00345 ast_free(pgdbuser);
00346 if (pgpassword)
00347 ast_free(pgpassword);
00348 if (pgdbport)
00349 ast_free(pgdbport);
00350 if (table)
00351 ast_free(table);
00352
00353 AST_RWLIST_WRLOCK(&psql_columns);
00354 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00355 ast_free(current);
00356 }
00357 AST_RWLIST_UNLOCK(&psql_columns);
00358
00359 return 0;
00360 }
00361
00362 static int config_module(int reload)
00363 {
00364 struct ast_variable *var;
00365 char *pgerror;
00366 struct columns *cur;
00367 PGresult *result;
00368 const char *tmp;
00369 struct ast_config *cfg;
00370 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00371
00372 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00373 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00374 return -1;
00375 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00376 return 0;
00377
00378 if (!(var = ast_variable_browse(cfg, "global"))) {
00379 ast_config_destroy(cfg);
00380 return 0;
00381 }
00382
00383 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00384 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
00385 tmp = "";
00386 }
00387
00388 if (pghostname)
00389 ast_free(pghostname);
00390 if (!(pghostname = ast_strdup(tmp))) {
00391 ast_config_destroy(cfg);
00392 return -1;
00393 }
00394
00395 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00396 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
00397 tmp = "asteriskcdrdb";
00398 }
00399
00400 if (pgdbname)
00401 ast_free(pgdbname);
00402 if (!(pgdbname = ast_strdup(tmp))) {
00403 ast_config_destroy(cfg);
00404 return -1;
00405 }
00406
00407 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00408 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
00409 tmp = "asterisk";
00410 }
00411
00412 if (pgdbuser)
00413 ast_free(pgdbuser);
00414 if (!(pgdbuser = ast_strdup(tmp))) {
00415 ast_config_destroy(cfg);
00416 return -1;
00417 }
00418
00419 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00420 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
00421 tmp = "";
00422 }
00423
00424 if (pgpassword)
00425 ast_free(pgpassword);
00426 if (!(pgpassword = ast_strdup(tmp))) {
00427 ast_config_destroy(cfg);
00428 return -1;
00429 }
00430
00431 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00432 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
00433 tmp = "5432";
00434 }
00435
00436 if (pgdbport)
00437 ast_free(pgdbport);
00438 if (!(pgdbport = ast_strdup(tmp))) {
00439 ast_config_destroy(cfg);
00440 return -1;
00441 }
00442
00443 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00444 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
00445 tmp = "cdr";
00446 }
00447
00448 if (table)
00449 ast_free(table);
00450 if (!(table = ast_strdup(tmp))) {
00451 ast_config_destroy(cfg);
00452 return -1;
00453 }
00454
00455 if (option_debug) {
00456 if (ast_strlen_zero(pghostname)) {
00457 ast_debug(1, "using default unix socket\n");
00458 } else {
00459 ast_debug(1, "got hostname of %s\n", pghostname);
00460 }
00461 ast_debug(1, "got port of %s\n", pgdbport);
00462 ast_debug(1, "got user of %s\n", pgdbuser);
00463 ast_debug(1, "got dbname of %s\n", pgdbname);
00464 ast_debug(1, "got password of %s\n", pgpassword);
00465 ast_debug(1, "got sql table name of %s\n", table);
00466 }
00467
00468 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00469 if (PQstatus(conn) != CONNECTION_BAD) {
00470 char sqlcmd[768];
00471 char *fname, *ftype, *flen, *fnotnull, *fdef;
00472 int i, rows, version;
00473 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00474 connected = 1;
00475 version = PQserverVersion(conn);
00476
00477 if (version >= 70300) {
00478 char *schemaname, *tablename;
00479 if (strchr(table, '.')) {
00480 schemaname = ast_strdupa(table);
00481 tablename = strchr(schemaname, '.');
00482 *tablename++ = '\0';
00483 } else {
00484 schemaname = "";
00485 tablename = table;
00486 }
00487
00488
00489 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00490 char *tmp = schemaname, *ptr;
00491
00492 ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00493 for (; *tmp; tmp++) {
00494 if (strchr("\\'", *tmp)) {
00495 *ptr++ = *tmp;
00496 }
00497 *ptr++ = *tmp;
00498 }
00499 *ptr = '\0';
00500 }
00501
00502 if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00503 char *tmp = tablename, *ptr;
00504
00505 ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00506 for (; *tmp; tmp++) {
00507 if (strchr("\\'", *tmp)) {
00508 *ptr++ = *tmp;
00509 }
00510 *ptr++ = *tmp;
00511 }
00512 *ptr = '\0';
00513 }
00514
00515 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",
00516 tablename,
00517 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00518 } else {
00519 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);
00520 }
00521
00522 result = PQexec(conn, sqlcmd);
00523 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00524 pgerror = PQresultErrorMessage(result);
00525 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00526 PQclear(result);
00527 unload_module();
00528 return AST_MODULE_LOAD_DECLINE;
00529 }
00530
00531 rows = PQntuples(result);
00532 for (i = 0; i < rows; i++) {
00533 fname = PQgetvalue(result, i, 0);
00534 ftype = PQgetvalue(result, i, 1);
00535 flen = PQgetvalue(result, i, 2);
00536 fnotnull = PQgetvalue(result, i, 3);
00537 fdef = PQgetvalue(result, i, 4);
00538 if (atoi(flen) == -1) {
00539
00540 flen = PQgetvalue(result, i, 5);
00541 }
00542 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00543 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00544 if (cur) {
00545 sscanf(flen, "%30d", &cur->len);
00546 cur->name = (char *)cur + sizeof(*cur);
00547 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00548 strcpy(cur->name, fname);
00549 strcpy(cur->type, ftype);
00550 if (*fnotnull == 't') {
00551 cur->notnull = 1;
00552 } else {
00553 cur->notnull = 0;
00554 }
00555 if (!ast_strlen_zero(fdef)) {
00556 cur->hasdefault = 1;
00557 } else {
00558 cur->hasdefault = 0;
00559 }
00560 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00561 }
00562 }
00563 PQclear(result);
00564 } else {
00565 pgerror = PQerrorMessage(conn);
00566 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00567 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00568 connected = 0;
00569 }
00570
00571 ast_config_destroy(cfg);
00572
00573 return ast_cdr_register(name, ast_module_info->description, pgsql_log);
00574 }
00575
00576 static int load_module(void)
00577 {
00578 return config_module(0) ? AST_MODULE_LOAD_DECLINE : 0;
00579 }
00580
00581 static int reload(void)
00582 {
00583 return config_module(1);
00584 }
00585
00586 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CDR Backend",
00587 .load = load_module,
00588 .unload = unload_module,
00589 .reload = reload,
00590 );