diff --git a/doc/configuration.txt b/doc/configuration.txt index cc4ecad90..bb25448ea 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -993,6 +993,7 @@ option log-health-checks (*) X - X X option log-separate-errors (*) X X X - option logasap (*) X X X - option mysql-check X - X X +option pgsql-check X - X X option nolinger (*) X X X X option originalto X X X X option persist (*) X - X X @@ -3266,7 +3267,8 @@ option httpchk server apache1 192.168.1.1:443 check port 80 See also : "option ssl-hello-chk", "option smtpchk", "option mysql-check", - "http-check" and the "check", "port" and "inter" server options. + "option pgsql-check", "http-check" and the "check", "port" and + "inter" server options. option httpclose @@ -3540,6 +3542,20 @@ option mysql-check [ user ] See also: "option httpchk" +option pgsql-check [ user ] + Use PostgreSQL health checks for server testing + May be used in sections : defaults | frontend | listen | backend + yes | no | yes | yes + Arguments : + user This is the username which will be used when connecting + to PostgreSQL server. + + The check sends a PostgreSQL StartupMessage and waits for either + Authentication request or ErrorResponse message. It is a basic but useful + test which does not produce error nor aborted connect on the server. + This check is identical with the "mysql-check". + + See also: "option httpchk" option nolinger no option nolinger @@ -6457,8 +6473,8 @@ check "port" parameter, the source address using the "source" address, and the interval and timers using the "inter", "rise" and "fall" parameters. The request method is define in the backend using the "httpchk", "smtpchk", - "mysql-check" and "ssl-hello-chk" options. Please refer to those options and - parameters for more information. + "mysql-check", "pgsql-check" and "ssl-hello-chk" options. Please refer to + those options and parameters for more information. Supported in default-server: No diff --git a/include/types/proxy.h b/include/types/proxy.h index 8da98a6c2..7a3276e2c 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -149,6 +149,8 @@ #define PR_O2_EXP_TYPE 0x03800000 /* mask for http-check expect type */ #define PR_O2_EXP_INV 0x04000000 /* http-check expect ! */ #define PR_O2_COOK_PSV 0x08000000 /* cookie ... preserve */ + +#define PR_O2_PGSQL_CHK 0x10000000 /* use PGSQL check for server health */ /* end of proxy->options2 */ /* bits for sticking rules */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 28eb1cab7..dd266af32 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -3312,6 +3312,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_PGSQL_CHK; curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options |= PR_O_HTTP_CHK; if (!*args[2]) { /* no argument */ @@ -3344,6 +3345,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_PGSQL_CHK; curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options2 |= PR_O2_SSL3_CHK; } @@ -3354,6 +3356,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_PGSQL_CHK; curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options |= PR_O_SMTP_CHK; @@ -3374,6 +3377,69 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } } } + else if (!strcmp(args[1], "pgsql-check")) { + /* use PostgreSQL request to check servers' health */ + if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL)) + err_code |= ERR_WARN; + + free(curproxy->check_req); + curproxy->check_req = NULL; + curproxy->options &= ~PR_O_HTTP_CHK; + curproxy->options &= ~PR_O_SMTP_CHK; + curproxy->options2 &= ~PR_O2_SSL3_CHK; + curproxy->options2 &= ~PR_O2_LDAP_CHK; + curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 |= PR_O2_PGSQL_CHK; + + if (*(args[2])) { + int cur_arg = 2; + + while (*(args[cur_arg])) { + if (strcmp(args[cur_arg], "user") == 0) { + char * packet; + uint32_t packet_len; + uint32_t pv; + + /* suboption header - needs additional argument for it */ + if (*(args[cur_arg+1]) == 0) { + Alert("parsing [%s:%d] : '%s %s %s' expects as argument.\n", + file, linenum, args[0], args[1], args[cur_arg]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + /* uint32_t + uint32_t + strlen("user")+1 + strlen(username)+1 + 1 */ + packet_len = 4 + 4 + 5 + strlen(args[cur_arg + 1])+1 +1; + pv = htonl(0x30000); /* protocol version 3.0 */ + + packet = (char*) calloc(1, packet_len); + + memcpy(packet + 4, &pv, 4); + + /* copy "user" */ + memcpy(packet + 8, "user", 4); + + /* copy username */ + memcpy(packet + 13, args[cur_arg+1], strlen(args[cur_arg+1])); + + free(curproxy->check_req); + curproxy->check_req = packet; + curproxy->check_len = packet_len; + + packet_len = htonl(packet_len); + memcpy(packet, &packet_len, 4); + cur_arg += 2; + } else { + /* unknown suboption - catchall */ + Alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n", + file, linenum, args[0], args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } /* end while loop */ + } + } + else if (!strcmp(args[1], "mysql-check")) { /* use MYSQL request to check servers' health */ if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL)) @@ -3385,6 +3451,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; curproxy->options2 &= ~PR_O2_LDAP_CHK; + curproxy->options2 &= ~PR_O2_PGSQL_CHK; curproxy->options2 |= PR_O2_MYSQL_CHK; /* This is an exemple of an MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte. @@ -3454,6 +3521,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_PGSQL_CHK; curproxy->options2 |= PR_O2_LDAP_CHK; curproxy->check_req = (char *) malloc(sizeof(DEF_LDAP_CHECK_REQ) - 1); diff --git a/src/checks.c b/src/checks.c index a23fd41e9..ee0b97bd7 100644 --- a/src/checks.c +++ b/src/checks.c @@ -747,6 +747,7 @@ static int event_srv_chk_w(int fd) (s->proxy->options & PR_O_SMTP_CHK) || (s->proxy->options2 & PR_O2_SSL3_CHK) || (s->proxy->options2 & PR_O2_MYSQL_CHK) || + (s->proxy->options2 & PR_O2_PGSQL_CHK) || (s->proxy->options2 & PR_O2_LDAP_CHK)) { int ret; const char *check_req = s->proxy->check_req; @@ -1001,6 +1002,22 @@ static int event_srv_chk_r(int fd) else set_server_check_status(s, HCHK_STATUS_L7STS, desc); } + else if (s->proxy->options2 & PR_O2_PGSQL_CHK) { + if (!done && s->check_data_len < 9) + goto wait_more_data; + + if (s->check_data[0] == 'R') { + set_server_check_status(s, HCHK_STATUS_L7OKD, "PostgreSQL server is ok"); + } + else { + if ((s->check_data[0] == 'E') && (s->check_data[5]!=0) && (s->check_data[6]!=0)) + desc = &s->check_data[6]; + else + desc = "PostgreSQL unknown error"; + + set_server_check_status(s, HCHK_STATUS_L7STS, desc); + } + } else if (s->proxy->options2 & PR_O2_MYSQL_CHK) { if (!done && s->check_data_len < 5) goto wait_more_data;