diff --git a/src/modules/db_postgres/doc/db_postgres_admin.xml b/src/modules/db_postgres/doc/db_postgres_admin.xml index bb04acd4669..ebee12784f8 100644 --- a/src/modules/db_postgres/doc/db_postgres_admin.xml +++ b/src/modules/db_postgres/doc/db_postgres_admin.xml @@ -86,9 +86,10 @@ modparam("db_postgres", "retries", 3) <varname>timeout</varname> (integer) Setting this variable to any value larger than zero (which is the - default value) enables both a connection timeout and a query - timeout. If a connection attempt or a query takes longer than this - many seconds, the operation will be aborted and an error will be + default value) enables mainly query timeout. For backward compatibility + it also enables connection timeout if connect_timeout param is not set + using the con_param below. If a connection attempt or a query takes longer + than this many seconds, the operation will be aborted and an error will be returned. @@ -170,6 +171,26 @@ modparam("db_postgres", "bytea_output_escape", 0) +
+ <varname>con_param</varname> (str) + + Specifies the connection string parameters. + Parameter definition must be in the format param1=value1;param2=value2. + Many parameters can be set. + + + NOTE: See postgres connection keyword list (https://www.postgresql.org/docs/12/libpq-connect.html#LIBPQ-PARAMKEYWORDS) + + + Set <varname>con_param</varname> parameter + +... +modparam("db_postgres", "con_param", "connect_timeout=15;tcp_user_timeout=5000") +... + + +
+
diff --git a/src/modules/db_postgres/km_pg_con.c b/src/modules/db_postgres/km_pg_con.c index 634381becd6..895c644db67 100644 --- a/src/modules/db_postgres/km_pg_con.c +++ b/src/modules/db_postgres/km_pg_con.c @@ -38,6 +38,7 @@ extern int pg_bytea_output_escape; +extern pg_con_param_t* pg_con_param_list; /*! * \brief Create a new connection @@ -52,9 +53,9 @@ struct pg_con *db_postgres_new_connection(struct db_id *id) struct pg_con *ptr; char *ports; int i = 0; - const char *keywords[10], *values[10]; - char to[16]; + const char *keywords[32], *values[32]; PGresult *res = NULL; + pg_con_param_t* pg_con_param; LM_DBG("db_id = %p\n", id); @@ -75,7 +76,6 @@ struct pg_con *db_postgres_new_connection(struct db_id *id) memset(keywords, 0, (sizeof(char *) * 10)); memset(values, 0, (sizeof(char *) * 10)); - memset(to, 0, (sizeof(char) * 16)); if(id->port) { ports = int2str(id->port, 0); @@ -97,10 +97,13 @@ struct pg_con *db_postgres_new_connection(struct db_id *id) values[i++] = id->username; keywords[i] = "password"; values[i++] = id->password; - if(pg_timeout > 0) { - snprintf(to, sizeof(to) - 1, "%d", pg_timeout + 3); - keywords[i] = "connect_timeout"; - values[i++] = to; + + /* add other connection parameters */ + pg_con_param = pg_con_param_list; + while(pg_con_param){ + keywords[i] = pg_con_param->name; + values[i++] = pg_con_param->value; + pg_con_param = pg_con_param->next; } keywords[i] = values[i] = NULL; diff --git a/src/modules/db_postgres/pg_con.c b/src/modules/db_postgres/pg_con.c index 62cf14922d5..83a262400a4 100644 --- a/src/modules/db_postgres/pg_con.c +++ b/src/modules/db_postgres/pg_con.c @@ -45,6 +45,7 @@ #include #include +extern pg_con_param_t* pg_con_param_list; /* Override the default notice processor to output the messages * using SER's output subsystem. @@ -244,8 +245,8 @@ int pg_con_connect(db_con_t *con) struct pg_uri *puri; char *port_str; int ret, i = 0; - const char *keywords[10], *values[10]; - char to[16]; + const char *keywords[32], *values[32]; + pg_con_param_t* pg_con_param; pcon = DB_GET_PAYLOAD(con); puri = DB_GET_PAYLOAD(con->uri); @@ -278,11 +279,14 @@ int pg_con_connect(db_con_t *con) values[i++] = puri->username; keywords[i] = "password"; values[i++] = puri->password; - if(pg_timeout > 0) { - snprintf(to, sizeof(to) - 1, "%d", pg_timeout + 3); - keywords[i] = "connect_timeout"; - values[i++] = to; - } + + /* add other connection parameters */ + pg_con_param = pg_con_param_list; + while(pg_con_param){ + keywords[i] = pg_con_param->name; + values[i++] = pg_con_param->value; + pg_con_param = pg_con_param->next; + } keywords[i] = values[i] = NULL; diff --git a/src/modules/db_postgres/pg_mod.c b/src/modules/db_postgres/pg_mod.c index 4fc40ac6e87..077af842619 100644 --- a/src/modules/db_postgres/pg_mod.c +++ b/src/modules/db_postgres/pg_mod.c @@ -39,6 +39,7 @@ #include "db_postgres.h" #include "../../core/sr_module.h" +#include "../../core/parser/parse_param.h" #ifdef PG_TEST #include @@ -50,7 +51,6 @@ MODULE_VERSION static int pg_mod_init(void); static void pg_mod_destroy(void); -int pg_connect_timeout = 0; /* Default is unlimited */ int pg_retries = 2; /* How many times should the module try re-execute failed commands. * 0 disables reconnecting */ @@ -60,6 +60,10 @@ int pg_timeout = 0; /* default = no timeout */ int pg_keepalive = 0; int pg_bytea_output_escape = 1; +pg_con_param_t* pg_con_param_list = 0; +static int pg_con_param(modparam_t type, void *val); +static int pg_init_com_params(); + /* * Postgres module interface */ @@ -93,6 +97,7 @@ static param_export_t params[] = { {"timeout", PARAM_INT, &pg_timeout}, {"tcp_keepalive", PARAM_INT, &pg_keepalive}, {"bytea_output_escape", PARAM_INT, &pg_bytea_output_escape}, + {"con_param", PARAM_STRING|USE_FUNC_PARAM, (void*)pg_con_param}, {0, 0, 0} }; @@ -543,6 +548,11 @@ static int pg_mod_init(void) #endif /* PG_TEST */ if(pg_init_lock_set(pg_lockset) < 0) return -1; + + if(pg_init_com_params() < 0){ + return -1; + } + return km_postgres_mod_init(); } @@ -551,4 +561,144 @@ static void pg_mod_destroy(void) pg_destroy_lock_set(); } +static void free_con_param_list() +{ + pg_con_param_t *tmp = NULL; + pg_con_param_t *con_param = pg_con_param_list; + while(con_param) + { + if(con_param->name) + { + shm_free(con_param->name); + } + if(con_param->value) + { + shm_free(con_param->value); + } + tmp = con_param->next; + shm_free(con_param); + con_param = tmp; + } +} + +static int add_con_param(str *name, str *value) +{ + /* malloc for param */ + pg_con_param_t *con_param = (pg_con_param_t*)shm_malloc(sizeof(pg_con_param_t)); + if(con_param == 0) { + LM_ERR("no more shm memory\n"); + goto error; + } + + /* parse name */ + con_param->name = (char*)shm_malloc(name->len + 1); + if(con_param->name == NULL){ + LM_ERR("no more shm memory while parsing name\n"); + goto error; + } + memcpy(con_param->name, name->s, name->len); + con_param->name[name->len] = '\0'; + + /* parse value */ + con_param->value = (char*)shm_malloc(value->len + 1); + if(con_param->value == NULL){ + LM_ERR("no more shm memory while parsing value\n"); + goto error; + } + memcpy(con_param->value, value->s, value->len); + con_param->value[value->len] = '\0'; + + /* add param to the linked list */ + con_param->next = pg_con_param_list; + pg_con_param_list = con_param; + return 0; + +error: + free_con_param_list(); + return -1; +} + +static int pg_init_com_params() +{ + str connect_timeout_str = str_init("connect_timeout"); + int ret = 0; + + int connect_timeout_set = 0; + if(pg_con_param_list != NULL) + { + LM_INFO("postgres connection params:"); + pg_con_param_t *con_param = pg_con_param_list; + while(con_param) + { + LM_INFO("%s=%s", con_param->name, con_param->value); + + /* check if connect_timeout parameter is set */ + if(strncmp(con_param->name, connect_timeout_str.s, connect_timeout_str.len) == 0) + { + connect_timeout_set = 1; + } + + con_param = con_param->next; + } + } + + /* For backward compatibility take pg_timeout param into account */ + if(pg_timeout > 0 && connect_timeout_set == 0) + { + str connect_timeout_val_str; + char timeout_val[16] = {0}; + + snprintf(timeout_val, sizeof(timeout_val) - 1, "%d", pg_timeout); + connect_timeout_val_str.s = timeout_val; + connect_timeout_val_str.len = strlen(connect_timeout_val_str.s); + + /* add connect_timeout parameter with pg_timeout value */ + ret = add_con_param(&connect_timeout_str, &connect_timeout_val_str); + LM_INFO("%.*s=%.*s added with given timeout param", + connect_timeout_str.len, connect_timeout_str.s, + connect_timeout_val_str.len, connect_timeout_val_str.s); + } + + return ret; +} + +static int pg_con_param(modparam_t type, void *val) { + param_t* params_list=NULL; + param_hooks_t phooks; + param_t *pit=NULL; + str s; + + if(val==NULL){ + free_con_param_list(); + return -1; + } + s.s = (char*)val; + s.len = strlen(s.s); + if(s.s[s.len-1]==';'){ + s.len--; + } + + if (parse_params(&s, CLASS_ANY, &phooks, ¶ms_list)<0){ + free_con_param_list(); + return -1; + } + + /* parse parameter values */ + for (pit=params_list; pit; pit=pit->next) { + + if(pit->name.len == 0 || pit->body.len == 0) { + LM_ERR("invalid con_param parameter\n"); + free_con_param_list(); + return -1; + } + + if(add_con_param(&pit->name, &pit->body) < 0){ + free_con_param_list(); + return -1; + } + + } + return 0; +} + /** @} */ diff --git a/src/modules/db_postgres/pg_mod.h b/src/modules/db_postgres/pg_mod.h index 4e622b60d1b..6b97a0878b1 100644 --- a/src/modules/db_postgres/pg_mod.h +++ b/src/modules/db_postgres/pg_mod.h @@ -38,6 +38,12 @@ extern int pg_retries; extern int pg_timeout; extern int pg_keepalive; +typedef struct pg_con_param_s { + char* name; + char* value; + struct pg_con_param_s* next; +} pg_con_param_t; + /** @} */ #endif /* _PG_MOD_H */