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,
355
unsigned int options)
358
struct sockaddr *tmp2;
368
tmp2 = malloc(addr_len);
373
memcpy(tmp2, addr, addr_len);
375
new = malloc(sizeof(struct host));
382
memset(new, 0, sizeof(struct host));
385
new->addr_len = addr_len;
387
new->proximity = proximity;
388
new->weight = weight;
389
new->options = options;
394
static int add_host(struct host **list, struct host *host)
396
struct host *this, *last;
406
if (this->proximity >= host->proximity)
414
if (this->proximity != host->proximity)
416
if (this->cost >= host->cost)
435
static void free_host(struct host *host)
443
static void remove_host(struct host **hosts, struct host *host)
445
struct host *last, *this;
447
if (host == *hosts) {
448
*hosts = (*hosts)->next;
465
last->next = this->next;
471
static void delete_host(struct host **hosts, struct host *host)
473
remove_host(hosts, host);
478
void free_host_list(struct host **list)
484
struct host *next = this->next;
491
static unsigned short get_port_option(const char *options)
499
start = strstr(options, "port=");
503
char optport[30], *opteq, *end;
506
end = strchr(start, ',');
507
len = end ? end - start : strlen(start);
508
strncpy(optport, start, len);
510
opteq = strchr(optport, '=');
512
port = atoi(opteq + 1);
518
return (unsigned short) port;
521
static unsigned int get_nfs_info(unsigned logopt, struct host *host,
522
struct conn_info *pm_info, struct conn_info *rpc_info,
523
const char *proto, unsigned int version,
526
char *have_port_opt = options ? strstr(options, "port=") : NULL;
527
unsigned int random_selection = host->options & MOUNT_FLAG_RANDOM_SELECT;
528
unsigned int use_weight_only = host->options & MOUNT_FLAG_USE_WEIGHT_ONLY;
529
socklen_t len = INET6_ADDRSTRLEN;
532
struct timeval start, end;
534
unsigned int supported = 0;
536
int status, count = 0;
539
debug(logopt, "called with host %s(%s) proto %s version 0x%x",
540
host->name, get_addr_string(host->addr, buf, len),
544
"called for host %s proto %s version 0x%x",
545
host->name, proto, version);
547
memset(&parms, 0, sizeof(struct pmap));
549
parms.pm_prog = NFS_PROGRAM;
551
/* Try to prode UDP first to conserve socket space */
552
rpc_info->proto = getprotobyname(proto);
553
if (!rpc_info->proto)
556
if (!(version & NFS4_REQUESTED))
559
if (!(rpc_info->port = get_port_option(options)))
562
if (rpc_info->proto->p_proto == IPPROTO_UDP)
563
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
565
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
567
gettimeofday(&start, &tz);
568
status = rpc_ping_proto(rpc_info);
569
gettimeofday(&end, &tz);
572
if (random_selection) {
573
/* Random value between 0 and 1 */
574
reply = ((float) random())/((float) RAND_MAX+1);
576
"nfs v4 random selection time: %f", reply);
578
reply = elapsed(start, end);
579
debug(logopt, "nfs v4 rpc ping time: %f", reply);
583
supported = NFS4_SUPPORTED;
588
if (!have_port_opt) {
589
status = rpc_portmap_getclient(pm_info,
590
host->name, host->addr, host->addr_len,
591
proto, RPC_CLOSE_DEFAULT);
596
if (!(version & NFS3_REQUESTED))
600
if (!(rpc_info->port = get_port_option(options)))
603
parms.pm_prot = rpc_info->proto->p_proto;
604
parms.pm_vers = NFS3_VERSION;
605
rpc_info->port = rpc_portmap_getport(pm_info, &parms);
610
if (rpc_info->proto->p_proto == IPPROTO_UDP)
611
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
613
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
615
gettimeofday(&start, &tz);
616
status = rpc_ping_proto(rpc_info);
617
gettimeofday(&end, &tz);
620
if (random_selection) {
621
/* Random value between 0 and 1 */
622
reply = ((float) random())/((float) RAND_MAX+1);
624
"nfs v3 random selection time: %f", reply);
626
reply = elapsed(start, end);
627
debug(logopt, "nfs v3 rpc ping time: %f", reply);
631
supported |= NFS3_SUPPORTED;
636
if (!(version & NFS2_REQUESTED))
640
if (!(rpc_info->port = get_port_option(options)))
643
parms.pm_prot = rpc_info->proto->p_proto;
644
parms.pm_vers = NFS2_VERSION;
645
rpc_info->port = rpc_portmap_getport(pm_info, &parms);
650
if (rpc_info->proto->p_proto == IPPROTO_UDP)
651
status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
653
status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
655
gettimeofday(&start, &tz);
656
status = rpc_ping_proto(rpc_info);
657
gettimeofday(&end, &tz);
660
if (random_selection) {
661
/* Random value between 0 and 1 */
662
reply = ((float) random())/((float) RAND_MAX+1);
664
"nfs v2 random selection time: %f", reply);
666
reply = elapsed(start, end);;
667
debug(logopt, "nfs v2 rpc ping time: %f", reply);
671
supported |= NFS2_SUPPORTED;
676
if (rpc_info->proto->p_proto == IPPROTO_UDP) {
677
rpc_destroy_udp_client(rpc_info);
678
rpc_destroy_udp_client(pm_info);
680
rpc_destroy_tcp_client(rpc_info);
681
rpc_destroy_tcp_client(pm_info);
686
* Average response time to 7 significant places as
692
host->cost = (unsigned long) ((taken * 1000000) / count);
694
/* Allow for user bias */
696
host->cost *= (host->weight + 1);
698
debug(logopt, "host %s cost %ld weight %d",
699
host->name, host->cost, host->weight);
705
static int get_vers_and_cost(unsigned logopt, struct host *host,
706
unsigned int version, const char *options)
708
struct conn_info pm_info, rpc_info;
709
time_t timeout = RPC_TIMEOUT;
710
unsigned int supported, vers = (NFS_VERS_MASK | NFS4_VERS_MASK);
713
memset(&pm_info, 0, sizeof(struct conn_info));
714
memset(&rpc_info, 0, sizeof(struct conn_info));
716
if (host->proximity == PROXIMITY_NET)
717
timeout = RPC_TIMEOUT * 2;
718
else if (host->proximity == PROXIMITY_OTHER)
719
timeout = RPC_TIMEOUT * 8;
721
rpc_info.host = host->name;
722
rpc_info.addr = host->addr;
723
rpc_info.addr_len = host->addr_len;
724
rpc_info.program = NFS_PROGRAM;
725
rpc_info.timeout.tv_sec = timeout;
726
rpc_info.close_option = RPC_CLOSE_DEFAULT;
727
rpc_info.client = NULL;
731
if (version & UDP_REQUESTED) {
732
supported = get_nfs_info(logopt, host,
733
&pm_info, &rpc_info, "udp", vers, options);
736
host->version |= (supported << 8);
740
if (version & TCP_REQUESTED) {
741
supported = get_nfs_info(logopt, host,
742
&pm_info, &rpc_info, "tcp", vers, options);
745
host->version |= supported;
752
static int get_supported_ver_and_cost(unsigned logopt, struct host *host,
753
unsigned int version, const char *options)
755
char *have_port_opt = options ? strstr(options, "port=") : NULL;
756
unsigned int random_selection = host->options & MOUNT_FLAG_RANDOM_SELECT;
757
unsigned int use_weight_only = host->options & MOUNT_FLAG_USE_WEIGHT_ONLY;
758
socklen_t len = INET6_ADDRSTRLEN;
760
struct conn_info pm_info, rpc_info;
764
struct timeval start, end;
767
time_t timeout = RPC_TIMEOUT;
771
debug(logopt, "called with host %s(%s) version 0x%x",
772
host->name, get_addr_string(host->addr, buf, len),
775
debug(logopt, "called with host %s version 0x%x",
776
host->name, version);
778
memset(&pm_info, 0, sizeof(struct conn_info));
779
memset(&rpc_info, 0, sizeof(struct conn_info));
780
memset(&parms, 0, sizeof(struct pmap));
782
if (host->proximity == PROXIMITY_NET)
783
timeout = RPC_TIMEOUT * 2;
784
else if (host->proximity == PROXIMITY_OTHER)
785
timeout = RPC_TIMEOUT * 8;
787
rpc_info.host = host->name;
788
rpc_info.addr = host->addr;
789
rpc_info.addr_len = host->addr_len;
790
rpc_info.program = NFS_PROGRAM;
791
rpc_info.timeout.tv_sec = timeout;
792
rpc_info.close_option = RPC_CLOSE_DEFAULT;
793
rpc_info.client = NULL;
795
parms.pm_prog = NFS_PROGRAM;
798
* The version passed in is the version as defined in
799
* include/replicated.h. However, the version we want to send
800
* off to the rpc calls should match the program version of NFS.
801
* So, we do the conversion here.
803
if (version & UDP_SELECTED_MASK) {
820
crit(logopt, "called with invalid version: 0x%x\n", version);
824
rpc_info.proto = getprotobyname(proto);
830
parms.pm_vers = vers;
831
if (have_port_opt || (vers & NFS4_VERSION)) {
832
if (!(rpc_info.port = get_port_option(options)))
835
int ret = rpc_portmap_getclient(&pm_info,
836
host->name, host->addr, host->addr_len,
837
proto, RPC_CLOSE_DEFAULT);
841
parms.pm_prot = rpc_info.proto->p_proto;
842
rpc_info.port = rpc_portmap_getport(&pm_info, &parms);
847
if (rpc_info.proto->p_proto == IPPROTO_UDP)
848
status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
850
status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
852
gettimeofday(&start, &tz);
853
status = rpc_ping_proto(&rpc_info);
854
gettimeofday(&end, &tz);
856
if (random_selection) {
857
/* Random value between 0 and 1 */
858
taken = ((float) random())/((float) RAND_MAX+1);
859
debug(logopt, "random selection time %f", taken);
861
taken = elapsed(start, end);
862
debug(logopt, "rpc ping time %f", taken);
867
if (rpc_info.proto->p_proto == IPPROTO_UDP) {
868
rpc_destroy_udp_client(&rpc_info);
869
rpc_destroy_udp_client(&pm_info);
871
rpc_destroy_tcp_client(&rpc_info);
872
rpc_destroy_tcp_client(&pm_info);
876
/* Response time to 7 significant places as integral type. */
880
host->cost = (unsigned long) (taken * 1000000);
882
/* Allow for user bias */
884
host->cost *= (host->weight + 1);
886
debug(logopt, "cost %ld weight %d", host->cost, host->weight);
894
int prune_host_list(unsigned logopt, struct host **list,
895
unsigned int vers, const char *options)
897
struct host *this, *last, *first;
898
struct host *new = NULL;
899
unsigned int proximity, selected_version = 0;
900
unsigned int v2_tcp_count, v3_tcp_count, v4_tcp_count;
901
unsigned int v2_udp_count, v3_udp_count, v4_udp_count;
902
unsigned int max_udp_count, max_tcp_count, max_count;
908
/* Use closest hosts to choose NFS version */
912
/* Get proximity of first entry after local entries */
914
while (this && this->proximity == PROXIMITY_LOCAL)
919
* Check for either a list containing only proximity local hosts
920
* or a single host entry whose proximity isn't local. If so
921
* return immediately as we don't want to add probe latency for
922
* the common case of a single filesystem mount request.
924
if (!this || !this->next)
927
proximity = this->proximity;
929
struct host *next = this->next;
931
if (this->proximity != proximity)
935
status = get_vers_and_cost(logopt, this, vers, options);
940
proximity = next->proximity;
942
delete_host(list, this);
949
* The list of hosts that aren't proximity local may now
950
* be empty if we haven't been able probe any so we need
951
* to check again for a list containing only proximity
959
/* Select NFS version of highest number of closest servers */
961
v4_tcp_count = v3_tcp_count = v2_tcp_count = 0;
962
v4_udp_count = v3_udp_count = v2_udp_count = 0;
966
if (this->version & NFS4_TCP_SUPPORTED)
969
if (this->version & NFS3_TCP_SUPPORTED)
972
if (this->version & NFS2_TCP_SUPPORTED)
975
if (this->version & NFS4_UDP_SUPPORTED)
978
if (this->version & NFS3_UDP_SUPPORTED)
981
if (this->version & NFS2_UDP_SUPPORTED)
985
} while (this && this != last);
987
max_tcp_count = mmax(v4_tcp_count, v3_tcp_count, v2_tcp_count);
988
max_udp_count = mmax(v4_udp_count, v3_udp_count, v2_udp_count);
989
max_count = max(max_tcp_count, max_udp_count);
991
if (max_count == v4_tcp_count) {
992
selected_version = NFS4_TCP_SUPPORTED;
994
"selected subset of hosts that support NFS4 over TCP");
995
} else if (max_count == v3_tcp_count) {
996
selected_version = NFS3_TCP_SUPPORTED;
998
"selected subset of hosts that support NFS3 over TCP");
999
} else if (max_count == v2_tcp_count) {
1000
selected_version = NFS2_TCP_SUPPORTED;
1002
"selected subset of hosts that support NFS2 over TCP");
1003
} else if (max_count == v4_udp_count) {
1004
selected_version = NFS4_UDP_SUPPORTED;
1006
"selected subset of hosts that support NFS4 over UDP");
1007
} else if (max_count == v3_udp_count) {
1008
selected_version = NFS3_UDP_SUPPORTED;
1010
"selected subset of hosts that support NFS3 over UDP");
1011
} else if (max_count == v2_udp_count) {
1012
selected_version = NFS2_UDP_SUPPORTED;
1014
"selected subset of hosts that support NFS2 over UDP");
1017
/* Add local and hosts with selected version to new list */
1020
struct host *next = this->next;
1021
if (this->version & selected_version ||
1022
this->proximity == PROXIMITY_LOCAL) {
1023
this->version = selected_version;
1024
remove_host(list, this);
1025
add_host(&new, this);
1028
} while (this && this != last);
1031
* Now go through rest of list and check for chosen version
1032
* and add to new list if selected version is supported.
1038
struct host *next = this->next;
1040
remove_host(list, this);
1041
add_host(&new, this);
1043
status = get_supported_ver_and_cost(logopt, this,
1044
selected_version, options);
1046
this->version = selected_version;
1047
remove_host(list, this);
1048
add_host(&new, this);
1054
free_host_list(list);
1060
static int add_new_host(struct host **list,
1061
const char *host, unsigned int weight,
1062
struct addrinfo *host_addr, unsigned int options)
1069
* If we are using random selection we pretend all hosts are at
1070
* the same proximity so hosts further away don't get excluded.
1071
* We can't use PROXIMITY_LOCAL or we won't perform an RPC ping
1072
* to remove hosts that may be down.
1074
if (options & MOUNT_FLAG_RANDOM_SELECT)
1075
prx = PROXIMITY_SUBNET;
1077
prx = get_proximity(host_addr->ai_addr);
1079
* If we want the weight to be the determining factor
1080
* when selecting a host then all hosts must have the
1081
* same proximity. However, if this is the local machine
1082
* it should always be used since it is certainly available.
1084
if (prx != PROXIMITY_LOCAL &&
1085
(options & MOUNT_FLAG_USE_WEIGHT_ONLY))
1086
prx = PROXIMITY_SUBNET;
1090
* If we tried to add an IPv6 address and we don't have IPv6
1091
* support return success in the hope of getting an IPv4
1094
if (prx == PROXIMITY_UNSUPPORTED)
1096
if (prx == PROXIMITY_ERROR)
1099
addr_len = sizeof(struct sockaddr);
1100
new = new_host(host, host_addr->ai_addr, addr_len, prx, weight, options);
1104
if (!add_host(list, new)) {
1112
static int add_host_addrs(struct host **list, const char *host,
1113
unsigned int weight, unsigned int options)
1115
struct addrinfo hints, *ni, *this;
1118
memset(&hints, 0, sizeof(hints));
1119
hints.ai_flags = AI_NUMERICHOST;
1120
hints.ai_family = AF_UNSPEC;
1121
hints.ai_socktype = SOCK_DGRAM;
1123
ret = getaddrinfo(host, NULL, &hints, &ni);
1129
ret = add_new_host(list, host, weight, this, options);
1132
this = this->ai_next;
1138
memset(&hints, 0, sizeof(hints));
1139
hints.ai_flags = AI_ADDRCONFIG;
1140
hints.ai_family = AF_UNSPEC;
1141
hints.ai_socktype = SOCK_DGRAM;
1143
ret = getaddrinfo(host, NULL, &hints, &ni);
1145
error(LOGOPT_ANY, "hostname lookup failed: %s",
1152
ret = add_new_host(list, host, weight, this, options);
1155
this = this->ai_next;
1162
static int add_path(struct host *hosts, const char *path, int len)
1167
tmp = alloca(len + 1);
1171
strncpy(tmp, path, len);
1188
static int add_local_path(struct host **hosts, const char *path)
1197
new = malloc(sizeof(struct host));
1203
memset(new, 0, sizeof(struct host));
1206
new->proximity = PROXIMITY_LOCAL;
1207
new->version = NFS_VERS_MASK;
1210
new->weight = new->cost = 0;
1212
add_host(hosts, new);
1217
static char *seek_delim(const char *s)
1222
delim = strpbrk(p, "(, \t:");
1223
if (delim && *delim != ':')
1231
if (!strncmp(p, ":/", 2))
1239
int parse_location(unsigned logopt, struct host **hosts,
1240
const char *list, unsigned int options)
1242
char *str, *p, *delim;
1243
unsigned int empty = 1;
1258
p += strspn(p, " \t,");
1259
delim = seek_delim(p);
1262
if (*delim == '(') {
1263
char *w = delim + 1;
1267
delim = strchr(w, ')');
1275
if (*delim == ':') {
1281
/* Oh boy - might have spaces in the path */
1283
while (*next && strncmp(next, ":/", 2))
1286
/* No spaces in host names at least */
1289
(*next != ' ' && *next != '\t'))
1295
if (!add_host_addrs(hosts, p, weight, options)) {
1302
if (!add_path(*hosts, path, strlen(path))) {
1303
free_host_list(hosts);
1308
if (!add_local_path(hosts, path)) {
1313
} else if (*delim != '\0') {
1317
if (!add_host_addrs(hosts, p, weight, options)) {
1325
/* syntax error - no mount path */
1326
free_host_list(hosts);
1338
void dump_host_list(struct host *hosts)
1347
logmsg("name %s path %s version %x proximity %u weight %u cost %u",
1348
this->name, this->path, this->version,
1349
this->proximity, this->weight, this->cost);