#include <proto/dns.h>
#include <netinet/tcp.h>
+#include <ebsttree.h>
+
static void srv_update_status(struct server *s);
static void srv_update_state(struct server *srv, int version, char **params);
static int srv_apply_lastaddr(struct server *srv, int *err_code);
static int srv_set_fqdn(struct server *srv, const char *fqdn, int dns_locked);
+static void srv_state_parse_line(char *buf, const int version, char **params, char **srv_params);
+static int srv_state_get_version(FILE *f);
/* List head of all known server keywords */
static struct srv_kw_list srv_keywords = {
.values = EB_ROOT_UNIQUE,
};
+/* tree where global state_file is loaded */
+struct eb_root state_file = EB_ROOT;
+
int srv_downtime(const struct server *s)
{
if ((s->cur_state != SRV_ST_STOPPED) && s->last_change < now.tv_sec) // ignore negative time
}
}
+
+/*
+ * read next line from file <f> and return the server state version if one found.
+ * If no version is found, then 0 is returned
+ * Note that this should be the first read on <f>
+ */
+static int srv_state_get_version(FILE *f) {
+ char buf[2];
+ int ret;
+
+ /* first character of first line of the file must contain the version of the export */
+ if (fgets(buf, 2, f) == NULL) {
+ return 0;
+ }
+
+ ret = atoi(buf);
+ if ((ret < SRV_STATE_FILE_VERSION_MIN) ||
+ (ret > SRV_STATE_FILE_VERSION_MAX))
+ return 0;
+
+ return ret;
+}
+
+
+/*
+ * parses server state line stored in <buf> and supposedly in version <version>.
+ * Set <params> and <srv_params> accordingly.
+ * In case of error, params[0] is set to NULL.
+ */
+static void srv_state_parse_line(char *buf, const int version, char **params, char **srv_params)
+{
+ int buflen, arg, srv_arg;
+ char *cur, *end;
+
+ buflen = strlen(buf);
+ cur = buf;
+ end = cur + buflen;
+
+ /* we need at least one character */
+ if (buflen == 0) {
+ params[0] = NULL;
+ return;
+ }
+
+ /* ignore blank characters at the beginning of the line */
+ while (isspace(*cur))
+ ++cur;
+
+ /* Ignore empty or commented lines */
+ if (cur == end || *cur == '#') {
+ params[0] = NULL;
+ return;
+ }
+
+ /* truncated lines */
+ if (buf[buflen - 1] != '\n') {
+ //ha_warning("server-state file '%s': truncated line\n", filepath);
+ params[0] = NULL;
+ return;
+ }
+
+ /* Removes trailing '\n' */
+ buf[buflen - 1] = '\0';
+
+ /* we're now ready to move the line into *srv_params[] */
+ params[0] = cur;
+ arg = 1;
+ srv_arg = 0;
+ while (*cur && arg < SRV_STATE_FILE_MAX_FIELDS) {
+ if (isspace(*cur)) {
+ *cur = '\0';
+ ++cur;
+ while (isspace(*cur))
+ ++cur;
+ switch (version) {
+ case 1:
+ /*
+ * srv_addr: params[4] => srv_params[0]
+ * srv_op_state: params[5] => srv_params[1]
+ * srv_admin_state: params[6] => srv_params[2]
+ * srv_uweight: params[7] => srv_params[3]
+ * srv_iweight: params[8] => srv_params[4]
+ * srv_last_time_change: params[9] => srv_params[5]
+ * srv_check_status: params[10] => srv_params[6]
+ * srv_check_result: params[11] => srv_params[7]
+ * srv_check_health: params[12] => srv_params[8]
+ * srv_check_state: params[13] => srv_params[9]
+ * srv_agent_state: params[14] => srv_params[10]
+ * bk_f_forced_id: params[15] => srv_params[11]
+ * srv_f_forced_id: params[16] => srv_params[12]
+ * srv_fqdn: params[17] => srv_params[13]
+ * srv_port: params[18] => srv_params[14]
+ * srvrecord: params[19] => srv_params[15]
+ */
+ if (arg >= 4) {
+ srv_params[srv_arg] = cur;
+ ++srv_arg;
+ }
+ break;
+ }
+
+ params[arg] = cur;
+ ++arg;
+ }
+ else {
+ ++cur;
+ }
+ }
+
+ /* if line is incomplete line, then ignore it.
+ * otherwise, update useful flags */
+ switch (version) {
+ case 1:
+ if (arg < SRV_STATE_FILE_NB_FIELDS_VERSION_1) {
+ params[0] = NULL;
+ return;
+ }
+ break;
+ }
+
+ return;
+}
+
+
/* This function parses all the proxies and only take care of the backends (since we're looking for server)
* For each proxy, it does the following:
* - opens its server state file (either one or local one)
*/
void apply_server_state(void)
{
- char *cur, *end;
char mybuf[SRV_STATE_LINE_MAXLEN];
- int mybuflen;
char *params[SRV_STATE_FILE_MAX_FIELDS] = {0};
char *srv_params[SRV_STATE_FILE_MAX_FIELDS] = {0};
- int arg, srv_arg, version;
+ int version, global_file_version;
FILE *f;
char *filepath;
char globalfilepath[MAXPATHLEN + 1];
int len, fileopenerr, globalfilepathlen, localfilepathlen;
struct proxy *curproxy, *bk;
struct server *srv;
+ char *line;
+ char *bkname, *srvname;
+ struct state_line *st;
+ struct ebmb_node *node, *next_node;
+
+ global_file_version = 0;
globalfilepathlen = 0;
/* create the globalfilepath variable */
if (global.server_state_file) {
if (globalfilepathlen == 0)
globalfilepath[0] = '\0';
- /* read servers state from local file */
+ /* Load global server state in a tree */
+ if (globalfilepathlen > 0) {
+ errno = 0;
+ f = fopen(globalfilepath, "r");
+ if (errno)
+ ha_warning("Can't open global server state file '%s': %s\n", globalfilepath, strerror(errno));
+
+ global_file_version = srv_state_get_version(f);
+ if (global_file_version == 0)
+ goto out_load_server_state_in_tree;
+
+ while (fgets(mybuf, SRV_STATE_LINE_MAXLEN, f)) {
+ line = NULL;
+
+ line = strdup(mybuf);
+ if (line == NULL)
+ continue;
+
+ srv_state_parse_line(mybuf, global_file_version, params, srv_params);
+ if (params[0] == NULL)
+ continue;
+
+ /* bkname */
+ bkname = params[1];
+ /* srvname */
+ srvname = params[3];
+
+ /* key */
+ chunk_printf(&trash, "%s %s", bkname, srvname);
+
+ /* store line in tree */
+ st = calloc(1, sizeof(*st) + trash.size);
+ if (st == NULL) {
+ goto nextline;
+ }
+ memcpy(st->name_name.key, trash.area, trash.size);
+ ebst_insert(&state_file, &st->name_name);
+
+ /* save line */
+ st->line = line;
+
+ continue;
+
+ nextline:
+ /* free up memory in case of error during the processing of the line */
+ free(line);
+ }
+ }
+ out_load_server_state_in_tree:
+
+ /* parse all proxies and load states form tree (global file) or from local file */
for (curproxy = proxies_list; curproxy != NULL; curproxy = curproxy->next) {
/* servers are only in backends */
if (!(curproxy->cap & PR_CAP_BE))
continue;
}
- /* preload global state file */
- errno = 0;
- f = fopen(filepath, "r");
- if (errno && fileopenerr)
- ha_warning("Can't open server state file '%s': %s\n", filepath, strerror(errno));
- if (!f)
- continue;
+ /* when global file is used, we get data from the tree
+ * Note that in such case we don't check backend name neither uuid.
+ * Backend name can't be wrong since it's used as a key to retrieve the server state
+ * line from the tree.
+ */
+ if (curproxy->load_server_state_from_file == PR_SRV_STATE_FILE_GLOBAL) {
+ struct server *srv = curproxy->srv;
+ while (srv) {
+ struct ebmb_node *node;
+ struct state_line *st;
- mybuf[0] = '\0';
- mybuflen = 0;
- version = 0;
+ chunk_printf(&trash, "%s %s", curproxy->id, srv->id);
+ node = ebst_lookup(&state_file, trash.area);
+ if (!node)
+ goto next;
- /* first character of first line of the file must contain the version of the export */
- if (fgets(mybuf, SRV_STATE_LINE_MAXLEN, f) == NULL) {
- ha_warning("Can't read first line of the server state file '%s'\n", filepath);
- goto fileclose;
- }
+ st = container_of(node, struct state_line, name_name);
+ memcpy(mybuf, st->line, strlen(st->line));
+ mybuf[strlen(st->line)] = 0;
- cur = mybuf;
- version = atoi(cur);
- if ((version < SRV_STATE_FILE_VERSION_MIN) ||
- (version > SRV_STATE_FILE_VERSION_MAX))
- goto fileclose;
+ srv_state_parse_line(mybuf, global_file_version, params, srv_params);
+ if (params[0] == NULL)
+ goto next;
- while (fgets(mybuf, SRV_STATE_LINE_MAXLEN, f)) {
- int bk_f_forced_id = 0;
- int check_id = 0;
- int check_name = 0;
-
- mybuflen = strlen(mybuf);
- cur = mybuf;
- end = cur + mybuflen;
+ srv_update_state(srv, global_file_version, srv_params);
- bk = NULL;
- srv = NULL;
+ next:
+ srv = srv->next;
+ }
- /* we need at least one character */
- if (mybuflen == 0)
+ continue; /* next proxy in list */
+ }
+ else {
+ /* load 'local' state file */
+ errno = 0;
+ f = fopen(filepath, "r");
+ if (errno && fileopenerr)
+ ha_warning("Can't open server state file '%s': %s\n", filepath, strerror(errno));
+ if (!f)
continue;
- /* ignore blank characters at the beginning of the line */
- while (isspace(*cur))
- ++cur;
-
- /* Ignore empty or commented lines */
- if (cur == end || *cur == '#')
- continue;
+ mybuf[0] = '\0';
- /* truncated lines */
- if (mybuf[mybuflen - 1] != '\n') {
- ha_warning("server-state file '%s': truncated line\n", filepath);
- continue;
+ /* first character of first line of the file must contain the version of the export */
+ version = srv_state_get_version(f);
+ if (version == 0) {
+ ha_warning("Can't get version of the server state file '%s'\n", filepath);
+ goto fileclose;
}
- /* Removes trailing '\n' */
- mybuf[mybuflen - 1] = '\0';
-
- /* we're now ready to move the line into *srv_params[] */
- params[0] = cur;
- arg = 1;
- srv_arg = 0;
- while (*cur && arg < SRV_STATE_FILE_MAX_FIELDS) {
- if (isspace(*cur)) {
- *cur = '\0';
- ++cur;
- while (isspace(*cur))
- ++cur;
- switch (version) {
- case 1:
- /*
- * srv_addr: params[4] => srv_params[0]
- * srv_op_state: params[5] => srv_params[1]
- * srv_admin_state: params[6] => srv_params[2]
- * srv_uweight: params[7] => srv_params[3]
- * srv_iweight: params[8] => srv_params[4]
- * srv_last_time_change: params[9] => srv_params[5]
- * srv_check_status: params[10] => srv_params[6]
- * srv_check_result: params[11] => srv_params[7]
- * srv_check_health: params[12] => srv_params[8]
- * srv_check_state: params[13] => srv_params[9]
- * srv_agent_state: params[14] => srv_params[10]
- * bk_f_forced_id: params[15] => srv_params[11]
- * srv_f_forced_id: params[16] => srv_params[12]
- * srv_fqdn: params[17] => srv_params[13]
- * srv_port: params[18] => srv_params[14]
- * srvrecord: params[19] => srv_params[15]
- */
- if (arg >= 4) {
- srv_params[srv_arg] = cur;
- ++srv_arg;
- }
- break;
- }
+ while (fgets(mybuf, SRV_STATE_LINE_MAXLEN, f)) {
+ int bk_f_forced_id = 0;
+ int check_id = 0;
+ int check_name = 0;
+
+ srv_state_parse_line(mybuf, version, params, srv_params);
- params[arg] = cur;
- ++arg;
+ if (params[0] == NULL) {
+ continue;
}
- else {
- ++cur;
+
+ /* if line is incomplete line, then ignore it.
+ * otherwise, update useful flags */
+ switch (version) {
+ case 1:
+ bk_f_forced_id = (atoi(params[15]) & PR_O_FORCED_ID);
+ check_id = (atoi(params[0]) == curproxy->uuid);
+ check_name = (strcmp(curproxy->id, params[1]) == 0);
+ break;
}
- }
- /* if line is incomplete line, then ignore it.
- * otherwise, update useful flags */
- switch (version) {
- case 1:
- if (arg < SRV_STATE_FILE_NB_FIELDS_VERSION_1)
+ bk = curproxy;
+
+ /* if backend can't be found, let's continue */
+ if (!check_id && !check_name)
+ continue;
+ else if (!check_id && check_name) {
+ ha_warning("backend ID mismatch: from server state file: '%s', from running config '%d'\n", params[0], bk->uuid);
+ send_log(bk, LOG_NOTICE, "backend ID mismatch: from server state file: '%s', from running config '%d'\n", params[0], bk->uuid);
+ }
+ else if (check_id && !check_name) {
+ ha_warning("backend name mismatch: from server state file: '%s', from running config '%s'\n", params[1], bk->id);
+ send_log(bk, LOG_NOTICE, "backend name mismatch: from server state file: '%s', from running config '%s'\n", params[1], bk->id);
+ /* if name doesn't match, we still want to update curproxy if the backend id
+ * was forced in previous the previous configuration */
+ if (!bk_f_forced_id)
continue;
- bk_f_forced_id = (atoi(params[15]) & PR_O_FORCED_ID);
- check_id = (atoi(params[0]) == curproxy->uuid);
- check_name = (strcmp(curproxy->id, params[1]) == 0);
- break;
- }
+ }
- bk = curproxy;
+ /* look for the server by its name: param[3] */
+ srv = server_find_best_match(bk, params[3], 0, NULL);
- /* if backend can't be found, let's continue */
- if (!check_id && !check_name)
- continue;
- else if (!check_id && check_name) {
- ha_warning("backend ID mismatch: from server state file: '%s', from running config '%d'\n", params[0], bk->uuid);
- send_log(bk, LOG_NOTICE, "backend ID mismatch: from server state file: '%s', from running config '%d'\n", params[0], bk->uuid);
- }
- else if (check_id && !check_name) {
- ha_warning("backend name mismatch: from server state file: '%s', from running config '%s'\n", params[1], bk->id);
- send_log(bk, LOG_NOTICE, "backend name mismatch: from server state file: '%s', from running config '%s'\n", params[1], bk->id);
- /* if name doesn't match, we still want to update curproxy if the backend id
- * was forced in previous the previous configuration */
- if (!bk_f_forced_id)
+ if (!srv) {
+ /* if no server found, then warning and continue with next line */
+ ha_warning("can't find server '%s' in backend '%s'\n",
+ params[3], params[1]);
+ send_log(bk, LOG_NOTICE, "can't find server '%s' in backend '%s'\n",
+ params[3], params[1]);
continue;
- }
-
- /* look for the server by its name: param[3] */
- srv = server_find_best_match(bk, params[3], 0, NULL);
+ }
- if (!srv) {
- /* if no server found, then warning and continue with next line */
- ha_warning("can't find server '%s' in backend '%s'\n",
- params[3], params[1]);
- send_log(bk, LOG_NOTICE, "can't find server '%s' in backend '%s'\n",
- params[3], params[1]);
- continue;
+ /* now we can proceed with server's state update */
+ srv_update_state(srv, version, srv_params);
}
-
- /* now we can proceed with server's state update */
- srv_update_state(srv, version, srv_params);
}
fileclose:
fclose(f);
}
+
+ /* now free memory allocated for the tree */
+ for (node = ebmb_first(&state_file), next_node = node ? ebmb_next(node) : NULL;
+ node;
+ node = next_node, next_node = node ? ebmb_next(node) : NULL) {
+ st = container_of(node, struct state_line, name_name);
+ ebmb_delete(&st->name_name);
+ free(st->line);
+ free(st);
+ }
+
}
/*