~ubuntu-branches/ubuntu/precise/autofs5/precise

« back to all changes in this revision

Viewing changes to .pc/autofs-5.0.5-mount-using-address-for-rr.patch/modules/replicated.c

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-07-03 14:35:46 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110703143546-nej26krjij0rf792
Tags: 5.0.6-0ubuntu1
* New upstream release:
  - Dropped upstream patches 
  - Refreshed debian/patches/17ld.patch.

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 INET6
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
 
#ifndef INET6
221
 
                        if (host_addr->sa_family == AF_INET6)
222
 
                                break;
223
 
#endif
224
 
                        if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
225
 
                        ret = memcmp(&if_addr->sin_addr, hst_addr, addr_len);
226
 
                        if (!ret) {
227
 
                                close(sock);
228
 
                                free(ifc.ifc_req);
229
 
                                return PROXIMITY_LOCAL;
230
 
                        }
231
 
                        break;
232
 
 
233
 
                case AF_INET6:
234
 
#ifdef INET6
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
 
#ifndef INET6
274
 
                        if (host_addr->sa_family == AF_INET6)
275
 
                                break;
276
 
#endif
277
 
                        if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
278
 
                        ia =  ntohl((uint32_t) if_addr->sin_addr.s_addr);
279
 
 
280
 
                        /* Is the address within a localiy attached subnet */
281
 
 
282
 
                        msk_addr = (struct sockaddr_in *) &nmptr.ifr_netmask;
283
 
                        mask = ntohl((uint32_t) msk_addr->sin_addr.s_addr);
284
 
 
285
 
                        if ((ia & mask) == (ha & mask)) {
286
 
                                close(sock);
287
 
                                free(ifc.ifc_req);
288
 
                                return PROXIMITY_SUBNET;
289
 
                        }
290
 
 
291
 
                        /*
292
 
                         * Is the address within a local ipv4 network.
293
 
                         *
294
 
                         * Bit position 31 == 0 => class A.
295
 
                         * Bit position 30 == 0 => class B.
296
 
                         * Bit position 29 == 0 => class C.
297
 
                         */
298
 
 
299
 
                        if (!getbits(ia, 31, 1))
300
 
                                mask = MASK_A;
301
 
                        else if (!getbits(ia, 30, 1))
302
 
                                mask = MASK_B;
303
 
                        else if (!getbits(ia, 29, 1))
304
 
                                mask = MASK_C;
305
 
                        else
306
 
                                break;
307
 
 
308
 
                        if ((ia & mask) == (ha & mask)) {
309
 
                                close(sock);
310
 
                                free(ifc.ifc_req);
311
 
                                return PROXIMITY_NET;
312
 
                        }
313
 
                        break;
314
 
 
315
 
                case AF_INET6:
316
 
#ifdef INET6
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) {
567
 
                gettimeofday(&start, &tz);
568
 
                status = rpc_ping_proto(rpc_info);
569
 
                gettimeofday(&end, &tz);
570
 
                if (status) {
571
 
                        double reply;
572
 
                        if (random_selection) {
573
 
                                /* Random value between 0 and 1 */
574
 
                                reply = ((float) random())/((float) RAND_MAX+1);
575
 
                                debug(logopt,
576
 
                                      "nfs v4 random selection time: %f", reply);
577
 
                        } else {
578
 
                                reply = elapsed(start, end);
579
 
                                debug(logopt, "nfs v4 rpc ping time: %f", reply);
580
 
                        }
581
 
                        taken += reply;
582
 
                        count++;
583
 
                        supported = NFS4_SUPPORTED;
584
 
                }
585
 
        }
586
 
 
587
 
v3_ver:
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);
592
 
                if (!status)
593
 
                        goto done_ver;
594
 
        }
595
 
 
596
 
        if (!(version & NFS3_REQUESTED))
597
 
                goto v2_ver;
598
 
 
599
 
        if (have_port_opt) {
600
 
                if (!(rpc_info->port = get_port_option(options)))
601
 
                        goto done_ver;
602
 
        } else {
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);
606
 
                if (!rpc_info->port)
607
 
                        goto v2_ver;
608
 
        }
609
 
 
610
 
        if (rpc_info->proto->p_proto == IPPROTO_UDP)
611
 
                status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
612
 
        else
613
 
                status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
614
 
        if (status) {
615
 
                gettimeofday(&start, &tz);
616
 
                status = rpc_ping_proto(rpc_info);
617
 
                gettimeofday(&end, &tz);
618
 
                if (status) {
619
 
                        double reply;
620
 
                        if (random_selection) {
621
 
                                /* Random value between 0 and 1 */
622
 
                                reply = ((float) random())/((float) RAND_MAX+1);
623
 
                                debug(logopt,
624
 
                                      "nfs v3 random selection time: %f", reply);
625
 
                        } else {
626
 
                                reply = elapsed(start, end);
627
 
                                debug(logopt, "nfs v3 rpc ping time: %f", reply);
628
 
                        }
629
 
                        taken += reply;
630
 
                        count++;
631
 
                        supported |= NFS3_SUPPORTED;
632
 
                }
633
 
        }
634
 
 
635
 
v2_ver:
636
 
        if (!(version & NFS2_REQUESTED))
637
 
                goto done_ver;
638
 
 
639
 
        if (have_port_opt) {
640
 
                if (!(rpc_info->port = get_port_option(options)))
641
 
                        goto done_ver;
642
 
        } else {
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);
646
 
                if (!rpc_info->port)
647
 
                        goto done_ver;
648
 
        }
649
 
 
650
 
        if (rpc_info->proto->p_proto == IPPROTO_UDP)
651
 
                status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
652
 
        else
653
 
                status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
654
 
        if (status) {
655
 
                gettimeofday(&start, &tz);
656
 
                status = rpc_ping_proto(rpc_info);
657
 
                gettimeofday(&end, &tz);
658
 
                if (status) {
659
 
                        double reply;
660
 
                        if (random_selection) {
661
 
                                /* Random value between 0 and 1 */
662
 
                                reply = ((float) random())/((float) RAND_MAX+1);
663
 
                                debug(logopt,
664
 
                                      "nfs v2 random selection time: %f", reply);
665
 
                        } else {
666
 
                                reply = elapsed(start, end);;
667
 
                                debug(logopt, "nfs v2 rpc ping time: %f", reply);
668
 
                        }
669
 
                        taken += reply;
670
 
                        count++;
671
 
                        supported |= NFS2_SUPPORTED;
672
 
                }
673
 
        }
674
 
 
675
 
done_ver:
676
 
        if (rpc_info->proto->p_proto == IPPROTO_UDP) {
677
 
                rpc_destroy_udp_client(rpc_info);
678
 
                rpc_destroy_udp_client(pm_info);
679
 
        } else {
680
 
                rpc_destroy_tcp_client(rpc_info);
681
 
                rpc_destroy_tcp_client(pm_info);
682
 
        }
683
 
 
684
 
        if (count) {
685
 
                /*
686
 
                 * Average response time to 7 significant places as
687
 
                 * integral type.
688
 
                 */
689
 
                if (use_weight_only)
690
 
                        host->cost = 1;
691
 
                else
692
 
                        host->cost = (unsigned long) ((taken * 1000000) / count);
693
 
 
694
 
                /* Allow for user bias */
695
 
                if (host->weight)
696
 
                        host->cost *= (host->weight + 1);
697
 
 
698
 
                debug(logopt, "host %s cost %ld weight %d",
699
 
                      host->name, host->cost, host->weight);
700
 
        }
701
 
 
702
 
        return supported;
703
 
}
704
 
 
705
 
static int get_vers_and_cost(unsigned logopt, struct host *host,
706
 
                             unsigned int version, const char *options)
707
 
{
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);
711
 
        int ret = 0;
712
 
 
713
 
        memset(&pm_info, 0, sizeof(struct conn_info));
714
 
        memset(&rpc_info, 0, sizeof(struct conn_info));
715
 
 
716
 
        if (host->proximity == PROXIMITY_NET)
717
 
                timeout = RPC_TIMEOUT * 2;
718
 
        else if (host->proximity == PROXIMITY_OTHER)
719
 
                timeout = RPC_TIMEOUT * 8;
720
 
 
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;
728
 
 
729
 
        vers &= version;
730
 
 
731
 
        if (version & UDP_REQUESTED) {
732
 
                supported = get_nfs_info(logopt, host,
733
 
                                   &pm_info, &rpc_info, "udp", vers, options);
734
 
                if (supported) {
735
 
                        ret = 1;
736
 
                        host->version |= (supported << 8);
737
 
                }
738
 
        }
739
 
 
740
 
        if (version & TCP_REQUESTED) {
741
 
                supported = get_nfs_info(logopt, host,
742
 
                                   &pm_info, &rpc_info, "tcp", vers, options);
743
 
                if (supported) {
744
 
                        ret = 1;
745
 
                        host->version |= supported;
746
 
                }
747
 
        }
748
 
 
749
 
        return ret;
750
 
}
751
 
 
752
 
static int get_supported_ver_and_cost(unsigned logopt, struct host *host,
753
 
                                      unsigned int version, const char *options)
754
 
{
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;
759
 
        char buf[len + 1];
760
 
        struct conn_info pm_info, rpc_info;
761
 
        struct pmap parms;
762
 
        const char *proto;
763
 
        unsigned int vers;
764
 
        struct timeval start, end;
765
 
        struct timezone tz;
766
 
        double taken = 0;
767
 
        time_t timeout = RPC_TIMEOUT;
768
 
        int status;
769
 
 
770
 
        if (host->addr)
771
 
                debug(logopt, "called with host %s(%s) version 0x%x",
772
 
                        host->name, get_addr_string(host->addr, buf, len),
773
 
                        version);
774
 
        else
775
 
                debug(logopt, "called with host %s version 0x%x",
776
 
                        host->name, version);
777
 
 
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));
781
 
 
782
 
        if (host->proximity == PROXIMITY_NET)
783
 
                timeout = RPC_TIMEOUT * 2;
784
 
        else if (host->proximity == PROXIMITY_OTHER)
785
 
                timeout = RPC_TIMEOUT * 8;
786
 
 
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;
794
 
 
795
 
        parms.pm_prog = NFS_PROGRAM;
796
 
 
797
 
        /*
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.
802
 
         */
803
 
        if (version & UDP_SELECTED_MASK) {
804
 
                proto = "udp";
805
 
                version >>= 8;
806
 
        } else
807
 
                proto = "tcp";
808
 
 
809
 
        switch (version) {
810
 
        case NFS2_SUPPORTED:
811
 
                vers = NFS2_VERSION;
812
 
                break;
813
 
        case NFS3_SUPPORTED:
814
 
                vers = NFS3_VERSION;
815
 
                break;
816
 
        case NFS4_SUPPORTED:
817
 
                vers = NFS4_VERSION;
818
 
                break;
819
 
        default:
820
 
                crit(logopt, "called with invalid version: 0x%x\n", version);
821
 
                return 0;
822
 
        }
823
 
 
824
 
        rpc_info.proto = getprotobyname(proto);
825
 
        if (!rpc_info.proto)
826
 
                return 0;
827
 
 
828
 
        status = 0;
829
 
 
830
 
        parms.pm_vers = vers;
831
 
        if (have_port_opt || (vers & NFS4_VERSION)) {
832
 
                if (!(rpc_info.port = get_port_option(options)))
833
 
                        return 0;
834
 
        } else {
835
 
                int ret = rpc_portmap_getclient(&pm_info,
836
 
                                host->name, host->addr, host->addr_len,
837
 
                                proto, RPC_CLOSE_DEFAULT);
838
 
                if (!ret)
839
 
                        return 0;
840
 
 
841
 
                parms.pm_prot = rpc_info.proto->p_proto;
842
 
                rpc_info.port = rpc_portmap_getport(&pm_info, &parms);
843
 
                if (!rpc_info.port)
844
 
                        goto done;
845
 
        }
846
 
 
847
 
        if (rpc_info.proto->p_proto == IPPROTO_UDP)
848
 
                status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
849
 
        else
850
 
                status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
851
 
        if (status) {
852
 
                gettimeofday(&start, &tz);
853
 
                status = rpc_ping_proto(&rpc_info);
854
 
                gettimeofday(&end, &tz);
855
 
                if (status) {
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);
860
 
                        } else {
861
 
                                taken = elapsed(start, end);
862
 
                                debug(logopt, "rpc ping time %f", taken);
863
 
                        }
864
 
                }
865
 
        }
866
 
done:
867
 
        if (rpc_info.proto->p_proto == IPPROTO_UDP) {
868
 
                rpc_destroy_udp_client(&rpc_info);
869
 
                rpc_destroy_udp_client(&pm_info);
870
 
        } else {
871
 
                rpc_destroy_tcp_client(&rpc_info);
872
 
                rpc_destroy_tcp_client(&pm_info);
873
 
        }
874
 
 
875
 
        if (status) {
876
 
                /* Response time to 7 significant places as integral type. */
877
 
                if (use_weight_only)
878
 
                        host->cost = 1;
879
 
                else
880
 
                        host->cost = (unsigned long) (taken * 1000000);
881
 
 
882
 
                /* Allow for user bias */
883
 
                if (host->weight)
884
 
                        host->cost *= (host->weight + 1);
885
 
 
886
 
                debug(logopt, "cost %ld weight %d", host->cost, host->weight);
887
 
 
888
 
                return 1;
889
 
        }
890
 
 
891
 
        return 0;
892
 
}
893
 
 
894
 
int prune_host_list(unsigned logopt, struct host **list,
895
 
                    unsigned int vers, const char *options)
896
 
{
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;
903
 
        int status;
904
 
 
905
 
        if (!*list)
906
 
                return 0;
907
 
 
908
 
        /* Use closest hosts to choose NFS version */
909
 
 
910
 
        first = *list;
911
 
 
912
 
        /* Get proximity of first entry after local entries */
913
 
        this = first;
914
 
        while (this && this->proximity == PROXIMITY_LOCAL)
915
 
                this = this->next;
916
 
        first = this;
917
 
 
918
 
        /*
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.
923
 
         */
924
 
        if (!this || !this->next)
925
 
                return 1;
926
 
 
927
 
        proximity = this->proximity;
928
 
        while (this) {
929
 
                struct host *next = this->next;
930
 
 
931
 
                if (this->proximity != proximity)
932
 
                        break;
933
 
 
934
 
                if (this->name) {
935
 
                        status = get_vers_and_cost(logopt, this, vers, options);
936
 
                        if (!status) {
937
 
                                if (this == first) {
938
 
                                        first = next;
939
 
                                        if (next)
940
 
                                                proximity = next->proximity;
941
 
                                }
942
 
                                delete_host(list, this);
943
 
                        }
944
 
                }
945
 
                this = next;
946
 
        }
947
 
 
948
 
        /*
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
952
 
         * local hosts.
953
 
         */
954
 
        if (!first)
955
 
                return 1;
956
 
 
957
 
        last = this;
958
 
 
959
 
        /* Select NFS version of highest number of closest servers */
960
 
 
961
 
        v4_tcp_count = v3_tcp_count = v2_tcp_count = 0;
962
 
        v4_udp_count = v3_udp_count = v2_udp_count = 0;
963
 
 
964
 
        this = first;
965
 
        do {
966
 
                if (this->version & NFS4_TCP_SUPPORTED)
967
 
                        v4_tcp_count++;
968
 
 
969
 
                if (this->version & NFS3_TCP_SUPPORTED)
970
 
                        v3_tcp_count++;
971
 
 
972
 
                if (this->version & NFS2_TCP_SUPPORTED)
973
 
                        v2_tcp_count++;
974
 
 
975
 
                if (this->version & NFS4_UDP_SUPPORTED)
976
 
                        v4_udp_count++;
977
 
 
978
 
                if (this->version & NFS3_UDP_SUPPORTED)
979
 
                        v3_udp_count++;
980
 
 
981
 
                if (this->version & NFS2_UDP_SUPPORTED)
982
 
                        v2_udp_count++;
983
 
 
984
 
                this = this->next; 
985
 
        } while (this && this != last);
986
 
 
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);
990
 
 
991
 
        if (max_count == v4_tcp_count) {
992
 
                selected_version = NFS4_TCP_SUPPORTED;
993
 
                debug(logopt,
994
 
                      "selected subset of hosts that support NFS4 over TCP");
995
 
        } else if (max_count == v3_tcp_count) {
996
 
                selected_version = NFS3_TCP_SUPPORTED;
997
 
                debug(logopt,
998
 
                      "selected subset of hosts that support NFS3 over TCP");
999
 
        } else if (max_count == v2_tcp_count) {
1000
 
                selected_version = NFS2_TCP_SUPPORTED;
1001
 
                debug(logopt,
1002
 
                      "selected subset of hosts that support NFS2 over TCP");
1003
 
        } else if (max_count == v4_udp_count) {
1004
 
                selected_version = NFS4_UDP_SUPPORTED;
1005
 
                debug(logopt,
1006
 
                      "selected subset of hosts that support NFS4 over UDP");
1007
 
        } else if (max_count == v3_udp_count) {
1008
 
                selected_version = NFS3_UDP_SUPPORTED;
1009
 
                debug(logopt,
1010
 
                      "selected subset of hosts that support NFS3 over UDP");
1011
 
        } else if (max_count == v2_udp_count) {
1012
 
                selected_version = NFS2_UDP_SUPPORTED;
1013
 
                debug(logopt,
1014
 
                      "selected subset of hosts that support NFS2 over UDP");
1015
 
        }
1016
 
 
1017
 
        /* Add local and hosts with selected version to new list */
1018
 
        this = *list;
1019
 
        do {
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);
1026
 
                }
1027
 
                this = next;
1028
 
        } while (this && this != last);
1029
 
 
1030
 
        /*
1031
 
         * Now go through rest of list and check for chosen version
1032
 
         * and add to new list if selected version is supported.
1033
 
         */ 
1034
 
 
1035
 
        first = last;
1036
 
        this = first;
1037
 
        while (this) {
1038
 
                struct host *next = this->next;
1039
 
                if (!this->name) {
1040
 
                        remove_host(list, this);
1041
 
                        add_host(&new, this);
1042
 
                } else {
1043
 
                        status = get_supported_ver_and_cost(logopt, this,
1044
 
                                                selected_version, options);
1045
 
                        if (status) {
1046
 
                                this->version = selected_version;
1047
 
                                remove_host(list, this);
1048
 
                                add_host(&new, this);
1049
 
                        }
1050
 
                }
1051
 
                this = next;
1052
 
        }
1053
 
 
1054
 
        free_host_list(list);
1055
 
        *list = new;
1056
 
 
1057
 
        return 1;
1058
 
}
1059
 
 
1060
 
static int add_new_host(struct host **list,
1061
 
                        const char *host, unsigned int weight,
1062
 
                        struct addrinfo *host_addr, unsigned int options)
1063
 
{
1064
 
        struct host *new;
1065
 
        unsigned int prx;
1066
 
        int addr_len;
1067
 
 
1068
 
        /*
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.
1073
 
         */
1074
 
        if (options & MOUNT_FLAG_RANDOM_SELECT)
1075
 
                prx = PROXIMITY_SUBNET;
1076
 
        else {
1077
 
                prx = get_proximity(host_addr->ai_addr);
1078
 
                /*
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.
1083
 
                 */
1084
 
                if (prx != PROXIMITY_LOCAL &&
1085
 
                   (options & MOUNT_FLAG_USE_WEIGHT_ONLY))
1086
 
                        prx = PROXIMITY_SUBNET;
1087
 
        }
1088
 
 
1089
 
        /*
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
1092
 
         * address later.
1093
 
         */
1094
 
        if (prx == PROXIMITY_UNSUPPORTED)
1095
 
                return 1;
1096
 
        if (prx == PROXIMITY_ERROR)
1097
 
                return 0;
1098
 
 
1099
 
        addr_len = sizeof(struct sockaddr);
1100
 
        new = new_host(host, host_addr->ai_addr, addr_len, prx, weight, options);
1101
 
        if (!new)
1102
 
                return 0;
1103
 
 
1104
 
        if (!add_host(list, new)) {
1105
 
                free_host(new);
1106
 
                return 0;
1107
 
        }
1108
 
 
1109
 
        return 1;
1110
 
}
1111
 
 
1112
 
static int add_host_addrs(struct host **list, const char *host,
1113
 
                          unsigned int weight, unsigned int options)
1114
 
{
1115
 
        struct addrinfo hints, *ni, *this;
1116
 
        int ret;
1117
 
 
1118
 
        memset(&hints, 0, sizeof(hints));
1119
 
        hints.ai_flags = AI_NUMERICHOST;
1120
 
        hints.ai_family = AF_UNSPEC;
1121
 
        hints.ai_socktype = SOCK_DGRAM;
1122
 
 
1123
 
        ret = getaddrinfo(host, NULL, &hints, &ni);
1124
 
        if (ret)
1125
 
                goto try_name;
1126
 
 
1127
 
        this = ni;
1128
 
        while (this) {
1129
 
                ret = add_new_host(list, host, weight, this, options);
1130
 
                if (!ret)
1131
 
                        break;
1132
 
                this = this->ai_next;
1133
 
        }
1134
 
        freeaddrinfo(ni);
1135
 
        goto done;
1136
 
 
1137
 
try_name:
1138
 
        memset(&hints, 0, sizeof(hints));
1139
 
        hints.ai_flags = AI_ADDRCONFIG;
1140
 
        hints.ai_family = AF_UNSPEC;
1141
 
        hints.ai_socktype = SOCK_DGRAM;
1142
 
 
1143
 
        ret = getaddrinfo(host, NULL, &hints, &ni);
1144
 
        if (ret) {
1145
 
                error(LOGOPT_ANY, "hostname lookup failed: %s",
1146
 
                      gai_strerror(ret));
1147
 
                return 0;
1148
 
        }
1149
 
 
1150
 
        this = ni;
1151
 
        while (this) {
1152
 
                ret = add_new_host(list, host, weight, this, options);
1153
 
                if (!ret)
1154
 
                        break;
1155
 
                this = this->ai_next;
1156
 
        }
1157
 
        freeaddrinfo(ni);
1158
 
done:
1159
 
        return ret;
1160
 
}
1161
 
 
1162
 
static int add_path(struct host *hosts, const char *path, int len)
1163
 
{
1164
 
        struct host *this;
1165
 
        char *tmp, *tmp2;
1166
 
 
1167
 
        tmp = alloca(len + 1);
1168
 
        if (!tmp)
1169
 
                return 0;
1170
 
 
1171
 
        strncpy(tmp, path, len);
1172
 
        tmp[len] = '\0';
1173
 
 
1174
 
        this = hosts;
1175
 
        while (this) {
1176
 
                if (!this->path) {
1177
 
                        tmp2 = strdup(tmp);
1178
 
                        if (!tmp2)
1179
 
                                return 0;
1180
 
                        this->path = tmp2;
1181
 
                }
1182
 
                this = this->next;
1183
 
        }
1184
 
 
1185
 
        return 1;
1186
 
}
1187
 
 
1188
 
static int add_local_path(struct host **hosts, const char *path)
1189
 
{
1190
 
        struct host *new;
1191
 
        char *tmp;
1192
 
 
1193
 
        tmp = strdup(path);
1194
 
        if (!tmp)
1195
 
                return 0;
1196
 
 
1197
 
        new = malloc(sizeof(struct host));
1198
 
        if (!new) {
1199
 
                free(tmp);
1200
 
                return 0;
1201
 
        }
1202
 
 
1203
 
        memset(new, 0, sizeof(struct host));
1204
 
 
1205
 
        new->path = tmp;
1206
 
        new->proximity = PROXIMITY_LOCAL;
1207
 
        new->version = NFS_VERS_MASK;
1208
 
        new->name = NULL;
1209
 
        new->addr = NULL;
1210
 
        new->weight = new->cost = 0;
1211
 
 
1212
 
        add_host(hosts, new);
1213
 
 
1214
 
        return 1;
1215
 
}
1216
 
 
1217
 
static char *seek_delim(const char *s)
1218
 
{
1219
 
        const char *p = s;
1220
 
        char *delim;
1221
 
 
1222
 
        delim = strpbrk(p, "(, \t:");
1223
 
        if (delim && *delim != ':')
1224
 
                return delim;
1225
 
 
1226
 
        while (*p) {
1227
 
                if (*p != ':') {
1228
 
                        p++;
1229
 
                        continue;
1230
 
                }
1231
 
                if (!strncmp(p, ":/", 2))
1232
 
                        return (char *) p;
1233
 
                p++;
1234
 
        }
1235
 
 
1236
 
        return NULL;
1237
 
}
1238
 
 
1239
 
int parse_location(unsigned logopt, struct host **hosts,
1240
 
                   const char *list, unsigned int options)
1241
 
{
1242
 
        char *str, *p, *delim;
1243
 
        unsigned int empty = 1;
1244
 
 
1245
 
        if (!list)
1246
 
                return 0;
1247
 
 
1248
 
        str = strdup(list);
1249
 
        if (!str)
1250
 
                return 0;
1251
 
 
1252
 
        p = str;
1253
 
 
1254
 
        while (p && *p) {
1255
 
                char *next = NULL;
1256
 
                int weight = 0;
1257
 
 
1258
 
                p += strspn(p, " \t,");
1259
 
                delim = seek_delim(p);
1260
 
 
1261
 
                if (delim) {
1262
 
                        if (*delim == '(') {
1263
 
                                char *w = delim + 1;
1264
 
 
1265
 
                                *delim = '\0';
1266
 
 
1267
 
                                delim = strchr(w, ')');
1268
 
                                if (delim) {
1269
 
                                        *delim = '\0';
1270
 
                                        weight = atoi(w);
1271
 
                                }
1272
 
                                delim++;
1273
 
                        }
1274
 
 
1275
 
                        if (*delim == ':') {
1276
 
                                char *path;
1277
 
 
1278
 
                                *delim = '\0';
1279
 
                                path = delim + 1;
1280
 
 
1281
 
                                /* Oh boy - might have spaces in the path */
1282
 
                                next = path;
1283
 
                                while (*next && strncmp(next, ":/", 2))
1284
 
                                        next++;
1285
 
 
1286
 
                                /* No spaces in host names at least */
1287
 
                                if (*next == ':') {
1288
 
                                        while (*next &&
1289
 
                                              (*next != ' ' && *next != '\t'))
1290
 
                                                next--;
1291
 
                                        *next++ = '\0';
1292
 
                                }
1293
 
 
1294
 
                                if (p != delim) {
1295
 
                                        if (!add_host_addrs(hosts, p, weight, options)) {
1296
 
                                                if (empty) {
1297
 
                                                        p = next;
1298
 
                                                        continue;
1299
 
                                                }
1300
 
                                        }
1301
 
 
1302
 
                                        if (!add_path(*hosts, path, strlen(path))) {
1303
 
                                                free_host_list(hosts);
1304
 
                                                free(str);
1305
 
                                                return 0;
1306
 
                                        }
1307
 
                                } else {
1308
 
                                        if (!add_local_path(hosts, path)) {
1309
 
                                                p = next;
1310
 
                                                continue;
1311
 
                                        }
1312
 
                                }
1313
 
                        } else if (*delim != '\0') {
1314
 
                                *delim = '\0';
1315
 
                                next = delim + 1;
1316
 
 
1317
 
                                if (!add_host_addrs(hosts, p, weight, options)) {
1318
 
                                        p = next;
1319
 
                                        continue;
1320
 
                                }
1321
 
 
1322
 
                                empty = 0;
1323
 
                        }
1324
 
                } else {
1325
 
                        /* syntax error - no mount path */
1326
 
                        free_host_list(hosts);
1327
 
                        free(str);
1328
 
                        return 0;
1329
 
                }
1330
 
 
1331
 
                p = next;
1332
 
        }
1333
 
 
1334
 
        free(str);
1335
 
        return 1;
1336
 
}
1337
 
 
1338
 
void dump_host_list(struct host *hosts)
1339
 
{
1340
 
        struct host *this;
1341
 
 
1342
 
        if (!hosts)
1343
 
                return;
1344
 
 
1345
 
        this = hosts;
1346
 
        while (this) {
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);
1350
 
                this = this->next;
1351
 
        }
1352
 
        return;
1353
 
}
1354