Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 146224 Details for
Bug 223083
autofs failed to mount using wildkey and replicated configuration
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
Backport of the v5 replicated server code
autofs-4.1.3-replicated-backport.diff (text/plain), 75.75 KB, created by
Jeff Moyer
on 2007-01-22 20:36:28 UTC
(
hide
)
Description:
Backport of the v5 replicated server code
Filename:
MIME Type:
Creator:
Jeff Moyer
Created:
2007-01-22 20:36:28 UTC
Size:
75.75 KB
patch
obsolete
>diff -uprN autofs-4.1.3/include/automount.h autofs-4.1.3-replicated/include/automount.h >--- autofs-4.1.3/include/automount.h 2007-01-22 15:03:00.000000000 -0500 >+++ autofs-4.1.3-replicated/include/automount.h 2007-01-22 14:40:36.000000000 -0500 >@@ -64,6 +64,8 @@ > #define LKP_ERR_MOUNT 0x2000 > #define LKP_NOTSUP 0x4000 > >+#define MAX_ERR_BUF 128 >+ > #ifdef DEBUG > #define DB(x) do { x; } while(0) > #else >@@ -262,18 +264,6 @@ int cat_path(char *buf, size_t len, cons > int ncat_path(char *buf, size_t len, > const char *dir, const char *base, size_t blen); > >-/* rpc helper subs */ >-#define RPC_PING_FAIL 0x0000 >-#define RPC_PING_V2 NFS2_VERSION >-#define RPC_PING_V3 NFS3_VERSION >-#define RPC_PING_UDP 0x0100 >-#define RPC_PING_TCP 0x0200 >- >-unsigned int rpc_ping(const char *host, long seconds, long micros); >-int rpc_time(const char *host, >- unsigned int ping_vers, unsigned int ping_proto, >- long seconds, long micros, double *result); >- > /* nsswitch parsing */ > char *get_nsswitch_map(const char *, char *); > >@@ -291,19 +281,19 @@ extern int dumpmap; > > #define info(msg, args...) \ > if (do_verbose || do_debug) \ >- syslog(LOG_INFO, msg, ##args); >+ syslog(LOG_INFO, msg, ##args) > > #define warn(msg, args...) \ > if (do_verbose || do_debug) \ >- syslog(LOG_WARNING, msg, ##args); >+ syslog(LOG_WARNING, msg, ##args) > >-#define error(msg, args...) syslog(LOG_ERR, msg, ##args); >+#define error(msg, args...) syslog(LOG_ERR, msg, ##args) > >-#define crit(msg, args...) syslog(LOG_CRIT, msg, ##args); >+#define crit(msg, args...) syslog(LOG_CRIT, msg, ##args) > > #define debug(msg, args...) \ > if (do_debug) \ >- syslog(LOG_DEBUG, msg, ##args); >+ syslog(LOG_DEBUG, msg, ##args) > > /* Define reentrant logging macros for signal handlers */ > >diff -uprN autofs-4.1.3/include/replicated.h autofs-4.1.3-replicated/include/replicated.h >--- autofs-4.1.3/include/replicated.h 1969-12-31 19:00:00.000000000 -0500 >+++ autofs-4.1.3-replicated/include/replicated.h 2007-01-18 15:56:36.000000000 -0500 >@@ -0,0 +1,69 @@ >+/* ----------------------------------------------------------------------- * >+ * >+ * repl_list.h - header file for replicated mount server selection >+ * >+ * Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved >+ * Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, >+ * USA; either version 2 of the License, or (at your option) any later >+ * version; incorporated herein by reference. >+ * >+ * ----------------------------------------------------------------------- */ >+ >+#ifndef _REPLICATED_H >+#define _REPLICATED_H >+ >+#define PROXIMITY_ERROR 0x0000 >+#define PROXIMITY_LOCAL 0x0001 >+#define PROXIMITY_SUBNET 0x0002 >+#define PROXIMITY_NET 0x0004 >+#define PROXIMITY_OTHER 0x0008 >+ >+#define NFS2_SUPPORTED 0x0010 >+#define NFS3_SUPPORTED 0x0020 >+#define NFS4_SUPPORTED 0x0040 >+#define NFS_VERS_MASK (NFS2_SUPPORTED|NFS3_SUPPORTED) >+#define NFS4_VERS_MASK (NFS4_SUPPORTED) >+ >+#define NFS2_REQUESTED NFS2_SUPPORTED >+#define NFS3_REQUESTED NFS3_SUPPORTED >+#define NFS4_REQUESTED NFS4_SUPPORTED >+ >+#define TCP_SUPPORTED 0x0001 >+#define UDP_SUPPORTED 0x0002 >+#define TCP_REQUESTED TCP_SUPPORTED >+#define UDP_REQUESTED UDP_SUPPORTED >+#define NFS_PROTO_MASK (TCP_SUPPORTED|UDP_SUPPORTED) >+ >+#define NFS2_TCP_SUPPORTED NFS2_SUPPORTED >+#define NFS3_TCP_SUPPORTED NFS3_SUPPORTED >+#define NFS4_TCP_SUPPORTED NFS4_SUPPORTED >+#define NFS2_UDP_SUPPORTED (NFS2_SUPPORTED << 8) >+#define NFS3_UDP_SUPPORTED (NFS3_SUPPORTED << 8) >+#define NFS4_UDP_SUPPORTED (NFS4_SUPPORTED << 8) >+#define TCP_SELECTED_MASK 0x00FF >+#define UDP_SELECTED_MASK 0xFF00 >+ >+#define RPC_TIMEOUT 5 >+ >+struct host { >+ char *name; >+ char *addr; >+ char *path; >+ unsigned int version; >+ unsigned int proximity; >+ unsigned int weight; >+ unsigned long cost; >+ struct host *next; >+}; >+ >+void free_host_list(struct host **); >+int parse_location(struct host **, const char *); >+int prune_host_list(struct host **, unsigned int, const char *); >+void dump_host_list(struct host *); >+ >+#endif >+ >diff -uprN autofs-4.1.3/include/rpc_subs.h autofs-4.1.3-replicated/include/rpc_subs.h >--- autofs-4.1.3/include/rpc_subs.h 1969-12-31 19:00:00.000000000 -0500 >+++ autofs-4.1.3-replicated/include/rpc_subs.h 2007-01-18 17:13:26.000000000 -0500 >@@ -0,0 +1,90 @@ >+/* ----------------------------------------------------------------------- * >+ * >+ * rpc_subs.h - header file for rpc discovery >+ * >+ * Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved >+ * Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, >+ * USA; either version 2 of the License, or (at your option) any later >+ * version; incorporated herein by reference. >+ * >+ * ----------------------------------------------------------------------- */ >+ >+#ifndef _RPC_SUBS_H >+#define _RPC_SUBS_H >+ >+#include <rpc/rpc.h> >+#include <rpc/pmap_prot.h> >+//#include <nfs/nfs.h> >+#include <linux/nfs2.h> >+#include <linux/nfs3.h> >+ >+#define NFS4_VERSION 4 >+ >+#if 0 >+/* rpc helper subs */ >+#define RPC_PING_FAIL 0x0000 >+#define RPC_PING_V2 NFS2_VERSION >+#define RPC_PING_V3 NFS3_VERSION >+#define RPC_PING_UDP 0x0100 >+#define RPC_PING_TCP 0x0200 >+ >+unsigned int rpc_ping(const char *host, long seconds, long micros); >+// new -- int rpc_ping(const char *, long, long, unsigned int); >+int rpc_time(const char *host, >+ unsigned int ping_vers, unsigned int ping_proto, >+ long seconds, long micros, double *result); >+// new -- int rpc_time(const char *, unsigned int, unsigned int, long, long, unsigned int, double *); >+ >+#endif >+ >+/* rpc helper subs */ >+#define RPC_PING_FAIL 0x0000 >+#define RPC_PING_V2 NFS2_VERSION >+#define RPC_PING_V3 NFS3_VERSION >+#define RPC_PING_V4 NFS4_VERSION >+#define RPC_PING_UDP 0x0100 >+#define RPC_PING_TCP 0x0200 >+/* >+ * Close options to allow some choice in how and where the TIMED_WAIT >+ * happens. >+ */ >+#define RPC_CLOSE_DEFAULT 0x0000 >+#define RPC_CLOSE_ACTIVE RPC_CLOSE_DEFAULT >+#define RPC_CLOSE_NOLINGER 0x0001 >+ >+#define PMAP_TOUT_UDP 3 >+#define PMAP_TOUT_TCP 5 >+ >+#define HOST_ENT_BUF_SIZE 2048 >+ >+struct conn_info { >+ const char *host; >+ unsigned short port; >+ unsigned long program; >+ unsigned long version; >+ struct protoent *proto; >+ unsigned int send_sz; >+ unsigned int recv_sz; >+ struct timeval timeout; >+ unsigned int close_option; >+ CLIENT *client; >+}; >+ >+int rpc_ping(const char *, long, long, unsigned int); >+int rpc_time(const char *, unsigned int, unsigned int, long, long, unsigned int, double *); >+ >+int rpc_udp_getclient(struct conn_info *, unsigned int, unsigned int); >+void rpc_destroy_udp_client(struct conn_info *); >+int rpc_tcp_getclient(struct conn_info *, unsigned int, unsigned int); >+void rpc_destroy_tcp_client(struct conn_info *); >+int rpc_portmap_getclient(struct conn_info *, const char *, const char *, unsigned int); >+unsigned short rpc_portmap_getport(struct conn_info *, struct pmap *); >+int rpc_ping_proto(struct conn_info *); >+double elapsed(struct timeval, struct timeval); >+ >+#endif >+ >Binary files autofs-4.1.3/lib/nsswitch and autofs-4.1.3-replicated/lib/nsswitch differ >diff -uprN autofs-4.1.3/lib/rpc_subs.c autofs-4.1.3-replicated/lib/rpc_subs.c >--- autofs-4.1.3/lib/rpc_subs.c 2007-01-22 15:02:59.000000000 -0500 >+++ autofs-4.1.3-replicated/lib/rpc_subs.c 2007-01-18 17:14:11.000000000 -0500 >@@ -13,87 +13,179 @@ > * > * ----------------------------------------------------------------------- */ > >+#ifndef _GNU_SOURCE >+#define _GNU_SOURCE >+#endif >+ >+#include <rpc/types.h> > #include <rpc/rpc.h> > #include <rpc/pmap_prot.h> >-#include <nfs/nfs.h> >-#include <linux/nfs2.h> >-#include <linux/nfs3.h> >-#include <rpc/xdr.h> > > #include <unistd.h> > #include <sys/socket.h> > #include <netdb.h> >+#include <net/if.h> > #include <netinet/in.h> >+#include <arpa/inet.h> > #include <sys/fcntl.h> >+#include <rpcsvc/ypclnt.h> >+#include <rpcsvc/nfs_prot.h> > #include <errno.h> >+#include <sys/ioctl.h> >+ >+#include "mount.h" >+#include "rpc_subs.h" > >+/* #define STANDALONE */ >+#ifdef STANDALONE >+#define error(logopt, msg, args...) fprintf(stderr, msg "\n", ##args) >+#else > #include "automount.h" >+#endif > >-#define PMAP_TOUT_UDP 3 >-#define PMAP_TOUT_TCP 6 >+#define MAX_ERR_BUF 512 > >-struct conn_info { >- const char *host; >- unsigned short port; >- unsigned long program; >- unsigned long version; >- struct protoent *proto; >- unsigned int send_sz; >- unsigned int recv_sz; >- struct timeval timeout; >-}; >+static char *ypdomain = NULL; > > /* > * Create a UDP RPC client > */ >-static CLIENT* create_udp_client(struct conn_info *info) >+static CLIENT *create_udp_client(struct conn_info *info) > { >- int fd; >+ int fd, cl_flags, ret, ghn_errno; > CLIENT *client; > struct sockaddr_in laddr, raddr; >- struct hostent *hp; >+ struct hostent hp; >+ struct hostent *php = &hp; >+ struct hostent *result; >+ char buf[HOST_ENT_BUF_SIZE]; >+ size_t len; > > if (info->proto->p_proto != IPPROTO_UDP) > return NULL; > >+ if (info->client) { >+ if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) { >+ fd = -1; >+ clnt_destroy(info->client); >+ info->client = NULL; >+ } else { >+ clnt_control(info->client, CLSET_FD_NCLOSE, NULL); >+ clnt_destroy(info->client); >+ } >+ } >+ > memset(&laddr, 0, sizeof(laddr)); > memset(&raddr, 0, sizeof(raddr)); > >- hp = gethostbyname(info->host); >- if (!hp) >- return NULL; >- > raddr.sin_family = AF_INET; >+ if (inet_aton(info->host, &raddr.sin_addr)) >+ goto got_addr; >+ >+ memset(&hp, 0, sizeof(struct hostent)); >+ >+ ret = gethostbyname_r(info->host, php, >+ buf, HOST_ENT_BUF_SIZE, &result, &ghn_errno); >+ if (ret || !result) { >+ int err = ghn_errno == -1 ? errno : ghn_errno; >+ char *estr = strerror_r(err, buf, HOST_ENT_BUF_SIZE); >+ error("hostname lookup failed: %s", estr); >+ goto out_close; >+ } >+ memcpy(&raddr.sin_addr.s_addr, php->h_addr, php->h_length); >+ >+got_addr: > raddr.sin_port = htons(info->port); >- memcpy(&raddr.sin_addr.s_addr, hp->h_addr, hp->h_length); > >- /* >- * bind to any unused port. If we left this up to the rpc >- * layer, it would bind to a reserved port, which has been shown >- * to exhaust the reserved port range in some situations. >- */ >- fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); >- if (fd < 0) >- return NULL; >- laddr.sin_family = AF_INET; >- laddr.sin_port = 0; >- laddr.sin_addr.s_addr = htonl(INADDR_ANY); >+ if (!info->client) { >+ /* >+ * bind to any unused port. If we left this up to the rpc >+ * layer, it would bind to a reserved port, which has been shown >+ * to exhaust the reserved port range in some situations. >+ */ >+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); >+ if (fd < 0) >+ return NULL; >+ >+ if ((cl_flags = fcntl(fd, F_GETFD, 0)) != -1) { >+ cl_flags |= FD_CLOEXEC; >+ fcntl(fd, F_SETFD, cl_flags); >+ } > >- if (bind(fd, (struct sockaddr *)&laddr, >- sizeof(struct sockaddr_in)) < 0) { >- close(fd); >- fd = RPC_ANYSOCK; >- /* FALLTHROUGH */ >+ laddr.sin_family = AF_INET; >+ laddr.sin_port = 0; >+ laddr.sin_addr.s_addr = htonl(INADDR_ANY); >+ >+ len = sizeof(struct sockaddr_in); >+ if (bind(fd, (struct sockaddr *)&laddr, len) < 0) { >+ close(fd); >+ fd = RPC_ANYSOCK; >+ /* FALLTHROUGH */ >+ } > } > > client = clntudp_bufcreate(&raddr, > info->program, info->version, > info->timeout, &fd, > info->send_sz, info->recv_sz); >- if (client) >- clnt_control(client, CLSET_FD_CLOSE, NULL); >+ >+ if (!client) { >+ info->client = NULL; >+ goto out_close; >+ } >+ >+ /* Close socket fd on destroy, as is default for rpcowned fds */ >+ if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) { >+ clnt_destroy(client); >+ info->client = NULL; >+ goto out_close; >+ } > > return client; >+ >+out_close: >+ if (fd != -1) >+ close(fd); >+ return NULL; >+} >+ >+int rpc_udp_getclient(struct conn_info *info, >+ unsigned int program, unsigned int version) >+{ >+ struct protoent *pe_proto; >+ CLIENT *client; >+ >+ if (!info->client) { >+ pe_proto = getprotobyname("udp"); >+ if (!pe_proto) >+ return 0; >+ >+ info->proto = pe_proto; >+ info->send_sz = UDPMSGSIZE; >+ info->recv_sz = UDPMSGSIZE; >+ } >+ >+ info->program = program; >+ info->version = version; >+ >+ client = create_udp_client(info); >+ >+ if (!client) >+ return 0; >+ >+ info->client = client; >+ >+ return 1; >+} >+ >+void rpc_destroy_udp_client(struct conn_info *info) >+{ >+ if (!info->client) >+ return; >+ >+ clnt_destroy(info->client); >+ info->client = NULL; >+ return; > } > > /* >@@ -104,7 +196,8 @@ static CLIENT* create_udp_client(struct > */ > static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout) > { >- int flags, ret, len; >+ int flags, ret; >+ socklen_t len; > fd_set wset, rset; > > flags = fcntl(fd, F_GETFL, 0); >@@ -115,9 +208,17 @@ static int connect_nb(int fd, struct soc > if (ret < 0) > return -1; > >- ret = connect(fd, (struct sockaddr *)addr, sizeof(struct sockaddr)); >- if (ret < 0 && errno != EINPROGRESS) >+ /* >+ * From here on subsequent sys calls could change errno so >+ * we set ret = -errno to capture it in case we decide to >+ * use it later. >+ */ >+ len = sizeof(struct sockaddr); >+ ret = connect(fd, (struct sockaddr *)addr, len); >+ if (ret < 0 && errno != EINPROGRESS) { >+ ret = -errno; > goto done; >+ } > > if (ret == 0) > goto done; >@@ -128,17 +229,20 @@ static int connect_nb(int fd, struct soc > wset = rset; > > ret = select(fd + 1, &rset, &wset, NULL, tout); >- if (!ret) { >- ret = -errno; >+ if (ret <= 0) { >+ if (ret == 0) >+ ret = -ETIMEDOUT; >+ else >+ ret = -errno; > goto done; > } > > if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { >- int stat; >+ int status; > > len = sizeof(ret); >- stat = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len); >- if (stat < 0) { >+ status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len); >+ if (status < 0) { > ret = -errno; > goto done; > } >@@ -156,190 +260,383 @@ done: > /* > * Create a TCP RPC client using non-blocking connect > */ >-static CLIENT* create_tcp_client(struct conn_info *info) >+static CLIENT *create_tcp_client(struct conn_info *info) > { >- int fd; >+ int fd, cl_flags, ghn_errno; > CLIENT *client; > struct sockaddr_in addr; >- struct hostent *hp; >+ struct hostent hp; >+ struct hostent *php = &hp; >+ struct hostent *result; >+ char buf[HOST_ENT_BUF_SIZE]; > int ret; > > if (info->proto->p_proto != IPPROTO_TCP) > return NULL; > >- memset(&addr, 0, sizeof(addr)); >+ if (info->client) { >+ if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) { >+ fd = -1; >+ clnt_destroy(info->client); >+ info->client = NULL; >+ } else { >+ clnt_control(info->client, CLSET_FD_NCLOSE, NULL); >+ clnt_destroy(info->client); >+ } >+ } > >- hp = gethostbyname(info->host); >- if (!hp) >- return NULL; >+ memset(&addr, 0, sizeof(addr)); > > addr.sin_family = AF_INET; >- addr.sin_port = htons(info->port); >- memcpy(&addr.sin_addr.s_addr, hp->h_addr, hp->h_length); >+ if (inet_aton(info->host, &addr.sin_addr)) >+ goto got_addr; > >- fd = socket(PF_INET, SOCK_STREAM, info->proto->p_proto); >- if (fd < 0) >- return NULL; >+ memset(&hp, 0, sizeof(struct hostent)); > >- ret = connect_nb(fd, &addr, &info->timeout); >- if (ret < 0) >+ ret = gethostbyname_r(info->host, php, >+ buf, HOST_ENT_BUF_SIZE, &result, &ghn_errno); >+ if (ret || !result) { >+ int err = ghn_errno == -1 ? errno : ghn_errno; >+ char *estr = strerror_r(err, buf, HOST_ENT_BUF_SIZE); >+ error("hostname lookup failed: %s", estr); > goto out_close; >+ } >+ memcpy(&addr.sin_addr.s_addr, php->h_addr, php->h_length); >+ >+got_addr: >+ addr.sin_port = htons(info->port); >+ >+ if (!info->client) { >+ fd = socket(PF_INET, SOCK_STREAM, info->proto->p_proto); >+ if (fd < 0) >+ return NULL; >+ >+ if ((cl_flags = fcntl(fd, F_GETFD, 0)) != -1) { >+ cl_flags |= FD_CLOEXEC; >+ fcntl(fd, F_SETFD, cl_flags); >+ } >+ >+ ret = connect_nb(fd, &addr, &info->timeout); >+ if (ret < 0) >+ goto out_close; >+ } > > client = clnttcp_create(&addr, > info->program, info->version, &fd, > info->send_sz, info->recv_sz); >- if (!client) >+ >+ if (!client) { >+ info->client = NULL; >+ goto out_close; >+ } >+ >+ /* Close socket fd on destroy, as is default for rpcowned fds */ >+ if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) { >+ clnt_destroy(client); >+ info->client = NULL; > goto out_close; >+ } > >- clnt_control(client, CLSET_FD_CLOSE, NULL); > return client; > > out_close: >- close(fd); >+ if (fd != -1) >+ close(fd); > return NULL; > } > >-static unsigned short portmap_getport(struct conn_info *info) >+int rpc_tcp_getclient(struct conn_info *info, >+ unsigned int program, unsigned int version) > { >- struct conn_info pmap_info; >- unsigned short port = 0; >+ struct protoent *pe_proto; > CLIENT *client; >- enum clnt_stat stat; >- struct pmap parms; > >- pmap_info.host = info->host; >- pmap_info.port = PMAPPORT; >- pmap_info.program = PMAPPROG; >- pmap_info.version = PMAPVERS; >- pmap_info.proto = info->proto; >- pmap_info.send_sz = RPCSMALLMSGSIZE; >- pmap_info.recv_sz = RPCSMALLMSGSIZE; >- pmap_info.timeout.tv_sec = PMAP_TOUT_UDP; >- pmap_info.timeout.tv_usec = 0; >+ if (!info->client) { >+ pe_proto = getprotobyname("tcp"); >+ if (!pe_proto) >+ return 0; >+ >+ info->proto = pe_proto; >+ info->send_sz = 0; >+ info->recv_sz = 0; >+ } > >- if (info->proto->p_proto == IPPROTO_TCP) { >- pmap_info.timeout.tv_sec = PMAP_TOUT_TCP; >- client = create_tcp_client(&pmap_info); >- } else >- client = create_udp_client(&pmap_info); >+ info->program = program; >+ info->version = version; >+ >+ client = create_tcp_client(info); > > if (!client) > return 0; >- >- parms.pm_prog = info->program; >- parms.pm_vers = info->version; >- parms.pm_prot = info->proto->p_proto; >- parms.pm_port = 0; > >- stat = clnt_call(client, PMAPPROC_GETPORT, >- (xdrproc_t) xdr_pmap, (caddr_t) &parms, >- (xdrproc_t) xdr_u_short, (caddr_t) &port, >- pmap_info.timeout); >+ info->client = client; > >- clnt_destroy(client); >+ return 1; >+} > >- if (stat != RPC_SUCCESS) >- return 0; >+void rpc_destroy_tcp_client(struct conn_info *info) >+{ >+ struct linger lin = { 1, 0 }; >+ socklen_t lin_len = sizeof(struct linger); >+ int fd; > >- return port; >+ if (!info->client) >+ return; >+ >+ if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) >+ fd = -1; >+ >+ switch (info->close_option) { >+ case RPC_CLOSE_NOLINGER: >+ if (fd >= 0) >+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len); >+ break; >+ } >+ >+ clnt_destroy(info->client); >+ info->client = NULL; >+ >+ return; > } > >-static int rpc_ping_proto(const char *host, >- unsigned long nfs_version, >- const char *proto, >- long seconds, long micros) >+int rpc_portmap_getclient(struct conn_info *info, >+ const char *host, const char *proto, >+ unsigned int option) > { >- struct conn_info info; >+ struct protoent *pe_proto; > CLIENT *client; >- enum clnt_stat stat; >- struct protoent *prot; >- >- prot = getprotobyname(proto); >- if (!prot) >- return 1; >- >- info.host = host; >- info.program = NFS_PROGRAM; >- info.version = nfs_version; >- info.proto = prot; >- info.send_sz = 0; >- info.recv_sz = 0; >- info.timeout.tv_sec = seconds; >- info.timeout.tv_usec = micros; > >- info.port = portmap_getport(&info); >- if (!info.port) >+ pe_proto = getprotobyname(proto); >+ if (!pe_proto) > return 0; > >- if (prot->p_proto == IPPROTO_UDP) { >- info.send_sz = UDPMSGSIZE; >- info.recv_sz = UDPMSGSIZE; >- client = create_udp_client(&info); >+ info->host = host; >+ info->program = PMAPPROG; >+ info->port = PMAPPORT; >+ info->version = PMAPVERS; >+ info->proto = pe_proto; >+ info->send_sz = RPCSMALLMSGSIZE; >+ info->recv_sz = RPCSMALLMSGSIZE; >+ info->timeout.tv_sec = PMAP_TOUT_UDP; >+ info->timeout.tv_usec = 0; >+ info->close_option = option; >+ info->client = NULL; >+ >+ if (pe_proto->p_proto == IPPROTO_TCP) { >+ info->timeout.tv_sec = PMAP_TOUT_TCP; >+ client = create_tcp_client(info); > } else >- client = create_tcp_client(&info); >+ client = create_udp_client(info); > > if (!client) > return 0; > >- clnt_control(client, CLSET_TIMEOUT, (char *) &info.timeout); >- clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info.timeout); >+ info->client = client; >+ >+ return 1; >+} >+ >+unsigned short rpc_portmap_getport(struct conn_info *info, struct pmap *parms) >+{ >+ struct conn_info pmap_info; >+ unsigned short port = 0; >+ CLIENT *client; >+ enum clnt_stat status; >+ int proto = info->proto->p_proto; >+ >+ memset(&pmap_info, 0, sizeof(struct conn_info)); >+ >+ if (proto == IPPROTO_TCP) >+ pmap_info.timeout.tv_sec = PMAP_TOUT_TCP; >+ else >+ pmap_info.timeout.tv_sec = PMAP_TOUT_UDP; > >- stat = clnt_call(client, NFSPROC_NULL, >+ if (info->client) >+ client = info->client; >+ else { >+ pmap_info.host = info->host; >+ pmap_info.port = PMAPPORT; >+ pmap_info.program = PMAPPROG; >+ pmap_info.version = PMAPVERS; >+ pmap_info.proto = info->proto; >+ pmap_info.send_sz = RPCSMALLMSGSIZE; >+ pmap_info.recv_sz = RPCSMALLMSGSIZE; >+ >+ if (proto == IPPROTO_TCP) >+ client = create_tcp_client(&pmap_info); >+ else >+ client = create_udp_client(&pmap_info); >+ >+ if (!client) >+ return 0; >+ } >+ >+ /* >+ * Check to see if server is up otherwise a getport will take >+ * forever to timeout. >+ */ >+ status = clnt_call(client, PMAPPROC_NULL, > (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0, >- info.timeout); >+ pmap_info.timeout); > >- clnt_destroy(client); >+ if (status == RPC_SUCCESS) { >+ status = clnt_call(client, PMAPPROC_GETPORT, >+ (xdrproc_t) xdr_pmap, (caddr_t) parms, >+ (xdrproc_t) xdr_u_short, (caddr_t) &port, >+ pmap_info.timeout); >+ } >+ >+ if (!info->client) { >+ /* >+ * Only play with the close options if we think it >+ * completed OK >+ */ >+ if (proto == IPPROTO_TCP && status == RPC_SUCCESS) { >+ struct linger lin = { 1, 0 }; >+ socklen_t lin_len = sizeof(struct linger); >+ int fd; >+ >+ if (!clnt_control(client, CLGET_FD, (char *) &fd)) >+ fd = -1; >+ >+ switch (info->close_option) { >+ case RPC_CLOSE_NOLINGER: >+ if (fd >= 0) >+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len); >+ break; >+ } >+ } >+ clnt_destroy(client); >+ } > >- if (stat != RPC_SUCCESS) >+ if (status != RPC_SUCCESS) > return 0; > >- return 1; >+ return port; > } > >-static unsigned int rpc_ping_v2(const char *host, long seconds, long micros) >+int rpc_ping_proto(struct conn_info *info) > { >- unsigned int status = RPC_PING_FAIL; >+ CLIENT *client; >+ enum clnt_stat status; >+ int proto = info->proto->p_proto; > >- status = rpc_ping_proto(host, NFS2_VERSION, "udp", seconds, micros); >- if (status) >- return RPC_PING_V2 | RPC_PING_UDP; >+ if (info->client) >+ client = info->client; >+ else { >+ if (info->proto->p_proto == IPPROTO_UDP) { >+ info->send_sz = UDPMSGSIZE; >+ info->recv_sz = UDPMSGSIZE; >+ client = create_udp_client(info); >+ } else >+ client = create_tcp_client(info); > >- status = rpc_ping_proto(host, NFS2_VERSION, "tcp", seconds, micros); >- if (status) >- return RPC_PING_V2 | RPC_PING_TCP; >+ if (!client) >+ return 0; >+ } > >- return status; >+ clnt_control(client, CLSET_TIMEOUT, (char *) &info->timeout); >+ clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout); >+ >+ status = clnt_call(client, NFSPROC_NULL, >+ (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0, >+ info->timeout); >+ >+ if (!info->client) { >+ /* >+ * Only play with the close options if we think it >+ * completed OK >+ */ >+ if (proto == IPPROTO_TCP && status == RPC_SUCCESS) { >+ struct linger lin = { 1, 0 }; >+ socklen_t lin_len = sizeof(struct linger); >+ int fd; >+ >+ if (!clnt_control(client, CLGET_FD, (char *) &fd)) >+ fd = -1; >+ >+ switch (info->close_option) { >+ case RPC_CLOSE_NOLINGER: >+ if (fd >= 0) >+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len); >+ break; >+ } >+ } >+ clnt_destroy(client); >+ } >+ >+ if (status != RPC_SUCCESS) >+ return 0; >+ >+ return 1; > } > >-static unsigned int rpc_ping_v3(const char *host, long seconds, long micros) >+static unsigned int __rpc_ping(const char *host, >+ unsigned long version, >+ char *proto, >+ long seconds, long micros, >+ unsigned int option) > { >- unsigned int status = RPC_PING_FAIL; >+ unsigned int status; >+ struct conn_info info; >+ struct pmap parms; > >- status = rpc_ping_proto(host, NFS3_VERSION, "udp", seconds, micros); >- if (status) >- return RPC_PING_V3 | RPC_PING_UDP; >+ info.host = host; >+ info.program = NFS_PROGRAM; >+ info.version = version; >+ info.send_sz = 0; >+ info.recv_sz = 0; >+ info.timeout.tv_sec = seconds; >+ info.timeout.tv_usec = micros; >+ info.close_option = option; >+ info.client = NULL; > >- status = rpc_ping_proto(host, NFS3_VERSION, "tcp", seconds, micros); >- if (status) >- return RPC_PING_V3 | RPC_PING_TCP; >+ status = RPC_PING_FAIL; >+ >+ info.proto = getprotobyname(proto); >+ if (!info.proto) >+ return status; >+ >+ parms.pm_prog = NFS_PROGRAM; >+ parms.pm_vers = version; >+ parms.pm_prot = info.proto->p_proto; >+ parms.pm_port = 0; >+ >+ info.port = rpc_portmap_getport(&info, &parms); >+ if (!info.port) >+ return status; >+ >+ status = rpc_ping_proto(&info); > > return status; > } > >-unsigned int rpc_ping(const char *host, long seconds, long micros) >+int rpc_ping(const char *host, long seconds, long micros, unsigned int option) > { >+ unsigned long vers3 = NFS3_VERSION; >+ unsigned long vers2 = NFS2_VERSION; > unsigned int status; > >- status = rpc_ping_v2(host, seconds, micros); >+ status = __rpc_ping(host, vers2, "udp", seconds, micros, option); > if (status) >- return status; >+ return RPC_PING_V2 | RPC_PING_UDP; >+ >+ status = __rpc_ping(host, vers3, "udp", seconds, micros, option); >+ if (status) >+ return RPC_PING_V3 | RPC_PING_UDP; >+ >+ status = __rpc_ping(host, vers2, "tcp", seconds, micros, option); >+ if (status) >+ return RPC_PING_V2 | RPC_PING_TCP; >+ >+ status = __rpc_ping(host, vers3, "tcp", seconds, micros, option); >+ if (status) >+ return RPC_PING_V3 | RPC_PING_TCP; > >- status = rpc_ping_v3(host, seconds, micros); >- > return status; > } > >-static double elapsed(struct timeval start, struct timeval end) >+double elapsed(struct timeval start, struct timeval end) > { > double t1, t2; > t1 = (double)start.tv_sec + (double)start.tv_usec/(1000*1000); >@@ -349,16 +646,17 @@ static double elapsed(struct timeval sta > > int rpc_time(const char *host, > unsigned int ping_vers, unsigned int ping_proto, >- long seconds, long micros, double *result) >+ long seconds, long micros, unsigned int option, double *result) > { > int status; > double taken; > struct timeval start, end; > struct timezone tz; > char *proto = (ping_proto & RPC_PING_UDP) ? "udp" : "tcp"; >+ unsigned long vers = ping_vers; > > gettimeofday(&start, &tz); >- status = rpc_ping_proto(host, ping_vers, proto, seconds, micros); >+ status = __rpc_ping(host, vers, proto, seconds, micros, option); > gettimeofday(&end, &tz); > > if (!status) { >@@ -373,6 +671,449 @@ int rpc_time(const char *host, > return status; > } > >+static int rpc_get_exports_proto(struct conn_info *info, exports *exp) >+{ >+ CLIENT *client; >+ enum clnt_stat status; >+ int proto = info->proto->p_proto; >+ unsigned int option = info->close_option; >+ >+ if (info->proto->p_proto == IPPROTO_UDP) { >+ info->send_sz = UDPMSGSIZE; >+ info->recv_sz = UDPMSGSIZE; >+ client = create_udp_client(info); >+ } else >+ client = create_tcp_client(info); >+ >+ if (!client) >+ return 0; >+ >+ clnt_control(client, CLSET_TIMEOUT, (char *) &info->timeout); >+ clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout); >+ >+ client->cl_auth = authunix_create_default(); >+ >+ status = clnt_call(client, MOUNTPROC_EXPORT, >+ (xdrproc_t) xdr_void, NULL, >+ (xdrproc_t) xdr_exports, (caddr_t) exp, >+ info->timeout); >+ >+ /* Only play with the close options if we think it completed OK */ >+ if (proto == IPPROTO_TCP && status == RPC_SUCCESS) { >+ struct linger lin = { 1, 0 }; >+ socklen_t lin_len = sizeof(struct linger); >+ int fd; >+ >+ if (!clnt_control(client, CLGET_FD, (char *) &fd)) >+ fd = -1; >+ >+ switch (option) { >+ case RPC_CLOSE_NOLINGER: >+ if (fd >= 0) >+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len); >+ break; >+ } >+ } >+ auth_destroy(client->cl_auth); >+ clnt_destroy(client); >+ >+ if (status != RPC_SUCCESS) >+ return 0; >+ >+ return 1; >+} >+ >+static void rpc_export_free(exports item) >+{ >+ groups grp; >+ groups tmp; >+ >+ if (item->ex_dir) >+ free(item->ex_dir); >+ >+ grp = item->ex_groups; >+ while (grp) { >+ if (grp->gr_name) >+ free(grp->gr_name); >+ tmp = grp; >+ grp = grp->gr_next; >+ free(tmp); >+ } >+ free(item); >+} >+ >+void rpc_exports_free(exports list) >+{ >+ exports tmp; >+ >+ while (list) { >+ tmp = list; >+ list = list->ex_next; >+ rpc_export_free(tmp); >+ } >+ return; >+} >+ >+static int masked_match(const char *addr, const char *mask) >+{ >+ char buf[MAX_ERR_BUF], *ptr; >+ struct sockaddr_in saddr; >+ struct sockaddr_in6 saddr6; >+ struct ifconf ifc; >+ struct ifreq *ifr; >+ int sock, cl_flags, ret, i, is_ipv4, is_ipv6; >+ unsigned int msize; >+ >+ sock = socket(AF_INET, SOCK_DGRAM, 0); >+ if (sock < 0) { >+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF); >+ error("socket creation failed: %s", estr); >+ return 0; >+ } >+ >+ if ((cl_flags = fcntl(sock, F_GETFD, 0)) != -1) { >+ cl_flags |= FD_CLOEXEC; >+ fcntl(sock, F_SETFD, cl_flags); >+ } >+ >+ ifc.ifc_len = sizeof(buf); >+ ifc.ifc_req = (struct ifreq *) buf; >+ ret = ioctl(sock, SIOCGIFCONF, &ifc); >+ if (ret == -1) { >+ close(sock); >+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF); >+ error("ioctl: %s", estr); >+ return 0; >+ } >+ >+ is_ipv4 = is_ipv6 = 0; >+ is_ipv4 = inet_pton(AF_INET, addr, &saddr.sin_addr); >+ if (!is_ipv4) >+ is_ipv6 = inet_pton(AF_INET6, addr, &saddr6.sin6_addr); >+ >+ if (strchr(mask, '.')) { >+ struct sockaddr_in maddr; >+ uint32_t ma; >+ int i = 0; >+ >+ ret = inet_aton(mask, &maddr.sin_addr); >+ if (!ret) { >+ close(sock); >+ return 0; >+ } >+ >+ ma = ntohl((uint32_t) maddr.sin_addr.s_addr); >+ while (!(ma & 1)) { >+ i++; >+ ma = ma >> 1; >+ } >+ >+ msize = i; >+ } else >+ msize = atoi(mask); >+ >+ i = 0; >+ ptr = (char *) &ifc.ifc_buf[0]; >+ >+ while (ptr < buf + ifc.ifc_len) { >+ ifr = (struct ifreq *) ptr; >+ >+ switch (ifr->ifr_addr.sa_family) { >+ case AF_INET: >+ { >+ struct sockaddr_in *if_addr; >+ uint32_t m, ia, ha; >+ >+ if (!is_ipv4 || msize > 32) >+ break; >+ >+ m = -1; >+ m = m << (32 - msize); >+ ha = ntohl((uint32_t) saddr.sin_addr.s_addr); >+ >+ if_addr = (struct sockaddr_in *) &ifr->ifr_addr; >+ ia = ntohl((uint32_t) if_addr->sin_addr.s_addr); >+ >+ if ((ia & m) == (ha & m)) { >+ close(sock); >+ return 1; >+ } >+ break; >+ } >+ >+ /* glibc rpc only understands IPv4 atm */ >+ case AF_INET6: >+ break; >+ >+ default: >+ break; >+ } >+ >+ i++; >+ ptr = (char *) &ifc.ifc_req[i]; >+ } >+ >+ close(sock); >+ return 0; >+} >+ >+/* >+ * This function has been adapted from the match_patern function >+ * found in OpenSSH and is used in accordance with the copyright >+ * notice found their. >+ * >+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland. >+ */ >+/* >+ * Returns true if the given string matches the pattern (which >+ * may contain ? and * as wildcards), and zero if it does not >+ * match. >+ */ >+static int pattern_match(const char *s, const char *pattern) >+{ >+ for (;;) { >+ /* If at end of pattern, accept if also at end of string. */ >+ if (!*pattern) >+ return !*s; >+ >+ if (*pattern == '*') { >+ /* Skip the asterisk. */ >+ pattern++; >+ >+ /* If at end of pattern, accept immediately. */ >+ if (!*pattern) >+ return 1; >+ >+ /* If next character in pattern is known, optimize. */ >+ if (*pattern != '?' && *pattern != '*') { >+ /* >+ * Look instances of the next character in >+ * pattern, and try to match starting from >+ * those. >+ */ >+ for (; *s; s++) >+ if (*s == *pattern && >+ pattern_match(s + 1, pattern + 1)) >+ return 1; >+ >+ /* Failed. */ >+ return 0; >+ } >+ /* >+ * Move ahead one character at a time and try to >+ * match at each position. >+ */ >+ for (; *s; s++) >+ if (pattern_match(s, pattern)) >+ return 1; >+ /* Failed. */ >+ return 0; >+ } >+ /* >+ * There must be at least one more character in the string. >+ * If we are at the end, fail. >+ */ >+ if (!*s) >+ return 0; >+ >+ /* Check if the next character of the string is acceptable. */ >+ if (*pattern != '?' && *pattern != *s) >+ return 0; >+ >+ /* Move to the next character, both in string and in pattern. */ >+ s++; >+ pattern++; >+ } >+ /* NOTREACHED */ >+} >+ >+static int string_match(const char *myname, const char *pattern) >+{ >+ struct addrinfo hints, *ni; >+ int ret; >+ >+ memset(&hints, 0, sizeof(hints)); >+ hints.ai_flags = AI_CANONNAME; >+ hints.ai_family = 0; >+ hints.ai_socktype = 0; >+ >+ ret = getaddrinfo(myname, NULL, &hints, &ni); >+ if (ret) { >+ error("name lookup failed: %s", gai_strerror(ret)); >+ return 0; >+ } >+ >+ if (strchr(pattern, '*') || strchr(pattern, '?')) { >+ ret = pattern_match(myname, pattern); >+ if (!ret) >+ ret = pattern_match(ni->ai_canonname, pattern); >+ } else { >+ /* Match simple nane or FQDN */ >+ ret = !memcmp(myname, pattern, strlen(pattern)); >+ if (!ret) >+ ret = !memcmp(ni->ai_canonname, pattern, strlen(pattern)); >+ >+ /* Name could still be a netgroup (Solaris) */ >+ if (!ret && ypdomain) { >+ ret = innetgr(pattern, myname, NULL, ypdomain); >+ if (!ret) >+ ret = innetgr(pattern, >+ ni->ai_canonname, NULL, ypdomain); >+ } >+ >+ } >+ freeaddrinfo(ni); >+ return ret; >+} >+ >+static int host_match(char *pattern) >+{ >+ unsigned int negate = (*pattern == '-'); >+ const char *m_pattern = (negate ? pattern + 1 : pattern); >+ char myname[MAXHOSTNAMELEN + 1] = "\0"; >+ struct in_addr tmp; >+ int ret = 0; >+ >+ if (gethostname(myname, MAXHOSTNAMELEN)) >+ return 0; >+ >+ if (yp_get_default_domain(&ypdomain)) >+ ypdomain = NULL; >+ >+ if (*m_pattern == '@') { >+ if (ypdomain) >+ ret = innetgr(m_pattern + 1, myname, NULL, ypdomain); >+ } else if (inet_aton(m_pattern, &tmp) || strchr(m_pattern, '/')) { >+ size_t len = strlen(m_pattern) + 1; >+ char *addr, *mask; >+ >+ addr = alloca(len); >+ if (!addr) >+ return 0; >+ >+ memset(addr, 0, len); >+ memcpy(addr, m_pattern, len - 1); >+ mask = strchr(addr, '/'); >+ if (mask) { >+ *mask++ = '\0'; >+ ret = masked_match(addr, mask); >+ } else >+ ret = masked_match(addr, "32"); >+ } else if (!strcmp(m_pattern, "gss/krb5")) { >+ /* Leave this to the GSS layer */ >+ ret = 1; >+ } else >+ ret = string_match(myname, m_pattern); >+ >+ if (negate) >+ ret = !ret; >+ >+ return ret; >+} >+ >+static int rpc_export_allowed(groups grouplist) >+{ >+ groups grp = grouplist; >+ >+ /* NULL group list => everyone */ >+ if (!grp) >+ return 1; >+ >+ while (grp) { >+ if (host_match(grp->gr_name)) >+ return 1; >+ grp = grp->gr_next; >+ } >+ return 0; >+} >+ >+exports rpc_exports_prune(exports list) >+{ >+ exports head = list; >+ exports exp; >+ exports last; >+ int res; >+ >+ exp = list; >+ last = NULL; >+ while (exp) { >+ res = rpc_export_allowed(exp->ex_groups); >+ if (!res) { >+ if (last == NULL) { >+ head = exp->ex_next; >+ rpc_export_free(exp); >+ exp = head; >+ } else { >+ last->ex_next = exp->ex_next; >+ rpc_export_free(exp); >+ exp = last->ex_next; >+ } >+ continue; >+ } >+ last = exp; >+ exp = exp->ex_next; >+ } >+ return head; >+} >+ >+exports rpc_get_exports(const char *host, long seconds, long micros, unsigned int option) >+{ >+ struct conn_info info; >+ exports exportlist; >+ struct pmap parms; >+ int status; >+ >+ info.host = host; >+ info.program = MOUNTPROG; >+ info.version = MOUNTVERS; >+ info.send_sz = 0; >+ info.recv_sz = 0; >+ info.timeout.tv_sec = seconds; >+ info.timeout.tv_usec = micros; >+ info.close_option = option; >+ info.client = NULL; >+ >+ parms.pm_prog = info.program; >+ parms.pm_vers = info.version; >+ parms.pm_port = 0; >+ >+ /* Try UDP first */ >+ info.proto = getprotobyname("udp"); >+ if (!info.proto) >+ goto try_tcp; >+ >+ parms.pm_prot = info.proto->p_proto; >+ >+ info.port = rpc_portmap_getport(&info, &parms); >+ if (!info.port) >+ goto try_tcp; >+ >+ memset(&exportlist, '\0', sizeof(exportlist)); >+ >+ status = rpc_get_exports_proto(&info, &exportlist); >+ if (status) >+ return exportlist; >+ >+try_tcp: >+ info.proto = getprotobyname("tcp"); >+ if (!info.proto) >+ return NULL; >+ >+ parms.pm_prot = info.proto->p_proto; >+ >+ info.port = rpc_portmap_getport(&info, &parms); >+ if (!info.port) >+ return NULL; >+ >+ memset(&exportlist, '\0', sizeof(exportlist)); >+ >+ status = rpc_get_exports_proto(&info, &exportlist); >+ if (!status) >+ return NULL; >+ >+ return exportlist; >+} >+ > #if 0 > #include <stdio.h> > >@@ -380,25 +1121,55 @@ int main(int argc, char **argv) > { > int ret; > double res = 0.0; >+ exports exportlist, tmp; >+ groups grouplist; >+ int n, maxlen; > >- ret = rpc_ping("budgie", 10, 0); >+/* >+ ret = rpc_ping("budgie", 10, 0, RPC_CLOSE_DEFAULT); > printf("ret = %d\n", ret); > > res = 0.0; >- ret = rpc_time("raven", NFS2_VERSION, RPC_PING_TCP, 10, 0, &res); >+ ret = rpc_time("budgie", NFS2_VERSION, RPC_PING_TCP, 10, 0, RPC_CLOSE_DEFAULT, &res); > printf("v2 tcp ret = %d, res = %f\n", ret, res); > > res = 0.0; >- ret = rpc_time("raven", NFS3_VERSION, RPC_PING_TCP, 10, 0, &res); >+ ret = rpc_time("budgie", NFS3_VERSION, RPC_PING_TCP, 10, 0, RPC_CLOSE_DEFAULT, &res); > printf("v3 tcp ret = %d, res = %f\n", ret, res); > > res = 0.0; >- ret = rpc_time("raven", NFS2_VERSION, RPC_PING_UDP, 10, 0, &res); >+ ret = rpc_time("budgie", NFS2_VERSION, RPC_PING_UDP, 10, 0, RPC_CLOSE_DEFAULT, &res); > printf("v2 udp ret = %d, res = %f\n", ret, res); > > res = 0.0; >- ret = rpc_time("raven", NFS3_VERSION, RPC_PING_UDP, 10, 0, &res); >+ ret = rpc_time("budgie", NFS3_VERSION, RPC_PING_UDP, 10, 0, RPC_CLOSE_DEFAULT, &res); > printf("v3 udp ret = %d, res = %f\n", ret, res); >+*/ >+ exportlist = rpc_get_exports("budgie", 10, 0, RPC_CLOSE_NOLINGER); >+ exportlist = rpc_exports_prune(exportlist); >+ >+ maxlen = 0; >+ for (tmp = exportlist; tmp; tmp = tmp->ex_next) { >+ if ((n = strlen(tmp->ex_dir)) > maxlen) >+ maxlen = n; >+ } >+ >+ if (exportlist) { >+ while (exportlist) { >+ printf("%-*s ", maxlen, exportlist->ex_dir); >+ grouplist = exportlist->ex_groups; >+ if (grouplist) { >+ while (grouplist) { >+ printf("%s%s", grouplist->gr_name, >+ grouplist->gr_next ? "," : ""); >+ grouplist = grouplist->gr_next; >+ } >+ } >+ printf("\n"); >+ exportlist = exportlist->ex_next; >+ } >+ } >+ rpc_exports_free(exportlist); > > exit(0); > } >diff -uprN autofs-4.1.3/modules/Makefile autofs-4.1.3-replicated/modules/Makefile >--- autofs-4.1.3/modules/Makefile 2007-01-22 15:03:00.000000000 -0500 >+++ autofs-4.1.3-replicated/modules/Makefile 2007-01-18 15:57:58.000000000 -0500 >@@ -94,3 +94,7 @@ lookup_ldap.so: lookup_ldap.c > parse_sun.so: parse_sun.c > $(CC) $(SOLDFLAGS) $(CFLAGS) -o parse_sun.so parse_sun.c $(AUTOFS_LIB) $(LIBNSL) > $(STRIP) parse_sun.so >+ >+mount_nfs.so: mount_nfs.c replicated.o >+ $(CC) $(SOLDFLAGS) $(CFLAGS) -o mount_nfs.so mount_nfs.c replicated.o $(AUTOFS_LIB) $(LIBNSL) >+ $(STRIP) mount_nfs.so >diff -uprN autofs-4.1.3/modules/mount_nfs.c autofs-4.1.3-replicated/modules/mount_nfs.c >--- autofs-4.1.3/modules/mount_nfs.c 2007-01-22 15:03:00.000000000 -0500 >+++ autofs-4.1.3-replicated/modules/mount_nfs.c 2007-01-22 14:41:23.000000000 -0500 >@@ -35,6 +35,7 @@ > > #define MODULE_MOUNT > #include "automount.h" >+#include "replicated.h" > > #define MODPREFIX "mount(nfs): " > >@@ -67,403 +68,6 @@ int mount_init(void **context) > return !mount_bind; > } > >-int add_bad_host(char ***badhosts_ptr, int *num_badhosts, char *badhost) >-{ >- char *newhost, *colon_loc; >- >- debug (MODPREFIX "add_bad_host: %s", badhost); >- (*num_badhosts)++; >- *badhosts_ptr = realloc(*badhosts_ptr, (*num_badhosts) * sizeof(char *)); >- >- if (*badhosts_ptr == NULL) { >- (*num_badhosts)--; >- return 0; >- } >- >- >- newhost = strdup(badhost); >- (*badhosts_ptr)[*num_badhosts-1] = newhost; >- >- /* Remove directory from string as we only compare hosts */ >- colon_loc = strchr(newhost, ':'); >- if (colon_loc) >- *colon_loc = '\0'; >- >- return 1; >-} >- >-int is_bad_host(char *host, char **badhosts, int num_badhosts) >-{ >- int i; >- >- debug(MODPREFIX "is_bad_host: %s", host); >- for (i = 0; i < num_badhosts; i++) { >- if (strcmp(badhosts[i], host) == 0) { >- debug(MODPREFIX "is_bad_host: %s is bad", host); >- return 1; >- } >- } >- return 0; >-} >- >-void free_bad_hosts(char **badhosts, int num_badhosts) >-{ >- int i; >- for (i = 0; i < num_badhosts; i++) >- free(badhosts[i]); >- free(badhosts); >-} >- >-int is_local_addr(const char *host, const char *host_addr, int addr_len) >-{ >- struct sockaddr_in src_addr, local_addr; >- int src_len = sizeof(src_addr); >- int local_len = sizeof(local_addr); >- int sock, ret; >- >- sock = socket(AF_INET, SOCK_DGRAM, udpproto); >- if (sock < 0) { >- error(MODPREFIX "socket creation failed: %m"); >- return -1; >- } >- >- src_addr.sin_family = AF_INET; >- memcpy(&src_addr.sin_addr, host_addr, addr_len); >- src_addr.sin_port = port_discard; >- >- ret = connect(sock, (struct sockaddr *) &src_addr, src_len); >- if (ret < 0 ) { >- error(MODPREFIX "connect failed for %s: %m", host); >- close(sock); >- return 0; >- } >- >- ret = getsockname(sock, (struct sockaddr *) &local_addr, &local_len); >- if (ret < 0) { >- error(MODPREFIX "getsockname failed: %m"); >- close(sock); >- return 0; >- } >- >- close(sock); >- >- ret = memcmp(&src_addr.sin_addr, &local_addr.sin_addr, addr_len); >- if (ret) >- return 0; >- >- return 1; >-} >- >-/* Check to see if the 'host:path' or 'host' is on the local machine >- Returns < 0 if there is a host lookup problem, otherwise returns 0 >- if it's not a local mount, and returns > 0 if it is a local mount. >-*/ >- >-int is_local_mount(const char *hostpath) >-{ >- struct hostent *he; >- char **haddr; >- char *delim; >- char *hostname; >- int hostnamelen; >- int local = 0; >- >- >- debug(MODPREFIX "is_local_mount: %s", hostpath); >- delim = strpbrk(hostpath,":"); >- >- if (delim) >- hostnamelen = delim - hostpath; >- else >- hostnamelen = strlen(hostpath); >- >- hostname = malloc(hostnamelen+1); >- strncpy(hostname,hostpath,hostnamelen); >- hostname[hostnamelen] = '\0'; >- he = gethostbyname(hostname); >- if (!he) { >- error(MODPREFIX "host %s: lookup failure", hostname); >- return -1; >- } >- >- for (haddr = he->h_addr_list; *haddr; haddr++) { >- local = is_local_addr(hostname, *haddr, he->h_length); >- if (local < 0) >- return local; >- if (local) { >- debug(MODPREFIX "host %s: is localhost", >- hostname); >- return local; >- } >- } >- return 0; >-} >- >-/* >- * Given a mount string, return (in the same string) the >- * best mount to use based on locality/weight/rpctime. >- * >- * If longtimeout is set to 0 then we only do 100 ms pings to hosts. In >- * the event that this fails, we call ourself recursively with the >- * longtimeout option set to 1. In this case we ping for up to 10s and >- * skip logic for detecting if a localhost has been passed. (if a local >- * host had been passed, we would have returned that mount as the best >- * mount. The skipping of local maps in this case is an optimization). >- * >- * - return -1 and what = '\0' on error, >- * 1 and what = local mount path if local bind, >- * else 0 and what = remote mount path >- */ >-int get_best_mount(char *what, const char *original, int longtimeout, char **badhosts, >- int num_badhosts, int *is_replicated, int skiplocal) >-{ >- char *p = what; >- char *winner = NULL; >- int winner_weight = INT_MAX, local = 0; >- double winner_time = 0; >- char *delim; >- int sec = (longtimeout) ? 10 : 0; >- int micros = (longtimeout) ? 0 : 100000; >- >- if (!p) { >- *what = '\0'; >- return -1; >- } >- >- /* If we are using a long timeout we can skip all localhost checks */ >- if (longtimeout) >- skiplocal = 1; >- >- /* >- * If the map doesn't contain a ',' and doesn't contain more than >- * one ':' then only one mountpoint has been passed in and we don't >- * need to do anything except strip whitespace from the end of >- * the string. >- */ >- >- if (!strchr(p, ',') && (strchr(p,':') == strrchr(p,':'))) { >- int ret; >- >- *is_replicated = 0; >- if (is_bad_host(p, badhosts, num_badhosts)) { >- *what = '\0'; >- return 0; >- } >- >- /* Check if the host is the localhost */ >- ret = is_local_mount(p); >- if ((ret < 0) || skiplocal) >- *what = '\0'; >- else if (ret > 0) { >- debug(MODPREFIX "host %s: is localhost", p); >- >- /* Strip off hostname and ':' */ >- delim = strchr(p,':'); >- while (delim && *delim != '\0') { >- delim++; >- *what = *delim; >- what++; >- } >- return 1; >- } >- >- return 0; >- } else >- *is_replicated = 1; >- >- while (p && *p) { >- char *next; >- unsigned int ping_stat = 0; >- >- p += strspn(p, " \t,"); >- delim = strpbrk(p, "(, \t:"); >- if (!delim) >- break; >- >- /* Find lowest weight whose server is alive */ >- if (*delim == '(') { >- char *weight = delim + 1; >- unsigned int alive; >- >- *delim = '\0'; >- >- delim = strchr(weight, ')'); >- if (delim) { >- int w; >- >- *delim = '\0'; >- w = atoi(weight); >- >- if (!is_bad_host(p, badhosts, num_badhosts)) >- alive = rpc_ping(p, sec, micros); >- else >- alive = 0; >- if (w < winner_weight && alive) { >- winner_weight = w; >- winner = p; >- } >- } >- delim++; >- } >- >- if (*delim == ':') { >- *delim = '\0'; >- next = delim + 1; >- >- /* Oh boy - might have spaces in the path */ >- while (*next && *next != ':') >- next++; >- /* No spaces in host names at least */ >- if (*next == ':') { >- while (*next && >- (*next != ' ' || *next != '\t')) >- next--; >- } >- } else if (*delim != '\0') { >- *delim = '\0'; >- next = delim + 1; >- } else >- break; >- >- if (is_bad_host(p, badhosts, num_badhosts)) { >- p = next; >- continue; >- } >- >- if (!skiplocal) { >- /* First, check if it's up and if it's localhost */ >- local = is_local_mount(p); >- if (local < 0) { >- local = 0; >- p = next; >- continue; >- } >- if (local) { >- winner = p; >- break; >- } >- } >- >- /* >- * If it's not local and it's a replicated server map entry >- * is it alive >- */ >- if (!local && !(ping_stat = rpc_ping(p, sec, micros))) { >- p = next; >- continue; >- } >- >- /* compare RPC times if there are no weighted hosts */ >- if (winner_weight == INT_MAX) { >- int status; >- double resp_time; >- unsigned int vers = NFS2_VERSION; >- unsigned int proto = RPC_PING_UDP; >- >- if (ap.random_multimount) { >- resp_time = random(); >- if (winner_time == 0 || >- resp_time < winner_time) { >- winner_time = resp_time; >- winner = p; >- } >- p = next; >- continue; >- } >- >- if (ping_stat) { >- vers = ping_stat & 0x00ff; >- proto = ping_stat & 0xff00; >- } >- >- status = rpc_time(p, vers, proto, sec, micros, &resp_time); >- /* did we time the first winner? */ >- if (winner_time == 0) { >- if (status) { >- winner = p; >- winner_time = resp_time; >- } else >- winner_time = 6; >- } else { >- if ((status) && (resp_time < winner_time)) { >- winner = p; >- winner_time = resp_time; >- } >- } >- } >- p = next; >- } >- >- debug(MODPREFIX "winner = %s local = %d", winner, local); >- >- /* >- * We didn't find a weighted winner or local and it's a replicated >- * server map entry >- */ >- if (!local && winner_weight == INT_MAX) { >- /* We had more than one contender and none responded in time */ >- if (!ap.random_multimount && >- (winner_time == 0 || winner_time > 5)) { >- /* We've already tried a longer timeout */ >- if (longtimeout) { >- /* SOL: Just pick the first one */ >- if (!is_bad_host(what, badhosts, num_badhosts)) >- winner = what; >- } >- /* Reset string and try again */ >- else { >- strcpy(what, original); >- >- debug(MODPREFIX >- "all hosts rpc timed out for '%s', " >- "retrying with longer timeout", >- original); >- >- return get_best_mount(what, original, 1, >- badhosts, num_badhosts, >- is_replicated, 1); >- } >- } >- } >- >- /* No winner found so bail */ >- if (!winner) { >- *what = '\0'; >- return 0; >- } >- >- /* >- * We now have our winner, copy it to the front of the string, >- * followed by the next :string<delim> >- */ >- >- /* if it's local */ >- if (!local) >- strcpy(what, winner); >- else >- what[0] = '\0'; >- >- /* We know we're only reading from p, so discard const */ >- p = (char *) original + (winner - what); >- delim = what + strlen(what); >- >- /* Find the colon (in the original string) */ >- while (*p && *p != ':') >- p++; >- >- /* skip : for local paths */ >- if (local) >- p++; >- >- /* copy to next space or end of string */ >- while (*p && *p != ' ' && *p != '\t') >- *delim++ = *p++; >- >- *delim = '\0'; >- >- return local; >-} >- > void seed_random(void) > { > int fd, ret; >@@ -487,26 +91,17 @@ int mount_mount(const char *root, const > const char *what, const char *fstype, const char *options, > void *context) > { >- char *colon, *fullpath; >- char *whatstr; >- char **badhosts = NULL; >+ char *fullpath, buf[MAX_ERR_BUF]; >+ struct host *this, *hosts = NULL; > char *nfsoptions = NULL; >- int local, err; >+ unsigned int vers; > int nosymlink = 0; >- int num_badhosts = 0; >- int is_replicated = 1; >- int status, existed = 1; >+ int len, rlen, status, err, existed = 1; >+ int ro = 0; > > debug(MODPREFIX " root=%s name=%s what=%s, fstype=%s, options=%s", > root, name, what, fstype, options); > >- whatstr = alloca(strlen(what) + 1); >- if (!whatstr) { >- error(MODPREFIX "alloca: %m"); >- return 1; >- } >- strcpy(whatstr, what); >- > seed_random(); > > /* Extract "nosymlink" pseudo-option which stops local filesystems >@@ -562,51 +157,50 @@ int mount_mount(const char *root, const > nfsoptions, nosymlink); > } > >- local = 0; >+ if (strcmp(fstype, "nfs4") == 0) >+ vers = NFS4_VERS_MASK | NFS_PROTO_MASK; >+ else >+ vers = NFS_VERS_MASK | NFS_PROTO_MASK; > >- colon = strchr(whatstr, ':'); >- if (!colon) { >- /* No colon, take this as a bind (local) entry */ >- local = 1; >- } else if (!nosymlink) { >- local = get_best_mount(whatstr, what, 0, badhosts, >- num_badhosts, &is_replicated, 0); >- if (!*whatstr) { >- warn(MODPREFIX "no host elected"); >- return 1; >- } >- debug(MODPREFIX "from %s elected %s", what, whatstr); >+ if (!parse_location(&hosts, what)) { >+ warn(MODPREFIX "no hosts available"); >+ return 1; > } >+ prune_host_list(&hosts, vers, nfsoptions); > >- fullpath = alloca(strlen(root) + name_len + 2); >- if (!fullpath) { >- error(MODPREFIX "alloca: %m"); >+ if (!hosts) { >+ warn(MODPREFIX "no hosts available"); > return 1; > } > >- if (name_len) >- sprintf(fullpath, "%s/%s", root, name); >- else >- sprintf(fullpath, "%s", root); >- >- if (local) { >- /* Local host -- do a "bind" */ >+ /* Construct and perhaps create mount point directory */ > >- debug(MODPREFIX "%s is local, doing bind", name); >- >- if (!mount_bind->mount_mount(root, name, name_len, >- whatstr, "bind", NULL, >- mount_bind->context)) >- return 0; >- else if (!is_replicated) >- return 1; >+ /* Root offset of multi-mount */ >+ if (*name == '/' && name_len == 1) { >+ rlen = strlen(root); >+ name_len = 0; >+ /* Direct mount name is absolute path so don't use root */ >+ } else if (*name == '/') >+ rlen = 0; >+ else >+ rlen = strlen(root); > >- strcpy(whatstr, what); >- get_best_mount(whatstr, what, 0, badhosts, >- num_badhosts, &is_replicated, 1); >+ fullpath = alloca(rlen + name_len + 2); >+ if (!fullpath) { >+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF); >+ error(MODPREFIX "alloca: %s", estr); >+ free_host_list(&hosts); >+ return 1; > } > >- /* Not a local host - do an NFS mount */ >+ if (name_len) { >+ if (rlen) >+ len = sprintf(fullpath, "%s/%s", root, name); >+ else >+ len = sprintf(fullpath, "%s", name); >+ } else >+ len = sprintf(fullpath, "%s", root); >+ fullpath[len] = '\0'; > > debug(MODPREFIX "calling mkdir_path %s", fullpath); > >@@ -619,54 +213,82 @@ int mount_mount(const char *root, const > if (!status) > existed = 0; > >- if (is_mounted(fullpath)) { >- error("BUG: %s already mounted", fullpath); >- return 0; >- } >+ this = hosts; >+ while (this) { >+ char *loc; > >- do { > wait_for_lock(); >+ if (is_mounted(fullpath)) { >+ error(MODPREFIX >+ "warning: %s is already mounted", fullpath); >+ free_host_list(&hosts); >+ break; >+ } >+ >+ if (this->proximity == PROXIMITY_LOCAL) { >+ /* Local host -- do a "bind" */ >+ const char *bind_options = ro ? "ro" : ""; >+ >+ debug(MODPREFIX "%s is local, attempt bind mount", >+ name); >+ >+ err = mount_bind->mount_mount(root, name, name_len, >+ this->path, "bind", bind_options, >+ mount_bind->context); >+ unlink(AUTOFS_LOCK); >+ >+ /* Success - we're done */ >+ if (!err) { >+ free_host_list(&hosts); >+ return 0; >+ } >+ >+ this = this->next; >+ continue; >+ } >+ >+ /* Not a local host - do an NFS mount */ >+ >+ loc = malloc(strlen(this->name) + 1 + strlen(this->path) + 1); >+ strcpy(loc, this->name); >+ strcat(loc, ":"); >+ strcat(loc, this->path); >+ > if (nfsoptions && *nfsoptions) { >- debug(MODPREFIX "calling mount -t nfs " SLOPPY >- " -o %s %s %s", nfsoptions, >- whatstr, fullpath); >- >- err = spawnl(LOG_NOTICE, MOUNTED_LOCK, >- PATH_MOUNT, PATH_MOUNT, "-t", >- "nfs", SLOPPYOPT "-o", >- nfsoptions, whatstr, fullpath, >- NULL); >+ debug(MODPREFIX "calling mount -t %s " SLOPPY >+ "-o %s %s %s", fstype, nfsoptions, loc, fullpath); >+ err = spawnl(LOG_NOTICE, MOUNTED_LOCK, >+ PATH_MOUNT, PATH_MOUNT, "-t", fstype, >+ SLOPPYOPT "-o", >+ nfsoptions, loc, fullpath, NULL); > } else { >- debug(MODPREFIX "calling mount -t nfs %s %s", >- whatstr, fullpath); >+ debug(MODPREFIX "calling mount -t %s %s %s", >+ fstype, loc, fullpath); > err = spawnl(LOG_NOTICE, MOUNTED_LOCK, >- PATH_MOUNT, PATH_MOUNT, "-t", >- "nfs", whatstr, fullpath, NULL); >+ PATH_MOUNT, PATH_MOUNT, "-t", fstype, >+ loc, fullpath, NULL); > } > unlink(AUTOFS_LOCK); > >- if (err) { >- add_bad_host(&badhosts, &num_badhosts, >- whatstr); >+ if (!err) { >+ info(MODPREFIX "mounted %s on %s", loc, fullpath); >+ free(loc); >+ free_host_list(&hosts); >+ return 0; > } >- } while (err && strcpy(whatstr, what) && is_replicated && >- !get_best_mount(whatstr, what, 0, badhosts, >- num_badhosts, &is_replicated, 1) && >- (*whatstr != '\0')); >- >- free_bad_hosts(badhosts, num_badhosts); >- >- if (err) { >- if ((!ap.ghost && name_len) || !existed) >- rmdir_path(name); > >- error(MODPREFIX "nfs: mount failure %s on %s", >- whatstr, fullpath); >- return 1; >- } else { >- debug(MODPREFIX "mounted %s on %s", whatstr, fullpath); >- return 0; >+ free(loc); >+ this = this->next; > } >+ >+ free_host_list(&hosts); >+ >+ /* If we get here we've failed to complete the mount */ >+ if ((!ap.ghost && name_len) || !existed) >+ rmdir_path(name); >+ >+ error(MODPREFIX "nfs: mount failure %s on %s", what, fullpath); >+ return 1; > } > > int mount_done(void *context) >diff -uprN autofs-4.1.3/modules/replicated.c autofs-4.1.3-replicated/modules/replicated.c >--- autofs-4.1.3/modules/replicated.c 1969-12-31 19:00:00.000000000 -0500 >+++ autofs-4.1.3-replicated/modules/replicated.c 2007-01-18 17:17:00.000000000 -0500 >@@ -0,0 +1,996 @@ >+/* ----------------------------------------------------------------------- * >+ * >+ * repl_list.h - routines for replicated mount server selection >+ * >+ * Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved >+ * Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, >+ * USA; either version 2 of the License, or (at your option) any later >+ * version; incorporated herein by reference. >+ * >+ * A priority ordered list of hosts is created by using the following >+ * selection rules. >+ * >+ * 1) Highest priority in selection is proximity. >+ * Proximity, in order of precedence is: >+ * - PROXIMITY_LOCAL, host corresponds to a local interface. >+ * - PROXIMITY_SUBNET, host is located in a subnet reachable >+ * through a local interface. >+ * - PROXIMITY_NETWORK, host is located in a network reachable >+ * through a local interface. >+ * - PROXIMITY_OTHER, host is on a network not directlty >+ * reachable through a local interface. >+ * >+ * 2) NFS version and protocol is selected by caclculating the largest >+ * number of hosts supporting an NFS version and protocol that >+ * have the closest proximity. These hosts are added to the list >+ * in response time order. Hosts may have a corresponding weight >+ * which essentially increaes response time and so influences the >+ * host order. >+ * >+ * 3) Hosts at further proximity that support the selected NFS version >+ * and protocol are also added to the list in response time order as >+ * in 2 above. >+ * >+ * ----------------------------------------------------------------------- */ >+ >+#ifndef _GNU_SOURCE >+#define _GNU_SOURCE >+#endif >+ >+#include <string.h> >+#include <stdlib.h> >+#include <sys/errno.h> >+#include <sys/types.h> >+#include <netinet/in.h> >+#include <sys/ioctl.h> >+#include <sys/socket.h> >+#include <arpa/inet.h> >+#include <net/if.h> >+#include <netinet/in.h> >+#include <rpcsvc/nfs_prot.h> >+#include <netdb.h> >+#include <unistd.h> >+#include <fcntl.h> >+ >+#include "rpc_subs.h" >+#include "replicated.h" >+#include "automount.h" >+ >+#ifndef MAX_ERR_BUF >+#define MAX_ERR_BUF 512 >+#endif >+#define MAX_IFC_BUF 2048 >+ >+#define MASK_A 0x7F000000 >+#define MASK_B 0xBFFF0000 >+#define MASK_C 0xDFFFFF00 >+ >+/* Get numeric value of the n bits starting at position p */ >+#define getbits(x, p, n) ((x >> (p + 1 - n)) & ~(~0 << n)) >+ >+#define max(x, y) (x >= y ? x : y) >+#define mmax(x, y, z) (max(x, y) == x ? max(x, z) : max(y, z)) >+ >+static unsigned int get_proximity(const char *host_addr, int addr_len) >+{ >+ struct sockaddr_in *msk_addr, *if_addr; >+ struct in_addr *hst_addr; >+ char tmp[20], buf[MAX_ERR_BUF], *ptr; >+ struct ifconf ifc; >+ struct ifreq *ifr, nmptr; >+ int sock, cl_flags, ret, i; >+ uint32_t mask, ha, ia; >+ >+ memcpy(tmp, host_addr, addr_len); >+ hst_addr = (struct in_addr *) tmp; >+ >+ ha = ntohl((uint32_t) hst_addr->s_addr); >+ >+ sock = socket(AF_INET, SOCK_DGRAM, 0); >+ if (sock < 0) { >+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF); >+ error("socket creation failed: %s", estr); >+ return PROXIMITY_ERROR; >+ } >+ >+ if ((cl_flags = fcntl(sock, F_GETFD, 0)) != -1) { >+ cl_flags |= FD_CLOEXEC; >+ fcntl(sock, F_SETFD, cl_flags); >+ } >+ >+ ifc.ifc_len = sizeof(buf); >+ ifc.ifc_req = (struct ifreq *) buf; >+ ret = ioctl(sock, SIOCGIFCONF, &ifc); >+ if (ret == -1) { >+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF); >+ error("ioctl: %s", estr); >+ close(sock); >+ return PROXIMITY_ERROR; >+ } >+ >+ /* For each interface */ >+ >+ /* Is the address a local interface */ >+ i = 0; >+ ptr = (char *) &ifc.ifc_buf[0]; >+ >+ while (ptr < buf + ifc.ifc_len) { >+ ifr = (struct ifreq *) ptr; >+ >+ switch (ifr->ifr_addr.sa_family) { >+ case AF_INET: >+ if_addr = (struct sockaddr_in *) &ifr->ifr_addr; >+ ret = memcmp(&if_addr->sin_addr, hst_addr, addr_len); >+ if (!ret) { >+ close(sock); >+ return PROXIMITY_LOCAL; >+ } >+ break; >+ >+ default: >+ break; >+ } >+ >+ i++; >+ ptr = (char *) &ifc.ifc_req[i]; >+ } >+ >+ i = 0; >+ ptr = (char *) &ifc.ifc_buf[0]; >+ >+ while (ptr < buf + ifc.ifc_len) { >+ ifr = (struct ifreq *) ptr; >+ >+ switch (ifr->ifr_addr.sa_family) { >+ case AF_INET: >+ if_addr = (struct sockaddr_in *) &ifr->ifr_addr; >+ ia = ntohl((uint32_t) if_addr->sin_addr.s_addr); >+ >+ /* Is the address within a localiy attached subnet */ >+ >+ nmptr = *ifr; >+ ret = ioctl(sock, SIOCGIFNETMASK, &nmptr); >+ if (ret == -1) { >+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF); >+ error("ioctl: %s", estr); >+ close(sock); >+ return PROXIMITY_ERROR; >+ } >+ >+ msk_addr = (struct sockaddr_in *) &nmptr.ifr_netmask; >+ mask = ntohl((uint32_t) msk_addr->sin_addr.s_addr); >+ >+ if ((ia & mask) == (ha & mask)) { >+ close(sock); >+ return PROXIMITY_SUBNET; >+ } >+ >+ /* >+ * Is the address within a local ipv4 network. >+ * >+ * Bit position 31 == 0 => class A. >+ * Bit position 30 == 0 => class B. >+ * Bit position 29 == 0 => class C. >+ */ >+ >+ if (!getbits(ia, 31, 1)) >+ mask = MASK_A; >+ else if (!getbits(ia, 30, 1)) >+ mask = MASK_B; >+ else if (!getbits(ia, 29, 1)) >+ mask = MASK_C; >+ else >+ break; >+ >+ if ((ia & mask) == (ha & mask)) { >+ close(sock); >+ return PROXIMITY_NET; >+ } >+ break; >+ >+ default: >+ break; >+ } >+ >+ i++; >+ ptr = (char *) &ifc.ifc_req[i]; >+ } >+ >+ close(sock); >+ >+ return PROXIMITY_OTHER; >+} >+ >+static struct host *new_host(const char *name, const char *addr, unsigned int proximity, unsigned int weight) >+{ >+ struct host *new; >+ char *tmp1, *tmp2; >+ >+ if (!name || !addr) >+ return NULL; >+ >+ tmp1 = strdup(name); >+ if (!tmp1) >+ return NULL; >+ >+ tmp2 = strdup(addr); >+ if (!tmp2) { >+ free(tmp1); >+ return NULL; >+ } >+ >+ new = malloc(sizeof(struct host)); >+ if (!new) { >+ free(tmp1); >+ free(tmp2); >+ return NULL; >+ } >+ >+ memset(new, 0, sizeof(struct host)); >+ >+ new->name = tmp1; >+ new->addr = tmp2; >+ new->proximity = proximity; >+ new->weight = weight; >+ >+ return new; >+} >+ >+static int add_host(struct host **list, struct host *host) >+{ >+ struct host *this, *last; >+ >+ if (!list) { >+ *list = host; >+ return 1; >+ } >+ >+ this = *list; >+ last = this; >+ while (this) { >+ if (this->proximity >= host->proximity) >+ break; >+ last = this; >+ this = this->next; >+ } >+ >+ if (host->cost) { >+ while (this) { >+ if (this->proximity != host->proximity) >+ break; >+ if (this->cost >= host->cost) >+ break; >+ last = this; >+ this = this->next; >+ } >+ } >+ >+ if (last == this) { >+ host->next = last; >+ *list = host; >+ return 1; >+ } >+ >+ last->next = host; >+ host->next = this; >+ >+ return 1; >+} >+ >+static void free_host(struct host *host) >+{ >+ free(host->name); >+ free(host->addr); >+ free(host->path); >+ free(host); >+} >+ >+static void remove_host(struct host **hosts, struct host *host) >+{ >+ struct host *last, *this; >+ >+ if (host == *hosts) { >+ *hosts = (*hosts)->next; >+ host->next = NULL; >+ return; >+ } >+ >+ this = *hosts; >+ last = NULL; >+ while (this) { >+ if (this == host) >+ break; >+ last = this; >+ this = this->next; >+ } >+ >+ if (!last || !this) >+ return; >+ >+ last->next = this->next; >+ host->next = NULL; >+ >+ return; >+} >+ >+static void delete_host(struct host **hosts, struct host *host) >+{ >+ remove_host(hosts, host); >+ free_host(host); >+ return; >+} >+ >+void free_host_list(struct host **list) >+{ >+ struct host *this; >+ >+ this = *list; >+ while (this) { >+ struct host *next = this->next; >+ free_host(this); >+ this = next; >+ } >+ *list = NULL; >+} >+ >+static unsigned short get_port_option(const char *options) >+{ >+ const char *start; >+ long port = 0; >+ >+ if (!options) >+ return NFS_PORT; >+ >+ start = strstr(options, "port="); >+ if (!start) >+ port = NFS_PORT; >+ else { >+ char optport[30], *opteq, *end; >+ int len; >+ >+ end = strchr(start, ','); >+ len = end ? end - start : strlen(start); >+ strncpy(optport, start, len); >+ optport[len] = '\0'; >+ opteq = strchr(optport, '='); >+ if (opteq) >+ port = atoi(opteq + 1); >+ } >+ >+ if (port < 0) >+ port = 0; >+ >+ return (unsigned short) port; >+} >+ >+static unsigned int get_nfs_info(struct host *host, >+ struct conn_info *pm_info, struct conn_info *rpc_info, >+ const char *proto, unsigned int version, >+ const char *options) >+{ >+ char *have_port_opt = options ? strstr(options, "port=") : NULL; >+ struct pmap parms; >+ struct timeval start, end; >+ struct timezone tz; >+ unsigned int supported = 0; >+ double taken = 0; >+ int status, count = 0; >+ >+ memset(&parms, 0, sizeof(struct pmap)); >+ >+ parms.pm_prog = NFS_PROGRAM; >+ >+ /* Try to prode UDP first to conserve socket space */ >+ rpc_info->proto = getprotobyname(proto); >+ if (!rpc_info->proto) >+ return 0; >+ >+ if (!(version & NFS4_REQUESTED)) >+ goto v3_ver; >+ >+ if (!(rpc_info->port = get_port_option(options))) >+ goto v3_ver; >+ >+ if (rpc_info->proto->p_proto == IPPROTO_UDP) >+ status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION); >+ else >+ status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION); >+ if (status) { >+ gettimeofday(&start, &tz); >+ status = rpc_ping_proto(rpc_info); >+ gettimeofday(&end, &tz); >+ if (status) { >+ taken += elapsed(start, end); >+ count++; >+ supported = NFS4_SUPPORTED; >+ } >+ } >+ >+v3_ver: >+ if (!have_port_opt) { >+ status = rpc_portmap_getclient(pm_info, >+ host->name, proto, RPC_CLOSE_DEFAULT); >+ if (!status) >+ goto done_ver; >+ } >+ >+ if (!(version & NFS3_REQUESTED)) >+ goto v2_ver; >+ >+ if (have_port_opt) { >+ if (!(rpc_info->port = get_port_option(options))) >+ goto done_ver; >+ } else { >+ parms.pm_prot = rpc_info->proto->p_proto; >+ parms.pm_vers = NFS3_VERSION; >+ rpc_info->port = rpc_portmap_getport(pm_info, &parms); >+ if (!rpc_info->port) >+ goto v2_ver; >+ } >+ >+ if (rpc_info->proto->p_proto == IPPROTO_UDP) >+ status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION); >+ else >+ status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION); >+ if (status) { >+ gettimeofday(&start, &tz); >+ status = rpc_ping_proto(rpc_info); >+ gettimeofday(&end, &tz); >+ if (status) { >+ taken += elapsed(start, end); >+ count++; >+ supported |= NFS3_SUPPORTED; >+ } >+ } >+ >+v2_ver: >+ if (!(version & NFS2_REQUESTED)) >+ goto done_ver; >+ >+ if (have_port_opt) { >+ if (!(rpc_info->port = get_port_option(options))) >+ goto done_ver; >+ } else { >+ parms.pm_prot = rpc_info->proto->p_proto; >+ parms.pm_vers = NFS2_VERSION; >+ rpc_info->port = rpc_portmap_getport(pm_info, &parms); >+ if (!rpc_info->port) >+ goto done_ver; >+ } >+ >+ if (rpc_info->proto->p_proto == IPPROTO_UDP) >+ status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION); >+ else >+ status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION); >+ if (status) { >+ gettimeofday(&start, &tz); >+ status = rpc_ping_proto(rpc_info); >+ gettimeofday(&end, &tz); >+ if (status) { >+ taken += elapsed(start, end); >+ count++; >+ supported |= NFS2_SUPPORTED; >+ } >+ } >+ >+done_ver: >+ if (rpc_info->proto->p_proto == IPPROTO_UDP) { >+ rpc_destroy_udp_client(rpc_info); >+ rpc_destroy_udp_client(pm_info); >+ } else { >+ rpc_destroy_tcp_client(rpc_info); >+ rpc_destroy_tcp_client(pm_info); >+ } >+ >+ if (count) { >+ /* >+ * Average response time to 7 significant places as >+ * integral type. >+ */ >+ host->cost = (unsigned long) ((taken * 1000000) / count); >+ >+ /* Allow for user bias */ >+ if (host->weight) >+ host->cost *= (host->weight + 1); >+ } >+ >+ return supported; >+} >+ >+static int get_vers_and_cost(struct host *host, unsigned int version, const char *options) >+{ >+ struct conn_info pm_info, rpc_info; >+ time_t timeout = RPC_TIMEOUT; >+ unsigned int supported, vers = (NFS_VERS_MASK | NFS4_VERS_MASK); >+ int ret = 0; >+ >+ memset(&pm_info, 0, sizeof(struct conn_info)); >+ memset(&rpc_info, 0, sizeof(struct conn_info)); >+ >+ if (host->proximity == PROXIMITY_NET) >+ timeout = RPC_TIMEOUT * 2; >+ else if (host->proximity == PROXIMITY_NET) >+ timeout = RPC_TIMEOUT * 8; >+ >+ rpc_info.host = host->name; >+ rpc_info.program = NFS_PROGRAM; >+ rpc_info.timeout.tv_sec = timeout; >+ rpc_info.close_option = RPC_CLOSE_DEFAULT; >+ rpc_info.client = NULL; >+ >+ vers &= version; >+ >+ if (version & UDP_REQUESTED) { >+ supported = get_nfs_info(host, &pm_info, &rpc_info, "udp", vers, options); >+ if (supported) { >+ ret = 1; >+ host->version |= (supported << 8); >+ } >+ } >+ >+ if (version & TCP_REQUESTED) { >+ supported = get_nfs_info(host, &pm_info, &rpc_info, "tcp", vers, options); >+ if (supported) { >+ ret = 1; >+ host->version |= supported; >+ } >+ } >+ >+ return ret; >+} >+ >+static int get_supported_ver_and_cost(struct host *host, unsigned int version, const char *options) >+{ >+ char *have_port_opt = options ? strstr(options, "port=") : NULL; >+ struct conn_info pm_info, rpc_info; >+ struct pmap parms; >+ const char *proto; >+ unsigned int vers; >+ struct timeval start, end; >+ struct timezone tz; >+ double taken = 0; >+ time_t timeout = RPC_TIMEOUT; >+ int status; >+ >+ memset(&pm_info, 0, sizeof(struct conn_info)); >+ memset(&rpc_info, 0, sizeof(struct conn_info)); >+ memset(&parms, 0, sizeof(struct pmap)); >+ >+ if (host->proximity == PROXIMITY_NET) >+ timeout = RPC_TIMEOUT * 2; >+ else if (host->proximity == PROXIMITY_NET) >+ timeout = RPC_TIMEOUT * 8; >+ >+ rpc_info.host = host->name; >+ rpc_info.program = NFS_PROGRAM; >+ rpc_info.timeout.tv_sec = timeout; >+ rpc_info.close_option = RPC_CLOSE_DEFAULT; >+ rpc_info.client = NULL; >+ >+ parms.pm_prog = NFS_PROGRAM; >+ >+ if (version & UDP_SELECTED_MASK) { >+ proto = "udp"; >+ vers = (version << 8); >+ } else { >+ proto = "tcp"; >+ vers = version; >+ } >+ >+ rpc_info.proto = getprotobyname(proto); >+ if (!rpc_info.proto) >+ return 0; >+ >+ status = 0; >+ >+ parms.pm_vers = vers; >+ if (have_port_opt || (vers & NFS4_VERSION)) { >+ if (!(rpc_info.port = get_port_option(options))) >+ return 0; >+ } else { >+ int ret = rpc_portmap_getclient(&pm_info, >+ host->name, proto, RPC_CLOSE_DEFAULT); >+ if (!ret) >+ return 0; >+ >+ parms.pm_prot = rpc_info.proto->p_proto; >+ rpc_info.port = rpc_portmap_getport(&pm_info, &parms); >+ if (!rpc_info.port) >+ goto done; >+ } >+ >+ if (rpc_info.proto->p_proto == IPPROTO_UDP) >+ status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers); >+ else >+ status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers); >+ if (status) { >+ gettimeofday(&start, &tz); >+ status = rpc_ping_proto(&rpc_info); >+ gettimeofday(&end, &tz); >+ if (status) >+ taken = elapsed(start, end); >+ } >+done: >+ if (rpc_info.proto->p_proto == IPPROTO_UDP) { >+ rpc_destroy_udp_client(&rpc_info); >+ rpc_destroy_udp_client(&pm_info); >+ } else { >+ rpc_destroy_tcp_client(&rpc_info); >+ rpc_destroy_tcp_client(&pm_info); >+ } >+ >+ if (status) { >+ /* Response time to 7 significant places as integral type. */ >+ host->cost = (unsigned long) (taken * 1000000); >+ >+ /* Allow for user bias */ >+ if (host->weight) >+ host->cost *= (host->weight + 1); >+ >+ return 1; >+ } >+ >+ return 0; >+} >+ >+int prune_host_list(struct host **list, unsigned int vers, const char *options) >+{ >+ struct host *this, *last, *first; >+ struct host *new = NULL; >+ unsigned int proximity, selected_version = 0; >+ unsigned int v2_tcp_count, v3_tcp_count, v4_tcp_count; >+ unsigned int v2_udp_count, v3_udp_count, v4_udp_count; >+ unsigned int max_udp_count, max_tcp_count, max_count; >+ int status; >+ >+ if (!*list) >+ return 0; >+ >+ /* Use closest hosts to choose NFS version */ >+ >+ first = *list; >+ this = first; >+ proximity = this->proximity; >+ >+ while (this && this->proximity == proximity) { >+ struct host *next = this->next; >+ >+ if (this->name) { >+ status = get_vers_and_cost(this, vers, options); >+ if (!status) { >+ if (this == first) { >+ first = next; >+ if (next) >+ proximity = next->proximity; >+ } >+ delete_host(list, this); >+ } >+ } >+ this = next; >+ } >+ >+ last = this; >+ >+ if (!first) >+ return 0; >+ >+ /* Select NFS version of highest number of closest servers */ >+ >+ v4_tcp_count = v3_tcp_count = v2_tcp_count = 0; >+ v4_udp_count = v3_udp_count = v2_udp_count = 0; >+ >+ this = first; >+ do { >+ if (this->version & NFS4_TCP_SUPPORTED) >+ v4_tcp_count++; >+ >+ if (this->version & NFS3_TCP_SUPPORTED) >+ v3_tcp_count++; >+ >+ if (this->version & NFS2_TCP_SUPPORTED) >+ v2_tcp_count++; >+ >+ if (this->version & NFS4_UDP_SUPPORTED) >+ v4_udp_count++; >+ >+ if (this->version & NFS3_UDP_SUPPORTED) >+ v3_udp_count++; >+ >+ if (this->version & NFS2_UDP_SUPPORTED) >+ v2_udp_count++; >+ >+ this = this->next; >+ } while (this && this != last); >+ >+ max_tcp_count = mmax(v4_tcp_count, v3_tcp_count, v2_tcp_count); >+ max_udp_count = mmax(v4_udp_count, v3_udp_count, v2_udp_count); >+ max_count = max(max_tcp_count, max_udp_count); >+ >+ if (max_count == v4_tcp_count) >+ selected_version = NFS4_TCP_SUPPORTED; >+ else if (max_count == v3_tcp_count) >+ selected_version = NFS3_TCP_SUPPORTED; >+ else if (max_count == v2_tcp_count) >+ selected_version = NFS2_TCP_SUPPORTED; >+ else if (max_count == v4_udp_count) >+ selected_version = NFS4_UDP_SUPPORTED; >+ else if (max_count == v3_udp_count) >+ selected_version = NFS3_UDP_SUPPORTED; >+ else if (max_count == v2_udp_count) >+ selected_version = NFS2_UDP_SUPPORTED; >+ >+ if (!selected_version) >+ return 0; >+ >+ /* Add hosts with selected version to new list */ >+ >+ this = first; >+ do { >+ struct host *next = this->next; >+ if (this->version & selected_version) { >+ this->version = selected_version; >+ remove_host(list, this); >+ add_host(&new, this); >+ } >+ this = next; >+ } while (this && this != last); >+ >+ /* >+ * Now go through rest of list and check for chosen version >+ * and add to new list if supported. >+ */ >+ >+ first = last; >+ this = first; >+ while (this) { >+ struct host *next = this->next; >+ if (!this->name) >+ add_host(&new, this); >+ else { >+ status = get_supported_ver_and_cost(this, selected_version, options); >+ if (status) { >+ this->version = selected_version; >+ remove_host(list, this); >+ add_host(&new, this); >+ } >+ } >+ this = next; >+ } >+ >+ free_host_list(list); >+ *list = new; >+ >+ return 1; >+} >+ >+static int add_host_addrs(struct host **list, const char *host, unsigned int weight) >+{ >+ struct hostent he; >+ struct hostent *phe = &he; >+ struct hostent *result; >+ struct sockaddr_in saddr; >+ char buf[MAX_IFC_BUF], **haddr; >+ int ghn_errno, ret; >+ struct host *new; >+ unsigned int prx; >+ >+ saddr.sin_family = AF_INET; >+ if (inet_aton(host, &saddr.sin_addr)) { >+ const char *thost = (const char *) &saddr.sin_addr; >+ >+ prx = get_proximity(thost, sizeof(saddr.sin_addr)); >+ if (prx == PROXIMITY_ERROR) >+ return 0; >+ >+ if (!(new = new_host(host, thost, prx, weight))) >+ return 0; >+ >+ if (!add_host(list, new)) >+ free_host(new); >+ >+ return 1; >+ } >+ >+ memset(buf, 0, MAX_IFC_BUF); >+ memset(&he, 0, sizeof(struct hostent)); >+ >+ ret = gethostbyname_r(host, phe, >+ buf, MAX_IFC_BUF, &result, &ghn_errno); >+ if (ret || !result) { >+ if (ghn_errno == -1) >+ error("host %s: lookup failure %d", host, errno); >+ else >+ error("host %s: lookup failure %d", host, ghn_errno); >+ return 0; >+ } >+ >+ for (haddr = phe->h_addr_list; *haddr; haddr++) { >+ prx = get_proximity(*haddr, phe->h_length); >+ if (prx == PROXIMITY_ERROR) >+ return 0; >+ >+ if (!(new = new_host(host, *haddr, prx, weight))) >+ return 0; >+ >+ if (!add_host(list, new)) { >+ free_host(new); >+ continue; >+ } >+ } >+ >+ return 1; >+} >+ >+static int add_path(struct host *hosts, const char *path, int len) >+{ >+ struct host *this; >+ char *tmp, *tmp2; >+ >+ tmp = alloca(len + 1); >+ if (!tmp) >+ return 0; >+ >+ strncpy(tmp, path, len); >+ tmp[len] = '\0'; >+ >+ this = hosts; >+ while (this) { >+ if (!this->path) { >+ tmp2 = strdup(tmp); >+ if (!tmp2) >+ return 0; >+ this->path = tmp2; >+ } >+ this = this->next; >+ } >+ >+ return 1; >+} >+ >+static int add_local_path(struct host **hosts, const char *path) >+{ >+ struct host *new; >+ char *tmp; >+ >+ tmp = strdup(path); >+ if (!tmp) >+ return 0; >+ >+ new = malloc(sizeof(struct host)); >+ if (!new) { >+ free(tmp); >+ return 0; >+ } >+ >+ memset(new, 0, sizeof(struct host)); >+ >+ new->path = tmp; >+ new->proximity = PROXIMITY_LOCAL; >+ new->version = NFS_VERS_MASK; >+ new->name = new->addr = NULL; >+ new->weight = new->cost = 0; >+ >+ add_host(hosts, new); >+ >+ return 1; >+} >+ >+int parse_location(struct host **hosts, const char *list) >+{ >+ char *str, *p, *delim; >+ unsigned int empty = 1; >+ >+ if (!list) >+ return 0; >+ >+ str = strdup(list); >+ if (!str) >+ return 0; >+ >+ p = str; >+ >+ while (p && *p) { >+ char *next = NULL; >+ int weight = 0; >+ >+ p += strspn(p, " \t,"); >+ delim = strpbrk(p, "(, \t:"); >+ >+ if (delim) { >+ if (*delim == '(') { >+ char *w = delim + 1; >+ >+ *delim = '\0'; >+ >+ delim = strchr(w, ')'); >+ if (delim) { >+ *delim = '\0'; >+ weight = atoi(w); >+ } >+ delim++; >+ } >+ >+ if (*delim == ':') { >+ char *path; >+ >+ *delim = '\0'; >+ path = delim + 1; >+ >+ /* Oh boy - might have spaces in the path */ >+ next = path; >+ while (*next && *next != ':') >+ next++; >+ >+ /* No spaces in host names at least */ >+ if (*next == ':') { >+ while (*next && >+ (*next != ' ' && *next != '\t')) >+ next--; >+ *next++ = '\0'; >+ } >+ >+ if (p != delim) { >+ if (!add_host_addrs(hosts, p, weight)) { >+ if (empty) { >+ p = next; >+ continue; >+ } >+ } >+ >+ if (!add_path(*hosts, path, strlen(path))) { >+ free_host_list(hosts); >+ free(str); >+ return 0; >+ } >+ } else { >+ if (!add_local_path(hosts, path)) { >+ p = next; >+ continue; >+ } >+ } >+ } else if (*delim != '\0') { >+ *delim = '\0'; >+ next = delim + 1; >+ >+ if (!add_host_addrs(hosts, p, weight)) { >+ p = next; >+ continue; >+ } >+ >+ empty = 0; >+ } >+ } else { >+ /* syntax error - no mount path */ >+ free_host_list(hosts); >+ free(str); >+ return 0; >+ } >+ >+ p = next; >+ } >+ >+ free(str); >+ return 1; >+} >+ >+void dump_host_list(struct host *hosts) >+{ >+ struct host *this; >+ >+ if (!hosts) >+ return; >+ >+ this = hosts; >+ while (this) { >+ debug("name %s path %s version %x proximity %u weight %u cost %lu", >+ this->name, this->path, this->version, >+ this->proximity, this->weight, this->cost); >+ this = this->next; >+ } >+ return; >+} >+
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 223083
:
146224
|
146253
|
146325
|
186291