00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #define AST_MODULE "cdr_pgsql_ng"
00016
00017 #include <asterisk.h>
00018
00019 #include <sys/types.h>
00020 #include <stdio.h>
00021 #include <string.h>
00022
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <time.h>
00026
00027 #include <pgsql/libpq-fe.h>
00028
00029 #include <asterisk/config.h>
00030 #include <asterisk/options.h>
00031 #include <asterisk/channel.h>
00032 #include <asterisk/cdr.h>
00033 #include <asterisk/module.h>
00034 #include <asterisk/logger.h>
00035
00036 #define DATE_FORMAT "%Y-%m-%d %T"
00037
00038
00039
00040
00041
00042 static char *insertcmd = NULL;
00043 static const char *insertcmd_in = "INSERT INTO %s "
00044 "(calldate,clid,src,dst,dcontext,channel,dstchannel,"
00045 "lastapp,lastdata,duration,billsec,disposition,amaflags," "accountcode,uniqueid,userfield) VALUES ($1, $2, $3, $4, $5," "$6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)";
00046
00047 static char *tdesc = "PostgreSQL CDR Backend new generation";
00048 static char *name = "pgsql_ng";
00049 static char *config_file = "cdr_pgsql_ng.conf";
00050 static int connected = 0;
00051
00052 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00053
00054 static PGconn *conn;
00055 static PGresult *result;
00056
00057 struct query {
00058 char *query;
00059 AST_LIST_ENTRY(query) list;
00060 };
00061
00062
00063 static AST_LIST_HEAD_STATIC(queries, query);
00064
00065 enum {
00066 CFG_HOSTNAME,
00067 CFG_DBNAME,
00068 CFG_TABLE,
00069 CFG_DBPORT,
00070 CFG_USER,
00071 CFG_PASSWORD,
00072 CFG_MAX
00073 };
00074
00075 static char *config[CFG_MAX];
00076
00077 static int cdr_pgsql_connect(void)
00078 {
00079 char *pgerror;
00080
00081 conn = PQsetdbLogin(config[CFG_HOSTNAME], config[CFG_DBPORT], NULL, NULL, config[CFG_DBNAME], config[CFG_USER], config[CFG_PASSWORD]);
00082 if (PQstatus(conn) != CONNECTION_BAD) {
00083 if (option_debug)
00084 ast_log(LOG_DEBUG, "Successfully connected to PostgreSQL database.\n");
00085 connected = 1;
00086 } else {
00087 pgerror = PQerrorMessage(conn);
00088 ast_log(LOG_ERROR, "Unable to connect to database server\n");
00089 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00090 return -1;
00091 }
00092 result = PQprepare(conn, "", insertcmd, 16, NULL);
00093 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00094 char *pgerror = PQresultErrorMessage(result);
00095 ast_log(LOG_ERROR, "Can't prepare insert statement!\n");
00096 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00097 }
00098 return 0;
00099 }
00100
00101 static int pgsql_test_connection(void)
00102 {
00103 char *pgerror;
00104 if (!connected) {
00105
00106 if (cdr_pgsql_connect() == -1)
00107 return -1;
00108 }
00109
00110
00111
00112
00113 if (PQstatus(conn) != CONNECTION_OK) {
00114 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
00115 PQreset(conn);
00116 if (PQstatus(conn) == CONNECTION_OK) {
00117 ast_log(LOG_ERROR, "Connection reestablished.\n");
00118 } else {
00119 pgerror = PQerrorMessage(conn);
00120 ast_log(LOG_ERROR, "Unable to reconnect to database server\n");
00121 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00122 return -1;
00123 }
00124 }
00125 return 0;
00126 }
00127
00128
00129 static int pgsql_log(struct ast_cdr *cdr)
00130 {
00131 int i;
00132 struct tm tm;
00133 char timestr[128];
00134 char duration[64];
00135 char billsec[64];
00136 char amaflags[64];
00137 char *params[16] = {
00138 timestr,
00139 cdr->clid,
00140 cdr->src,
00141 cdr->dst,
00142 cdr->dcontext,
00143 cdr->channel,
00144 cdr->dstchannel,
00145 cdr->lastapp,
00146 cdr->lastdata,
00147 duration,
00148 billsec,
00149 ast_cdr_disp2str(cdr->disposition),
00150 amaflags,
00151 cdr->accountcode,
00152 cdr->uniqueid,
00153 cdr->userfield
00154 };
00155
00156 localtime_r(&cdr->start.tv_sec, &tm);
00157 strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
00158
00159 snprintf(duration, sizeof(duration), "%ld", cdr->duration);
00160 snprintf(billsec, sizeof(billsec), "%ld", cdr->billsec);
00161 snprintf(amaflags, sizeof(amaflags), "%ld", cdr->amaflags);
00162
00163 ast_mutex_lock(&pgsql_lock);
00164 pgsql_test_connection();
00165 result = PQexecPrepared(conn, "", 16, (const char *const *) params, NULL, NULL, 0);
00166 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00167 char *pgerror = PQresultErrorMessage(result);
00168 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
00169 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00170 }
00171 ast_mutex_unlock(&pgsql_lock);
00172 return 0;
00173 }
00174
00175 static char *cdr_pgsql_read_config(struct ast_config *cfg, const char const *section, const char *option, char *def, const char *log)
00176 {
00177 const char *tmp = ast_variable_retrieve(cfg, section, option);
00178 if (tmp == NULL) {
00179 ast_log(LOG_WARNING, "%s not specified, assuming %s\n", log, S_OR(def, "blank"));
00180
00181 return strdup(def);
00182 }
00183 return strdup(tmp);
00184 }
00185
00186 static int database_first_connect(struct ast_config *cfg)
00187 {
00188 int res = 0, i;
00189
00190 for (i = 0; i < CFG_MAX; i++)
00191 config[i] = NULL;
00192
00193 if (ast_variable_browse(cfg, "global") == NULL) {
00194
00195 ast_log(LOG_WARNING, "cdr_pgsql not configured, exiting\n");
00196 return 0;
00197 }
00198
00199 config[CFG_HOSTNAME] = cdr_pgsql_read_config(cfg, "global", "hostname", "", "PostgreSQL server hostname");
00200 config[CFG_DBNAME] = cdr_pgsql_read_config(cfg, "global", "dbname", "asterisk", "PostgreSQL database");
00201 config[CFG_USER] = cdr_pgsql_read_config(cfg, "global", "dbuser", "asterisk", "PostgreSQL database user");
00202 config[CFG_PASSWORD] = cdr_pgsql_read_config(cfg, "global", "password", "", "PostgreSQL database password");
00203 config[CFG_DBPORT] = cdr_pgsql_read_config(cfg, "global", "port", "5432", "PostgreSQL database port");
00204 config[CFG_TABLE] = cdr_pgsql_read_config(cfg, "global", "table", "cdr", "CDR table");
00205
00206 for (i = 0; i < CFG_MAX; i++) {
00207 if (config[i] == NULL) {
00208 ast_log(LOG_ERROR, "Out of memory error\n");
00209 for (i = 0; i < CFG_MAX; i++)
00210 if (config[i]) {
00211 free(config[i]);
00212 config[i] = 0;
00213 }
00214 return -1;
00215 }
00216 }
00217
00218 if (option_debug) {
00219 ast_log(LOG_DEBUG, "got hostname of %s\ngot port of %s\ngot user of %s\ngot dbname of %s\ngot password of %s\ngot sql table name of %s\n", config[CFG_HOSTNAME], config[CFG_DBPORT],
00220 config[CFG_USER], config[CFG_DBNAME], config[CFG_PASSWORD], config[CFG_TABLE]);
00221 }
00222 if (insertcmd)
00223 free(insertcmd);
00224
00225 asprintf(&insertcmd, insertcmd_in, config[CFG_TABLE]);
00226
00227 cdr_pgsql_connect();
00228 return res;
00229 }
00230
00231 static int load_module(void)
00232 {
00233 struct ast_config *cfg;
00234 int res;
00235
00236 cfg = ast_config_load(config_file);
00237 if (!cfg) {
00238 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config_file);
00239 return 0;
00240 }
00241
00242 res = database_first_connect(cfg);
00243 ast_config_destroy(cfg);
00244
00245 res = ast_cdr_register(name, tdesc, pgsql_log);
00246 if (res)
00247 ast_log(LOG_ERROR, "Unable to register PGSQL CDR handling\n");
00248 return res;
00249 }
00250
00251 static int unload_module(void)
00252 {
00253 unsigned int i;
00254
00255 ast_cdr_unregister(name);
00256
00257 ast_mutex_lock(&pgsql_lock);
00258 if (conn)
00259 PQfinish(conn);
00260
00261 for (i = 0; i < CFG_MAX; i++)
00262 if (config[i])
00263 free(config[i]);
00264
00265 if (insertcmd)
00266 free(insertcmd);
00267
00268 ast_mutex_unlock(&pgsql_lock);
00269 return 0;
00270 }
00271
00272 static int reload(void)
00273 {
00274 struct ast_config *cfg;
00275 int i;
00276
00277 cfg = ast_config_load(config_file);
00278 if (!cfg) {
00279 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config_file);
00280 return -1;
00281 }
00282
00283 ast_mutex_lock(&pgsql_lock);
00284
00285 if (conn)
00286 PQfinish(conn);
00287
00288 for (i = 0; i < CFG_MAX; i++)
00289 if (config[i])
00290 free(config[i]);
00291
00292 if (database_first_connect(cfg) != 0)
00293 ast_log(LOG_ERROR, "Error in reloading config, data would be cached\n");
00294 ast_config_destroy(cfg);
00295
00296 ast_mutex_unlock(&pgsql_lock);
00297 return 0;
00298 }
00299
00300 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "PostgreSQL CDR Backend new generation");