# HG changeset patch # User Timo Sirainen # Date 1415768317 -7200 # Node ID a7e830b9b967b79b55d4209d1caf28576a4eabdb # Parent 26679856fbd527d7b6072ffab9228fd7ef34c4f9 director: Added support for backend cluster "tags". This allows using a single director ring for multiple backend clusters. By default everything has an empty tag. A passdb lookup can return "director_tag" field containing the wanted tag name. If there aren't any backend servers with the wanted tag, it's treated the same as if there aren't any backend servers available (= wait for 30 secs for a backend and then return temporary failure). Tags can be added to configuration by adding @tag suffix to IPs/hosts. For example: director_mail_servers = 10.0.0.100-10.0.0.110@name1 10.0.0.120@name2 "doveadm director add" can also add tags either with @tag suffix or with -t parameter. "doveadm director status user@domain" requires giving the user's correct tag with -t parameter or the results won't be correct (empty tag's results are shown). Tags can't currently be changed for an existing host without removing it first. diff -r 26679856fbd5 -r a7e830b9b967 src/director/director-connection.c --- a/src/director/director-connection.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/director-connection.c Wed Nov 12 06:58:37 2014 +0200 @@ -35,6 +35,7 @@ #include "istream.h" #include "ostream.h" #include "str.h" +#include "strescape.h" #include "master-service.h" #include "mail-host.h" #include "director.h" @@ -821,15 +822,18 @@ { struct mail_host *host; struct ip_addr ip; + const char *tag = ""; unsigned int vhost_count; bool update; - if (str_array_length(args) != 2 || + if (str_array_length(args) < 2 || net_addr2ip(args[0], &ip) < 0 || str_to_uint(args[1], &vhost_count) < 0) { director_cmd_error(conn, "Invalid parameters"); return FALSE; } + if (args[2] != NULL) + tag = args[2]; if (conn->ignore_host_events) { /* remote is sending hosts in a handshake, but it doesn't have a completed ring and we do. */ @@ -839,10 +843,17 @@ host = mail_host_lookup(conn->dir->mail_hosts, &ip); if (host == NULL) { - host = mail_host_add_ip(conn->dir->mail_hosts, &ip); + host = mail_host_add_ip(conn->dir->mail_hosts, &ip, tag); update = TRUE; } else { update = host->vhost_count != vhost_count; + if (strcmp(tag, host->tag) != 0) { + i_error("director(%s): Host %s changed tag from '%s' to '%s'", + conn->name, net_ip2addr(&host->ip), + host->tag, tag); + mail_host_set_tag(host, tag); + update = TRUE; + } } if (update) { @@ -1541,8 +1552,13 @@ str_printfa(str, "HOST-HAND-START\t%u\n", conn->dir->ring_handshaked); array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) { - str_printfa(str, "HOST\t%s\t%u\n", + str_printfa(str, "HOST\t%s\t%u", net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count); + if ((*hostp)->tag[0] != '\0') { + str_append_c(str, '\t'); + str_append_tabescaped(str, (*hostp)->tag); + } + str_append_c(str, '\n'); } str_printfa(str, "HOST-HAND-END\t%u\n", conn->dir->ring_handshaked); } diff -r 26679856fbd5 -r a7e830b9b967 src/director/director-request.c --- a/src/director/director-request.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/director-request.c Wed Nov 12 06:58:37 2014 +0200 @@ -36,11 +36,18 @@ time_t create_time; unsigned int username_hash; enum director_request_delay_reason delay_reason; + char *username_tag; director_request_callback *callback; void *context; }; +static void director_request_free(struct director_request *request) +{ + i_free(request->username_tag); + i_free(request); +} + static const char * director_request_get_timeout_error(struct director_request *request, struct user *user, string_t *str) @@ -68,7 +75,10 @@ str_printfa(str, ", user refreshed %u secs ago", (unsigned int)(ioloop_time - user->timestamp)); } - str_printfa(str, "hash=%u)", request->username_hash); + str_printfa(str, ", hash=%u", request->username_hash); + if (request->username_tag != NULL) + str_printfa(str, ", tag=%s", request->username_tag); + str_append_c(str, ')'); return str_c(str); } @@ -103,7 +113,7 @@ T_BEGIN { request->callback(NULL, errormsg, request->context); } T_END; - i_free(request); + director_request_free(request); } if (array_count(&dir->pending_requests) == 0 && dir->to_request != NULL) @@ -111,6 +121,7 @@ } void director_request(struct director *dir, const char *username, + const char *tag, director_request_callback *callback, void *context) { struct director_request *request; @@ -121,6 +132,7 @@ request->dir = dir; request->create_time = ioloop_time; request->username_hash = username_hash; + request->username_tag = tag[0] == '\0' ? NULL : i_strdup(tag); request->callback = callback; request->context = context; @@ -159,7 +171,8 @@ } static bool -director_request_existing(struct director_request *request, struct user *user) +director_request_existing(struct director_request *request, struct user *user, + const char *tag) { struct director *dir = request->dir; struct mail_host *host; @@ -193,7 +206,7 @@ /* user is close to being expired. another director may have already expired it. */ - host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash); + host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash, tag); if (!dir->ring_synced) { /* try again later once ring is synced */ request->delay_reason = REQUEST_DELAY_RINGNOTSYNCED; @@ -253,6 +266,7 @@ struct director *dir = request->dir; struct mail_host *host; struct user *user; + const char *tag; if (!dir->ring_handshaked) { /* delay requests until ring handshaking is complete */ @@ -264,8 +278,9 @@ } user = user_directory_lookup(dir->users, request->username_hash); + tag = request->username_tag == NULL ? "" : request->username_tag; if (user != NULL) { - if (!director_request_existing(request, user)) + if (!director_request_existing(request, user, tag)) return FALSE; user_directory_refresh(dir->users, user); dir_debug("request: %u refreshed timeout to %u", @@ -280,7 +295,7 @@ return FALSE; } host = mail_host_get_by_hash(dir->mail_hosts, - request->username_hash); + request->username_hash, tag); if (host == NULL) { /* all hosts have been removed */ request->delay_reason = REQUEST_DELAY_NOHOSTS; @@ -299,6 +314,6 @@ T_BEGIN { request->callback(&user->host->ip, NULL, request->context); } T_END; - i_free(request); + director_request_free(request); return TRUE; } diff -r 26679856fbd5 -r a7e830b9b967 src/director/director-request.h --- a/src/director/director-request.h Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/director-request.h Wed Nov 12 06:58:37 2014 +0200 @@ -9,6 +9,7 @@ void *context); void director_request(struct director *dir, const char *username, + const char *tag, director_request_callback *callback, void *context); bool director_request_continue(struct director_request *request); diff -r 26679856fbd5 -r a7e830b9b967 src/director/director.c --- a/src/director/director.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/director.c Wed Nov 12 06:58:37 2014 +0200 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "array.h" #include "str.h" +#include "strescape.h" #include "ipc-client.h" #include "user-directory.h" #include "mail-host.h" @@ -503,6 +504,8 @@ struct director_host *orig_src, struct mail_host *host) { + string_t *str; + /* update state in case this is the first mail host being added */ director_set_state_changed(dir); @@ -511,10 +514,25 @@ orig_src->last_seq++; } - director_update_send(dir, src, t_strdup_printf( - "HOST\t%s\t%u\t%u\t%s\t%u\n", - net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq, - net_ip2addr(&host->ip), host->vhost_count)); + str = t_str_new(128); + str_printfa(str, "HOST\t%s\t%u\t%u\t%s\t%u", + net_ip2addr(&orig_src->ip), orig_src->port, + orig_src->last_seq, + net_ip2addr(&host->ip), host->vhost_count); + if (host->tag[0] == '\0') + ; + else if (dir->ring_handshaked && + dir->ring_min_version < DIRECTOR_VERSION_TAGS) { + i_error("Ring has directors that don't support tags - removing host %s with tag '%s'", + net_ip2addr(&host->ip), host->tag); + director_remove_host(dir, NULL, NULL, host); + return; + } else { + str_append_c(str, '\t'); + str_append_tabescaped(str, host->tag); + } + str_append_c(str, '\n'); + director_update_send(dir, src, str_c(str)); director_sync(dir); } diff -r 26679856fbd5 -r a7e830b9b967 src/director/director.h --- a/src/director/director.h Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/director.h Wed Nov 12 06:58:37 2014 +0200 @@ -18,6 +18,8 @@ #define DIRECTOR_VERSION_USER_KICK 4 /* options supported in handshake */ #define DIRECTOR_VERSION_OPTIONS 5 +/* user tags supported */ +#define DIRECTOR_VERSION_TAGS 5 /* Minimum time between even attempting to communicate with a director that failed due to a protocol error. */ diff -r 26679856fbd5 -r a7e830b9b967 src/director/doveadm-connection.c --- a/src/director/doveadm-connection.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/doveadm-connection.c Wed Nov 12 06:58:37 2014 +0200 @@ -7,6 +7,7 @@ #include "ostream.h" #include "array.h" #include "str.h" +#include "strescape.h" #include "llist.h" #include "master-service.h" #include "user-directory.h" @@ -46,9 +47,11 @@ string_t *str = t_str_new(1024); array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) { - str_printfa(str, "%s\t%u\t%u\n", + str_printfa(str, "%s\t%u\t%u\t", net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count, (*hostp)->user_count); + str_append_tabescaped(str, (*hostp)->tag); + str_append_c(str, '\n'); } str_append_c(str, '\n'); o_stream_nsend(conn->output, str_data(str), str_len(str)); @@ -244,14 +247,21 @@ doveadm_cmd_host_set(struct doveadm_connection *conn, const char *line) { struct director *dir = conn->dir; - const char *const *args; + const char *const *args, *ip_str, *tag = ""; struct mail_host *host; struct ip_addr ip; unsigned int vhost_count = UINT_MAX; args = t_strsplit_tab(line); - if (args[0] == NULL || - net_addr2ip(args[0], &ip) < 0 || + ip_str = args[0]; + if (ip_str != NULL) { + tag = strchr(ip_str, '@'); + if (tag == NULL) + tag = ""; + else + ip_str = t_strdup_until(ip_str, tag++); + } + if (ip_str == NULL || net_addr2ip(ip_str, &ip) < 0 || (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0)) { i_error("doveadm sent invalid HOST-SET parameters: %s", line); return FALSE; @@ -262,9 +272,12 @@ } host = mail_host_lookup(dir->mail_hosts, &ip); if (host == NULL) - host = mail_host_add_ip(dir->mail_hosts, &ip); + host = mail_host_add_ip(dir->mail_hosts, &ip, tag); if (vhost_count != UINT_MAX) mail_host_set_vhost_count(dir->mail_hosts, host, vhost_count); + /* NOTE: we don't supporting changing a tag for an existing host. + it needs to be removed first. otherwise it would be a bit ugly to + handle. */ director_update_host(dir, dir->self_host, NULL, host); o_stream_nsend(conn->output, "OK\n", 3); @@ -340,10 +353,19 @@ { struct user *user; struct mail_host *host; + const char *username, *tag, *const *args; unsigned int username_hash; string_t *str = t_str_new(256); - if (str_to_uint(line, &username_hash) < 0) + args = t_strsplit_tab(line); + if (args[0] == NULL) { + username = ""; + tag = ""; + } else { + username = args[0]; + tag = args[1] != NULL ? args[1] : ""; + } + if (str_to_uint(username, &username_hash) < 0) username_hash = user_directory_get_username_hash(conn->dir->users, line); /* get user's current host */ @@ -357,7 +379,7 @@ } /* get host if it wasn't in user directory */ - host = mail_host_get_by_hash(conn->dir->mail_hosts, username_hash); + host = mail_host_get_by_hash(conn->dir->mail_hosts, username_hash, tag); if (host == NULL) str_append(str, "\t"); else @@ -365,7 +387,7 @@ /* get host with default configuration */ host = mail_host_get_by_hash(conn->dir->orig_config_hosts, - username_hash); + username_hash, tag); if (host == NULL) str_append(str, "\t"); else diff -r 26679856fbd5 -r a7e830b9b967 src/director/login-connection.c --- a/src/director/login-connection.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/login-connection.c Wed Nov 12 06:58:37 2014 +0200 @@ -109,7 +109,7 @@ { struct login_connection *conn = context; struct login_host_request *request; - const char *const *args, *line_params, *username = NULL; + const char *const *args, *line_params, *username = NULL, *tag = ""; bool proxy = FALSE, host = FALSE; if (line == NULL) { @@ -142,6 +142,8 @@ host = TRUE; else if (strncmp(*args, "destuser=", 9) == 0) username = *args + 9; + else if (strncmp(*args, "director_tag=", 13) == 0) + tag = *args + 13; else if (strncmp(*args, "user=", 5) == 0) { if (username == NULL) username = *args + 5; @@ -165,7 +167,7 @@ request->username = i_strdup(username); conn->refcount++; - director_request(conn->dir, username, login_host_callback, request); + director_request(conn->dir, username, tag, login_host_callback, request); } struct login_connection * diff -r 26679856fbd5 -r a7e830b9b967 src/director/mail-host.c --- a/src/director/mail-host.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/mail-host.c Wed Nov 12 06:58:37 2014 +0200 @@ -116,20 +116,25 @@ } struct mail_host * -mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip) +mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip, + const char *tag) { struct mail_host *host; + i_assert(tag != NULL); + host = i_new(struct mail_host, 1); host->vhost_count = VHOST_MULTIPLIER; host->ip = *ip; + host->tag = i_strdup(tag); array_append(&list->hosts, &host, 1); list->hosts_unsorted = TRUE; return host; } -static int mail_host_add(struct mail_host_list *list, const char *host) +static int +mail_host_add(struct mail_host_list *list, const char *host, const char *tag) { struct ip_addr *ips; unsigned int i, ips_count; @@ -140,13 +145,13 @@ } for (i = 0; i < ips_count; i++) - (void)mail_host_add_ip(list, &ips[i]); + (void)mail_host_add_ip(list, &ips[i], tag); return 0; } static int mail_hosts_add_range(struct mail_host_list *list, - struct ip_addr ip1, struct ip_addr ip2) + struct ip_addr ip1, struct ip_addr ip2, const char *tag) { uint32_t *ip1_arr, *ip2_arr; uint32_t i1, i2; @@ -201,7 +206,7 @@ /* create hosts from the final bits */ do { ip1_arr[i] = ntohl(i1); - (void)mail_host_add_ip(list, &ip1); + (void)mail_host_add_ip(list, &ip1, tag); i1++; } while (ip1_arr[i] != ip2_arr[i]); return 0; @@ -218,21 +223,30 @@ tmp = t_strsplit_spaces(hosts_string, " "); for (; *tmp != NULL; tmp++) { - p = strchr(*tmp, '-'); + const char *tag, *value = *tmp; + + p = strchr(value, '@'); + if (p == NULL) + tag = ""; + else { + value = t_strdup_until(value, p++); + tag = p; + } + p = strchr(value, '-'); if (p != NULL) { /* see if this is ip1-ip2 range */ - host1 = t_strdup_until(*tmp, p); + host1 = t_strdup_until(value, p); host2 = p + 1; if (net_addr2ip(host1, &ip1) == 0 && net_addr2ip(host2, &ip2) == 0) { - if (mail_hosts_add_range(list, ip1, - ip2) < 0) + if (mail_hosts_add_range(list, ip1, ip2, + tag) < 0) ret = -1; continue; } } - if (mail_host_add(list, *tmp) < 0) + if (mail_host_add(list, value, tag) < 0) ret = -1; } } T_END; @@ -247,6 +261,14 @@ return ret; } +void mail_host_set_tag(struct mail_host *host, const char *tag) +{ + i_assert(tag != NULL); + + i_free(host->tag); + host->tag = i_strdup(tag); +} + void mail_host_set_vhost_count(struct mail_host_list *list, struct mail_host *host, unsigned int vhost_count) { @@ -254,6 +276,12 @@ list->hosts_unsorted = TRUE; } +static void mail_host_free(struct mail_host *host) +{ + i_free(host->tag); + i_free(host); +} + void mail_host_remove(struct mail_host_list *list, struct mail_host *host) { struct mail_host *const *hosts; @@ -267,7 +295,7 @@ } } - i_free(host); + mail_host_free(host); list->hosts_unsorted = TRUE; } @@ -287,46 +315,69 @@ } static struct mail_host * -mail_host_get_by_hash_ring(struct mail_host_list *list, unsigned int hash) +mail_host_get_by_hash_ring(struct mail_host_list *list, unsigned int hash, + const char *tag) { + struct mail_host *host; const struct mail_vhost *vhosts; - unsigned int count, idx; + unsigned int i, count, idx; vhosts = array_get(&list->vhosts, &count); array_bsearch_insert_pos(&list->vhosts, &hash, mail_vhost_hash_cmp, &idx); + i_assert(idx <= count); if (idx == count) { if (count == 0) return NULL; idx = 0; } - return vhosts[idx].host; + for (i = 0; i < count; i++) { + host = vhosts[(idx + i) % count].host; + if (strcmp(host->tag, tag) == 0) + return host; + } + return NULL; } static struct mail_host * -mail_host_get_by_hash_direct(struct mail_host_list *list, unsigned int hash) +mail_host_get_by_hash_direct(struct mail_host_list *list, unsigned int hash, + const char *tag) { + struct mail_host *host; const struct mail_vhost *vhosts; - unsigned int count; + unsigned int i, count; vhosts = array_get(&list->vhosts, &count); if (count == 0) return NULL; - return vhosts[hash % count].host; + for (i = 0; i < count; i++) { + host = vhosts[(hash + i) % count].host; + if (strcmp(host->tag, tag) == 0) + return host; + } + return NULL; } struct mail_host * -mail_host_get_by_hash(struct mail_host_list *list, unsigned int hash) +mail_host_get_by_hash(struct mail_host_list *list, unsigned int hash, + const char *tag) { if (list->hosts_unsorted) mail_hosts_sort(list); if (list->consistent_hashing) - return mail_host_get_by_hash_ring(list, hash); + return mail_host_get_by_hash_ring(list, hash, tag); else - return mail_host_get_by_hash_direct(list, hash); + return mail_host_get_by_hash_direct(list, hash, tag); +} + +bool mail_hosts_have_usable(struct mail_host_list *list) +{ + if (list->hosts_unsorted) + mail_hosts_sort(list); + return array_count(&list->vhosts) > 0; } const ARRAY_TYPE(mail_host) *mail_hosts_get(struct mail_host_list *list) @@ -355,7 +406,7 @@ *_list = NULL; array_foreach_modifiable(&list->hosts, hostp) - i_free(*hostp); + mail_host_free(*hostp); array_free(&list->hosts); array_free(&list->vhosts); i_free(list); @@ -367,6 +418,7 @@ dest = i_new(struct mail_host, 1); *dest = *src; + dest->tag = i_strdup(src->tag); return dest; } diff -r 26679856fbd5 -r a7e830b9b967 src/director/mail-host.h --- a/src/director/mail-host.h Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/mail-host.h Wed Nov 12 06:58:37 2014 +0200 @@ -10,23 +10,28 @@ unsigned int vhost_count; struct ip_addr ip; + char *tag; }; ARRAY_DEFINE_TYPE(mail_host, struct mail_host *); struct mail_host * -mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip); +mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip, + const char *tag); struct mail_host * mail_host_lookup(struct mail_host_list *list, const struct ip_addr *ip); struct mail_host * -mail_host_get_by_hash(struct mail_host_list *list, unsigned int hash); +mail_host_get_by_hash(struct mail_host_list *list, unsigned int hash, + const char *tag); int mail_hosts_parse_and_add(struct mail_host_list *list, const char *hosts_string); +void mail_host_set_tag(struct mail_host *host, const char *tag); void mail_host_set_vhost_count(struct mail_host_list *list, struct mail_host *host, unsigned int vhost_count); void mail_host_remove(struct mail_host_list *list, struct mail_host *host); +bool mail_hosts_have_usable(struct mail_host_list *list); const ARRAY_TYPE(mail_host) *mail_hosts_get(struct mail_host_list *list); struct mail_host_list *mail_hosts_init(bool consistent_hashing); diff -r 26679856fbd5 -r a7e830b9b967 src/director/main.c --- a/src/director/main.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/director/main.c Wed Nov 12 06:58:37 2014 +0200 @@ -119,8 +119,7 @@ ARRAY(struct director_request *) new_requests; bool ret; - if (!dir->ring_synced || - mail_host_get_by_hash(dir->mail_hosts, 0) == NULL) + if (!dir->ring_synced || !mail_hosts_have_usable(dir->mail_hosts)) return; /* if there are any pending client requests, finish them now */ diff -r 26679856fbd5 -r a7e830b9b967 src/doveadm/doveadm-director.c --- a/src/doveadm/doveadm-director.c Wed Nov 12 06:46:45 2014 +0200 +++ b/src/doveadm/doveadm-director.c Wed Nov 12 06:58:37 2014 +0200 @@ -21,6 +21,7 @@ struct director_context { const char *socket_path; const char *users_path; + const char *tag; struct istream *input; bool explicit_socket_path; bool hash_map, user_map; @@ -111,6 +112,9 @@ case 'u': ctx->user_map = TRUE; break; + case 't': + ctx->tag = optarg; + break; default: director_cmd_help(cmd); } @@ -121,12 +125,14 @@ } static void -cmd_director_status_user(struct director_context *ctx, const char *user) +cmd_director_status_user(struct director_context *ctx, char *argv[]) { + const char *user = argv[0], *tag = argv[1]; const char *line, *const *args; unsigned int expires; - director_send(ctx, t_strdup_printf("USER-LOOKUP\t%s\n", user)); + director_send(ctx, t_strdup_printf("USER-LOOKUP\t%s\t%s\n", user, + tag != NULL ? tag : "")); line = i_stream_read_next_line(ctx->input); if (line == NULL) { i_error("Lookup failed"); @@ -158,14 +164,15 @@ struct director_context *ctx; const char *line, *const *args; - ctx = cmd_director_init(argc, argv, "a:", cmd_director_status); + ctx = cmd_director_init(argc, argv, "a:t:", cmd_director_status); if (argv[optind] != NULL) { - cmd_director_status_user(ctx, argv[optind]); + cmd_director_status_user(ctx, argv+optind); return; } doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); doveadm_print_header_simple("mail server ip"); + doveadm_print_header_simple("tag"); doveadm_print_header("vhosts", "vhosts", DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY); doveadm_print_header("users", "users", @@ -177,8 +184,9 @@ break; T_BEGIN { args = t_strsplit_tab(line); - if (str_array_length(args) >= 3) { + if (str_array_length(args) >= 4) { doveadm_print(args[0]); + doveadm_print(args[3]); doveadm_print(args[1]); doveadm_print(args[2]); } @@ -390,9 +398,12 @@ struct director_context *ctx; struct ip_addr *ips; unsigned int i, ips_count, vhost_count = UINT_MAX; - const char *host, *cmd, *line; + const char *host, *line; + string_t *cmd; - ctx = cmd_director_init(argc, argv, "a:", cmd_director_add); + ctx = cmd_director_init(argc, argv, "a:t:", cmd_director_add); + if (ctx->tag != NULL && ctx->tag[0] == '\0') + ctx->tag = NULL; host = argv[optind++]; if (host == NULL) director_cmd_help(cmd_director_add); @@ -403,14 +414,22 @@ if (argv[optind] != NULL) director_cmd_help(cmd_director_add); + if (ctx->tag == NULL) { + ctx->tag = strchr(host, '@'); + if (ctx->tag != NULL) + host = t_strdup_until(host, ctx->tag++); + } director_get_host(host, &ips, &ips_count); + cmd = t_str_new(128); for (i = 0; i < ips_count; i++) { - cmd = vhost_count == UINT_MAX ? - t_strdup_printf("HOST-SET\t%s\n", - net_ip2addr(&ips[i])) : - t_strdup_printf("HOST-SET\t%s\t%u\n", - net_ip2addr(&ips[i]), vhost_count); - director_send(ctx, cmd); + str_truncate(cmd, 0); + str_printfa(cmd, "HOST-SET\t%s", net_ip2addr(&ips[i])); + if (ctx->tag != NULL) + str_printfa(cmd, "@%s", ctx->tag); + if (vhost_count != UINT_MAX) + str_printfa(cmd, "\t%u", vhost_count); + str_append_c(cmd, '\n'); + director_send(ctx, str_c(cmd)); } for (i = 0; i < ips_count; i++) { line = i_stream_read_next_line(ctx->input); @@ -752,7 +771,7 @@ { cmd_director_map, "director map", "[-a ] [-f ] [-h | -u] []" }, { cmd_director_add, "director add", - "[-a ] []" }, + "[-a ] [-t ] []" }, { cmd_director_remove, "director remove", "[-a ] " }, { cmd_director_move, "director move",