cel_pgsql, cdr_pgsql, res_config_pgsql: Add PostgreSQL application_name support
authorMatthew Jordan <mjordan@digium.com>
Wed, 16 Jul 2014 13:55:36 +0000 (13:55 +0000)
committerMatthew Jordan <mjordan@digium.com>
Wed, 16 Jul 2014 13:55:36 +0000 (13:55 +0000)
This patch adds support for the PostgreSQL application_name connection setting.
When the appropriate PostgreSQL module's configuration is set with an
application name, the name will be passed to PostgreSQL on connection and
displayed in the database's pg_stat_activity view, as well as in CSV logs. This
aids in managing which applications/servers are connected to a PostgreSQL
database, as well as tracing the activity of those connections.

Review: https://reviewboard.asterisk.org/r/3591

ASTERISK-23737 #close
Reported by: Gergely Domodi
patches:
  pgsql_application_name.patch uploaded by Gergely Domodi (License 6610)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@418755 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
cdr/cdr_pgsql.c
cel/cel_pgsql.c
configs/cdr_pgsql.conf.sample
configs/cel_pgsql.conf.sample
configs/res_pgsql.conf.sample
res/res_config_pgsql.c

diff --git a/CHANGES b/CHANGES
index a6d3a6b..ef2ff22 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -50,6 +50,20 @@ cdr_sqlite
  * This module was deprecated and has been removed. Users of cdr_sqlite
    should use cdr_sqlite3_custom.
 
+cdr_pgsql
+------------------
+ * Added the ability to support PostgreSQL application_name on connections.
+   This allows PostgreSQL to display the configured name in the
+   pg_stat_activity view and CSV log entries. This setting is configurable
+   for cdr_pgsql via the appname configuration setting in cdr_pgsql.conf.
+
+cel_pgsql
+------------------
+ * Added the ability to support PostgreSQL application_name on connections.
+   This allows PostgreSQL to display the configured name in the
+   pg_stat_activity view and CSV log entries. This setting is configurable
+   for cel_pgsql via the appname configuration setting in cel_pgsql.conf.
+
 CEL
 ------------------
  * The "bridge_technology" extra field key has been added to BRIDGE_ENTER
@@ -156,6 +170,14 @@ VoiceMail
    - jb-wa: article wa
    - jb-wo: article wo
 
+res_config_pgsql
+------------------
+ * Added the ability to support PostgreSQL application_name on connections.
+   This allows PostgreSQL to display the configured name in the
+   pg_stat_activity view and CSV log entries. This setting is configurable
+   for res_config_pgsql via the dbappname configuration setting in
+   res_pgsql.conf.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
 ------------------------------------------------------------------------------
index 8322da9..815bb6a 100644 (file)
@@ -63,7 +63,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static const char name[] = "pgsql";
 static const char config[] = "cdr_pgsql.conf";
-static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL, *encoding = NULL, *tz = NULL;
+
+static char *pghostname;
+static char *pgdbname;
+static char *pgdbuser;
+static char *pgpassword;
+static char *pgappname;
+static char *pgdbport;
+static char *table;
+static char *encoding;
+static char *tz;
+
 static int connected = 0;
 static int maxsize = 512, maxsize2 = 512;
 static time_t connect_time = 0;
@@ -174,6 +184,34 @@ static char *handle_cdr_pgsql_status(struct ast_cli_entry *e, int cmd, struct as
        return CLI_SUCCESS;
 }
 
+static void pgsql_reconnect(void)
+{
+       struct ast_str *conn_info = ast_str_create(128);
+       if (!conn_info) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for connection string.\n");
+               return;
+       }
+
+       if (conn) {
+               PQfinish(conn);
+               conn = NULL;
+       }
+
+       ast_str_set(&conn_info, 0, "host=%s port=%s dbname=%s user=%s",
+               pghostname, pgdbport, pgdbname, pgdbuser);
+
+       if (!ast_strlen_zero(pgappname)) {
+               ast_str_append(&conn_info, 0, " application_name=%s", pgappname);
+       }
+
+       if (!ast_strlen_zero(pgpassword)) {
+               ast_str_append(&conn_info, 0, " password=%s", pgpassword);
+       }
+
+       conn = PQconnectdb(ast_str_buffer(conn_info));
+       ast_free(conn_info);
+}
+
 static int pgsql_log(struct ast_cdr *cdr)
 {
        struct ast_tm tm;
@@ -183,7 +221,8 @@ static int pgsql_log(struct ast_cdr *cdr)
        ast_mutex_lock(&pgsql_lock);
 
        if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
-               conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
+               pgsql_reconnect();
+
                if (PQstatus(conn) != CONNECTION_BAD) {
                        connected = 1;
                        connect_time = time(NULL);
@@ -441,12 +480,15 @@ static int unload_module(void)
 
        ast_cli_unregister_multiple(cdr_pgsql_status_cli, ARRAY_LEN(cdr_pgsql_status_cli));
 
-       PQfinish(conn);
-
+       if (conn) {
+               PQfinish(conn);
+               conn = NULL;
+       }
        ast_free(pghostname);
        ast_free(pgdbname);
        ast_free(pgdbuser);
        ast_free(pgpassword);
+       ast_free(pgappname);
        ast_free(pgdbport);
        ast_free(table);
        ast_free(encoding);
@@ -519,6 +561,18 @@ static int config_module(int reload)
                return -1;
        }
 
+       if (!(tmp = ast_variable_retrieve(cfg, "global", "appname"))) {
+               tmp = "";
+       }
+
+       ast_free(pgappname);
+       if (!(pgappname = ast_strdup(tmp))) {
+               ast_config_destroy(cfg);
+               ast_mutex_unlock(&pgsql_lock);
+               return -1;
+       }
+
+
        if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
                ast_log(LOG_WARNING, "PostgreSQL database password not specified.  Assuming blank\n");
                tmp = "";
@@ -590,12 +644,14 @@ static int config_module(int reload)
                ast_debug(1, "got user of %s\n", pgdbuser);
                ast_debug(1, "got dbname of %s\n", pgdbname);
                ast_debug(1, "got password of %s\n", pgpassword);
+               ast_debug(1, "got application name of %s\n", pgappname);
                ast_debug(1, "got sql table name of %s\n", table);
                ast_debug(1, "got encoding of %s\n", encoding);
                ast_debug(1, "got timezone of %s\n", tz);
        }
 
-       conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
+       pgsql_reconnect();
+
        if (PQstatus(conn) != CONNECTION_BAD) {
                char sqlcmd[768];
                char *fname, *ftype, *flen, *fnotnull, *fdef;
@@ -719,6 +775,8 @@ static int config_module(int reload)
                ast_log(LOG_ERROR, "Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
                ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
                connected = 0;
+               PQfinish(conn);
+               conn = NULL;
        }
 
        ast_config_destroy(cfg);
index cd50af0..f673ceb 100644 (file)
@@ -4,8 +4,8 @@
  * Copyright (C) 2008
  *
  * Steve Murphy - adapted to CEL, from:
- * Matthew D. Hardeman <mhardemn@papersoft.com> 
- * Adapted from the MySQL CDR logger originally by James Sharp 
+ * Matthew D. Hardeman <mhardemn@papersoft.com>
+ * Adapted from the MySQL CDR logger originally by James Sharp
  *
  * Modified April, 2007; Dec, 2008
  * Steve Murphy <murf@digium.com>
@@ -26,8 +26,8 @@
 
 /*! \file
  *
- * \brief PostgreSQL CEL logger 
- * 
+ * \brief PostgreSQL CEL logger
+ *
  * \author Steve Murphy <murf@digium.com>
  * PostgreSQL http://www.postgresql.org/
  *
@@ -61,7 +61,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define PGSQL_BACKEND_NAME "CEL PGSQL backend"
 
 static char *config = "cel_pgsql.conf";
-static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL;
+
+static char *pghostname;
+static char *pgdbname;
+static char *pgdbuser;
+static char *pgpassword;
+static char *pgappname;
+static char *pgdbport;
+static char *table;
+
 static int connected = 0;
 static int maxsize = 512, maxsize2 = 512;
 
@@ -114,6 +122,35 @@ static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
                } \
        } while (0)
 
+static void pgsql_reconnect(void)
+{
+       struct ast_str *conn_info = ast_str_create(128);
+       if (!conn_info) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for connection string.\n");
+               return;
+       }
+
+       if (conn) {
+               PQfinish(conn);
+               conn = NULL;
+       }
+
+       ast_str_set(&conn_info, 0, "host=%s port=%s dbname=%s user=%s",
+               pghostname, pgdbport, pgdbname, pgdbuser);
+
+       if (!ast_strlen_zero(pgappname)) {
+               ast_str_append(&conn_info, 0, " application_name=%s", pgappname);
+       }
+
+       if (!ast_strlen_zero(pgpassword)) {
+               ast_str_append(&conn_info, 0, " password=%s", pgpassword);
+       }
+
+       conn = PQconnectdb(ast_str_buffer(conn_info));
+       ast_free(conn_info);
+}
+
+
 static void pgsql_log(struct ast_event *event)
 {
        struct ast_tm tm;
@@ -133,7 +170,7 @@ static void pgsql_log(struct ast_event *event)
        ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
 
        if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
-               conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
+               pgsql_reconnect();
                if (PQstatus(conn) != CONNECTION_BAD) {
                        connected = 1;
                } else {
@@ -368,6 +405,10 @@ static int my_unload_module(void)
                ast_free(pgpassword);
                pgpassword = NULL;
        }
+       if (pgappname) {
+               ast_free(pgappname);
+               pgappname = NULL;
+       }
        if (pgdbport) {
                ast_free(pgdbport);
                pgdbport = NULL;
@@ -440,6 +481,17 @@ static int process_my_load_module(struct ast_config *cfg)
                ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying password info\n");
                return AST_MODULE_LOAD_DECLINE;
        }
+       if (!(tmp = ast_variable_retrieve(cfg, "global", "appname"))) {
+               tmp = "";
+       }
+       if (pgappname) {
+               ast_free(pgappname);
+       }
+       if (!(pgappname = ast_strdup(tmp))) {
+               ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying appname info\n");
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
        if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) {
                ast_log(LOG_WARNING,"PostgreSQL database port not specified.  Using default 5432.\n");
                tmp = "5432";
@@ -478,7 +530,7 @@ static int process_my_load_module(struct ast_config *cfg)
                        cel_show_user_def ? "Yes" : "No");
        }
 
-       conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
+       pgsql_reconnect();
        if (PQstatus(conn) != CONNECTION_BAD) {
                char sqlcmd[512];
                char *fname, *ftype, *flen, *fnotnull, *fdef;
@@ -540,6 +592,8 @@ static int process_my_load_module(struct ast_config *cfg)
                ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s.  CALLS WILL NOT BE LOGGED!!\n", pghostname);
                ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror);
                connected = 0;
+               PQfinish(conn);
+               conn = NULL;
        }
        return AST_MODULE_LOAD_SUCCESS;
 }
index 7a90910..286baab 100644 (file)
@@ -10,6 +10,7 @@
 ;dbname=asterisk
 ;password=password
 ;user=postgres
-;table=cdr             ;SQL table where CDRs will be inserted
-;encoding=LATIN9       ; Encoding of logged characters in Asterisk
-;timezone=UTC          ; Uncomment if you want datetime fields in UTC/GMT
+;appname=asterisk    ; Postgres application_name support (optional). Whitespace not allowed.
+;table=cdr           ; SQL table where CDRs will be inserted
+;encoding=LATIN9     ; Encoding of logged characters in Asterisk
+;timezone=UTC        ; Uncomment if you want datetime fields in UTC/GMT
index cc9b9ff..10e3285 100644 (file)
@@ -64,4 +64,5 @@
 ;dbname=asterisk
 ;password=password
 ;user=postgres
-;table=cel             ;SQL table where CEL's will be inserted
+;table=cel          ; SQL table where CEL's will be inserted
+;appname=asterisk   ; Postgres application_name support (optional). Whitespace not allowed.
index b889244..015d68c 100644 (file)
@@ -12,12 +12,13 @@ dbport=5432
 dbname=asterisk
 dbuser=asterisk
 dbpass=password
+;dbappname=asterisk    ; Postgres application_name support (optional). Whitespace not allowed.
 ;
 ; dbsock is specified as the directory where the socket file may be found. The
 ; actual socket is constructed as a combination of dbsock and dbport.  For
 ; example, the values of '/tmp' and '5432', respectively, will specify a socket
 ; file of '/tmp/.s.PGSQL.5432'.
-; 
+;
 ;dbsock=/tmp
 ;
 ; requirements - At startup, each realtime family will make requirements
index b8ab994..3ee5e4e 100644 (file)
@@ -79,6 +79,7 @@ static char dbhost[MAX_DB_OPTION_SIZE] = "";
 static char dbuser[MAX_DB_OPTION_SIZE] = "";
 static char dbpass[MAX_DB_OPTION_SIZE] = "";
 static char dbname[MAX_DB_OPTION_SIZE] = "";
+static char dbappname[MAX_DB_OPTION_SIZE] = "";
 static char dbsock[MAX_DB_OPTION_SIZE] = "";
 static int dbport = 5432;
 static time_t connect_time = 0;
@@ -1436,6 +1437,12 @@ static int parse_config(int is_reload)
                dbport = atoi(s);
        }
 
+       if (!(s = ast_variable_retrieve(config, "general", "dbappname"))) {
+               dbappname[0] = '\0';
+       } else {
+               ast_copy_string(dbappname, s, sizeof(dbappname));
+       }
+
        if (!ast_strlen_zero(dbhost)) {
                /* No socket needed */
        } else if (!(s = ast_variable_retrieve(config, "general", "dbsock"))) {
@@ -1499,18 +1506,27 @@ static int pgsql_reconnect(const char *database)
 
        /* DB password can legitimately be 0-length */
        if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
-               struct ast_str *connInfo = ast_str_create(128);
+               struct ast_str *conn_info = ast_str_create(128);
+
+               if (!conn_info) {
+                       ast_log(LOG_ERROR, "PostgreSQL RealTime: Failed to allocate memory for connection string.\n");
+                       return 0;
+               }
 
-               ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
+               ast_str_set(&conn_info, 0, "host=%s port=%d dbname=%s user=%s",
                        S_OR(dbhost, dbsock), dbport, my_database, dbuser);
-               if (!ast_strlen_zero(dbpass))
-                       ast_str_append(&connInfo, 0, " password=%s", dbpass);
-
-               ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
-               pgsqlConn = PQconnectdb(ast_str_buffer(connInfo));
-               ast_debug(1, "%u connInfo=%s\n", (unsigned int)ast_str_size(connInfo), ast_str_buffer(connInfo));
-               ast_free(connInfo);
-               connInfo = NULL;
+
+               if (!ast_strlen_zero(dbappname)) {
+                       ast_str_append(&conn_info, 0, " application_name=%s", dbappname);
+               }
+
+               if (!ast_strlen_zero(dbpass)) {
+                       ast_str_append(&conn_info, 0, " password=%s", dbpass);
+               }
+
+               pgsqlConn = PQconnectdb(ast_str_buffer(conn_info));
+               ast_free(conn_info);
+               conn_info = NULL;
 
                ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
                if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {