1
/* ----------------------------------------------------------------------- *
3
* repl_list.h - routines for replicated mount server selection
5
* Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved
6
* Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
11
* USA; either version 2 of the License, or (at your option) any later
12
* version; incorporated herein by reference.
14
* A priority ordered list of hosts is created by using the following
17
* 1) Highest priority in selection is proximity.
18
* Proximity, in order of precedence is:
19
* - PROXIMITY_LOCAL, host corresponds to a local interface.
20
* - PROXIMITY_SUBNET, host is located in a subnet reachable
21
* through a local interface.
22
* - PROXIMITY_NETWORK, host is located in a network reachable
23
* through a local interface.
24
* - PROXIMITY_OTHER, host is on a network not directlty
25
* reachable through a local interface.
27
* 2) NFS version and protocol is selected by caclculating the largest
28
* number of hosts supporting an NFS version and protocol that
29
* have the closest proximity. These hosts are added to the list
30
* in response time order. Hosts may have a corresponding weight
31
* which essentially increaes response time and so influences the
34
* 3) Hosts at further proximity that support the selected NFS version
35
* and protocol are also added to the list in response time order as
38
* ----------------------------------------------------------------------- */
46
#include <sys/errno.h>
47
#include <sys/types.h>
49
#include <sys/ioctl.h>
50
#include <sys/socket.h>
51
#include <arpa/inet.h>
53
#include <netinet/in.h>
57
#include "replicated.h"
58
#include "automount.h"
61
#define MAX_ERR_BUF 512
64
#define MAX_IFC_BUF 2048
65
static int volatile ifc_buf_len = MAX_IFC_BUF;
66
static int volatile ifc_last_len = 0;
68
#define MASK_A 0x7F000000
69
#define MASK_B 0xBFFF0000
70
#define MASK_C 0xDFFFFF00
72
/* Get numeric value of the n bits starting at position p */
73
#define getbits(x, p, n) ((x >> (p + 1 - n)) & ~(~0 << n))
75
#define max(x, y) (x >= y ? x : y)
76
#define mmax(x, y, z) (max(x, y) == x ? max(x, z) : max(y, z))
78
unsigned int ipv6_mask_cmp(uint32_t *host, uint32_t *iface, uint32_t *mask)
83
for (i = 0; i < 4; i++) {
84
if ((host[i] & mask[i]) != (iface[i] & mask[i])) {
92
void seed_random(void)
97
fd = open_fd("/dev/urandom", O_RDONLY);
103
if (read(fd, &seed, sizeof(seed)) != -1)
113
static int alloc_ifreq(struct ifconf *ifc, int sock)
115
int ret, lastlen = ifc_last_len, len = ifc_buf_len;
116
char err_buf[MAX_ERR_BUF], *buf;
121
char *estr = strerror_r(errno, err_buf, MAX_ERR_BUF);
122
logerr("malloc: %s", estr);
127
ifc->ifc_req = (struct ifreq *) buf;
129
ret = ioctl(sock, SIOCGIFCONF, ifc);
131
char *estr = strerror_r(errno, err_buf, MAX_ERR_BUF);
132
logerr("ioctl: %s", estr);
137
if (ifc->ifc_len <= lastlen)
140
lastlen = ifc->ifc_len;
145
if (lastlen != ifc_last_len) {
146
ifc_last_len = lastlen;
153
static unsigned int get_proximity(struct sockaddr *host_addr)
155
struct sockaddr_in *addr, *msk_addr, *if_addr;
156
struct sockaddr_in6 *addr6, *msk6_addr, *if6_addr;
157
struct in_addr *hst_addr;
158
struct in6_addr *hst6_addr;
160
char buf[MAX_ERR_BUF], *ptr;
162
struct ifreq *ifr, nmptr;
164
uint32_t mask, ha, ia, *mask6, *ha6, *ia6;
174
switch (host_addr->sa_family) {
176
addr = (struct sockaddr_in *) host_addr;
177
hst_addr = (struct in_addr *) &addr->sin_addr;
178
ha = ntohl((uint32_t) hst_addr->s_addr);
179
addr_len = sizeof(hst_addr);
184
return PROXIMITY_UNSUPPORTED;
186
addr6 = (struct sockaddr_in6 *) host_addr;
187
hst6_addr = (struct in6_addr *) &addr6->sin6_addr;
188
ha6 = &hst6_addr->s6_addr32[0];
189
addr_len = sizeof(hst6_addr);
194
return PROXIMITY_ERROR;
197
sock = open_sock(AF_INET, SOCK_DGRAM, 0);
199
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
200
logerr("socket creation failed: %s", estr);
201
return PROXIMITY_ERROR;
204
if (!alloc_ifreq(&ifc, sock)) {
206
return PROXIMITY_ERROR;
209
/* For each interface */
211
/* Is the address a local interface */
213
ptr = (char *) &ifc.ifc_buf[0];
215
while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) {
216
ifr = (struct ifreq *) ptr;
218
switch (ifr->ifr_addr.sa_family) {
221
if (host_addr->sa_family == AF_INET6)
224
if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
225
ret = memcmp(&if_addr->sin_addr, hst_addr, addr_len);
229
return PROXIMITY_LOCAL;
235
if (host_addr->sa_family == AF_INET)
238
if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr;
239
ret = memcmp(&if6_addr->sin6_addr, hst6_addr, addr_len);
243
return PROXIMITY_LOCAL;
252
ptr = (char *) &ifc.ifc_req[i];
256
ptr = (char *) &ifc.ifc_buf[0];
258
while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) {
259
ifr = (struct ifreq *) ptr;
262
ret = ioctl(sock, SIOCGIFNETMASK, &nmptr);
264
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
265
logerr("ioctl: %s", estr);
268
return PROXIMITY_ERROR;
271
switch (ifr->ifr_addr.sa_family) {
274
if (host_addr->sa_family == AF_INET6)
277
if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
278
ia = ntohl((uint32_t) if_addr->sin_addr.s_addr);
280
/* Is the address within a localiy attached subnet */
282
msk_addr = (struct sockaddr_in *) &nmptr.ifr_netmask;
283
mask = ntohl((uint32_t) msk_addr->sin_addr.s_addr);
285
if ((ia & mask) == (ha & mask)) {
288
return PROXIMITY_SUBNET;
292
* Is the address within a local ipv4 network.
294
* Bit position 31 == 0 => class A.
295
* Bit position 30 == 0 => class B.
296
* Bit position 29 == 0 => class C.
299
if (!getbits(ia, 31, 1))
301
else if (!getbits(ia, 30, 1))
303
else if (!getbits(ia, 29, 1))
308
if ((ia & mask) == (ha & mask)) {
311
return PROXIMITY_NET;
317
if (host_addr->sa_family == AF_INET)
320
if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr;
321
ia6 = &if6_addr->sin6_addr.s6_addr32[0];
323
/* Is the address within the network of the interface */
325
msk6_addr = (struct sockaddr_in6 *) &nmptr.ifr_netmask;
326
mask6 = &msk6_addr->sin6_addr.s6_addr32[0];
328
if (ipv6_mask_cmp(ha6, ia6, mask6)) {
331
return PROXIMITY_SUBNET;
334
/* How do we define "local network" in ipv6? */
343
ptr = (char *) &ifc.ifc_req[i];
349
return PROXIMITY_OTHER;
352
static struct host *new_host(const char *name,
353
struct sockaddr *addr, size_t addr_len,
354
unsigned int proximity, unsigned int weight)
357
struct sockaddr *tmp2;
367
tmp2 = malloc(addr_len);
372
memcpy(tmp2, addr, addr_len);
374
new = malloc(sizeof(struct host));
381
memset(new, 0, sizeof(struct host));
384
new->addr_len = addr_len;
386
new->proximity = proximity;
387
new->weight = weight;
392
static int add_host(struct host **list, struct host *host)
394
struct host *this, *last;
404
if (this->proximity >= host->proximity)
412
if (this->proximity != host->proximity)
414
if (this->cost >= host->cost)
433
static void free_host(struct host *host)
441
static void remove_host(struct host **hosts, struct host *host)
443
struct host *last, *this;
445
if (host == *hosts) {
446
*hosts = (*hosts)->next;
463
last->next = this->next;
469
static void delete_host(struct host **hosts, struct host *host)
471
remove_host(hosts, host);
476
void free_host_list(struct host **list)
482
struct host *next = this->next;
489
static unsigned short get_port_option(const char *options)
497
start = strstr(options, "port=");
501
char optport[30], *opteq, *end;
504
end = strchr(start, ',');
505
len = end ? end - start : strlen(start);
506
strncpy(optport, start, len);
508
opteq = strchr(optport, '=');
510
port = atoi(opteq + 1);
516
return (unsigned short) port;
519
static unsigned int get_nfs_info(unsigned logopt, struct host *host,
520
struct conn_info *pm_info, struct conn_info *rpc_info,
521
const char *proto, unsigned int version,
522
const char *options, unsigned int random_selection)
524
char *have_port_opt = options ? strstr(options, "port=") : NULL;
526
struct timeval start, end;
528
unsigned int supported = 0;
530
int status, count = 0;
533
"called for host %s proto %s version 0x%x",
534
host->name, proto, version);
536
memset(&parms, 0, sizeof(struct pmap));
538
parms.pm_prog = NFS_PROGRAM;
540
/* Try to prode UDP first to conserve socket space */
541
rpc_info->proto = getprotobyname(proto);
542
if (!rpc_info->proto)
545
if (!(version & NFS4_REQUESTED))
548
if (!(rpc_info->port = get_port_option(options)))
551
if (rpc_info->proto->p_proto == IPPROTO_UDP)
552
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
554
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
556
gettimeofday(&start, &tz);
557
status = rpc_ping_proto(rpc_info);
558
gettimeofday(&end, &tz);
561
if (random_selection) {
562
/* Random value between 0 and 1 */
563
reply = ((float) random())/((float) RAND_MAX+1);
565
"nfs v4 random selection time: %f", reply);
567
reply = elapsed(start, end);
568
debug(logopt, "nfs v4 rpc ping time: %f", reply);
572
supported = NFS4_SUPPORTED;
577
if (!have_port_opt) {
578
status = rpc_portmap_getclient(pm_info,
579
host->name, host->addr, host->addr_len,
580
proto, RPC_CLOSE_DEFAULT);
585
if (!(version & NFS3_REQUESTED))
589
if (!(rpc_info->port = get_port_option(options)))
592
parms.pm_prot = rpc_info->proto->p_proto;
593
parms.pm_vers = NFS3_VERSION;
594
rpc_info->port = rpc_portmap_getport(pm_info, &parms);
599
if (rpc_info->proto->p_proto == IPPROTO_UDP)
600
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
602
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
604
gettimeofday(&start, &tz);
605
status = rpc_ping_proto(rpc_info);
606
gettimeofday(&end, &tz);
609
if (random_selection) {
610
/* Random value between 0 and 1 */
611
reply = ((float) random())/((float) RAND_MAX+1);
613
"nfs v3 random selection time: %f", reply);
615
reply = elapsed(start, end);
616
debug(logopt, "nfs v3 rpc ping time: %f", reply);
620
supported |= NFS3_SUPPORTED;
625
if (!(version & NFS2_REQUESTED))
629
if (!(rpc_info->port = get_port_option(options)))
632
parms.pm_prot = rpc_info->proto->p_proto;
633
parms.pm_vers = NFS2_VERSION;
634
rpc_info->port = rpc_portmap_getport(pm_info, &parms);
639
if (rpc_info->proto->p_proto == IPPROTO_UDP)
640
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
642
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
644
gettimeofday(&start, &tz);
645
status = rpc_ping_proto(rpc_info);
646
gettimeofday(&end, &tz);
649
if (random_selection) {
650
/* Random value between 0 and 1 */
651
reply = ((float) random())/((float) RAND_MAX+1);
653
"nfs v2 random selection time: %f", reply);
655
reply = elapsed(start, end);;
656
debug(logopt, "nfs v2 rpc ping time: %f", reply);
660
supported |= NFS2_SUPPORTED;
665
if (rpc_info->proto->p_proto == IPPROTO_UDP) {
666
rpc_destroy_udp_client(rpc_info);
667
rpc_destroy_udp_client(pm_info);
669
rpc_destroy_tcp_client(rpc_info);
670
rpc_destroy_tcp_client(pm_info);
675
* Average response time to 7 significant places as
678
host->cost = (unsigned long) ((taken * 1000000) / count);
680
/* Allow for user bias */
682
host->cost *= (host->weight + 1);
684
debug(logopt, "host %s cost %ld weight %d",
685
host->name, host->cost, host->weight);
691
static int get_vers_and_cost(unsigned logopt, struct host *host,
692
unsigned int version, const char *options,
693
unsigned int random_selection)
695
struct conn_info pm_info, rpc_info;
696
time_t timeout = RPC_TIMEOUT;
697
unsigned int supported, vers = (NFS_VERS_MASK | NFS4_VERS_MASK);
700
memset(&pm_info, 0, sizeof(struct conn_info));
701
memset(&rpc_info, 0, sizeof(struct conn_info));
703
if (host->proximity == PROXIMITY_NET)
704
timeout = RPC_TIMEOUT * 2;
705
else if (host->proximity == PROXIMITY_OTHER)
706
timeout = RPC_TIMEOUT * 8;
708
rpc_info.host = host->name;
709
rpc_info.addr = host->addr;
710
rpc_info.addr_len = host->addr_len;
711
rpc_info.program = NFS_PROGRAM;
712
rpc_info.timeout.tv_sec = timeout;
713
rpc_info.close_option = RPC_CLOSE_DEFAULT;
714
rpc_info.client = NULL;
718
if (version & UDP_REQUESTED) {
719
supported = get_nfs_info(logopt, host,
720
&pm_info, &rpc_info, "udp", vers,
721
options, random_selection);
724
host->version |= (supported << 8);
728
if (version & TCP_REQUESTED) {
729
supported = get_nfs_info(logopt, host,
730
&pm_info, &rpc_info, "tcp", vers,
731
options, random_selection);
734
host->version |= supported;
741
static int get_supported_ver_and_cost(unsigned logopt, struct host *host,
742
unsigned int version, const char *options,
743
unsigned int random_selection)
745
char *have_port_opt = options ? strstr(options, "port=") : NULL;
746
struct conn_info pm_info, rpc_info;
750
struct timeval start, end;
753
time_t timeout = RPC_TIMEOUT;
757
"called with host %s version 0x%x", host->name, version);
759
memset(&pm_info, 0, sizeof(struct conn_info));
760
memset(&rpc_info, 0, sizeof(struct conn_info));
761
memset(&parms, 0, sizeof(struct pmap));
763
if (host->proximity == PROXIMITY_NET)
764
timeout = RPC_TIMEOUT * 2;
765
else if (host->proximity == PROXIMITY_OTHER)
766
timeout = RPC_TIMEOUT * 8;
768
rpc_info.host = host->name;
769
rpc_info.addr = host->addr;
770
rpc_info.addr_len = host->addr_len;
771
rpc_info.program = NFS_PROGRAM;
772
rpc_info.timeout.tv_sec = timeout;
773
rpc_info.close_option = RPC_CLOSE_DEFAULT;
774
rpc_info.client = NULL;
776
parms.pm_prog = NFS_PROGRAM;
779
* The version passed in is the version as defined in
780
* include/replicated.h. However, the version we want to send
781
* off to the rpc calls should match the program version of NFS.
782
* So, we do the conversion here.
784
if (version & UDP_SELECTED_MASK) {
801
crit(logopt, "called with invalid version: 0x%x\n", version);
805
rpc_info.proto = getprotobyname(proto);
811
parms.pm_vers = vers;
812
if (have_port_opt || (vers & NFS4_VERSION)) {
813
if (!(rpc_info.port = get_port_option(options)))
816
int ret = rpc_portmap_getclient(&pm_info,
817
host->name, host->addr, host->addr_len,
818
proto, RPC_CLOSE_DEFAULT);
822
parms.pm_prot = rpc_info.proto->p_proto;
823
rpc_info.port = rpc_portmap_getport(&pm_info, &parms);
828
if (rpc_info.proto->p_proto == IPPROTO_UDP)
829
status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
831
status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
833
gettimeofday(&start, &tz);
834
status = rpc_ping_proto(&rpc_info);
835
gettimeofday(&end, &tz);
837
if (random_selection) {
838
/* Random value between 0 and 1 */
839
taken = ((float) random())/((float) RAND_MAX+1);
840
debug(logopt, "random selection time %f", taken);
842
taken = elapsed(start, end);
843
debug(logopt, "rpc ping time %f", taken);
848
if (rpc_info.proto->p_proto == IPPROTO_UDP) {
849
rpc_destroy_udp_client(&rpc_info);
850
rpc_destroy_udp_client(&pm_info);
852
rpc_destroy_tcp_client(&rpc_info);
853
rpc_destroy_tcp_client(&pm_info);
857
/* Response time to 7 significant places as integral type. */
858
host->cost = (unsigned long) (taken * 1000000);
860
/* Allow for user bias */
862
host->cost *= (host->weight + 1);
864
debug(logopt, "cost %ld weight %d", host->cost, host->weight);
872
int prune_host_list(unsigned logopt, struct host **list,
873
unsigned int vers, const char *options,
874
unsigned int random_selection)
876
struct host *this, *last, *first;
877
struct host *new = NULL;
878
unsigned int proximity, selected_version = 0;
879
unsigned int v2_tcp_count, v3_tcp_count, v4_tcp_count;
880
unsigned int v2_udp_count, v3_udp_count, v4_udp_count;
881
unsigned int max_udp_count, max_tcp_count, max_count;
887
/* Use closest hosts to choose NFS version */
891
/* Get proximity of first entry after local entries */
893
while (this && this->proximity == PROXIMITY_LOCAL)
897
* Check for either a list containing only proximity local hosts
898
* or a single host entry whose proximity isn't local. If so
899
* return immediately as we don't want to add probe latency for
900
* the common case of a single filesystem mount request.
902
if (!this || !this->next)
905
proximity = this->proximity;
909
struct host *next = this->next;
911
if (this->proximity != proximity)
915
status = get_vers_and_cost(logopt, this, vers,
916
options, random_selection);
921
proximity = next->proximity;
923
delete_host(list, this);
930
* The list of hosts that aren't proximity local may now
931
* be empty if we haven't been able probe any so we need
932
* to check again for a list containing only proximity
940
/* Select NFS version of highest number of closest servers */
942
v4_tcp_count = v3_tcp_count = v2_tcp_count = 0;
943
v4_udp_count = v3_udp_count = v2_udp_count = 0;
947
if (this->version & NFS4_TCP_SUPPORTED)
950
if (this->version & NFS3_TCP_SUPPORTED)
953
if (this->version & NFS2_TCP_SUPPORTED)
956
if (this->version & NFS4_UDP_SUPPORTED)
959
if (this->version & NFS3_UDP_SUPPORTED)
962
if (this->version & NFS2_UDP_SUPPORTED)
966
} while (this && this != last);
968
max_tcp_count = mmax(v4_tcp_count, v3_tcp_count, v2_tcp_count);
969
max_udp_count = mmax(v4_udp_count, v3_udp_count, v2_udp_count);
970
max_count = max(max_tcp_count, max_udp_count);
972
if (max_count == v4_tcp_count) {
973
selected_version = NFS4_TCP_SUPPORTED;
975
"selected subset of hosts that support NFS4 over TCP");
976
} else if (max_count == v3_tcp_count) {
977
selected_version = NFS3_TCP_SUPPORTED;
979
"selected subset of hosts that support NFS3 over TCP");
980
} else if (max_count == v2_tcp_count) {
981
selected_version = NFS2_TCP_SUPPORTED;
983
"selected subset of hosts that support NFS2 over TCP");
984
} else if (max_count == v4_udp_count) {
985
selected_version = NFS4_UDP_SUPPORTED;
987
"selected subset of hosts that support NFS4 over UDP");
988
} else if (max_count == v3_udp_count) {
989
selected_version = NFS3_UDP_SUPPORTED;
991
"selected subset of hosts that support NFS3 over UDP");
992
} else if (max_count == v2_udp_count) {
993
selected_version = NFS2_UDP_SUPPORTED;
995
"selected subset of hosts that support NFS2 over UDP");
998
/* Add local and hosts with selected version to new list */
1001
struct host *next = this->next;
1002
if (this->version & selected_version ||
1003
this->proximity == PROXIMITY_LOCAL) {
1004
this->version = selected_version;
1005
remove_host(list, this);
1006
add_host(&new, this);
1009
} while (this && this != last);
1012
* Now go through rest of list and check for chosen version
1013
* and add to new list if selected version is supported.
1019
struct host *next = this->next;
1021
remove_host(list, this);
1022
add_host(&new, this);
1024
status = get_supported_ver_and_cost(logopt, this,
1025
selected_version, options,
1028
this->version = selected_version;
1029
remove_host(list, this);
1030
add_host(&new, this);
1036
free_host_list(list);
1042
static int add_new_host(struct host **list,
1043
const char *host, unsigned int weight,
1044
struct addrinfo *host_addr)
1050
prx = get_proximity(host_addr->ai_addr);
1052
* If we tried to add an IPv6 address and we don't have IPv6
1053
* support return success in the hope of getting an IPv4
1056
if (prx == PROXIMITY_UNSUPPORTED)
1058
if (prx == PROXIMITY_ERROR)
1061
addr_len = sizeof(struct sockaddr);
1062
new = new_host(host, host_addr->ai_addr, addr_len, prx, weight);
1066
if (!add_host(list, new)) {
1074
static int add_host_addrs(struct host **list, const char *host, unsigned int weight)
1076
struct addrinfo hints, *ni, *this;
1079
memset(&hints, 0, sizeof(hints));
1080
hints.ai_flags = AI_NUMERICHOST;
1081
hints.ai_family = AF_UNSPEC;
1082
hints.ai_socktype = SOCK_DGRAM;
1084
ret = getaddrinfo(host, NULL, &hints, &ni);
1090
ret = add_new_host(list, host, weight, this);
1093
this = this->ai_next;
1099
memset(&hints, 0, sizeof(hints));
1100
hints.ai_flags = AI_ADDRCONFIG;
1101
hints.ai_family = AF_UNSPEC;
1102
hints.ai_socktype = SOCK_DGRAM;
1104
ret = getaddrinfo(host, NULL, &hints, &ni);
1106
error(LOGOPT_ANY, "hostname lookup failed: %s",
1113
ret = add_new_host(list, host, weight, this);
1116
this = this->ai_next;
1123
static int add_path(struct host *hosts, const char *path, int len)
1128
tmp = alloca(len + 1);
1132
strncpy(tmp, path, len);
1149
static int add_local_path(struct host **hosts, const char *path)
1158
new = malloc(sizeof(struct host));
1164
memset(new, 0, sizeof(struct host));
1167
new->proximity = PROXIMITY_LOCAL;
1168
new->version = NFS_VERS_MASK;
1171
new->weight = new->cost = 0;
1173
add_host(hosts, new);
1178
static char *seek_delim(const char *s)
1183
delim = strpbrk(p, "(, \t:");
1184
if (delim && *delim != ':')
1192
if (!strncmp(p, ":/", 2))
1200
int parse_location(unsigned logopt, struct host **hosts, const char *list)
1202
char *str, *p, *delim;
1203
unsigned int empty = 1;
1218
p += strspn(p, " \t,");
1219
delim = seek_delim(p);
1222
if (*delim == '(') {
1223
char *w = delim + 1;
1227
delim = strchr(w, ')');
1235
if (*delim == ':') {
1241
/* Oh boy - might have spaces in the path */
1243
while (*next && strncmp(next, ":/", 2))
1246
/* No spaces in host names at least */
1249
(*next != ' ' && *next != '\t'))
1255
if (!add_host_addrs(hosts, p, weight)) {
1262
if (!add_path(*hosts, path, strlen(path))) {
1263
free_host_list(hosts);
1268
if (!add_local_path(hosts, path)) {
1273
} else if (*delim != '\0') {
1277
if (!add_host_addrs(hosts, p, weight)) {
1285
/* syntax error - no mount path */
1286
free_host_list(hosts);
1298
void dump_host_list(struct host *hosts)
1307
logmsg("name %s path %s version %x proximity %u weight %u cost %u",
1308
this->name, this->path, this->version,
1309
this->proximity, this->weight, this->cost);