summaryrefslogtreecommitdiffstats
path: root/src/server.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/server.c1295
1 files changed, 854 insertions, 441 deletions
diff --git a/src/server.c b/src/server.c
index 9196fac..caf2f40 100644
--- a/src/server.c
+++ b/src/server.c
@@ -28,6 +28,7 @@
#include <haproxy/dict-t.h>
#include <haproxy/errors.h>
#include <haproxy/global.h>
+#include <haproxy/guid.h>
#include <haproxy/log.h>
#include <haproxy/mailers.h>
#include <haproxy/namespace.h>
@@ -140,18 +141,10 @@ const char *srv_op_st_chg_cause(enum srv_op_st_chg_cause cause)
int srv_downtime(const struct server *s)
{
- if ((s->cur_state != SRV_ST_STOPPED) || s->last_change >= ns_to_sec(now_ns)) // ignore negative time
+ if ((s->cur_state != SRV_ST_STOPPED) || s->counters.last_change >= ns_to_sec(now_ns)) // ignore negative time
return s->down_time;
- return ns_to_sec(now_ns) - s->last_change + s->down_time;
-}
-
-int srv_lastsession(const struct server *s)
-{
- if (s->counters.last_sess)
- return ns_to_sec(now_ns) - s->counters.last_sess;
-
- return -1;
+ return ns_to_sec(now_ns) - s->counters.last_change + s->down_time;
}
int srv_getinter(const struct check *check)
@@ -170,7 +163,7 @@ int srv_getinter(const struct check *check)
/* Update server's addr:svc_port tuple in INET context
*
- * Must be called under thread isolation to ensure consistent readings accross
+ * Must be called under thread isolation to ensure consistent readings across
* all threads (addr:svc_port might be read without srv lock being held).
*/
static void _srv_set_inetaddr_port(struct server *srv,
@@ -184,6 +177,11 @@ static void _srv_set_inetaddr_port(struct server *srv,
else
srv->flags &= ~SRV_F_MAPPORTS;
+ if (srv->proxy->lbprm.update_server_eweight) {
+ /* some balancers (chash in particular) may use the addr in their routing decisions */
+ srv->proxy->lbprm.update_server_eweight(srv);
+ }
+
if (srv->log_target && srv->log_target->type == LOG_TARGET_DGRAM) {
/* server is used as a log target, manually update log target addr for DGRAM */
ipcpy(addr, srv->log_target->addr);
@@ -268,7 +266,7 @@ static struct task *server_atomic_sync(struct task *task, void *context, unsigne
px = proxy_find_by_id(data->server.safe.proxy_uuid, PR_CAP_BE, 0);
if (!px)
continue;
- srv = findserver_unique_id(px, data->server.safe.puid, data->server.safe.rid);
+ srv = server_find_by_id_unique(px, data->server.safe.puid, data->server.safe.rid);
if (!srv)
continue;
@@ -295,7 +293,7 @@ static struct task *server_atomic_sync(struct task *task, void *context, unsigne
/*
* this requires thread isolation, which is safe since we're the only
* task working for the current subscription and we don't hold locks
- * or ressources that other threads may depend on to complete a running
+ * or resources that other threads may depend on to complete a running
* cycle. Note that we do this way because we assume that this event is
* rather rare.
*/
@@ -306,9 +304,24 @@ static struct task *server_atomic_sync(struct task *task, void *context, unsigne
_srv_set_inetaddr_port(srv, &new_addr,
data->safe.next.port.svc, data->safe.next.port.map);
- /* propagate the changes */
- if (data->safe.purge_conn) /* force connection cleanup on the given server? */
- srv_cleanup_connections(srv);
+ /* propagate the changes, force connection cleanup */
+ if (new_addr.ss_family != AF_UNSPEC &&
+ (srv->next_admin & SRV_ADMF_RMAINT)) {
+ /* server was previously put under DNS maintenance due
+ * to DNS error, but addr resolves again, so we must
+ * put it out of maintenance
+ */
+ srv_clr_admin_flag(srv, SRV_ADMF_RMAINT);
+
+ /* thanks to valid DNS resolution? */
+ if (data->safe.updater.dns) {
+ chunk_reset(&trash);
+ chunk_printf(&trash, "Server %s/%s administratively READY thanks to valid DNS answer", srv->proxy->id, srv->id);
+ ha_warning("%s.\n", trash.area);
+ send_log(srv->proxy, LOG_NOTICE, "%s.\n", trash.area);
+ }
+ }
+ srv_cleanup_connections(srv);
srv_set_dyncookie(srv);
srv_set_addr_desc(srv, 1);
}
@@ -437,47 +450,25 @@ void _srv_event_hdl_prepare_state(struct event_hdl_cb_data_server_state *cb_data
*/
static void _srv_event_hdl_prepare_inetaddr(struct event_hdl_cb_data_server_inetaddr *cb_data,
struct server *srv,
- const struct sockaddr_storage *next_addr,
- unsigned int next_port, uint8_t next_mapports,
- uint8_t purge_conn)
+ const struct server_inetaddr *next_inetaddr,
+ struct server_inetaddr_updater updater)
{
- struct sockaddr_storage *prev_addr = &srv->addr;
- unsigned int prev_port = srv->svc_port;
- uint8_t prev_mapports = !!(srv->flags & SRV_F_MAPPORTS);
+ struct server_inetaddr prev_inetaddr;
+
+ server_get_inetaddr(srv, &prev_inetaddr);
/* only INET families are supported */
- BUG_ON((prev_addr->ss_family != AF_UNSPEC &&
- prev_addr->ss_family != AF_INET && prev_addr->ss_family != AF_INET6) ||
- (next_addr->ss_family != AF_UNSPEC &&
- next_addr->ss_family != AF_INET && next_addr->ss_family != AF_INET6));
+ BUG_ON((next_inetaddr->family != AF_UNSPEC &&
+ next_inetaddr->family != AF_INET && next_inetaddr->family != AF_INET6));
/* prev */
- cb_data->safe.prev.family = prev_addr->ss_family;
- memset(&cb_data->safe.prev.addr, 0, sizeof(cb_data->safe.prev.addr));
- if (prev_addr->ss_family == AF_INET)
- cb_data->safe.prev.addr.v4.s_addr =
- ((struct sockaddr_in *)prev_addr)->sin_addr.s_addr;
- else if (prev_addr->ss_family == AF_INET6)
- memcpy(&cb_data->safe.prev.addr.v6,
- &((struct sockaddr_in6 *)prev_addr)->sin6_addr,
- sizeof(struct in6_addr));
- cb_data->safe.prev.port.svc = prev_port;
- cb_data->safe.prev.port.map = prev_mapports;
+ cb_data->safe.prev = prev_inetaddr;
/* next */
- cb_data->safe.next.family = next_addr->ss_family;
- memset(&cb_data->safe.next.addr, 0, sizeof(cb_data->safe.next.addr));
- if (next_addr->ss_family == AF_INET)
- cb_data->safe.next.addr.v4.s_addr =
- ((struct sockaddr_in *)next_addr)->sin_addr.s_addr;
- else if (next_addr->ss_family == AF_INET6)
- memcpy(&cb_data->safe.next.addr.v6,
- &((struct sockaddr_in6 *)next_addr)->sin6_addr,
- sizeof(struct in6_addr));
- cb_data->safe.next.port.svc = next_port;
- cb_data->safe.next.port.map = next_mapports;
+ cb_data->safe.next = *next_inetaddr;
- cb_data->safe.purge_conn = purge_conn;
+ /* updater */
+ cb_data->safe.updater = updater;
}
/* server event publishing helper: publish in both global and
@@ -900,11 +891,6 @@ static int srv_parse_disabled(char **args, int *cur_arg,
static int srv_parse_enabled(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
{
- if (newsrv->flags & SRV_F_DYNAMIC) {
- ha_warning("Keyword 'enabled' is ignored for dynamic servers. It will be rejected from 3.0 onward.");
- return 0;
- }
-
newsrv->next_admin &= ~SRV_ADMF_CMAINT & ~SRV_ADMF_FMAINT;
newsrv->next_state = SRV_ST_RUNNING;
newsrv->check.state &= ~CHK_ST_PAUSED;
@@ -933,6 +919,28 @@ static int srv_parse_error_limit(char **args, int *cur_arg,
return 0;
}
+/* Parse the "guid" keyword */
+static int srv_parse_guid(char **args, int *cur_arg,
+ struct proxy *curproxy, struct server *newsrv, char **err)
+{
+ const char *guid;
+ char *guid_err = NULL;
+
+ if (!*args[*cur_arg + 1]) {
+ memprintf(err, "'%s' : expects an argument", args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ guid = args[*cur_arg + 1];
+ if (guid_insert(&newsrv->obj_type, guid, &guid_err)) {
+ memprintf(err, "'%s': %s", args[*cur_arg], guid_err);
+ ha_free(&guid_err);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ return 0;
+}
+
/* Parse the "ws" keyword */
static int srv_parse_ws(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
@@ -960,6 +968,32 @@ static int srv_parse_ws(char **args, int *cur_arg,
return 0;
}
+/* Parse the "hash-key" server keyword */
+static int srv_parse_hash_key(char **args, int *cur_arg,
+ struct proxy *curproxy, struct server *newsrv, char **err)
+{
+ if (!args[*cur_arg + 1]) {
+ memprintf(err, "'%s expects 'id', 'addr', or 'addr-port' value", args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ if (strcmp(args[*cur_arg + 1], "id") == 0) {
+ newsrv->hash_key = SRV_HASH_KEY_ID;
+ }
+ else if (strcmp(args[*cur_arg + 1], "addr") == 0) {
+ newsrv->hash_key = SRV_HASH_KEY_ADDR;
+ }
+ else if (strcmp(args[*cur_arg + 1], "addr-port") == 0) {
+ newsrv->hash_key = SRV_HASH_KEY_ADDR_PORT;
+ }
+ else {
+ memprintf(err, "'%s' has to be 'id', 'addr', or 'addr-port'", args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ return 0;
+}
+
/* Parse the "init-addr" server keyword */
static int srv_parse_init_addr(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
@@ -1119,6 +1153,26 @@ static int srv_parse_pool_purge_delay(char **args, int *cur_arg, struct proxy *c
return 0;
}
+static int srv_parse_pool_conn_name(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
+{
+ char *arg;
+
+ arg = args[*cur_arg + 1];
+ if (!*arg) {
+ memprintf(err, "'%s' expects <value> as argument", args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ ha_free(&newsrv->pool_conn_name);
+ newsrv->pool_conn_name = strdup(arg);
+ if (!newsrv->pool_conn_name) {
+ memprintf(err, "'%s' : out of memory", args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+
+ return 0;
+}
+
static int srv_parse_pool_low_conn(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
{
char *arg;
@@ -1199,6 +1253,7 @@ static int srv_parse_namespace(char **args, int *cur_arg,
if (strcmp(arg, "*") == 0) {
/* Use the namespace associated with the connection (if present). */
newsrv->flags |= SRV_F_USE_NS_FROM_PP;
+ global.last_checks |= LSTCHK_SYSADM;
return 0;
}
@@ -1217,6 +1272,7 @@ static int srv_parse_namespace(char **args, int *cur_arg,
memprintf(err, "Cannot open namespace '%s'", arg);
return ERR_ALERT | ERR_FATAL;
}
+ global.last_checks |= LSTCHK_SYSADM;
return 0;
#else
@@ -2230,9 +2286,11 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
{ "backup", srv_parse_backup, 0, 1, 1 }, /* Flag as backup server */
{ "cookie", srv_parse_cookie, 1, 1, 1 }, /* Assign a cookie to the server */
{ "disabled", srv_parse_disabled, 0, 1, 1 }, /* Start the server in 'disabled' state */
- { "enabled", srv_parse_enabled, 0, 1, 1 }, /* Start the server in 'enabled' state */
+ { "enabled", srv_parse_enabled, 0, 1, 0 }, /* Start the server in 'enabled' state */
{ "error-limit", srv_parse_error_limit, 1, 1, 1 }, /* Configure the consecutive count of check failures to consider a server on error */
+ { "guid", srv_parse_guid, 1, 0, 1 }, /* Set global unique ID of the server */
{ "ws", srv_parse_ws, 1, 1, 1 }, /* websocket protocol */
+ { "hash-key", srv_parse_hash_key, 1, 1, 1 }, /* Configure how chash keys are computed */
{ "id", srv_parse_id, 1, 0, 1 }, /* set id# of server */
{ "init-addr", srv_parse_init_addr, 1, 1, 0 }, /* */
{ "log-bufsize", srv_parse_log_bufsize, 1, 1, 0 }, /* Set the ring bufsize for log server (only for log backends) */
@@ -2251,6 +2309,7 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
{ "on-error", srv_parse_on_error, 1, 1, 1 }, /* Configure the action on check failure */
{ "on-marked-down", srv_parse_on_marked_down, 1, 1, 1 }, /* Configure the action when a server is marked down */
{ "on-marked-up", srv_parse_on_marked_up, 1, 1, 1 }, /* Configure the action when a server is marked up */
+ { "pool-conn-name", srv_parse_pool_conn_name, 1, 1, 1 }, /* Define expression to identify connections in idle pool */
{ "pool-low-conn", srv_parse_pool_low_conn, 1, 1, 1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
{ "pool-max-conn", srv_parse_pool_max_conn, 1, 1, 1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
{ "pool-purge-delay", srv_parse_pool_purge_delay, 1, 1, 1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
@@ -2290,17 +2349,19 @@ void server_recalc_eweight(struct server *sv, int must_update)
struct proxy *px = sv->proxy;
unsigned w;
- if (ns_to_sec(now_ns) < sv->last_change || ns_to_sec(now_ns) >= sv->last_change + sv->slowstart) {
- /* go to full throttle if the slowstart interval is reached */
- if (sv->next_state == SRV_ST_STARTING)
+ if (ns_to_sec(now_ns) < sv->counters.last_change || ns_to_sec(now_ns) >= sv->counters.last_change + sv->slowstart) {
+ /* go to full throttle if the slowstart interval is reached unless server is currently down */
+ if ((sv->cur_state != SRV_ST_STOPPED) && (sv->next_state == SRV_ST_STARTING))
sv->next_state = SRV_ST_RUNNING;
}
/* We must take care of not pushing the server to full throttle during slow starts.
* It must also start immediately, at least at the minimal step when leaving maintenance.
*/
- if ((sv->next_state == SRV_ST_STARTING) && (px->lbprm.algo & BE_LB_PROP_DYN))
- w = (px->lbprm.wdiv * (ns_to_sec(now_ns) - sv->last_change) + sv->slowstart) / sv->slowstart;
+ if ((sv->cur_state == SRV_ST_STOPPED) && (sv->next_state == SRV_ST_STARTING) && (px->lbprm.algo & BE_LB_PROP_DYN))
+ w = 1;
+ else if ((sv->next_state == SRV_ST_STARTING) && (px->lbprm.algo & BE_LB_PROP_DYN))
+ w = (px->lbprm.wdiv * (ns_to_sec(now_ns) - sv->counters.last_change) + sv->slowstart) / sv->slowstart;
else
w = px->lbprm.wdiv;
@@ -2334,7 +2395,7 @@ const char *server_parse_weight_change_request(struct server *sv,
w = strtol(weight_str, &end, 10);
if (end == weight_str)
- return "Empty weight string empty or preceded by garbage";
+ return "Empty weight string empty or preceded by garbage\n";
else if (end[0] == '%' && end[1] == '\0') {
if (w < 0)
return "Relative weight must be positive.\n";
@@ -2348,7 +2409,7 @@ const char *server_parse_weight_change_request(struct server *sv,
else if (w < 0 || w > 256)
return "Absolute weight can only be between 0 and 256 inclusive.\n";
else if (end[0] != '\0')
- return "Trailing garbage in weight string";
+ return "Trailing garbage in weight string\n";
if (w && w != sv->iweight && !(px->lbprm.algo & BE_LB_PROP_DYN))
return "Backend is using a static LB algorithm and only accepts weights '0%' and '100%'.\n";
@@ -2360,32 +2421,6 @@ const char *server_parse_weight_change_request(struct server *sv,
}
/*
- * Parses <addr_str> and configures <sv> accordingly. <from> precise
- * the source of the change in the associated message log.
- * Returns:
- * - error string on error
- * - NULL on success
- *
- * Must be called with the server lock held.
- */
-const char *server_parse_addr_change_request(struct server *sv,
- const char *addr_str, const char *updater)
-{
- unsigned char ip[INET6_ADDRSTRLEN];
-
- if (inet_pton(AF_INET6, addr_str, ip)) {
- srv_update_addr(sv, ip, AF_INET6, updater);
- return NULL;
- }
- if (inet_pton(AF_INET, addr_str, ip)) {
- srv_update_addr(sv, ip, AF_INET, updater);
- return NULL;
- }
-
- return "Could not understand IP address format.\n";
-}
-
-/*
* Must be called with the server lock held.
*/
const char *server_parse_maxconn_change_request(struct server *sv,
@@ -2399,9 +2434,9 @@ const char *server_parse_maxconn_change_request(struct server *sv,
v = strtol(maxconn_str, &end, 10);
if (end == maxconn_str)
- return "maxconn string empty or preceded by garbage";
+ return "maxconn string empty or preceded by garbage\n";
else if (end[0] != '\0')
- return "Trailing garbage in maxconn string";
+ return "Trailing garbage in maxconn string\n";
if (sv->maxconn == sv->minconn) { // static maxconn
sv->maxconn = sv->minconn = v;
@@ -2415,42 +2450,56 @@ const char *server_parse_maxconn_change_request(struct server *sv,
return NULL;
}
-static struct sample_expr *srv_sni_sample_parse_expr(struct server *srv, struct proxy *px,
- const char *file, int linenum, char **err)
+/* Interpret <expr> as sample expression. This function is reserved for
+ * internal server allocation. On parsing use parse_srv_expr() for extra sample
+ * check validity.
+ *
+ * Returns the allocated sample on success or NULL on error.
+ */
+struct sample_expr *_parse_srv_expr(char *expr, struct arg_list *args_px,
+ const char *file, int linenum, char **err)
{
int idx;
const char *args[] = {
- srv->sni_expr,
+ expr,
NULL,
};
idx = 0;
- px->conf.args.ctx = ARGC_SRV;
+ args_px->ctx = ARGC_SRV;
- return sample_parse_expr((char **)args, &idx, file, linenum, err, &px->conf.args, NULL);
+ return sample_parse_expr((char **)args, &idx, file, linenum, err, args_px, NULL);
}
-int server_parse_sni_expr(struct server *newsrv, struct proxy *px, char **err)
+/* Interpret <str> if not empty as a sample expression and store it into <out>.
+ * Contrary to _parse_srv_expr(), fetch scope validity is checked to ensure it
+ * is valid on a server line context. It also updates <px> HTTP mode
+ * requirement depending on fetch method used.
+ *
+ * Returns 0 on success else non zero.
+ */
+static int parse_srv_expr(char *str, struct sample_expr **out, struct proxy *px,
+ char **err)
{
struct sample_expr *expr;
- expr = srv_sni_sample_parse_expr(newsrv, px, px->conf.file, px->conf.line, err);
- if (!expr) {
- memprintf(err, "error detected while parsing sni expression : %s", *err);
+ if (!str)
+ return 0;
+
+ expr = _parse_srv_expr(str, &px->conf.args, px->conf.file, px->conf.line, err);
+ if (!expr)
return ERR_ALERT | ERR_FATAL;
- }
if (!(expr->fetch->val & SMP_VAL_BE_SRV_CON)) {
- memprintf(err, "error detected while parsing sni expression : "
- " fetch method '%s' extracts information from '%s', "
+ memprintf(err, "fetch method '%s' extracts information from '%s', "
"none of which is available here.",
- newsrv->sni_expr, sample_src_names(expr->fetch->use));
+ str, sample_src_names(expr->fetch->use));
return ERR_ALERT | ERR_FATAL;
}
px->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
- release_sample_expr(newsrv->ssl_ctx.sni);
- newsrv->ssl_ctx.sni = expr;
+ release_sample_expr(*out);
+ *out = expr;
return 0;
}
@@ -2634,6 +2683,45 @@ int srv_prepare_for_resolution(struct server *srv, const char *hostname)
return -1;
}
+/* Initialize default values for <srv>. Used both for dynamic servers and
+ * default servers. The latter are not initialized via new_server(), hence this
+ * function purpose. For static servers, srv_settings_cpy() is used instead
+ * reusing their default server instance.
+ */
+void srv_settings_init(struct server *srv)
+{
+ srv->check.inter = DEF_CHKINTR;
+ srv->check.fastinter = 0;
+ srv->check.downinter = 0;
+ srv->check.rise = DEF_RISETIME;
+ srv->check.fall = DEF_FALLTIME;
+ srv->check.port = 0;
+
+ srv->agent.inter = DEF_CHKINTR;
+ srv->agent.fastinter = 0;
+ srv->agent.downinter = 0;
+ srv->agent.rise = DEF_AGENT_RISETIME;
+ srv->agent.fall = DEF_AGENT_FALLTIME;
+ srv->agent.port = 0;
+
+ srv->maxqueue = 0;
+ srv->minconn = 0;
+ srv->maxconn = 0;
+
+ srv->max_reuse = -1;
+ srv->max_idle_conns = -1;
+ srv->pool_purge_delay = 5000;
+
+ srv->slowstart = 0;
+
+ srv->onerror = DEF_HANA_ONERR;
+ srv->consecutive_errors_limit = DEF_HANA_ERRLIMIT;
+
+ srv->uweight = srv->iweight = 1;
+
+ LIST_INIT(&srv->pp_tlvs);
+}
+
/*
* Copy <src> server settings to <srv> server allocating
* everything needed.
@@ -2704,6 +2792,7 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
srv->minconn = src->minconn;
srv->maxconn = src->maxconn;
srv->slowstart = src->slowstart;
+ srv->hash_key = src->hash_key;
srv->observe = src->observe;
srv->onerror = src->onerror;
srv->onmarkeddown = src->onmarkeddown;
@@ -2751,6 +2840,8 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
srv->tcp_ut = src->tcp_ut;
#endif
srv->mux_proto = src->mux_proto;
+ if (srv->pool_conn_name)
+ srv->pool_conn_name = strdup(srv->pool_conn_name);
srv->pool_purge_delay = src->pool_purge_delay;
srv->low_idle_conns = src->low_idle_conns;
srv->max_idle_conns = src->max_idle_conns;
@@ -2806,7 +2897,7 @@ struct server *new_server(struct proxy *proxy)
srv->rid = 0; /* rid defaults to 0 */
srv->next_state = SRV_ST_RUNNING; /* early server setup */
- srv->last_change = ns_to_sec(now_ns);
+ srv->counters.last_change = ns_to_sec(now_ns);
srv->check.obj_type = OBJ_TYPE_CHECK;
srv->check.status = HCHK_STATUS_INI;
@@ -2820,6 +2911,10 @@ struct server *new_server(struct proxy *proxy)
srv->agent.proxy = proxy;
srv->xprt = srv->check.xprt = srv->agent.xprt = xprt_get(XPRT_RAW);
+ MT_LIST_INIT(&srv->sess_conns);
+
+ guid_init(&srv->guid);
+
srv->extra_counters = NULL;
#ifdef USE_OPENSSL
HA_RWLOCK_INIT(&srv->ssl_ctx.lock);
@@ -2840,6 +2935,8 @@ void srv_take(struct server *srv)
/* deallocate common server parameters (may be used by default-servers) */
void srv_free_params(struct server *srv)
{
+ struct srv_pp_tlv_list *srv_tlv = NULL;
+
free(srv->cookie);
free(srv->rdr_pfx);
free(srv->hostname);
@@ -2848,6 +2945,8 @@ void srv_free_params(struct server *srv)
free(srv->per_thr);
free(srv->per_tgrp);
free(srv->curr_idle_thr);
+ free(srv->pool_conn_name);
+ release_sample_expr(srv->pool_conn_name_expr);
free(srv->resolvers_id);
free(srv->addr_node.key);
free(srv->lb_nodes);
@@ -2858,6 +2957,14 @@ void srv_free_params(struct server *srv)
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->destroy_srv)
xprt_get(XPRT_SSL)->destroy_srv(srv);
+
+ while (!LIST_ISEMPTY(&srv->pp_tlvs)) {
+ srv_tlv = LIST_ELEM(srv->pp_tlvs.n, struct srv_pp_tlv_list *, list);
+ LIST_DEL_INIT(&srv_tlv->list);
+ lf_expr_deinit(&srv_tlv->fmt);
+ ha_free(&srv_tlv->fmt_string);
+ ha_free(&srv_tlv);
+ }
}
/* Deallocate a server <srv> and its member. <srv> must be allocated. For
@@ -2882,6 +2989,8 @@ struct server *srv_drop(struct server *srv)
if (HA_ATOMIC_SUB_FETCH(&srv->refcount, 1))
goto end;
+ guid_remove(&srv->guid);
+
/* make sure we are removed from our 'next->prev_deleted' list
* This doesn't require full thread isolation as we're using mt lists
* However this could easily be turned into regular list if required
@@ -3018,6 +3127,12 @@ static int _srv_parse_tmpl_init(struct server *srv, struct proxy *px)
int i;
struct server *newsrv;
+ /* Set the first server's ID. */
+ _srv_parse_set_id_from_prefix(srv, srv->tmpl_info.prefix, srv->tmpl_info.nb_low);
+ srv->conf.name.key = srv->id;
+ ebis_insert(&curproxy->conf.used_server_name, &srv->conf.name);
+
+ /* then create other servers from this one */
for (i = srv->tmpl_info.nb_low + 1; i <= srv->tmpl_info.nb_high; i++) {
newsrv = new_server(px);
if (!newsrv)
@@ -3029,8 +3144,21 @@ static int _srv_parse_tmpl_init(struct server *srv, struct proxy *px)
srv_settings_cpy(newsrv, srv, 1);
srv_prepare_for_resolution(newsrv, srv->hostname);
+ /* Use sni as fallback if pool_conn_name isn't set */
+ if (!newsrv->pool_conn_name && newsrv->sni_expr) {
+ newsrv->pool_conn_name = strdup(newsrv->sni_expr);
+ if (!newsrv->pool_conn_name)
+ goto err;
+ }
+
+ if (newsrv->pool_conn_name) {
+ newsrv->pool_conn_name_expr = _parse_srv_expr(srv->pool_conn_name, &px->conf.args, NULL, 0, NULL);
+ if (!newsrv->pool_conn_name_expr)
+ goto err;
+ }
+
if (newsrv->sni_expr) {
- newsrv->ssl_ctx.sni = srv_sni_sample_parse_expr(newsrv, px, NULL, 0, NULL);
+ newsrv->ssl_ctx.sni = _parse_srv_expr(srv->sni_expr, &px->conf.args, NULL, 0, NULL);
if (!newsrv->ssl_ctx.sni)
goto err;
}
@@ -3045,6 +3173,9 @@ static int _srv_parse_tmpl_init(struct server *srv, struct proxy *px)
/* Linked backwards first. This will be restablished after parsing. */
newsrv->next = px->srv;
px->srv = newsrv;
+
+ newsrv->conf.name.key = newsrv->id;
+ ebis_insert(&curproxy->conf.used_server_name, &newsrv->conf.name);
}
_srv_parse_set_id_from_prefix(srv, srv->tmpl_info.prefix, srv->tmpl_info.nb_low);
@@ -3316,30 +3447,18 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
/* Copy default server settings to new server */
srv_settings_cpy(newsrv, &curproxy->defsrv, 0);
} else {
- /* Initialize dynamic server weight to 1 */
- newsrv->uweight = newsrv->iweight = 1;
+ srv_settings_init(newsrv);
/* A dynamic server is disabled on startup */
newsrv->next_admin = SRV_ADMF_FMAINT;
newsrv->next_state = SRV_ST_STOPPED;
server_recalc_eweight(newsrv, 0);
-
- /* Set default values for checks */
- newsrv->check.inter = DEF_CHKINTR;
- newsrv->check.rise = DEF_RISETIME;
- newsrv->check.fall = DEF_FALLTIME;
-
- newsrv->agent.inter = DEF_CHKINTR;
- newsrv->agent.rise = DEF_AGENT_RISETIME;
- newsrv->agent.fall = DEF_AGENT_FALLTIME;
}
HA_SPIN_INIT(&newsrv->lock);
}
else {
*srv = newsrv = &curproxy->defsrv;
*cur_arg = 1;
- newsrv->resolv_opts.family_prio = AF_INET6;
- newsrv->resolv_opts.accept_duplicate_ip = 0;
}
free(fqdn);
@@ -3426,25 +3545,6 @@ out:
return err_code;
}
-/* This function is first intended to be used through parse_server to
- * initialize a new server on startup.
- */
-static int _srv_parse_sni_expr_init(char **args, int cur_arg,
- struct server *srv, struct proxy *proxy,
- char **errmsg)
-{
- int ret;
-
- if (!srv->sni_expr)
- return 0;
-
- ret = server_parse_sni_expr(srv, proxy, errmsg);
- if (!ret)
- return 0;
-
- return ret;
-}
-
/* Server initializations finalization.
* Initialize health check, agent check, SNI expression and outgoing TLVs if enabled.
* Must not be called for a default server instance.
@@ -3471,9 +3571,27 @@ static int _srv_parse_finalize(char **args, int cur_arg,
return ERR_ALERT | ERR_FATAL;
}
- if ((ret = _srv_parse_sni_expr_init(args, cur_arg, srv, px, &errmsg)) != 0) {
+ if ((ret = parse_srv_expr(srv->sni_expr, &srv->ssl_ctx.sni, px, &errmsg))) {
if (errmsg) {
- ha_alert("%s\n", errmsg);
+ ha_alert("error detected while parsing sni expression : %s.\n", errmsg);
+ free(errmsg);
+ }
+ return ret;
+ }
+
+ /* Use sni as fallback if pool_conn_name isn't set */
+ if (!srv->pool_conn_name && srv->sni_expr) {
+ srv->pool_conn_name = strdup(srv->sni_expr);
+ if (!srv->pool_conn_name) {
+ ha_alert("out of memory\n");
+ return ERR_ALERT | ERR_FATAL;
+ }
+ }
+
+ if ((ret = parse_srv_expr(srv->pool_conn_name, &srv->pool_conn_name_expr,
+ px, &errmsg))) {
+ if (errmsg) {
+ ha_alert("error detected while parsing pool-conn-name expression : %s.\n", errmsg);
free(errmsg);
}
return ret;
@@ -3490,7 +3608,7 @@ static int _srv_parse_finalize(char **args, int cur_arg,
}
list_for_each_entry(srv_tlv, &srv->pp_tlvs, list) {
- LIST_INIT(&srv_tlv->fmt);
+ lf_expr_init(&srv_tlv->fmt);
if (srv_tlv->fmt_string && unlikely(!parse_logformat_string(srv_tlv->fmt_string,
srv->proxy, &srv_tlv->fmt, 0, SMP_VAL_BE_SRV_CON, &errmsg))) {
if (errmsg) {
@@ -3562,8 +3680,13 @@ int parse_server(const char *file, int linenum, char **args,
goto out;
}
- if (parse_flags & SRV_PARSE_TEMPLATE)
+ if (parse_flags & SRV_PARSE_TEMPLATE) {
_srv_parse_tmpl_init(newsrv, curproxy);
+ }
+ else if (!(parse_flags & SRV_PARSE_DEFAULT_SERVER)) {
+ newsrv->conf.name.key = newsrv->id;
+ ebis_insert(&curproxy->conf.used_server_name, &newsrv->conf.name);
+ }
/* If the server id is fixed, insert it in the proxy used_id tree.
* This is needed to detect a later duplicate id via srv_parse_id.
@@ -3610,6 +3733,25 @@ struct server *server_find_by_id(struct proxy *bk, int id)
return curserver;
}
+/*
+ * This function finds a server with matching "<puid> x <rid>" within
+ * selected backend <bk>.
+ * Using the combination of proxy-uid + revision id ensures that the function
+ * will either return the server we're expecting or NULL if it has been removed
+ * from the proxy (<id> is unique within the list, but it is not true over the
+ * process lifetime as new servers may reuse the id of a previously deleted
+ * server).
+ */
+struct server *server_find_by_id_unique(struct proxy *bk, int id, uint32_t rid)
+{
+ struct server *curserver;
+
+ curserver = server_find_by_id(bk, id);
+ if (!curserver || curserver->rid != rid)
+ return NULL;
+ return curserver;
+}
+
/* Returns a pointer to the first server matching either name <name>, or id
* if <name> starts with a '#'. NULL is returned if no match is found.
* the lookup is performed in the backend <bk>
@@ -3628,20 +3770,43 @@ struct server *server_find_by_name(struct proxy *bk, const char *name)
curserver = NULL;
if (*name == '#') {
curserver = server_find_by_id(bk, atoi(name + 1));
- if (curserver)
- return curserver;
}
else {
- curserver = bk->srv;
-
- while (curserver && (strcmp(curserver->id, name) != 0))
- curserver = curserver->next;
+ struct ebpt_node *node;
- if (curserver)
- return curserver;
+ node = ebis_lookup(&bk->conf.used_server_name, name);
+ if (node)
+ curserver = container_of(node, struct server, conf.name);
}
- return NULL;
+ return curserver;
+}
+
+/*
+ * This function finds a server with matching "<name> x <rid>" within
+ * selected backend <bk>.
+ * Using the combination of name + revision id ensures that the function
+ * will either return the server we're expecting or NULL if it has been removed
+ * from the proxy. For this we assume that <name> is unique within the list,
+ * which is the case in most setups, but in rare cases the user may have
+ * enforced duplicate server names in the initial config (ie: if he intends to
+ * use numerical IDs for identification instead). In this particular case, the
+ * function will not work as expected so server_find_by_id_unique() should be
+ * used to match a unique server instead.
+ *
+ * Just like server_find_by_id_unique(), if a server is deleted and a new server
+ * reuses the same name, the rid check will prevent the function from returning
+ * a different server from the one we were expecting to match against at a given
+ * time.
+ */
+struct server *server_find_by_name_unique(struct proxy *bk, const char *name, uint32_t rid)
+{
+ struct server *curserver;
+
+ curserver = server_find_by_name(bk, name);
+ if (!curserver || curserver->rid != rid)
+ return NULL;
+ return curserver;
}
struct server *server_find_best_match(struct proxy *bk, char *name, int id, int *diff)
@@ -3705,101 +3870,332 @@ struct server *server_find_best_match(struct proxy *bk, char *name, int id, int
return NULL;
}
-/*
- * update a server's current IP address.
- * ip is a pointer to the new IP address, whose address family is ip_sin_family.
- * ip is in network format.
- * updater is a string which contains an information about the requester of the update.
- * updater is used if not NULL.
+/* This functions retrieves server's addr and port to fill
+ * <inetaddr> struct passed as argument.
*
- * A log line and a stderr warning message is generated based on server's backend options.
- *
- * Must be called with the server lock held.
+ * This may only be used under inet context.
*/
-int srv_update_addr(struct server *s, void *ip, int ip_sin_family, const char *updater)
+void server_get_inetaddr(struct server *s, struct server_inetaddr *inetaddr)
{
- union {
- struct event_hdl_cb_data_server_inetaddr addr;
- struct event_hdl_cb_data_server common;
- } cb_data;
- struct sockaddr_storage new_addr = { }; // shut up gcc warning
+ struct sockaddr_storage *addr = &s->addr;
+ unsigned int port = s->svc_port;
+ uint8_t mapports = !!(s->flags & SRV_F_MAPPORTS);
- /* save the new IP family & address if necessary */
- switch (ip_sin_family) {
- case AF_INET:
- if (s->addr.ss_family == ip_sin_family &&
- !memcmp(ip, &((struct sockaddr_in *)&s->addr)->sin_addr.s_addr, 4))
- return 0;
- break;
- case AF_INET6:
- if (s->addr.ss_family == ip_sin_family &&
- !memcmp(ip, &((struct sockaddr_in6 *)&s->addr)->sin6_addr.s6_addr, 16))
- return 0;
- break;
- };
+ /* only INET families are supported */
+ BUG_ON((addr->ss_family != AF_UNSPEC &&
+ addr->ss_family != AF_INET && addr->ss_family != AF_INET6));
- /* generates a log line and a warning on stderr */
- if (1) {
- /* book enough space for both IPv4 and IPv6 */
- char oldip[INET6_ADDRSTRLEN];
- char newip[INET6_ADDRSTRLEN];
+ inetaddr->family = addr->ss_family;
+ memset(&inetaddr->addr, 0, sizeof(inetaddr->addr));
- memset(oldip, '\0', INET6_ADDRSTRLEN);
- memset(newip, '\0', INET6_ADDRSTRLEN);
+ if (addr->ss_family == AF_INET)
+ inetaddr->addr.v4 =
+ ((struct sockaddr_in *)addr)->sin_addr;
+ else if (addr->ss_family == AF_INET6)
+ inetaddr->addr.v6 =
+ ((struct sockaddr_in6 *)addr)->sin6_addr;
- /* copy old IP address in a string */
- switch (s->addr.ss_family) {
- case AF_INET:
- inet_ntop(s->addr.ss_family, &((struct sockaddr_in *)&s->addr)->sin_addr, oldip, INET_ADDRSTRLEN);
+ inetaddr->port.svc = port;
+ inetaddr->port.map = mapports;
+}
+
+/* get human readable name for server_inetaddr_updater .by struct member
+ */
+const char *server_inetaddr_updater_by_to_str(enum server_inetaddr_updater_by by)
+{
+ switch (by) {
+ case SERVER_INETADDR_UPDATER_BY_CLI:
+ return "stats socket command";
+ case SERVER_INETADDR_UPDATER_BY_LUA:
+ return "Lua script";
+ case SERVER_INETADDR_UPDATER_BY_DNS_AR:
+ return "DNS additional record";
+ case SERVER_INETADDR_UPDATER_BY_DNS_CACHE:
+ return "DNS cache";
+ case SERVER_INETADDR_UPDATER_BY_DNS_RESOLVER:
+ return "DNS resolver";
+ default:
+ /* unknown, don't mention updater */
break;
- case AF_INET6:
- inet_ntop(s->addr.ss_family, &((struct sockaddr_in6 *)&s->addr)->sin6_addr, oldip, INET6_ADDRSTRLEN);
+ }
+ return NULL;
+}
+
+/* append inetaddr updater info to chunk <out>
+ */
+static void _srv_append_inetaddr_updater_info(struct buffer *out,
+ struct server *s,
+ struct server_inetaddr_updater updater)
+{
+ switch (updater.by) {
+ case SERVER_INETADDR_UPDATER_BY_DNS_RESOLVER:
+ /* we need to report the resolver/nameserver id which is
+ * responsible for the update
+ */
+ {
+ struct resolvers *r = s->resolvers;
+ struct dns_nameserver *ns;
+
+ /* we already know that the update comes from the
+ * resolver section linked to the server, but we
+ * need to find out which nameserver handled the dns
+ * query
+ */
+ BUG_ON(!r);
+ ns = find_nameserver_by_resolvers_and_id(r, updater.u.dns_resolver.ns_id);
+ BUG_ON(!ns);
+ chunk_appendf(out, " by '%s/%s'", r->id, ns->id);
+ }
break;
default:
- strlcpy2(oldip, "(none)", sizeof(oldip));
+ {
+ const char *by_name;
+
+ by_name = server_inetaddr_updater_by_to_str(updater.by);
+ if (by_name)
+ chunk_appendf(out, " by '%s'", by_name);
+ }
break;
- };
+ }
+}
- /* copy new IP address in a string */
- switch (ip_sin_family) {
+/* server_set_inetaddr() helper */
+static void _addr_to_str(int family, const void *addr, char *addr_str, size_t len)
+{
+ memset(addr_str, 0, len);
+ switch (family) {
case AF_INET:
- inet_ntop(ip_sin_family, ip, newip, INET_ADDRSTRLEN);
- break;
case AF_INET6:
- inet_ntop(ip_sin_family, ip, newip, INET6_ADDRSTRLEN);
+ inet_ntop(family, addr, addr_str, len);
break;
- };
+ default:
+ strlcpy2(addr_str, "(none)", len);
+ break;
+ }
+}
+/* server_set_inetaddr() helper */
+static int _inetaddr_addr_cmp(const struct server_inetaddr *inetaddr, const struct sockaddr_storage *addr)
+{
+ struct in_addr *v4;
+ struct in6_addr *v6;
+
+ if (inetaddr->family != addr->ss_family)
+ return 1;
+
+ if (inetaddr->family == AF_INET) {
+ v4 = &((struct sockaddr_in *)addr)->sin_addr;
+ if (memcmp(&inetaddr->addr.v4, v4, sizeof(struct in_addr)))
+ return 1;
+ }
+ else if (inetaddr->family == AF_INET6) {
+ v6 = &((struct sockaddr_in6 *)addr)->sin6_addr;
+ if (memcmp(&inetaddr->addr.v6, v6, sizeof(struct in6_addr)))
+ return 1;
+ }
+
+ return 0; // both inetaddr storage are equivalent
+}
+
+/* This function sets a server's addr and port in inet context based on new
+ * inetaddr input
+ *
+ * The function first does the following, in that order:
+ * - checks if an update is required (new IP or port is different than current
+ * one)
+ * - check the update is allowed:
+ * - allow all changes if no CHECKS are configured
+ * - if CHECK is configured:
+ * - if switch to port map (SRV_F_MAPPORTS), ensure health check have their
+ * own ports
+ * - applies required changes to both ADDR and PORT if both 'required' and
+ * 'allowed' conditions are met.
+ *
+ * Caller can pass <msg> buffer so that it gets some information about the
+ * operation. It may as well provide <updater> so that messages mention that
+ * the update was performed on the behalf of it.
+ *
+ * <inetaddr> family may be set to UNSPEC to reset server's addr
+ *
+ * Caller must set <inetaddr>->port.map to 1 if <inetaddr>->port.svc must be
+ * handled as an offset
+ *
+ * The function returns 1 if an update was performed and 0 if nothing was
+ * changed.
+ */
+int server_set_inetaddr(struct server *s,
+ const struct server_inetaddr *inetaddr,
+ struct server_inetaddr_updater updater, struct buffer *msg)
+{
+ union {
+ struct event_hdl_cb_data_server_inetaddr addr;
+ struct event_hdl_cb_data_server common;
+ } cb_data;
+ char addr_str[INET6_ADDRSTRLEN];
+ uint16_t current_port;
+ uint8_t ip_change = 0;
+ uint8_t port_change = 0;
+ int ret = 0;
+
+ /* only INET families are supported */
+ BUG_ON((inetaddr->family != AF_UNSPEC &&
+ inetaddr->family != AF_INET && inetaddr->family != AF_INET6) ||
+ (s->addr.ss_family != AF_UNSPEC &&
+ s->addr.ss_family != AF_INET && s->addr.ss_family != AF_INET6));
+
+ /* ignore if no change */
+ if (!_inetaddr_addr_cmp(inetaddr, &s->addr))
+ goto port;
+
+ ip_change = 1;
+
+ /* update report for caller */
+ if (msg) {
+ void *from_ptr = NULL;
+
+ if (s->addr.ss_family == AF_INET)
+ from_ptr = &((struct sockaddr_in *)&s->addr)->sin_addr;
+ else if (s->addr.ss_family == AF_INET6)
+ from_ptr = &((struct sockaddr_in6 *)&s->addr)->sin6_addr;
- /* save log line into a buffer */
- chunk_printf(&trash, "%s/%s changed its IP from %s to %s by %s",
- s->proxy->id, s->id, oldip, newip, updater);
+ _addr_to_str(s->addr.ss_family, from_ptr, addr_str, sizeof(addr_str));
+ chunk_printf(msg, "IP changed from '%s'", addr_str);
+ _addr_to_str(inetaddr->family, &inetaddr->addr, addr_str, sizeof(addr_str));
+ chunk_appendf(msg, " to '%s'", addr_str);
+ }
+
+ if (inetaddr->family == AF_UNSPEC)
+ goto out; // ignore port information when unsetting addr
+
+ port:
+ /* collection data currently setup */
+ current_port = s->svc_port;
+
+ /* check if caller triggers a port mapped or offset */
+ if (inetaddr->port.map) {
+ /* check if server currently uses port map */
+ if (!(s->flags & SRV_F_MAPPORTS)) {
+ /* we're switching from a fixed port to a SRV_F_MAPPORTS
+ * (mapped) port, prevent PORT change if check is enabled
+ * and it doesn't have it's dedicated port while switching
+ * to port mapping
+ */
+ if ((s->check.state & CHK_ST_ENABLED) && !s->check.port) {
+ if (msg) {
+ if (ip_change)
+ chunk_appendf(msg, ", ");
+ chunk_appendf(msg, "can't change <port> to port map because it is incompatible with current health check port configuration (use 'port' statement from the 'server' directive).");
+ }
+ goto out;
+ }
+ /* switch from fixed port to port map mandatorily triggers
+ * a port change
+ */
+ port_change = 1;
+ }
+ /* else we're already using port maps */
+ else {
+ port_change = current_port != inetaddr->port.svc;
+ }
+ }
+ /* fixed port */
+ else {
+ if ((s->flags & SRV_F_MAPPORTS))
+ port_change = 1; // changing from mapped to fixed
+ else
+ port_change = current_port != inetaddr->port.svc;
+ }
+
+ /* update response message about PORT change */
+ if (port_change && msg) {
+ if (ip_change)
+ chunk_appendf(msg, ", ");
+
+ chunk_appendf(msg, "port changed from '");
+ if (s->flags & SRV_F_MAPPORTS)
+ chunk_appendf(msg, "+");
+
+ chunk_appendf(msg, "%d' to '", s->svc_port);
+ if (inetaddr->port.map)
+ chunk_appendf(msg, "+");
+ chunk_appendf(msg, "%d'", inetaddr->port.svc);
+ }
+
+ out:
+ if (ip_change || port_change) {
+ _srv_event_hdl_prepare(&cb_data.common, s, 0);
+ _srv_event_hdl_prepare_inetaddr(&cb_data.addr, s,
+ inetaddr,
+ updater);
+
+ /* server_atomic_sync_task will apply the changes for us */
+ _srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_INETADDR, cb_data, s);
+
+ ret = 1;
+ }
+
+ if (ret && msg && updater.by != SERVER_INETADDR_UPDATER_BY_NONE)
+ _srv_append_inetaddr_updater_info(msg, s, updater);
+ return ret;
+}
+
+/* Sets new server's addr and/or svc_port, then send a log and report a
+ * warning on stderr if something has changed.
+ *
+ * Returns 1 if something has changed, 0 otherwise.
+ * see server_set_inetaddr() for more information.
+ */
+int server_set_inetaddr_warn(struct server *s,
+ const struct server_inetaddr *inetaddr,
+ struct server_inetaddr_updater updater)
+{
+ struct buffer *msg = get_trash_chunk();
+ int ret;
+
+ chunk_reset(msg);
+
+ ret = server_set_inetaddr(s, inetaddr, updater, msg);
+ if (msg->data) {
/* write the buffer on stderr */
- ha_warning("%s.\n", trash.area);
+ ha_warning("%s/%s: %s.\n", s->proxy->id, s->id, msg->area);
/* send a log */
- send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
+ send_log(s->proxy, LOG_NOTICE, "%s/%s: %s.\n", s->proxy->id, s->id, msg->area);
}
+ return ret;
+}
+
+/*
+ * update a server's current IP address.
+ * ip is a pointer to the new IP address, whose address family is ip_sin_family.
+ * ip is in network format.
+ * updater is a string which contains an information about the requester of the update.
+ * updater is used if not NULL.
+ *
+ * A log line and a stderr warning message is generated based on server's backend options.
+ *
+ * Must be called with the server lock held.
+ */
+int srv_update_addr(struct server *s, void *ip, int ip_sin_family, struct server_inetaddr_updater updater)
+{
+ struct server_inetaddr inetaddr;
+
+ server_get_inetaddr(s, &inetaddr);
+ BUG_ON(ip_sin_family != AF_INET && ip_sin_family != AF_INET6);
/* save the new IP family */
- new_addr.ss_family = ip_sin_family;
+ inetaddr.family = ip_sin_family;
/* save the new IP address */
switch (ip_sin_family) {
case AF_INET:
- memcpy(&((struct sockaddr_in *)&new_addr)->sin_addr.s_addr, ip, 4);
+ memcpy(&inetaddr.addr.v4, ip, 4);
break;
case AF_INET6:
- memcpy(((struct sockaddr_in6 *)&new_addr)->sin6_addr.s6_addr, ip, 16);
+ memcpy(&inetaddr.addr.v6, ip, 16);
break;
};
- _srv_event_hdl_prepare(&cb_data.common, s, 0);
- _srv_event_hdl_prepare_inetaddr(&cb_data.addr, s,
- &new_addr, s->svc_port, !!(s->flags & SRV_F_MAPPORTS),
- 0);
-
- /* server_atomic_sync_task will apply the changes for us */
- _srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_INETADDR, cb_data, s);
+ server_set_inetaddr_warn(s, &inetaddr, updater);
return 0;
}
@@ -3906,40 +4302,37 @@ out:
/*
* This function update a server's addr and port only for AF_INET and AF_INET6 families.
*
- * Caller can pass its name through <updater> to get it integrated in the response message
- * returned by the function.
+ * Caller can pass its info through <updater> to get it integrated in the response
+ * message returned by the function.
*
* The function first does the following, in that order:
+ * - checks that don't switch from/to a family other than AF_INET and AF_INET6
* - validates the new addr and/or port
- * - checks if an update is required (new IP or port is different than current ones)
- * - checks the update is allowed:
- * - don't switch from/to a family other than AF_INET4 and AF_INET6
- * - allow all changes if no CHECKS are configured
- * - if CHECK is configured:
- * - if switch to port map (SRV_F_MAPPORTS), ensure health check have their own ports
- * - applies required changes to both ADDR and PORT if both 'required' and 'allowed'
- * conditions are met
+ * - calls server_set_inetaddr() to check and apply the change
*
* Must be called with the server lock held.
*/
-const char *srv_update_addr_port(struct server *s, const char *addr, const char *port, char *updater)
+const char *srv_update_addr_port(struct server *s, const char *addr, const char *port,
+ struct server_inetaddr_updater updater)
{
- union {
- struct event_hdl_cb_data_server_inetaddr addr;
- struct event_hdl_cb_data_server common;
- } cb_data;
struct sockaddr_storage sa;
- int ret;
- char current_addr[INET6_ADDRSTRLEN];
- uint16_t current_port, new_port = 0;
+ struct server_inetaddr inetaddr;
struct buffer *msg;
- int ip_change = 0;
- int port_change = 0;
- uint8_t mapports = !!(s->flags & SRV_F_MAPPORTS);
+ int ret;
msg = get_trash_chunk();
chunk_reset(msg);
+ /* even a simple port change is not supported outside of inet context, because
+ * s->svc_port is only relevant under inet context
+ */
+ if ((s->addr.ss_family != AF_INET) && (s->addr.ss_family != AF_INET6)) {
+ chunk_printf(msg, "Update for the current server address family is only supported through configuration file.");
+ goto out;
+ }
+
+ server_get_inetaddr(s, &inetaddr);
+
if (addr) {
memset(&sa, 0, sizeof(struct sockaddr_storage));
if (str2ip2(addr, &sa, 0) == NULL) {
@@ -3953,40 +4346,24 @@ const char *srv_update_addr_port(struct server *s, const char *addr, const char
goto out;
}
- /* collecting data currently setup */
- memset(current_addr, '\0', sizeof(current_addr));
- ret = addr_to_str(&s->addr, current_addr, sizeof(current_addr));
- /* changes are allowed on AF_INET* families only */
- if ((ret != AF_INET) && (ret != AF_INET6)) {
- chunk_printf(msg, "Update for the current server address family is only supported through configuration file");
- goto out;
- }
-
- /* applying ADDR changes if required and allowed
- * ipcmp returns 0 when both ADDR are the same
- */
- if (ipcmp(&s->addr, &sa, 0) == 0) {
- chunk_appendf(msg, "no need to change the addr");
- goto port;
+ inetaddr.family = sa.ss_family;
+ switch (inetaddr.family) {
+ case AF_INET:
+ inetaddr.addr.v4 = ((struct sockaddr_in *)&sa)->sin_addr;
+ break;
+ case AF_INET6:
+ inetaddr.addr.v6 = ((struct sockaddr_in6 *)&sa)->sin6_addr;
+ break;
}
- ip_change = 1;
-
- /* update report for caller */
- chunk_printf(msg, "IP changed from '%s' to '%s'", current_addr, addr);
}
- port:
if (port) {
+ uint16_t new_port;
char sign = '\0';
char *endptr;
- if (addr)
- chunk_appendf(msg, ", ");
-
- /* collecting data currently setup */
- current_port = s->svc_port;
-
sign = *port;
+
errno = 0;
new_port = strtol(port, &endptr, 10);
if ((errno != 0) || (port == endptr)) {
@@ -3995,98 +4372,46 @@ const char *srv_update_addr_port(struct server *s, const char *addr, const char
}
/* check if caller triggers a port mapped or offset */
- if (sign == '-' || (sign == '+')) {
- /* check if server currently uses port map */
- if (!(s->flags & SRV_F_MAPPORTS)) {
- /* check is configured
- * we're switching from a fixed port to a SRV_F_MAPPORTS (mapped) port
- * prevent PORT change if check doesn't have it's dedicated port while switching
- * to port mapping */
- if (!s->check.port) {
- chunk_appendf(msg, "can't change <port> to port map because it is incompatible with current health check port configuration (use 'port' statement from the 'server' directive.");
- goto out;
- }
- /* switch from fixed port to port map mandatorily triggers
- * a port change */
- port_change = 1;
- }
- /* we're already using port maps */
- else {
- port_change = current_port != new_port;
- }
- }
- /* fixed port */
- else {
- port_change = current_port != new_port;
- }
-
- /* applying PORT changes if required and update response message */
- if (port_change) {
- uint16_t new_port_print = new_port;
-
- /* prepare message */
- chunk_appendf(msg, "port changed from '");
- if (s->flags & SRV_F_MAPPORTS)
- chunk_appendf(msg, "+");
- chunk_appendf(msg, "%d' to '", current_port);
-
- if (sign == '-') {
- mapports = 1;
- chunk_appendf(msg, "%c", sign);
- /* just use for result output */
- new_port_print = -new_port_print;
- }
- else if (sign == '+') {
- mapports = 1;
- chunk_appendf(msg, "%c", sign);
- }
- else {
- mapports = 0;
- }
-
- chunk_appendf(msg, "%d'", new_port_print);
- }
- else {
- chunk_appendf(msg, "no need to change the port");
- }
+ if (sign == '-' || sign == '+')
+ inetaddr.port.map = 1;
+ else
+ inetaddr.port.map = 0;
+
+ inetaddr.port.svc = new_port;
+
+ /* note: negative offset was converted to positive offset
+ * (new_port is unsigned) to prevent later conversions errors
+ * since svc_port is handled as an unsigned int all along the
+ * chain. Unfortunately this is a one-way operation so the user
+ * could be surprised to see a negative offset reported using
+ * its equivalent positive offset in the generated message
+ * (-X = +(65535 - (X-1))), but thanks to proper wraparound it
+ * will be interpreted as a negative offset during port
+ * remapping so it will work as expected.
+ */
}
-out:
- if (ip_change || port_change) {
- _srv_event_hdl_prepare(&cb_data.common, s, 0);
- _srv_event_hdl_prepare_inetaddr(&cb_data.addr, s,
- ((ip_change) ? &sa : &s->addr),
- ((port_change) ? new_port : s->svc_port), mapports,
- 1);
+ ret = server_set_inetaddr(s, &inetaddr, updater, msg);
+ if (!ret)
+ chunk_printf(msg, "nothing changed");
- /* server_atomic_sync_task will apply the changes for us */
- _srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_INETADDR, cb_data, s);
- }
- if (updater)
- chunk_appendf(msg, " by '%s'", updater);
- chunk_appendf(msg, "\n");
+ out:
return msg->area;
}
/*
- * update server status based on result of SRV resolution
+ * put the server in maintenance because of failing SRV resolution
* returns:
- * 0 if server status is updated
+ * 0 if server was put under maintenance
* 1 if server status has not changed
*
* Must be called with the server lock held.
*/
-int srvrq_update_srv_status(struct server *s, int has_no_ip)
+int srvrq_set_srv_down(struct server *s)
{
if (!s->srvrq)
return 1;
- /* since this server has an IP, it can go back in production */
- if (has_no_ip == 0) {
- srv_clr_admin_flag(s, SRV_ADMF_RMAINT);
- return 1;
- }
-
if (s->next_admin & SRV_ADMF_RMAINT)
return 1;
@@ -4095,59 +4420,46 @@ int srvrq_update_srv_status(struct server *s, int has_no_ip)
}
/*
- * update server status based on result of name resolution
+ * put server under maintenance as a result of name resolution
* returns:
- * 0 if server status is updated
+ * 0 if server was put under maintenance
* 1 if server status has not changed
*
* Must be called with the server lock held.
*/
-int snr_update_srv_status(struct server *s, int has_no_ip)
+int snr_set_srv_down(struct server *s)
{
struct resolvers *resolvers = s->resolvers;
struct resolv_resolution *resolution = (s->resolv_requester ? s->resolv_requester->resolution : NULL);
int exp;
+ /* server already under maintenance */
+ if (s->next_admin & SRV_ADMF_RMAINT)
+ goto out;
+
/* If resolution is NULL we're dealing with SRV records Additional records */
if (resolution == NULL)
- return srvrq_update_srv_status(s, has_no_ip);
+ return srvrq_set_srv_down(s);
switch (resolution->status) {
case RSLV_STATUS_NONE:
/* status when HAProxy has just (re)started.
* Nothing to do, since the task is already automatically started */
- break;
+ goto out;
case RSLV_STATUS_VALID:
/*
- * resume health checks
- * server will be turned back on if health check is safe
+ * valid resolution but no usable server address
*/
- if (has_no_ip) {
- if (s->next_admin & SRV_ADMF_RMAINT)
- return 1;
- srv_set_admin_flag(s, SRV_ADMF_RMAINT, SRV_ADM_STCHGC_DNS_NOIP);
- return 0;
- }
-
- if (!(s->next_admin & SRV_ADMF_RMAINT))
- return 1;
- srv_clr_admin_flag(s, SRV_ADMF_RMAINT);
- chunk_printf(&trash, "Server %s/%s administratively READY thanks to valid DNS answer",
- s->proxy->id, s->id);
-
- ha_warning("%s.\n", trash.area);
- send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
+ srv_set_admin_flag(s, SRV_ADMF_RMAINT, SRV_ADM_STCHGC_DNS_NOIP);
return 0;
case RSLV_STATUS_NX:
/* stop server if resolution is NX for a long enough period */
exp = tick_add(resolution->last_valid, resolvers->hold.nx);
if (!tick_is_expired(exp, now_ms))
- break;
+ goto out; // not yet expired
- if (s->next_admin & SRV_ADMF_RMAINT)
- return 1;
srv_set_admin_flag(s, SRV_ADMF_RMAINT, SRV_ADM_STCHGC_DNS_NX);
return 0;
@@ -4155,10 +4467,8 @@ int snr_update_srv_status(struct server *s, int has_no_ip)
/* stop server if resolution is TIMEOUT for a long enough period */
exp = tick_add(resolution->last_valid, resolvers->hold.timeout);
if (!tick_is_expired(exp, now_ms))
- break;
+ goto out; // not yet expired
- if (s->next_admin & SRV_ADMF_RMAINT)
- return 1;
srv_set_admin_flag(s, SRV_ADMF_RMAINT, SRV_ADM_STCHGC_DNS_TIMEOUT);
return 0;
@@ -4166,10 +4476,8 @@ int snr_update_srv_status(struct server *s, int has_no_ip)
/* stop server if resolution is REFUSED for a long enough period */
exp = tick_add(resolution->last_valid, resolvers->hold.refused);
if (!tick_is_expired(exp, now_ms))
- break;
+ goto out; // not yet expired
- if (s->next_admin & SRV_ADMF_RMAINT)
- return 1;
srv_set_admin_flag(s, SRV_ADMF_RMAINT, SRV_ADM_STCHGC_DNS_REFUSED);
return 0;
@@ -4177,14 +4485,13 @@ int snr_update_srv_status(struct server *s, int has_no_ip)
/* stop server if resolution failed for a long enough period */
exp = tick_add(resolution->last_valid, resolvers->hold.other);
if (!tick_is_expired(exp, now_ms))
- break;
+ goto out; // not yet expired
- if (s->next_admin & SRV_ADMF_RMAINT)
- return 1;
srv_set_admin_flag(s, SRV_ADMF_RMAINT, SRV_ADM_STCHGC_DNS_UNSPEC);
return 0;
}
+ out:
return 1;
}
@@ -4210,7 +4517,6 @@ int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *c
void *serverip, *firstip;
short server_sin_family, firstip_sin_family;
int ret;
- struct buffer *chk = get_trash_chunk();
int has_no_ip = 0;
s = objt_server(requester->owner);
@@ -4269,12 +4575,6 @@ int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *c
has_no_ip = 1;
goto update_status;
- case RSLV_UPD_NAME_ERROR:
- /* update resolution status to OTHER error type */
- resolution->status = RSLV_STATUS_OTHER;
- has_no_ip = 1;
- goto update_status;
-
default:
has_no_ip = 1;
goto invalid;
@@ -4285,15 +4585,21 @@ int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *c
if (counters) {
counters->app.resolver.update++;
/* save the first ip we found */
- chunk_printf(chk, "%s/%s", counters->pid, counters->id);
+ srv_update_addr(s, firstip, firstip_sin_family,
+ SERVER_INETADDR_UPDATER_DNS_RESOLVER(counters->ns_puid));
}
else
- chunk_printf(chk, "DNS cache");
- srv_update_addr(s, firstip, firstip_sin_family, (char *) chk->area);
+ srv_update_addr(s, firstip, firstip_sin_family, SERVER_INETADDR_UPDATER_DNS_CACHE);
update_status:
- if (!snr_update_srv_status(s, has_no_ip) && has_no_ip)
- memset(&s->addr, 0, sizeof(s->addr));
+ if (has_no_ip && !snr_set_srv_down(s)) {
+ struct server_inetaddr srv_addr;
+
+ /* unset server's addr, keep port */
+ server_get_inetaddr(s, &srv_addr);
+ memset(&srv_addr.addr, 0, sizeof(srv_addr.addr));
+ server_set_inetaddr(s, &srv_addr, SERVER_INETADDR_UPDATER_NONE, NULL);
+ }
return 1;
invalid:
@@ -4301,8 +4607,14 @@ int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *c
counters->app.resolver.invalid++;
goto update_status;
}
- if (!snr_update_srv_status(s, has_no_ip) && has_no_ip)
- memset(&s->addr, 0, sizeof(s->addr));
+ if (has_no_ip && !snr_set_srv_down(s)) {
+ struct server_inetaddr srv_addr;
+
+ /* unset server's addr, keep port */
+ server_get_inetaddr(s, &srv_addr);
+ memset(&srv_addr.addr, 0, sizeof(srv_addr.addr));
+ server_set_inetaddr(s, &srv_addr, SERVER_INETADDR_UPDATER_NONE, NULL);
+ }
return 0;
}
@@ -4382,8 +4694,13 @@ int snr_resolution_error_cb(struct resolv_requester *requester, int error_code)
return 0;
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
- if (!snr_update_srv_status(s, 1)) {
- memset(&s->addr, 0, sizeof(s->addr));
+ if (!snr_set_srv_down(s)) {
+ struct server_inetaddr srv_addr;
+
+ /* unset server's addr, keep port */
+ server_get_inetaddr(s, &srv_addr);
+ memset(&srv_addr.addr, 0, sizeof(srv_addr.addr));
+ server_set_inetaddr(s, &srv_addr, SERVER_INETADDR_UPDATER_NONE, NULL);
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
resolv_detach_from_resolution_answer_items(requester->resolution, requester);
return 0;
@@ -4739,16 +5056,16 @@ struct server *cli_find_server(struct appctx *appctx, char *arg)
be_name = istsplit(&sv_name, '/');
if (!istlen(sv_name)) {
- cli_err(appctx, "Require 'backend/server'.");
+ cli_err(appctx, "Require 'backend/server'.\n");
return NULL;
}
if (!(px = proxy_be_by_name(ist0(be_name)))) {
- cli_err(appctx, "No such backend.");
+ cli_err(appctx, "No such backend.\n");
return NULL;
}
if (!(sv = server_find_by_name(px, ist0(sv_name)))) {
- cli_err(appctx, "No such server.");
+ cli_err(appctx, "No such server.\n");
return NULL;
}
@@ -4915,10 +5232,9 @@ static int cli_parse_set_server(char **args, char *payload, struct appctx *appct
port = args[6];
}
HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
- warning = srv_update_addr_port(sv, addr, port, "stats socket command");
+ warning = srv_update_addr_port(sv, addr, port, SERVER_INETADDR_UPDATER_CLI);
if (warning)
cli_msg(appctx, LOG_WARNING, warning);
- srv_clr_admin_flag(sv, SRV_ADMF_RMAINT);
HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
}
else if (strcmp(args[3], "fqdn") == 0) {
@@ -4994,12 +5310,12 @@ static int cli_parse_get_weight(char **args, char *payload, struct appctx *appct
be_name = istsplit(&sv_name, '/');
if (!istlen(sv_name))
- return cli_err(appctx, "Require 'backend/server'.");
+ return cli_err(appctx, "Require 'backend/server'.\n");
if (!(be = proxy_be_by_name(ist0(be_name))))
- return cli_err(appctx, "No such backend.");
+ return cli_err(appctx, "No such backend.\n");
if (!(sv = server_find_by_name(be, ist0(sv_name))))
- return cli_err(appctx, "No such server.");
+ return cli_err(appctx, "No such server.\n");
/* return server's effective weight at the moment */
snprintf(trash.area, trash.size, "%d (initial %d)\n", sv->uweight,
@@ -5234,7 +5550,7 @@ static int srv_alloc_lb(struct server *sv, struct proxy *be)
/* updates the server's weight during a warmup stage. Once the final weight is
* reached, the task automatically stops. Note that any server status change
- * must have updated s->last_change accordingly.
+ * must have updated s->counters.last_change accordingly.
*/
static struct task *server_warmup(struct task *t, void *context, unsigned int state)
{
@@ -5290,7 +5606,7 @@ static int init_srv_slowstart(struct server *srv)
if (srv->next_state == SRV_ST_STARTING) {
task_schedule(srv->warmup,
tick_add(now_ms,
- MS_TO_TICKS(MAX(1000, (ns_to_sec(now_ns) - srv->last_change)) / 20)));
+ MS_TO_TICKS(MAX(1000, (ns_to_sec(now_ns) - srv->counters.last_change)) / 20)));
}
}
@@ -5352,19 +5668,19 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
}
if (!*sv_name)
- return cli_err(appctx, "Require 'backend/server'.");
+ return cli_err(appctx, "Require 'backend/server'.\n");
be = proxy_be_by_name(be_name);
if (!be)
- return cli_err(appctx, "No such backend.");
+ return cli_err(appctx, "No such backend.\n");
if (!(be->lbprm.algo & BE_LB_PROP_DYN)) {
- cli_err(appctx, "Backend must use a dynamic load balancing to support dynamic servers.");
+ cli_err(appctx, "Backend must use a dynamic load balancing to support dynamic servers.\n");
return 1;
}
if (be->mode == PR_MODE_SYSLOG) {
- cli_err(appctx," Dynamic servers cannot be used with log backends.");
+ cli_err(appctx," Dynamic servers cannot be used with log backends.\n");
return 1;
}
@@ -5554,11 +5870,11 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
*/
if (srv->check.state & CHK_ST_CONFIGURED) {
if (!start_check_task(&srv->check, 0, 1, 1))
- ha_alert("System might be unstable, consider to execute a reload");
+ ha_alert("System might be unstable, consider to execute a reload\n");
}
if (srv->agent.state & CHK_ST_CONFIGURED) {
if (!start_check_task(&srv->agent, 0, 1, 1))
- ha_alert("System might be unstable, consider to execute a reload");
+ ha_alert("System might be unstable, consider to execute a reload\n");
}
if (srv->cklen && be->mode != PR_MODE_HTTP)
@@ -5594,6 +5910,72 @@ out:
return 1;
}
+/* Check if the server <bename>/<svname> exists and is ready for being deleted.
+ * Both <bename> and <svname> must be valid strings. This must be called under
+ * thread isolation. If pb/ps are not null, upon success, the pointer to
+ * the backend and server respectively will be put there. If pm is not null,
+ * a pointer to an error/success message is returned there (possibly NULL if
+ * nothing to say). Returned values:
+ * >0 if OK
+ * 0 if not yet (should wait if it can)
+ * <0 if not possible
+ */
+int srv_check_for_deletion(const char *bename, const char *svname, struct proxy **pb, struct server **ps, const char **pm)
+{
+ struct server *srv = NULL;
+ struct proxy *be = NULL;
+ const char *msg = NULL;
+ int ret;
+
+ /* First, unrecoverable errors */
+ ret = -1;
+
+ if (!(be = proxy_be_by_name(bename))) {
+ msg = "No such backend.";
+ goto leave;
+ }
+
+ if (!(srv = server_find_by_name(be, svname))) {
+ msg = "No such server.";
+ goto leave;
+ }
+
+ if (srv->flags & SRV_F_NON_PURGEABLE) {
+ msg = "This server cannot be removed at runtime due to other configuration elements pointing to it.";
+ goto leave;
+ }
+
+ /* Only servers in maintenance can be deleted. This ensures that the
+ * server is not present anymore in the lb structures (through
+ * lbprm.set_server_status_down).
+ */
+ if (!(srv->cur_admin & SRV_ADMF_MAINT)) {
+ msg = "Only servers in maintenance mode can be deleted.";
+ goto leave;
+ }
+
+ /* Second, conditions that may change over time */
+ ret = 0;
+
+ /* Ensure that there is no active/pending connection on the server. */
+ if (srv->curr_used_conns ||
+ !eb_is_empty(&srv->queue.head) || srv_has_streams(srv)) {
+ msg = "Server still has connections attached to it, cannot remove it.";
+ goto leave;
+ }
+
+ /* OK, let's go */
+ ret = 1;
+leave:
+ if (pb)
+ *pb = be;
+ if (ps)
+ *ps = srv;
+ if (pm)
+ *pm = msg;
+ return ret;
+}
+
/* Parse a "del server" command
* Returns 0 if the server has been successfully initialized, 1 on failure.
*/
@@ -5603,6 +5985,10 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap
struct server *srv;
struct server *prev_del;
struct ist be_name, sv_name;
+ struct mt_list *elt1, elt2;
+ struct sess_priv_conns *sess_conns = NULL;
+ const char *msg;
+ int ret, i;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
@@ -5620,42 +6006,71 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap
sv_name = ist(args[1]);
be_name = istsplit(&sv_name, '/');
if (!istlen(sv_name)) {
- cli_err(appctx, "Require 'backend/server'.");
+ cli_err(appctx, "Require 'backend/server'.\n");
goto out;
}
- if (!(be = proxy_be_by_name(ist0(be_name)))) {
- cli_err(appctx, "No such backend.");
- goto out;
- }
- if (!(srv = server_find_by_name(be, ist0(sv_name)))) {
- cli_err(appctx, "No such server.");
+ ret = srv_check_for_deletion(ist0(be_name), ist0(sv_name), &be, &srv, &msg);
+ if (ret <= 0) {
+ /* failure (recoverable or not) */
+ cli_err(appctx, msg);
goto out;
}
- if (srv->flags & SRV_F_NON_PURGEABLE) {
- cli_err(appctx, "This server cannot be removed at runtime due to other configuration elements pointing to it.");
- goto out;
- }
+ /* Close idle connections attached to this server. */
+ for (i = tid;;) {
+ struct list *list = &srv->per_thr[i].idle_conn_list;
+ struct connection *conn;
+
+ while (!LIST_ISEMPTY(list)) {
+ conn = LIST_ELEM(list->n, struct connection *, idle_list);
+ if (i != tid) {
+ if (conn->mux && conn->mux->takeover)
+ conn->mux->takeover(conn, i, 1);
+ else if (conn->xprt && conn->xprt->takeover)
+ conn->xprt->takeover(conn, conn->ctx, i, 1);
+ }
+ conn_release(conn);
+ }
- /* Only servers in maintenance can be deleted. This ensures that the
- * server is not present anymore in the lb structures (through
- * lbprm.set_server_status_down).
- */
- if (!(srv->cur_admin & SRV_ADMF_MAINT)) {
- cli_err(appctx, "Only servers in maintenance mode can be deleted.");
- goto out;
+ /* Also remove all purgeable conns as some of them may still
+ * reference the currently deleted server.
+ */
+ while ((conn = MT_LIST_POP(&idle_conns[i].toremove_conns,
+ struct connection *, toremove_list))) {
+ conn_release(conn);
+ }
+
+ if ((i = ((i + 1 == global.nbthread) ? 0 : i + 1)) == tid)
+ break;
}
- /* Ensure that there is no active/idle/pending connection on the server.
- *
- * TODO idle connections should not prevent server deletion. A proper
- * cleanup function should be implemented to be used here.
- */
- if (srv->curr_used_conns || srv->curr_idle_conns ||
- !eb_is_empty(&srv->queue.head) || srv_has_streams(srv)) {
- cli_err(appctx, "Server still has connections attached to it, cannot remove it.");
- goto out;
+ /* All idle connections should be removed now. */
+ BUG_ON(srv->curr_idle_conns);
+
+ /* Close idle private connections attached to this server. */
+ mt_list_for_each_entry_safe(sess_conns, &srv->sess_conns, srv_el, elt1, elt2) {
+ struct connection *conn, *conn_back;
+ list_for_each_entry_safe(conn, conn_back, &sess_conns->conn_list, sess_el) {
+
+ /* Only idle connections should be present if srv_check_for_deletion() is true. */
+ BUG_ON(!(conn->flags & CO_FL_SESS_IDLE));
+
+ LIST_DEL_INIT(&conn->sess_el);
+ conn->owner = NULL;
+ conn->flags &= ~CO_FL_SESS_IDLE;
+ if (sess_conns->tid != tid) {
+ if (conn->mux && conn->mux->takeover)
+ conn->mux->takeover(conn, sess_conns->tid, 1);
+ else if (conn->xprt && conn->xprt->takeover)
+ conn->xprt->takeover(conn, conn->ctx, sess_conns->tid, 1);
+ }
+ conn_release(conn);
+ }
+
+ LIST_DELETE(&sess_conns->sess_el);
+ MT_LIST_DELETE_SAFE(elt1);
+ pool_free(pool_head_sess_priv_conns, sess_conns);
}
/* removing cannot fail anymore when we reach this:
@@ -5724,13 +6139,11 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap
ha_notice("Server deleted.\n");
srv_drop(srv);
- cli_msg(appctx, LOG_INFO, "Server deleted.");
-
+ cli_msg(appctx, LOG_INFO, "Server deleted.\n");
return 0;
out:
thread_release();
-
return 1;
}
@@ -6334,8 +6747,8 @@ static void srv_update_status(struct server *s, int type, int cause)
if (srv_prev_state != s->cur_state) {
if (srv_prev_state == SRV_ST_STOPPED) {
/* server was down and no longer is */
- if (s->last_change < ns_to_sec(now_ns)) // ignore negative times
- s->down_time += ns_to_sec(now_ns) - s->last_change;
+ if (s->counters.last_change < ns_to_sec(now_ns)) // ignore negative times
+ s->down_time += ns_to_sec(now_ns) - s->counters.last_change;
_srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_UP, cb_data.common, s);
}
else if (s->cur_state == SRV_ST_STOPPED) {
@@ -6343,7 +6756,7 @@ static void srv_update_status(struct server *s, int type, int cause)
s->counters.down_trans++;
_srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_DOWN, cb_data.common, s);
}
- s->last_change = ns_to_sec(now_ns);
+ s->counters.last_change = ns_to_sec(now_ns);
/* publish the state change */
_srv_event_hdl_prepare_state(&cb_data.state,
@@ -6358,9 +6771,9 @@ static void srv_update_status(struct server *s, int type, int cause)
/* backend was down and is back up again:
* no helper function, updating last_change and backend downtime stats
*/
- if (s->proxy->last_change < ns_to_sec(now_ns)) // ignore negative times
- s->proxy->down_time += ns_to_sec(now_ns) - s->proxy->last_change;
- s->proxy->last_change = ns_to_sec(now_ns);
+ if (s->proxy->be_counters.last_change < ns_to_sec(now_ns)) // ignore negative times
+ s->proxy->down_time += ns_to_sec(now_ns) - s->proxy->be_counters.last_change;
+ s->proxy->be_counters.last_change = ns_to_sec(now_ns);
}
}