~ubuntu-branches/ubuntu/utopic/autofs/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/0001-Remove-kernel-mount.nfs-version-checks-on-Debian-Ubu.patch/modules/replicated.c

  • Committer: Package Import Robot
  • Author(s): Dmitrijs Ledkovs
  • Date: 2012-06-20 12:34:50 UTC
  • Revision ID: package-import@ubuntu.com-20120620123450-j5ril7l42dzr8jg0
Tags: 5.0.6-2ubuntu1
* Merge from Debian unstable (LP: #1006509), remaining changes:
  - debian/autofs.upstart - upstart job
  - debian/patches/16group_buffer_size.patch:
    + prevents package to eat the cpu if you have large groups.

* Dropped changes:
  - 17ld.patch, applied in debian as link-daemon-with-lpthread.patch
    and save-hesiod-libs-correctly.patch

* This upload renames autofs5 back to autofs, following debian's name
  change.

* Added patch descriptions/headers to 16group_buffer_size

* debian/patches/0001-Remove-kernel-mount.nfs-version-checks-on-Debian-Ubu.patch:
  Remove kernel & mount.nfs version checks on Debian/Ubuntu. (LP: #1016673)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ----------------------------------------------------------------------- *
 
2
 *
 
3
 *  repl_list.h - routines for replicated mount server selection
 
4
 *
 
5
 *   Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved
 
6
 *   Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved
 
7
 *
 
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.
 
13
 *
 
14
 * A priority ordered list of hosts is created by using the following
 
15
 * selection rules.
 
16
 *
 
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.
 
26
 *
 
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
 
32
 *      host order.
 
33
 *
 
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
 
36
 *      in 2 above.
 
37
 *
 
38
 * ----------------------------------------------------------------------- */
 
39
 
 
40
#ifndef _GNU_SOURCE
 
41
#define _GNU_SOURCE
 
42
#endif
 
43
 
 
44
#include <string.h>
 
45
#include <stdlib.h>
 
46
#include <sys/errno.h>
 
47
#include <sys/types.h>
 
48
#include <stdint.h>
 
49
#include <sys/ioctl.h>
 
50
#include <sys/socket.h>
 
51
#include <arpa/inet.h>
 
52
#include <net/if.h>
 
53
#include <netinet/in.h>
 
54
#include <netdb.h>
 
55
 
 
56
#include "rpc_subs.h"
 
57
#include "replicated.h"
 
58
#include "automount.h"
 
59
 
 
60
#ifndef MAX_ERR_BUF
 
61
#define MAX_ERR_BUF             512
 
62
#endif
 
63
 
 
64
#define MAX_IFC_BUF             2048
 
65
static int volatile ifc_buf_len = MAX_IFC_BUF;
 
66
static int volatile ifc_last_len = 0;
 
67
 
 
68
#define MASK_A  0x7F000000
 
69
#define MASK_B  0xBFFF0000
 
70
#define MASK_C  0xDFFFFF00
 
71
 
 
72
/* Get numeric value of the n bits starting at position p */
 
73
#define getbits(x, p, n)        ((x >> (p + 1 - n)) & ~(~0 << n))
 
74
 
 
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))
 
77
 
 
78
unsigned int ipv6_mask_cmp(uint32_t *host, uint32_t *iface, uint32_t *mask)
 
79
{
 
80
        unsigned int ret = 1;
 
81
        unsigned int i;
 
82
 
 
83
        for (i = 0; i < 4; i++) {
 
84
                if ((host[i] & mask[i]) != (iface[i] & mask[i])) {
 
85
                        ret = 0;
 
86
                        break;
 
87
                }
 
88
        }
 
89
        return ret;
 
90
}
 
91
 
 
92
void seed_random(void)
 
93
{
 
94
        int fd;
 
95
        unsigned int seed;
 
96
 
 
97
        fd = open_fd("/dev/urandom", O_RDONLY);
 
98
        if (fd < 0) {
 
99
                srandom(time(NULL));
 
100
                return;
 
101
        }
 
102
 
 
103
        if (read(fd, &seed, sizeof(seed)) != -1)
 
104
                srandom(seed);
 
105
        else
 
106
                srandom(time(NULL));
 
107
 
 
108
        close(fd);
 
109
 
 
110
        return;
 
111
}
 
112
 
 
113
static int alloc_ifreq(struct ifconf *ifc, int sock)
 
114
{
 
115
        int ret, lastlen = ifc_last_len, len = ifc_buf_len;
 
116
        char err_buf[MAX_ERR_BUF], *buf;
 
117
 
 
118
        while (1) {
 
119
                buf = malloc(len);
 
120
                if (!buf) {
 
121
                        char *estr = strerror_r(errno, err_buf, MAX_ERR_BUF);
 
122
                        logerr("malloc: %s", estr);
 
123
                        return 0;
 
124
                }
 
125
 
 
126
                ifc->ifc_len = len;
 
127
                ifc->ifc_req = (struct ifreq *) buf;
 
128
 
 
129
                ret = ioctl(sock, SIOCGIFCONF, ifc);
 
130
                if (ret == -1) {
 
131
                        char *estr = strerror_r(errno, err_buf, MAX_ERR_BUF);
 
132
                        logerr("ioctl: %s", estr);
 
133
                        free(buf);
 
134
                        return 0;
 
135
                }
 
136
 
 
137
                if (ifc->ifc_len <= lastlen)
 
138
                        break;
 
139
 
 
140
                lastlen = ifc->ifc_len;
 
141
                len += MAX_IFC_BUF;
 
142
                free(buf);
 
143
        }
 
144
 
 
145
        if (lastlen != ifc_last_len) {
 
146
                ifc_last_len = lastlen;
 
147
                ifc_buf_len = len;
 
148
        }
 
149
 
 
150
        return 1;
 
151
}
 
152
 
 
153
static unsigned int get_proximity(struct sockaddr *host_addr)
 
154
{
 
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;
 
159
        int addr_len;
 
160
        char buf[MAX_ERR_BUF], *ptr;
 
161
        struct ifconf ifc;
 
162
        struct ifreq *ifr, nmptr;
 
163
        int sock, ret, i;
 
164
        uint32_t mask, ha, ia, *mask6, *ha6, *ia6;
 
165
 
 
166
        addr = NULL;
 
167
        addr6 = NULL;
 
168
        hst_addr = NULL;
 
169
        hst6_addr = NULL;
 
170
        mask6 = NULL;
 
171
        ha6 = NULL;
 
172
        ia6 = NULL;
 
173
 
 
174
        switch (host_addr->sa_family) {
 
175
        case AF_INET:
 
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);
 
180
                break;
 
181
 
 
182
        case AF_INET6:
 
183
#ifndef WITH_LIBTIRPC
 
184
                return PROXIMITY_UNSUPPORTED;
 
185
#else
 
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);
 
190
                break;
 
191
#endif
 
192
 
 
193
        default:
 
194
                return PROXIMITY_ERROR;
 
195
        }
 
196
 
 
197
        sock = open_sock(AF_INET, SOCK_DGRAM, 0);
 
198
        if (sock < 0) {
 
199
                char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 
200
                logerr("socket creation failed: %s", estr);
 
201
                return PROXIMITY_ERROR;
 
202
        }
 
203
 
 
204
        if (!alloc_ifreq(&ifc, sock)) {
 
205
                close(sock);
 
206
                return PROXIMITY_ERROR;
 
207
        }
 
208
 
 
209
        /* For each interface */
 
210
 
 
211
        /* Is the address a local interface */
 
212
        i = 0;
 
213
        ptr = (char *) &ifc.ifc_buf[0];
 
214
 
 
215
        while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) {
 
216
                ifr = (struct ifreq *) ptr;
 
217
 
 
218
                switch (ifr->ifr_addr.sa_family) {
 
219
                case AF_INET:
 
220
                        if (host_addr->sa_family == AF_INET6)
 
221
                                break;
 
222
                        if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
 
223
                        ret = memcmp(&if_addr->sin_addr, hst_addr, addr_len);
 
224
                        if (!ret) {
 
225
                                close(sock);
 
226
                                free(ifc.ifc_req);
 
227
                                return PROXIMITY_LOCAL;
 
228
                        }
 
229
                        break;
 
230
 
 
231
                case AF_INET6:
 
232
#ifndef WITH_LIBTIRPC
 
233
                        return PROXIMITY_UNSUPPORTED;
 
234
#else
 
235
                        if (host_addr->sa_family == AF_INET)
 
236
                                break;
 
237
 
 
238
                        if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr;
 
239
                        ret = memcmp(&if6_addr->sin6_addr, hst6_addr, addr_len);
 
240
                        if (!ret) {
 
241
                                close(sock);
 
242
                                free(ifc.ifc_req);
 
243
                                return PROXIMITY_LOCAL;
 
244
                        }
 
245
#endif
 
246
 
 
247
                default:
 
248
                        break;
 
249
                }
 
250
 
 
251
                i++;
 
252
                ptr = (char *) &ifc.ifc_req[i];
 
253
        }
 
254
 
 
255
        i = 0;
 
256
        ptr = (char *) &ifc.ifc_buf[0];
 
257
 
 
258
        while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) {
 
259
                ifr = (struct ifreq *) ptr;
 
260
 
 
261
                nmptr = *ifr;
 
262
                ret = ioctl(sock, SIOCGIFNETMASK, &nmptr);
 
263
                if (ret == -1) {
 
264
                        char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 
265
                        logerr("ioctl: %s", estr);
 
266
                        close(sock);
 
267
                        free(ifc.ifc_req);
 
268
                        return PROXIMITY_ERROR;
 
269
                }
 
270
 
 
271
                switch (ifr->ifr_addr.sa_family) {
 
272
                case AF_INET:
 
273
                        if (host_addr->sa_family == AF_INET6)
 
274
                                break;
 
275
                        if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
 
276
                        ia =  ntohl((uint32_t) if_addr->sin_addr.s_addr);
 
277
 
 
278
                        /* Is the address within a localiy attached subnet */
 
279
 
 
280
                        msk_addr = (struct sockaddr_in *) &nmptr.ifr_netmask;
 
281
                        mask = ntohl((uint32_t) msk_addr->sin_addr.s_addr);
 
282
 
 
283
                        if ((ia & mask) == (ha & mask)) {
 
284
                                close(sock);
 
285
                                free(ifc.ifc_req);
 
286
                                return PROXIMITY_SUBNET;
 
287
                        }
 
288
 
 
289
                        /*
 
290
                         * Is the address within a local ipv4 network.
 
291
                         *
 
292
                         * Bit position 31 == 0 => class A.
 
293
                         * Bit position 30 == 0 => class B.
 
294
                         * Bit position 29 == 0 => class C.
 
295
                         */
 
296
 
 
297
                        if (!getbits(ia, 31, 1))
 
298
                                mask = MASK_A;
 
299
                        else if (!getbits(ia, 30, 1))
 
300
                                mask = MASK_B;
 
301
                        else if (!getbits(ia, 29, 1))
 
302
                                mask = MASK_C;
 
303
                        else
 
304
                                break;
 
305
 
 
306
                        if ((ia & mask) == (ha & mask)) {
 
307
                                close(sock);
 
308
                                free(ifc.ifc_req);
 
309
                                return PROXIMITY_NET;
 
310
                        }
 
311
                        break;
 
312
 
 
313
                case AF_INET6:
 
314
#ifndef WITH_LIBTIRPC
 
315
                        return PROXIMITY_UNSUPPORTED;
 
316
#else
 
317
                        if (host_addr->sa_family == AF_INET)
 
318
                                break;
 
319
 
 
320
                        if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr;
 
321
                        ia6 = &if6_addr->sin6_addr.s6_addr32[0];
 
322
 
 
323
                        /* Is the address within the network of the interface */
 
324
 
 
325
                        msk6_addr = (struct sockaddr_in6 *) &nmptr.ifr_netmask;
 
326
                        mask6 = &msk6_addr->sin6_addr.s6_addr32[0];
 
327
 
 
328
                        if (ipv6_mask_cmp(ha6, ia6, mask6)) {
 
329
                                close(sock);
 
330
                                free(ifc.ifc_req);
 
331
                                return PROXIMITY_SUBNET;
 
332
                        }
 
333
 
 
334
                        /* How do we define "local network" in ipv6? */
 
335
#endif
 
336
                        break;
 
337
 
 
338
                default:
 
339
                        break;
 
340
                }
 
341
 
 
342
                i++;
 
343
                ptr = (char *) &ifc.ifc_req[i];
 
344
        }
 
345
 
 
346
        close(sock);
 
347
        free(ifc.ifc_req);
 
348
 
 
349
        return PROXIMITY_OTHER;
 
350
}
 
351
 
 
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)
 
356
{
 
357
        struct host *new;
 
358
        struct sockaddr *tmp2;
 
359
        char *tmp1;
 
360
 
 
361
        if (!name || !addr)
 
362
                return NULL;
 
363
 
 
364
        tmp1 = strdup(name);
 
365
        if (!tmp1)
 
366
                return NULL;
 
367
 
 
368
        tmp2 = malloc(addr_len);
 
369
        if (!tmp2) {
 
370
                free(tmp1);
 
371
                return NULL;
 
372
        }
 
373
        memcpy(tmp2, addr, addr_len);
 
374
 
 
375
        new = malloc(sizeof(struct host));
 
376
        if (!new) {
 
377
                free(tmp1);
 
378
                free(tmp2);
 
379
                return NULL;
 
380
        }
 
381
 
 
382
        memset(new, 0, sizeof(struct host));
 
383
 
 
384
        new->name = tmp1;
 
385
        new->addr_len = addr_len;
 
386
        new->addr = tmp2;
 
387
        new->proximity = proximity;
 
388
        new->weight = weight;
 
389
        new->options = options;
 
390
 
 
391
        return new;
 
392
}
 
393
 
 
394
static int add_host(struct host **list, struct host *host)
 
395
{
 
396
        struct host *this, *last;
 
397
 
 
398
        if (!*list) {
 
399
                *list = host;
 
400
                return 1;
 
401
        }
 
402
 
 
403
        this = *list;
 
404
        last = this;
 
405
        while (this) {
 
406
                if (this->proximity >= host->proximity)
 
407
                        break;
 
408
                last = this;
 
409
                this = this->next;
 
410
        }
 
411
 
 
412
        if (host->cost) {
 
413
                while (this) {
 
414
                        if (this->proximity != host->proximity)
 
415
                                break;
 
416
                        if (this->cost >= host->cost)
 
417
                                break;
 
418
                        last = this;
 
419
                        this = this->next;
 
420
                }
 
421
        }
 
422
 
 
423
        if (last == this) {
 
424
                host->next = last;
 
425
                *list = host;
 
426
                return 1;
 
427
        }
 
428
 
 
429
        last->next = host;
 
430
        host->next = this;
 
431
 
 
432
        return 1;
 
433
}
 
434
 
 
435
static void free_host(struct host *host)
 
436
{
 
437
        free(host->name);
 
438
        free(host->addr);
 
439
        free(host->path);
 
440
        free(host);
 
441
}
 
442
 
 
443
static void remove_host(struct host **hosts, struct host *host)
 
444
{
 
445
        struct host *last, *this;
 
446
 
 
447
        if (host == *hosts) {
 
448
                *hosts = (*hosts)->next;
 
449
                host->next = NULL;
 
450
                return;
 
451
        }
 
452
 
 
453
        this = *hosts;
 
454
        last = NULL;
 
455
        while (this) {
 
456
                if (this == host)
 
457
                        break;
 
458
                last = this;
 
459
                this = this->next;
 
460
        }
 
461
 
 
462
        if (!last || !this)
 
463
                return;
 
464
 
 
465
        last->next = this->next;
 
466
        host->next = NULL;
 
467
 
 
468
        return;
 
469
}
 
470
 
 
471
static void delete_host(struct host **hosts, struct host *host)
 
472
{
 
473
        remove_host(hosts, host);
 
474
        free_host(host);
 
475
        return;
 
476
}
 
477
 
 
478
void free_host_list(struct host **list)
 
479
{
 
480
        struct host *this;
 
481
 
 
482
        this = *list;
 
483
        while (this) {
 
484
                struct host *next = this->next;
 
485
                free_host(this);
 
486
                this = next;
 
487
        }
 
488
        *list = NULL;
 
489
}
 
490
 
 
491
static unsigned short get_port_option(const char *options)
 
492
{
 
493
        const char *start;
 
494
        long port = 0;
 
495
 
 
496
        if (!options)
 
497
                return NFS_PORT;
 
498
 
 
499
        start = strstr(options, "port=");
 
500
        if (!start)
 
501
                port = NFS_PORT;
 
502
        else {
 
503
                char optport[30], *opteq, *end;
 
504
                int len;
 
505
 
 
506
                end = strchr(start, ',');
 
507
                len = end ? end - start : strlen(start);
 
508
                strncpy(optport, start, len);
 
509
                optport[len] = '\0';
 
510
                opteq = strchr(optport, '=');
 
511
                if (opteq)
 
512
                        port = atoi(opteq + 1);
 
513
        }
 
514
 
 
515
        if (port < 0)
 
516
                port = 0;
 
517
 
 
518
        return (unsigned short) port;
 
519
}
 
520
 
 
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,
 
524
                         const char *options)
 
525
{
 
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;
 
530
        char buf[len + 1];
 
531
        struct pmap parms;
 
532
        struct timeval start, end;
 
533
        struct timezone tz;
 
534
        unsigned int supported = 0;
 
535
        double taken = 0;
 
536
        int status, count = 0;
 
537
 
 
538
        if (host->addr)
 
539
                debug(logopt, "called with host %s(%s) proto %s version 0x%x",
 
540
                      host->name, get_addr_string(host->addr, buf, len),
 
541
                      proto, version);
 
542
        else
 
543
                debug(logopt,
 
544
                      "called for host %s proto %s version 0x%x",
 
545
                      host->name, proto, version);
 
546
 
 
547
        memset(&parms, 0, sizeof(struct pmap));
 
548
 
 
549
        parms.pm_prog = NFS_PROGRAM;
 
550
 
 
551
        /* Try to prode UDP first to conserve socket space */
 
552
        rpc_info->proto = getprotobyname(proto);
 
553
        if (!rpc_info->proto)
 
554
                return 0;
 
555
 
 
556
        if (!(version & NFS4_REQUESTED))
 
557
                goto v3_ver;
 
558
 
 
559
        if (!(rpc_info->port = get_port_option(options)))
 
560
                goto v3_ver;
 
561
 
 
562
        if (rpc_info->proto->p_proto == IPPROTO_UDP)
 
563
                status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
 
564
        else
 
565
                status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
 
566
        if (status == -EHOSTUNREACH)
 
567
                return (unsigned int) status;
 
568
        else if (!status) {
 
569
                gettimeofday(&start, &tz);
 
570
                status = rpc_ping_proto(rpc_info);
 
571
                gettimeofday(&end, &tz);
 
572
                if (status == -ETIMEDOUT)
 
573
                        return (unsigned int) status;
 
574
                else if (status > 0) {
 
575
                        double reply;
 
576
                        if (random_selection) {
 
577
                                /* Random value between 0 and 1 */
 
578
                                reply = ((float) random())/((float) RAND_MAX+1);
 
579
                                debug(logopt,
 
580
                                      "nfs v4 random selection time: %f", reply);
 
581
                        } else {
 
582
                                reply = elapsed(start, end);
 
583
                                debug(logopt, "nfs v4 rpc ping time: %f", reply);
 
584
                        }
 
585
                        taken += reply;
 
586
                        count++;
 
587
                        supported = NFS4_SUPPORTED;
 
588
                }
 
589
        }
 
590
 
 
591
v3_ver:
 
592
        if (!have_port_opt) {
 
593
                status = rpc_portmap_getclient(pm_info,
 
594
                                host->name, host->addr, host->addr_len,
 
595
                                proto, RPC_CLOSE_DEFAULT);
 
596
                if (status == -EHOSTUNREACH) {
 
597
                        supported = status;
 
598
                        goto done_ver;
 
599
                } else if (status)
 
600
                        goto done_ver;
 
601
        }
 
602
 
 
603
        if (!(version & NFS3_REQUESTED))
 
604
                goto v2_ver;
 
605
 
 
606
        if (have_port_opt) {
 
607
                if (!(rpc_info->port = get_port_option(options)))
 
608
                        goto done_ver;
 
609
        } else {
 
610
                parms.pm_prot = rpc_info->proto->p_proto;
 
611
                parms.pm_vers = NFS3_VERSION;
 
612
                status = rpc_portmap_getport(pm_info, &parms, &rpc_info->port);
 
613
                if (status == -EHOSTUNREACH || status == -ETIMEDOUT) {
 
614
                        supported = status;
 
615
                        goto done_ver;
 
616
                } else if (status < 0)
 
617
                        goto v2_ver;
 
618
        }
 
619
 
 
620
        if (rpc_info->proto->p_proto == IPPROTO_UDP)
 
621
                status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
 
622
        else
 
623
                status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
 
624
        if (status == -EHOSTUNREACH) {
 
625
                supported = status;
 
626
                goto done_ver;
 
627
        } else if (!status) {
 
628
                gettimeofday(&start, &tz);
 
629
                status = rpc_ping_proto(rpc_info);
 
630
                gettimeofday(&end, &tz);
 
631
                if (status == -ETIMEDOUT) {
 
632
                        supported = status;
 
633
                        goto done_ver;
 
634
                } else if (status > 0) {
 
635
                        double reply;
 
636
                        if (random_selection) {
 
637
                                /* Random value between 0 and 1 */
 
638
                                reply = ((float) random())/((float) RAND_MAX+1);
 
639
                                debug(logopt,
 
640
                                      "nfs v3 random selection time: %f", reply);
 
641
                        } else {
 
642
                                reply = elapsed(start, end);
 
643
                                debug(logopt, "nfs v3 rpc ping time: %f", reply);
 
644
                        }
 
645
                        taken += reply;
 
646
                        count++;
 
647
                        supported |= NFS3_SUPPORTED;
 
648
                }
 
649
        }
 
650
 
 
651
v2_ver:
 
652
        if (!(version & NFS2_REQUESTED))
 
653
                goto done_ver;
 
654
 
 
655
        if (have_port_opt) {
 
656
                if (!(rpc_info->port = get_port_option(options)))
 
657
                        goto done_ver;
 
658
        } else {
 
659
                parms.pm_prot = rpc_info->proto->p_proto;
 
660
                parms.pm_vers = NFS2_VERSION;
 
661
                status = rpc_portmap_getport(pm_info, &parms, &rpc_info->port);
 
662
                if (status == -EHOSTUNREACH || status == -ETIMEDOUT) {
 
663
                        supported = status;
 
664
                        goto done_ver;
 
665
                } else if (status < 0)
 
666
                        goto done_ver;
 
667
        }
 
668
 
 
669
        if (rpc_info->proto->p_proto == IPPROTO_UDP)
 
670
                status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
 
671
        else
 
672
                status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
 
673
        if (status == -EHOSTUNREACH) {
 
674
                supported = status;
 
675
                goto done_ver;
 
676
        } else if (!status) {
 
677
                gettimeofday(&start, &tz);
 
678
                status = rpc_ping_proto(rpc_info);
 
679
                gettimeofday(&end, &tz);
 
680
                if (status == -ETIMEDOUT)
 
681
                        supported = status;
 
682
                else if (status > 0) {
 
683
                        double reply;
 
684
                        if (random_selection) {
 
685
                                /* Random value between 0 and 1 */
 
686
                                reply = ((float) random())/((float) RAND_MAX+1);
 
687
                                debug(logopt,
 
688
                                      "nfs v2 random selection time: %f", reply);
 
689
                        } else {
 
690
                                reply = elapsed(start, end);;
 
691
                                debug(logopt, "nfs v2 rpc ping time: %f", reply);
 
692
                        }
 
693
                        taken += reply;
 
694
                        count++;
 
695
                        supported |= NFS2_SUPPORTED;
 
696
                }
 
697
        }
 
698
 
 
699
done_ver:
 
700
        if (rpc_info->proto->p_proto == IPPROTO_UDP) {
 
701
                rpc_destroy_udp_client(rpc_info);
 
702
                rpc_destroy_udp_client(pm_info);
 
703
        } else {
 
704
                rpc_destroy_tcp_client(rpc_info);
 
705
                rpc_destroy_tcp_client(pm_info);
 
706
        }
 
707
 
 
708
        if (count) {
 
709
                /*
 
710
                 * Average response time to 7 significant places as
 
711
                 * integral type.
 
712
                 */
 
713
                if (use_weight_only)
 
714
                        host->cost = 1;
 
715
                else
 
716
                        host->cost = (unsigned long) ((taken * 1000000) / count);
 
717
 
 
718
                /* Allow for user bias */
 
719
                if (host->weight)
 
720
                        host->cost *= (host->weight + 1);
 
721
 
 
722
                debug(logopt, "host %s cost %ld weight %d",
 
723
                      host->name, host->cost, host->weight);
 
724
        }
 
725
 
 
726
        return supported;
 
727
}
 
728
 
 
729
static int get_vers_and_cost(unsigned logopt, struct host *host,
 
730
                             unsigned int version, const char *options)
 
731
{
 
732
        struct conn_info pm_info, rpc_info;
 
733
        time_t timeout = RPC_TIMEOUT;
 
734
        unsigned int supported, vers = (NFS_VERS_MASK | NFS4_VERS_MASK);
 
735
        int ret = 0;
 
736
 
 
737
        memset(&pm_info, 0, sizeof(struct conn_info));
 
738
        memset(&rpc_info, 0, sizeof(struct conn_info));
 
739
 
 
740
        if (host->proximity == PROXIMITY_NET)
 
741
                timeout = RPC_TIMEOUT * 2;
 
742
        else if (host->proximity == PROXIMITY_OTHER)
 
743
                timeout = RPC_TIMEOUT * 8;
 
744
 
 
745
        rpc_info.host = host->name;
 
746
        rpc_info.addr = host->addr;
 
747
        rpc_info.addr_len = host->addr_len;
 
748
        rpc_info.program = NFS_PROGRAM;
 
749
        rpc_info.timeout.tv_sec = timeout;
 
750
        rpc_info.close_option = RPC_CLOSE_DEFAULT;
 
751
        rpc_info.client = NULL;
 
752
 
 
753
        vers &= version;
 
754
 
 
755
        if (version & TCP_REQUESTED) {
 
756
                supported = get_nfs_info(logopt, host,
 
757
                                   &pm_info, &rpc_info, "tcp", vers, options);
 
758
                if (IS_ERR(supported)) {
 
759
                        if (ERR(supported) == EHOSTUNREACH ||
 
760
                            ERR(supported) == ETIMEDOUT)
 
761
                                return ret;
 
762
                } else if (supported) {
 
763
                        ret = 1;
 
764
                        host->version |= supported;
 
765
                }
 
766
        }
 
767
 
 
768
        if (version & UDP_REQUESTED) {
 
769
                supported = get_nfs_info(logopt, host,
 
770
                                   &pm_info, &rpc_info, "udp", vers, options);
 
771
                if (IS_ERR(supported)) {
 
772
                        if (ERR(supported) == ETIMEDOUT)
 
773
                                return ret;
 
774
                } else if (supported) {
 
775
                        ret = 1;
 
776
                        host->version |= (supported << 8);
 
777
                }
 
778
        }
 
779
 
 
780
        return ret;
 
781
}
 
782
 
 
783
static int get_supported_ver_and_cost(unsigned logopt, struct host *host,
 
784
                                      unsigned int version, const char *options)
 
785
{
 
786
        char *have_port_opt = options ? strstr(options, "port=") : NULL;
 
787
        unsigned int random_selection = host->options & MOUNT_FLAG_RANDOM_SELECT;
 
788
        unsigned int use_weight_only = host->options & MOUNT_FLAG_USE_WEIGHT_ONLY;
 
789
        socklen_t len = INET6_ADDRSTRLEN;
 
790
        char buf[len + 1];
 
791
        struct conn_info pm_info, rpc_info;
 
792
        struct pmap parms;
 
793
        const char *proto;
 
794
        unsigned int vers;
 
795
        struct timeval start, end;
 
796
        struct timezone tz;
 
797
        double taken = 0;
 
798
        time_t timeout = RPC_TIMEOUT;
 
799
        int status;
 
800
 
 
801
        if (host->addr)
 
802
                debug(logopt, "called with host %s(%s) version 0x%x",
 
803
                        host->name, get_addr_string(host->addr, buf, len),
 
804
                        version);
 
805
        else
 
806
                debug(logopt, "called with host %s version 0x%x",
 
807
                        host->name, version);
 
808
 
 
809
        memset(&pm_info, 0, sizeof(struct conn_info));
 
810
        memset(&rpc_info, 0, sizeof(struct conn_info));
 
811
        memset(&parms, 0, sizeof(struct pmap));
 
812
 
 
813
        if (host->proximity == PROXIMITY_NET)
 
814
                timeout = RPC_TIMEOUT * 2;
 
815
        else if (host->proximity == PROXIMITY_OTHER)
 
816
                timeout = RPC_TIMEOUT * 8;
 
817
 
 
818
        rpc_info.host = host->name;
 
819
        rpc_info.addr = host->addr;
 
820
        rpc_info.addr_len = host->addr_len;
 
821
        rpc_info.program = NFS_PROGRAM;
 
822
        rpc_info.timeout.tv_sec = timeout;
 
823
        rpc_info.close_option = RPC_CLOSE_DEFAULT;
 
824
        rpc_info.client = NULL;
 
825
 
 
826
        parms.pm_prog = NFS_PROGRAM;
 
827
 
 
828
        /*
 
829
         *  The version passed in is the version as defined in
 
830
         *  include/replicated.h.  However, the version we want to send
 
831
         *  off to the rpc calls should match the program version of NFS.
 
832
         *  So, we do the conversion here.
 
833
         */
 
834
        if (version & UDP_SELECTED_MASK) {
 
835
                proto = "udp";
 
836
                version >>= 8;
 
837
        } else
 
838
                proto = "tcp";
 
839
 
 
840
        switch (version) {
 
841
        case NFS2_SUPPORTED:
 
842
                vers = NFS2_VERSION;
 
843
                break;
 
844
        case NFS3_SUPPORTED:
 
845
                vers = NFS3_VERSION;
 
846
                break;
 
847
        case NFS4_SUPPORTED:
 
848
                vers = NFS4_VERSION;
 
849
                break;
 
850
        default:
 
851
                crit(logopt, "called with invalid version: 0x%x\n", version);
 
852
                return 0;
 
853
        }
 
854
 
 
855
        rpc_info.proto = getprotobyname(proto);
 
856
        if (!rpc_info.proto)
 
857
                return 0;
 
858
 
 
859
        status = 0;
 
860
 
 
861
        parms.pm_vers = vers;
 
862
        if (have_port_opt || (vers & NFS4_VERSION)) {
 
863
                if (!(rpc_info.port = get_port_option(options)))
 
864
                        return 0;
 
865
        } else {
 
866
                int ret = rpc_portmap_getclient(&pm_info,
 
867
                                host->name, host->addr, host->addr_len,
 
868
                                proto, RPC_CLOSE_DEFAULT);
 
869
                if (ret)
 
870
                        return 0;
 
871
 
 
872
                parms.pm_prot = rpc_info.proto->p_proto;
 
873
                ret = rpc_portmap_getport(&pm_info, &parms, &rpc_info.port);
 
874
                if (ret < 0)
 
875
                        goto done;
 
876
        }
 
877
 
 
878
        if (rpc_info.proto->p_proto == IPPROTO_UDP)
 
879
                status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
 
880
        else
 
881
                status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
 
882
        if (status == -EHOSTUNREACH)
 
883
                goto done;
 
884
        else if (!status) {
 
885
                gettimeofday(&start, &tz);
 
886
                status = rpc_ping_proto(&rpc_info);
 
887
                gettimeofday(&end, &tz);
 
888
                if (status > 0) {
 
889
                        if (random_selection) {
 
890
                                /* Random value between 0 and 1 */
 
891
                                taken = ((float) random())/((float) RAND_MAX+1);
 
892
                                debug(logopt, "random selection time %f", taken);
 
893
                        } else {
 
894
                                taken = elapsed(start, end);
 
895
                                debug(logopt, "rpc ping time %f", taken);
 
896
                        }
 
897
                }
 
898
        }
 
899
done:
 
900
        if (rpc_info.proto->p_proto == IPPROTO_UDP) {
 
901
                rpc_destroy_udp_client(&rpc_info);
 
902
                rpc_destroy_udp_client(&pm_info);
 
903
        } else {
 
904
                rpc_destroy_tcp_client(&rpc_info);
 
905
                rpc_destroy_tcp_client(&pm_info);
 
906
        }
 
907
 
 
908
        if (status) {
 
909
                /* Response time to 7 significant places as integral type. */
 
910
                if (use_weight_only)
 
911
                        host->cost = 1;
 
912
                else
 
913
                        host->cost = (unsigned long) (taken * 1000000);
 
914
 
 
915
                /* Allow for user bias */
 
916
                if (host->weight)
 
917
                        host->cost *= (host->weight + 1);
 
918
 
 
919
                debug(logopt, "cost %ld weight %d", host->cost, host->weight);
 
920
 
 
921
                return 1;
 
922
        }
 
923
 
 
924
        return 0;
 
925
}
 
926
 
 
927
int prune_host_list(unsigned logopt, struct host **list,
 
928
                    unsigned int vers, const char *options)
 
929
{
 
930
        struct host *this, *last, *first;
 
931
        struct host *new = NULL;
 
932
        unsigned int proximity, selected_version = 0;
 
933
        unsigned int v2_tcp_count, v3_tcp_count, v4_tcp_count;
 
934
        unsigned int v2_udp_count, v3_udp_count, v4_udp_count;
 
935
        unsigned int max_udp_count, max_tcp_count, max_count;
 
936
        int status;
 
937
        int kern_vers;
 
938
 
 
939
        if (!*list)
 
940
                return 0;
 
941
 
 
942
        /* Use closest hosts to choose NFS version */
 
943
 
 
944
        first = *list;
 
945
 
 
946
        /* Get proximity of first entry after local entries */
 
947
        this = first;
 
948
        while (this && this->proximity == PROXIMITY_LOCAL)
 
949
                this = this->next;
 
950
        first = this;
 
951
 
 
952
        /*
 
953
         * Check for either a list containing only proximity local hosts
 
954
         * or a single host entry whose proximity isn't local. If so
 
955
         * return immediately as we don't want to add probe latency for
 
956
         * the common case of a single filesystem mount request.
 
957
         *
 
958
         * But, if the kernel understands text nfs mount options then
 
959
         * mount.nfs most likely bypasses its probing and lets the kernel
 
960
         * do all the work. This can lead to long timeouts for hosts that
 
961
         * are not available so check the kernel version and mount.nfs
 
962
         * version and probe singleton mounts if the kernel version is
 
963
         * greater than 2.6.22 and mount.nfs version is greater than 1.1.1.
 
964
         * But also allow the MOUNT_WAIT configuration parameter to override
 
965
         * the probing.
 
966
         */
 
967
        if (nfs_mount_uses_string_options &&
 
968
            defaults_get_mount_wait() == -1 &&
 
969
           (kern_vers = linux_version_code()) > KERNEL_VERSION(2, 6, 22)) {
 
970
                if (!this)
 
971
                        return 1;
 
972
        } else {
 
973
                if (!this || !this->next)
 
974
                        return 1;
 
975
        }
 
976
 
 
977
        proximity = this->proximity;
 
978
        while (this) {
 
979
                struct host *next = this->next;
 
980
 
 
981
                if (this->proximity != proximity)
 
982
                        break;
 
983
 
 
984
                if (this->name) {
 
985
                        status = get_vers_and_cost(logopt, this, vers, options);
 
986
                        if (!status) {
 
987
                                if (this == first) {
 
988
                                        first = next;
 
989
                                        if (next)
 
990
                                                proximity = next->proximity;
 
991
                                }
 
992
                                delete_host(list, this);
 
993
                        }
 
994
                }
 
995
                this = next;
 
996
        }
 
997
 
 
998
        /*
 
999
         * The list of hosts that aren't proximity local may now
 
1000
         * be empty if we haven't been able probe any so we need
 
1001
         * to check again for a list containing only proximity
 
1002
         * local hosts.
 
1003
         */
 
1004
        if (!first)
 
1005
                return 1;
 
1006
 
 
1007
        last = this;
 
1008
 
 
1009
        /* Select NFS version of highest number of closest servers */
 
1010
 
 
1011
        v4_tcp_count = v3_tcp_count = v2_tcp_count = 0;
 
1012
        v4_udp_count = v3_udp_count = v2_udp_count = 0;
 
1013
 
 
1014
        this = first;
 
1015
        do {
 
1016
                if (this->version & NFS4_TCP_SUPPORTED)
 
1017
                        v4_tcp_count++;
 
1018
 
 
1019
                if (this->version & NFS3_TCP_SUPPORTED)
 
1020
                        v3_tcp_count++;
 
1021
 
 
1022
                if (this->version & NFS2_TCP_SUPPORTED)
 
1023
                        v2_tcp_count++;
 
1024
 
 
1025
                if (this->version & NFS4_UDP_SUPPORTED)
 
1026
                        v4_udp_count++;
 
1027
 
 
1028
                if (this->version & NFS3_UDP_SUPPORTED)
 
1029
                        v3_udp_count++;
 
1030
 
 
1031
                if (this->version & NFS2_UDP_SUPPORTED)
 
1032
                        v2_udp_count++;
 
1033
 
 
1034
                this = this->next; 
 
1035
        } while (this && this != last);
 
1036
 
 
1037
        max_tcp_count = mmax(v4_tcp_count, v3_tcp_count, v2_tcp_count);
 
1038
        max_udp_count = mmax(v4_udp_count, v3_udp_count, v2_udp_count);
 
1039
        max_count = max(max_tcp_count, max_udp_count);
 
1040
 
 
1041
        if (max_count == v4_tcp_count) {
 
1042
                selected_version = NFS4_TCP_SUPPORTED;
 
1043
                debug(logopt,
 
1044
                      "selected subset of hosts that support NFS4 over TCP");
 
1045
        } else if (max_count == v3_tcp_count) {
 
1046
                selected_version = NFS3_TCP_SUPPORTED;
 
1047
                debug(logopt,
 
1048
                      "selected subset of hosts that support NFS3 over TCP");
 
1049
        } else if (max_count == v2_tcp_count) {
 
1050
                selected_version = NFS2_TCP_SUPPORTED;
 
1051
                debug(logopt,
 
1052
                      "selected subset of hosts that support NFS2 over TCP");
 
1053
        } else if (max_count == v4_udp_count) {
 
1054
                selected_version = NFS4_UDP_SUPPORTED;
 
1055
                debug(logopt,
 
1056
                      "selected subset of hosts that support NFS4 over UDP");
 
1057
        } else if (max_count == v3_udp_count) {
 
1058
                selected_version = NFS3_UDP_SUPPORTED;
 
1059
                debug(logopt,
 
1060
                      "selected subset of hosts that support NFS3 over UDP");
 
1061
        } else if (max_count == v2_udp_count) {
 
1062
                selected_version = NFS2_UDP_SUPPORTED;
 
1063
                debug(logopt,
 
1064
                      "selected subset of hosts that support NFS2 over UDP");
 
1065
        }
 
1066
 
 
1067
        /* Add local and hosts with selected version to new list */
 
1068
        this = *list;
 
1069
        do {
 
1070
                struct host *next = this->next;
 
1071
                if (this->version & selected_version ||
 
1072
                    this->proximity == PROXIMITY_LOCAL) {
 
1073
                        this->version = selected_version;
 
1074
                        remove_host(list, this);
 
1075
                        add_host(&new, this);
 
1076
                }
 
1077
                this = next;
 
1078
        } while (this && this != last);
 
1079
 
 
1080
        /*
 
1081
         * Now go through rest of list and check for chosen version
 
1082
         * and add to new list if selected version is supported.
 
1083
         */ 
 
1084
 
 
1085
        first = last;
 
1086
        this = first;
 
1087
        while (this) {
 
1088
                struct host *next = this->next;
 
1089
                if (!this->name) {
 
1090
                        remove_host(list, this);
 
1091
                        add_host(&new, this);
 
1092
                } else {
 
1093
                        status = get_supported_ver_and_cost(logopt, this,
 
1094
                                                selected_version, options);
 
1095
                        if (status) {
 
1096
                                this->version = selected_version;
 
1097
                                remove_host(list, this);
 
1098
                                add_host(&new, this);
 
1099
                        }
 
1100
                }
 
1101
                this = next;
 
1102
        }
 
1103
 
 
1104
        free_host_list(list);
 
1105
        *list = new;
 
1106
 
 
1107
        return 1;
 
1108
}
 
1109
 
 
1110
static int add_new_host(struct host **list,
 
1111
                        const char *host, unsigned int weight,
 
1112
                        struct addrinfo *host_addr,
 
1113
                        unsigned int rr, unsigned int options)
 
1114
{
 
1115
        struct host *new;
 
1116
        unsigned int prx;
 
1117
        int addr_len;
 
1118
 
 
1119
        /*
 
1120
         * If we are using random selection we pretend all hosts are at
 
1121
         * the same proximity so hosts further away don't get excluded.
 
1122
         * We can't use PROXIMITY_LOCAL or we won't perform an RPC ping
 
1123
         * to remove hosts that may be down.
 
1124
         */
 
1125
        if (!host_addr)
 
1126
                prx = PROXIMITY_SUBNET;
 
1127
        else {
 
1128
                prx = get_proximity(host_addr->ai_addr);
 
1129
                /*
 
1130
                 * If we want the weight to be the determining factor
 
1131
                 * when selecting a host, or we are using random selection,
 
1132
                 * then all hosts must have the same proximity. However,
 
1133
                 * if this is the local machine it should always be used
 
1134
                 * since it is certainly available.
 
1135
                 */
 
1136
                if (prx != PROXIMITY_LOCAL &&
 
1137
                   (options & (MOUNT_FLAG_USE_WEIGHT_ONLY |
 
1138
                               MOUNT_FLAG_RANDOM_SELECT)))
 
1139
                        prx = PROXIMITY_SUBNET;
 
1140
        }
 
1141
 
 
1142
        /*
 
1143
         * If we tried to add an IPv6 address and we don't have IPv6
 
1144
         * support return success in the hope of getting an IPv4
 
1145
         * address later.
 
1146
         */
 
1147
        if (prx == PROXIMITY_UNSUPPORTED)
 
1148
                return 1;
 
1149
        if (prx == PROXIMITY_ERROR)
 
1150
                return 0;
 
1151
 
 
1152
        if (host_addr->ai_addr->sa_family == AF_INET)
 
1153
                addr_len = INET_ADDRSTRLEN;
 
1154
        else if (host_addr->ai_addr->sa_family == AF_INET6)
 
1155
                addr_len = INET6_ADDRSTRLEN;
 
1156
        else
 
1157
                return 0;
 
1158
 
 
1159
        new = new_host(host, host_addr->ai_addr, addr_len, prx, weight, options);
 
1160
        if (!new)
 
1161
                return 0;
 
1162
 
 
1163
        if (!add_host(list, new)) {
 
1164
                free_host(new);
 
1165
                return 0;
 
1166
        }
 
1167
        new->rr = rr;
 
1168
 
 
1169
        return 1;
 
1170
}
 
1171
 
 
1172
static int add_host_addrs(struct host **list, const char *host,
 
1173
                          unsigned int weight, unsigned int options)
 
1174
{
 
1175
        struct addrinfo hints, *ni, *this;
 
1176
        char *n_ptr;
 
1177
        char *name = n_ptr = strdup(host);
 
1178
        int len;
 
1179
        char buf[MAX_ERR_BUF];
 
1180
        int rr = 0, rr4 = 0, rr6 = 0;
 
1181
        int ret;
 
1182
 
 
1183
        if (!name) {
 
1184
                char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 
1185
                error(LOGOPT_ANY, "strdup: %s", estr);
 
1186
                error(LOGOPT_ANY, "failed to add host %s", host);
 
1187
                return 0;
 
1188
        }
 
1189
        len = strlen(name);
 
1190
 
 
1191
        if (name[0] == '[' && name[--len] == ']') {
 
1192
                name[len] = '\0';
 
1193
                name++;
 
1194
        }
 
1195
 
 
1196
        memset(&hints, 0, sizeof(hints));
 
1197
        hints.ai_flags = AI_NUMERICHOST;
 
1198
        hints.ai_family = AF_UNSPEC;
 
1199
        hints.ai_socktype = SOCK_DGRAM;
 
1200
 
 
1201
        ret = getaddrinfo(name, NULL, &hints, &ni);
 
1202
        if (ret)
 
1203
                goto try_name;
 
1204
 
 
1205
        this = ni;
 
1206
        while (this) {
 
1207
                ret = add_new_host(list, host, weight, this, 0, options);
 
1208
                if (!ret)
 
1209
                        break;
 
1210
                this = this->ai_next;
 
1211
        }
 
1212
        freeaddrinfo(ni);
 
1213
        goto done;
 
1214
 
 
1215
try_name:
 
1216
        memset(&hints, 0, sizeof(hints));
 
1217
        hints.ai_flags = AI_ADDRCONFIG;
 
1218
        hints.ai_family = AF_UNSPEC;
 
1219
        hints.ai_socktype = SOCK_DGRAM;
 
1220
 
 
1221
        ret = getaddrinfo(name, NULL, &hints, &ni);
 
1222
        if (ret) {
 
1223
                error(LOGOPT_ANY, "hostname lookup failed: %s",
 
1224
                      gai_strerror(ret));
 
1225
                free(name);
 
1226
                return 0;
 
1227
        }
 
1228
 
 
1229
        this = ni;
 
1230
        while (this->ai_next) {
 
1231
                if (this->ai_family == AF_INET) {
 
1232
                        struct sockaddr_in *addr = (struct sockaddr_in *) this->ai_addr;
 
1233
                        if (addr->sin_addr.s_addr != INADDR_LOOPBACK)
 
1234
                                rr4++;
 
1235
                } else if (this->ai_family == AF_INET6) {
 
1236
                        struct sockaddr_in6 *addr = (struct sockaddr_in6 *) this->ai_addr;
 
1237
                        if (!IN6_IS_ADDR_LOOPBACK(addr->sin6_addr.__in6_u.__u6_addr32))
 
1238
                                rr6++;
 
1239
                }
 
1240
                this = this->ai_next;
 
1241
        }
 
1242
        if (rr4 > 1 || rr6 > 1)
 
1243
                rr++;
 
1244
        this = ni;
 
1245
        while (this) {
 
1246
                ret = add_new_host(list, host, weight, this, rr, options);
 
1247
                if (!ret)
 
1248
                        break;
 
1249
                this = this->ai_next;
 
1250
        }
 
1251
        freeaddrinfo(ni);
 
1252
done:
 
1253
        free(n_ptr);
 
1254
        return ret;
 
1255
}
 
1256
 
 
1257
static int add_path(struct host *hosts, const char *path, int len)
 
1258
{
 
1259
        struct host *this;
 
1260
        char *tmp, *tmp2;
 
1261
 
 
1262
        tmp = alloca(len + 1);
 
1263
        if (!tmp)
 
1264
                return 0;
 
1265
 
 
1266
        strncpy(tmp, path, len);
 
1267
        tmp[len] = '\0';
 
1268
 
 
1269
        this = hosts;
 
1270
        while (this) {
 
1271
                if (!this->path) {
 
1272
                        tmp2 = strdup(tmp);
 
1273
                        if (!tmp2)
 
1274
                                return 0;
 
1275
                        this->path = tmp2;
 
1276
                }
 
1277
                this = this->next;
 
1278
        }
 
1279
 
 
1280
        return 1;
 
1281
}
 
1282
 
 
1283
static int add_local_path(struct host **hosts, const char *path)
 
1284
{
 
1285
        struct host *new;
 
1286
        char *tmp;
 
1287
 
 
1288
        tmp = strdup(path);
 
1289
        if (!tmp)
 
1290
                return 0;
 
1291
 
 
1292
        new = malloc(sizeof(struct host));
 
1293
        if (!new) {
 
1294
                free(tmp);
 
1295
                return 0;
 
1296
        }
 
1297
 
 
1298
        memset(new, 0, sizeof(struct host));
 
1299
 
 
1300
        new->path = tmp;
 
1301
        new->proximity = PROXIMITY_LOCAL;
 
1302
        new->version = NFS_VERS_MASK;
 
1303
        new->name = NULL;
 
1304
        new->addr = NULL;
 
1305
        new->weight = new->cost = 0;
 
1306
 
 
1307
        add_host(hosts, new);
 
1308
 
 
1309
        return 1;
 
1310
}
 
1311
 
 
1312
static char *seek_delim(const char *s)
 
1313
{
 
1314
        const char *p = s;
 
1315
        char *delim;
 
1316
 
 
1317
        delim = strpbrk(p, "(, \t:");
 
1318
        if (delim && *delim != ':' && (delim == s || *(delim - 1) != '\\'))
 
1319
                return delim;
 
1320
 
 
1321
        while (*p) {
 
1322
                if (*p != ':') {
 
1323
                        p++;
 
1324
                        continue;
 
1325
                }
 
1326
                if (!strncmp(p, ":/", 2))
 
1327
                        return (char *) p;
 
1328
                p++;
 
1329
        }
 
1330
 
 
1331
        return NULL;
 
1332
}
 
1333
 
 
1334
int parse_location(unsigned logopt, struct host **hosts,
 
1335
                   const char *list, unsigned int options)
 
1336
{
 
1337
        char *str, *p, *delim;
 
1338
        unsigned int empty = 1;
 
1339
 
 
1340
        if (!list)
 
1341
                return 0;
 
1342
 
 
1343
        str = strdup(list);
 
1344
        if (!str)
 
1345
                return 0;
 
1346
 
 
1347
        p = str;
 
1348
 
 
1349
        while (p && *p) {
 
1350
                char *next = NULL;
 
1351
                int weight = 0;
 
1352
 
 
1353
                p += strspn(p, " \t,");
 
1354
                delim = seek_delim(p);
 
1355
 
 
1356
                if (delim) {
 
1357
                        if (*delim == '(') {
 
1358
                                char *w = delim + 1;
 
1359
 
 
1360
                                *delim = '\0';
 
1361
 
 
1362
                                delim = strchr(w, ')');
 
1363
                                if (delim) {
 
1364
                                        *delim = '\0';
 
1365
                                        weight = atoi(w);
 
1366
                                }
 
1367
                                else {
 
1368
                                        /* syntax error - Mismatched brackets */
 
1369
                                        free_host_list(hosts);
 
1370
                                        free(str);
 
1371
                                        return 0;
 
1372
                                }
 
1373
                                delim++;
 
1374
                        }
 
1375
 
 
1376
                        if (*delim == ':') {
 
1377
                                char *path;
 
1378
 
 
1379
                                *delim = '\0';
 
1380
                                path = delim + 1;
 
1381
 
 
1382
                                /* Oh boy - might have spaces in the path */
 
1383
                                next = path;
 
1384
                                while (*next && strncmp(next, ":/", 2))
 
1385
                                        next++;
 
1386
 
 
1387
                                /* No spaces in host names at least */
 
1388
                                if (*next == ':') {
 
1389
                                        while (*next &&
 
1390
                                              (*next != ' ' && *next != '\t'))
 
1391
                                                next--;
 
1392
                                        *next++ = '\0';
 
1393
                                }
 
1394
 
 
1395
                                if (p != delim) {
 
1396
                                        if (!add_host_addrs(hosts, p, weight, options)) {
 
1397
                                                if (empty) {
 
1398
                                                        p = next;
 
1399
                                                        continue;
 
1400
                                                }
 
1401
                                        }
 
1402
 
 
1403
                                        if (!add_path(*hosts, path, strlen(path))) {
 
1404
                                                free_host_list(hosts);
 
1405
                                                free(str);
 
1406
                                                return 0;
 
1407
                                        }
 
1408
                                } else {
 
1409
                                        if (!add_local_path(hosts, path)) {
 
1410
                                                p = next;
 
1411
                                                continue;
 
1412
                                        }
 
1413
                                }
 
1414
                        } else if (*delim != '\0') {
 
1415
                                *delim = '\0';
 
1416
                                next = delim + 1;
 
1417
 
 
1418
                                if (!add_host_addrs(hosts, p, weight, options)) {
 
1419
                                        p = next;
 
1420
                                        continue;
 
1421
                                }
 
1422
 
 
1423
                                empty = 0;
 
1424
                        }
 
1425
                } else {
 
1426
                        /* syntax error - no mount path */
 
1427
                        free_host_list(hosts);
 
1428
                        free(str);
 
1429
                        return 0;
 
1430
                }
 
1431
 
 
1432
                p = next;
 
1433
        }
 
1434
 
 
1435
        free(str);
 
1436
        return 1;
 
1437
}
 
1438
 
 
1439
void dump_host_list(struct host *hosts)
 
1440
{
 
1441
        struct host *this;
 
1442
 
 
1443
        if (!hosts)
 
1444
                return;
 
1445
 
 
1446
        this = hosts;
 
1447
        while (this) {
 
1448
                logmsg("name %s path %s version %x proximity %u weight %u cost %u",
 
1449
                      this->name, this->path, this->version,
 
1450
                      this->proximity, this->weight, this->cost);
 
1451
                this = this->next;
 
1452
        }
 
1453
        return;
 
1454
}
 
1455