#include <haproxy/counters.h>
#include <haproxy/dict-t.h>
#include <haproxy/errors.h>
+#include <haproxy/event_hdl.h>
#include <haproxy/global.h>
#include <haproxy/guid.h>
#include <haproxy/log.h>
#include <haproxy/time.h>
#include <haproxy/tools.h>
#include <haproxy/xxhash.h>
-#include <haproxy/event_hdl.h>
static void srv_update_status(struct server *s, int type, int cause);
static int srv_apply_lastaddr(struct server *srv, int *err_code);
task_destroy(srv->srvrq_check);
free(srv->id);
+
#ifdef USE_QUIC
if (srv->per_thr) {
for (i = 0; i < global.nbthread; i++)
return msg->area;
}
+/* Rename a server at runtime. This function handles all precondition checks,
+ * tree re-indexing under thread_isolate(), and event publication.
+ *
+ * The caller must NOT hold any lock — this function uses thread_isolate()
+ * internally for tree manipulation.
+ *
+ * Preconditions enforced:
+ * - server must be administratively in maintenance
+ * - new name must not conflict with an existing server in the backend
+ * - new name must be syntactically valid (invalid_char() rules, non-empty,
+ * fits in event data name field)
+ * - server must not have SRV_F_NAME_REFD set: this covers servers whose
+ * name is referenced statically in the running configuration
+ * ('use-server' rules, 'track' chains, sample-fetch ARGT_SRV
+ * arguments). Renaming such a server would leave the config text
+ * inconsistent with the running state. Note that sample fetches with
+ * ARGT_SRV arguments are resolved to pointers at config-check time and
+ * are flagged via SRV_F_NAME_REFD by the arg resolver.
+ *
+ * Returns NULL on success, or a pointer to a static/trash error message
+ * string on failure. On success, a ha_notice() is emitted and the
+ * EVENT_HDL_SUB_SERVER_NAME event is published.
+ */
+static const char *srv_update_server_name(struct server *srv, const char *new_name)
+{
+ struct proxy *be = srv->proxy;
+ char *old_name;
+ char *dup;
+ const char *err;
+
+ /* validate name syntax: must be non-empty, valid chars, not too long */
+ if (!*new_name)
+ return "Require a server name.\n";
+
+ err = invalid_char(new_name);
+ if (err)
+ return "Server name contains invalid characters.\n";
+
+ if (strlen(new_name) >= sizeof(((struct event_hdl_cb_data_server_name *)0)->safe.new_name))
+ return "Server name too long.\n";
+
+ /* server must be administratively down (in maintenance) */
+ if (!(srv->cur_admin & SRV_ADMF_MAINT))
+ return "Server must be in maintenance mode to be renamed (set server <b>/<s> state maint).\n";
+
+ /* reject if the server's name is statically referenced in the running
+ * config ('use-server' rules, 'track' chains, sample-fetch ARGT_SRV
+ * arguments): renaming would leave the running state inconsistent with
+ * the configuration text.
+ */
+ if (srv->flags & SRV_F_NAME_REFD)
+ return "Cannot rename: server's name is statically referenced (use-server, track, or sample argument).\n";
+
+ /* same name is a no-op success */
+ if (strcmp(srv->id, new_name) == 0)
+ return NULL;
+
+ /* allocate new name before taking isolation */
+ dup = strdup(new_name);
+ if (!dup)
+ return "Out of memory allocating new server name.\n";
+
+ /* tree manipulation requires thread isolation (same pattern as
+ * add/del server). This is rare enough that the cost is acceptable.
+ */
+ thread_isolate();
+
+ /* re-check for name conflict under isolation — another rename or
+ * add server could have raced before we isolated.
+ */
+ if (server_find_by_name(be, new_name)) {
+ thread_release();
+ free(dup);
+ return "A server with the same name already exists in this backend.\n";
+ }
+
+ /* --- point of no return --- */
+
+ old_name = srv->id;
+
+ /* re-index in the name tree */
+ cebis_item_delete(&be->conf.used_server_name, conf.name_node, id, srv);
+ srv->id = dup;
+ cebis_item_insert(&be->conf.used_server_name, conf.name_node, id, srv);
+
+ /* publish rename event with both old and new names */
+ {
+ struct event_hdl_cb_data_server_name cb_data;
+
+ _srv_event_hdl_prepare(&cb_data.server, srv, 1);
+ snprintf(cb_data.safe.old_name, sizeof(cb_data.safe.old_name), "%s", old_name);
+ snprintf(cb_data.safe.new_name, sizeof(cb_data.safe.new_name), "%s", new_name);
+ _srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_NAME, cb_data, srv);
+ }
+
+ /* Emit the rename notice while old_name is still valid. */
+ ha_notice("Server %s/%s renamed from '%s'.\n", be->id, srv->id, old_name);
+
+ /* The old name string is no longer reachable from any structure: the
+ * tree has been re-indexed and srv->id now points at the new copy.
+ * Sample consumers retrieve srv->id via smp_fetch_srv_name and act on
+ * it within the current task (ACL evaluation, log format expansion,
+ * etc.), without retaining the raw pointer across wake-ups, so it is
+ * safe to release the old name immediately under thread isolation.
+ */
+ free(old_name);
+
+ thread_release();
+
+ return NULL;
+}
+
/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
* and returns the pointer to the server. Otherwise, display adequate error messages
#else
cli_msg(appctx, LOG_NOTICE, "server ssl setting not supported.\n");
#endif
- } else {
+ }
+ else if (strcmp(args[3], "name") == 0) {
+ if (!*args[4]) {
+ cli_err(appctx, "set server <b>/<s> name requires a new name.\n");
+ goto out;
+ }
+ /* srv_update_server_name() handles its own locking via
+ * thread_isolate(), so do NOT hold the server lock here.
+ */
+ warning = srv_update_server_name(sv, args[4]);
+ if (warning)
+ cli_err(appctx, warning);
+ else
+ cli_msg(appctx, LOG_NOTICE, "Server name updated.\n");
+ }
+ else {
cli_err(appctx,
"usage: set server <backend>/<server> "
"addr | agent | agent-addr | agent-port | agent-send | "
- "check-addr | check-port | fqdn | health | ssl | "
+ "check-addr | check-port | fqdn | health | name | ssl | "
"state | weight\n");
}
out:
{ { "enable", "health", NULL }, "enable health : enable health checks", cli_parse_enable_health, NULL },
{ { "enable", "server", NULL }, "enable server (DEPRECATED) : enable a disabled server (use 'set server' instead)", cli_parse_enable_server, NULL },
{ { "set", "maxconn", "server", NULL }, "set maxconn server <bk>/<srv> : change a server's maxconn setting", cli_parse_set_maxconn_server, NULL },
- { { "set", "server", NULL }, "set server <bk>/<srv> [opts] : change a server's state, weight, address or ssl", cli_parse_set_server },
+ { { "set", "server", NULL }, "set server <bk>/<srv> [opts] : change a server's state, weight, address, name or ssl", cli_parse_set_server },
{ { "get", "weight", NULL }, "get weight <bk>/<srv> : report a server's current weight", cli_parse_get_weight },
{ { "set", "weight", NULL }, "set weight <bk>/<srv> (DEPRECATED) : change a server's weight (use 'set server' instead)", cli_parse_set_weight },
{ { "add", "server", NULL }, "add server <bk>/<srv> : create a new server", cli_parse_add_server, cli_io_handler_add_server },