xref: /linux/fs/lockd/mon.c (revision 7e44d3bea21fbb9494930d1cd35ca92a9a4a3279)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/lockd/mon.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * The kernel statd client.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/types.h>
101da177e4SLinus Torvalds #include <linux/utsname.h>
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
130896a725S\"Talpey, Thomas\ #include <linux/sunrpc/xprtsock.h>
141da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
151da177e4SLinus Torvalds #include <linux/lockd/lockd.h>
161da177e4SLinus Torvalds #include <linux/lockd/sm_inter.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #define NLMDBG_FACILITY		NLMDBG_MONITOR
2036e8e668SChuck Lever #define NSM_PROGRAM		100024
2136e8e668SChuck Lever #define NSM_VERSION		1
2236e8e668SChuck Lever 
2336e8e668SChuck Lever enum {
2436e8e668SChuck Lever 	NSMPROC_NULL,
2536e8e668SChuck Lever 	NSMPROC_STAT,
2636e8e668SChuck Lever 	NSMPROC_MON,
2736e8e668SChuck Lever 	NSMPROC_UNMON,
2836e8e668SChuck Lever 	NSMPROC_UNMON_ALL,
2936e8e668SChuck Lever 	NSMPROC_SIMU_CRASH,
3036e8e668SChuck Lever 	NSMPROC_NOTIFY,
3136e8e668SChuck Lever };
321da177e4SLinus Torvalds 
339c1bfd03SChuck Lever struct nsm_args {
349c1bfd03SChuck Lever 	__be32			addr;		/* remote address */
359c1bfd03SChuck Lever 	u32			prog;		/* RPC callback info */
369c1bfd03SChuck Lever 	u32			vers;
379c1bfd03SChuck Lever 	u32			proc;
389c1bfd03SChuck Lever 
399c1bfd03SChuck Lever 	char			*mon_name;
409c1bfd03SChuck Lever };
419c1bfd03SChuck Lever 
429c1bfd03SChuck Lever struct nsm_res {
439c1bfd03SChuck Lever 	u32			status;
449c1bfd03SChuck Lever 	u32			state;
459c1bfd03SChuck Lever };
469c1bfd03SChuck Lever 
471da177e4SLinus Torvalds static struct rpc_clnt *	nsm_create(void);
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds static struct rpc_program	nsm_program;
5067c6d107SChuck Lever static				LIST_HEAD(nsm_handles);
5167c6d107SChuck Lever static				DEFINE_SPINLOCK(nsm_lock);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds /*
541da177e4SLinus Torvalds  * Local NSM state
551da177e4SLinus Torvalds  */
56460f5cacSOlaf Kirch int				nsm_local_state;
571da177e4SLinus Torvalds 
5867c6d107SChuck Lever static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf,
5967c6d107SChuck Lever 				     const size_t len)
6067c6d107SChuck Lever {
6167c6d107SChuck Lever 	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
6267c6d107SChuck Lever 	snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
6367c6d107SChuck Lever }
6467c6d107SChuck Lever 
6567c6d107SChuck Lever static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf,
6667c6d107SChuck Lever 				     const size_t len)
6767c6d107SChuck Lever {
6867c6d107SChuck Lever 	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
6967c6d107SChuck Lever 
7067c6d107SChuck Lever 	if (ipv6_addr_v4mapped(&sin6->sin6_addr))
7167c6d107SChuck Lever 		snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
7267c6d107SChuck Lever 	else if (sin6->sin6_scope_id != 0)
7367c6d107SChuck Lever 		snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
7467c6d107SChuck Lever 				sin6->sin6_scope_id);
7567c6d107SChuck Lever 	else
7667c6d107SChuck Lever 		snprintf(buf, len, "%pI6", &sin6->sin6_addr);
7767c6d107SChuck Lever }
7867c6d107SChuck Lever 
7967c6d107SChuck Lever static void nsm_display_address(const struct sockaddr *sap,
8067c6d107SChuck Lever 				char *buf, const size_t len)
8167c6d107SChuck Lever {
8267c6d107SChuck Lever 	switch (sap->sa_family) {
8367c6d107SChuck Lever 	case AF_INET:
8467c6d107SChuck Lever 		nsm_display_ipv4_address(sap, buf, len);
8567c6d107SChuck Lever 		break;
8667c6d107SChuck Lever 	case AF_INET6:
8767c6d107SChuck Lever 		nsm_display_ipv6_address(sap, buf, len);
8867c6d107SChuck Lever 		break;
8967c6d107SChuck Lever 	default:
9067c6d107SChuck Lever 		snprintf(buf, len, "unsupported address family");
9167c6d107SChuck Lever 		break;
9267c6d107SChuck Lever 	}
9367c6d107SChuck Lever }
9467c6d107SChuck Lever 
951da177e4SLinus Torvalds /*
9636e8e668SChuck Lever  * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls
971da177e4SLinus Torvalds  */
981da177e4SLinus Torvalds static int
999502c522SOlaf Kirch nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
1021da177e4SLinus Torvalds 	int		status;
103a4846750SChuck Lever 	struct nsm_args args = {
104a4846750SChuck Lever 		.addr		= nsm_addr_in(nsm)->sin_addr.s_addr,
105a4846750SChuck Lever 		.prog		= NLM_PROGRAM,
106a4846750SChuck Lever 		.vers		= 3,
107a4846750SChuck Lever 		.proc		= NLMPROC_NSM_NOTIFY,
10829ed1407SChuck Lever 		.mon_name	= nsm->sm_mon_name,
109a4846750SChuck Lever 	};
110dead28daSChuck Lever 	struct rpc_message msg = {
111dead28daSChuck Lever 		.rpc_argp	= &args,
112dead28daSChuck Lever 		.rpc_resp	= res,
113dead28daSChuck Lever 	};
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	clnt = nsm_create();
1161da177e4SLinus Torvalds 	if (IS_ERR(clnt)) {
1171da177e4SLinus Torvalds 		status = PTR_ERR(clnt);
1185acf4315SChuck Lever 		dprintk("lockd: failed to create NSM upcall transport, "
1195acf4315SChuck Lever 				"status=%d\n", status);
1201da177e4SLinus Torvalds 		goto out;
1211da177e4SLinus Torvalds 	}
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	memset(res, 0, sizeof(*res));
1241da177e4SLinus Torvalds 
125dead28daSChuck Lever 	msg.rpc_proc = &clnt->cl_procinfo[proc];
126dead28daSChuck Lever 	status = rpc_call_sync(clnt, &msg, 0);
1271da177e4SLinus Torvalds 	if (status < 0)
1285acf4315SChuck Lever 		dprintk("lockd: NSM upcall RPC failed, status=%d\n",
1291da177e4SLinus Torvalds 				status);
1301da177e4SLinus Torvalds 	else
1311da177e4SLinus Torvalds 		status = 0;
13290c5755fSTrond Myklebust 	rpc_shutdown_client(clnt);
1331da177e4SLinus Torvalds  out:
1341da177e4SLinus Torvalds 	return status;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
1371e49323cSChuck Lever /**
1381e49323cSChuck Lever  * nsm_monitor - Notify a peer in case we reboot
1391e49323cSChuck Lever  * @host: pointer to nlm_host of peer to notify
1401e49323cSChuck Lever  *
1411e49323cSChuck Lever  * If this peer is not already monitored, this function sends an
1421e49323cSChuck Lever  * upcall to the local rpc.statd to record the name/address of
1431e49323cSChuck Lever  * the peer to notify in case we reboot.
1441e49323cSChuck Lever  *
1451e49323cSChuck Lever  * Returns zero if the peer is monitored by the local rpc.statd;
1461e49323cSChuck Lever  * otherwise a negative errno value is returned.
1471da177e4SLinus Torvalds  */
1481e49323cSChuck Lever int nsm_monitor(const struct nlm_host *host)
1491da177e4SLinus Torvalds {
1508dead0dbSOlaf Kirch 	struct nsm_handle *nsm = host->h_nsmhandle;
1511da177e4SLinus Torvalds 	struct nsm_res	res;
1521da177e4SLinus Torvalds 	int		status;
1531da177e4SLinus Torvalds 
1549fee4902SChuck Lever 	dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
1558dead0dbSOlaf Kirch 
1568dead0dbSOlaf Kirch 	if (nsm->sm_monitored)
157977faf39SOlaf Kirch 		return 0;
1581da177e4SLinus Torvalds 
15929ed1407SChuck Lever 	/*
16029ed1407SChuck Lever 	 * Choose whether to record the caller_name or IP address of
16129ed1407SChuck Lever 	 * this peer in the local rpc.statd's database.
16229ed1407SChuck Lever 	 */
16329ed1407SChuck Lever 	nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
16429ed1407SChuck Lever 
16536e8e668SChuck Lever 	status = nsm_mon_unmon(nsm, NSMPROC_MON, &res);
1665d254b11SChuck Lever 	if (res.status != 0)
1675d254b11SChuck Lever 		status = -EIO;
1685d254b11SChuck Lever 	if (status < 0)
1699fee4902SChuck Lever 		printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
1701da177e4SLinus Torvalds 	else
1718dead0dbSOlaf Kirch 		nsm->sm_monitored = 1;
1721da177e4SLinus Torvalds 	return status;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
175356c3eb4SChuck Lever /**
176356c3eb4SChuck Lever  * nsm_unmonitor - Unregister peer notification
177356c3eb4SChuck Lever  * @host: pointer to nlm_host of peer to stop monitoring
178356c3eb4SChuck Lever  *
179356c3eb4SChuck Lever  * If this peer is monitored, this function sends an upcall to
180356c3eb4SChuck Lever  * tell the local rpc.statd not to send this peer a notification
181356c3eb4SChuck Lever  * when we reboot.
1821da177e4SLinus Torvalds  */
183356c3eb4SChuck Lever void nsm_unmonitor(const struct nlm_host *host)
1841da177e4SLinus Torvalds {
1858dead0dbSOlaf Kirch 	struct nsm_handle *nsm = host->h_nsmhandle;
1861da177e4SLinus Torvalds 	struct nsm_res	res;
187356c3eb4SChuck Lever 	int status;
1881da177e4SLinus Torvalds 
1899502c522SOlaf Kirch 	if (atomic_read(&nsm->sm_count) == 1
1909502c522SOlaf Kirch 	 && nsm->sm_monitored && !nsm->sm_sticky) {
1919fee4902SChuck Lever 		dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
1929502c522SOlaf Kirch 
19336e8e668SChuck Lever 		status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res);
1940c7aef45SChuck Lever 		if (res.status != 0)
1950c7aef45SChuck Lever 			status = -EIO;
1961da177e4SLinus Torvalds 		if (status < 0)
1979502c522SOlaf Kirch 			printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
1989fee4902SChuck Lever 					nsm->sm_name);
1999502c522SOlaf Kirch 		else
2008dead0dbSOlaf Kirch 			nsm->sm_monitored = 0;
201977faf39SOlaf Kirch 	}
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
204*7e44d3beSChuck Lever /*
205*7e44d3beSChuck Lever  * Construct a unique cookie to match this nsm_handle to this monitored
206*7e44d3beSChuck Lever  * host.  It is passed to the local rpc.statd via NSMPROC_MON, and
207*7e44d3beSChuck Lever  * returned via NLMPROC_SM_NOTIFY, in the "priv" field of these
208*7e44d3beSChuck Lever  * requests.
209*7e44d3beSChuck Lever  *
210*7e44d3beSChuck Lever  * Linux provides the raw IP address of the monitored host,
211*7e44d3beSChuck Lever  * left in network byte order.
212*7e44d3beSChuck Lever  */
213*7e44d3beSChuck Lever static void nsm_init_private(struct nsm_handle *nsm)
214*7e44d3beSChuck Lever {
215*7e44d3beSChuck Lever 	__be32 *p = (__be32 *)&nsm->sm_priv.data;
216*7e44d3beSChuck Lever 	*p = nsm_addr_in(nsm)->sin_addr.s_addr;
217*7e44d3beSChuck Lever }
218*7e44d3beSChuck Lever 
21967c6d107SChuck Lever /**
22067c6d107SChuck Lever  * nsm_find - Find or create a cached nsm_handle
22167c6d107SChuck Lever  * @sap: pointer to socket address of handle to find
22267c6d107SChuck Lever  * @salen: length of socket address
22367c6d107SChuck Lever  * @hostname: pointer to C string containing hostname to find
22467c6d107SChuck Lever  * @hostname_len: length of C string
22567c6d107SChuck Lever  * @create: one means create new handle if not found in cache
22667c6d107SChuck Lever  *
22767c6d107SChuck Lever  * Behavior is modulated by the global nsm_use_hostnames variable
22867c6d107SChuck Lever  * and by the @create argument.
22967c6d107SChuck Lever  *
23067c6d107SChuck Lever  * Returns a cached nsm_handle after bumping its ref count, or if
23167c6d107SChuck Lever  * @create is set, returns a fresh nsm_handle if a handle that
23267c6d107SChuck Lever  * matches @sap and/or @hostname cannot be found in the handle cache.
23367c6d107SChuck Lever  * Returns NULL if an error occurs.
23467c6d107SChuck Lever  */
23567c6d107SChuck Lever struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen,
23667c6d107SChuck Lever 			    const char *hostname, const size_t hostname_len,
23767c6d107SChuck Lever 			    const int create)
23867c6d107SChuck Lever {
23967c6d107SChuck Lever 	struct nsm_handle *nsm = NULL;
24067c6d107SChuck Lever 	struct nsm_handle *pos;
24167c6d107SChuck Lever 
24267c6d107SChuck Lever 	if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
24367c6d107SChuck Lever 		if (printk_ratelimit()) {
24467c6d107SChuck Lever 			printk(KERN_WARNING "Invalid hostname \"%.*s\" "
24567c6d107SChuck Lever 					    "in NFS lock request\n",
24667c6d107SChuck Lever 				(int)hostname_len, hostname);
24767c6d107SChuck Lever 		}
24867c6d107SChuck Lever 		return NULL;
24967c6d107SChuck Lever 	}
25067c6d107SChuck Lever 
25167c6d107SChuck Lever retry:
25267c6d107SChuck Lever 	spin_lock(&nsm_lock);
25367c6d107SChuck Lever 	list_for_each_entry(pos, &nsm_handles, sm_link) {
25467c6d107SChuck Lever 
25567c6d107SChuck Lever 		if (hostname && nsm_use_hostnames) {
25667c6d107SChuck Lever 			if (strlen(pos->sm_name) != hostname_len
25767c6d107SChuck Lever 			 || memcmp(pos->sm_name, hostname, hostname_len))
25867c6d107SChuck Lever 				continue;
25967c6d107SChuck Lever 		} else if (!nlm_cmp_addr(nsm_addr(pos), sap))
26067c6d107SChuck Lever 			continue;
26167c6d107SChuck Lever 		atomic_inc(&pos->sm_count);
26267c6d107SChuck Lever 		kfree(nsm);
26367c6d107SChuck Lever 		nsm = pos;
2645cf1c4b1SChuck Lever 		dprintk("lockd: found nsm_handle for %s (%s), cnt %d\n",
2655cf1c4b1SChuck Lever 				pos->sm_name, pos->sm_addrbuf,
2665cf1c4b1SChuck Lever 				atomic_read(&pos->sm_count));
26767c6d107SChuck Lever 		goto found;
26867c6d107SChuck Lever 	}
26967c6d107SChuck Lever 	if (nsm) {
27067c6d107SChuck Lever 		list_add(&nsm->sm_link, &nsm_handles);
2715cf1c4b1SChuck Lever 		dprintk("lockd: created nsm_handle for %s (%s)\n",
2725cf1c4b1SChuck Lever 				nsm->sm_name, nsm->sm_addrbuf);
27367c6d107SChuck Lever 		goto found;
27467c6d107SChuck Lever 	}
27567c6d107SChuck Lever 	spin_unlock(&nsm_lock);
27667c6d107SChuck Lever 
27767c6d107SChuck Lever 	if (!create)
27867c6d107SChuck Lever 		return NULL;
27967c6d107SChuck Lever 
28067c6d107SChuck Lever 	nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
28167c6d107SChuck Lever 	if (nsm == NULL)
28267c6d107SChuck Lever 		return NULL;
28367c6d107SChuck Lever 
28467c6d107SChuck Lever 	memcpy(nsm_addr(nsm), sap, salen);
28567c6d107SChuck Lever 	nsm->sm_addrlen = salen;
28667c6d107SChuck Lever 	nsm->sm_name = (char *) (nsm + 1);
28767c6d107SChuck Lever 	memcpy(nsm->sm_name, hostname, hostname_len);
28867c6d107SChuck Lever 	nsm->sm_name[hostname_len] = '\0';
289*7e44d3beSChuck Lever 	nsm_init_private(nsm);
29067c6d107SChuck Lever 	nsm_display_address((struct sockaddr *)&nsm->sm_addr,
29167c6d107SChuck Lever 				nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
29267c6d107SChuck Lever 	atomic_set(&nsm->sm_count, 1);
29367c6d107SChuck Lever 	goto retry;
29467c6d107SChuck Lever 
29567c6d107SChuck Lever found:
29667c6d107SChuck Lever 	spin_unlock(&nsm_lock);
29767c6d107SChuck Lever 	return nsm;
29867c6d107SChuck Lever }
29967c6d107SChuck Lever 
30067c6d107SChuck Lever /**
30167c6d107SChuck Lever  * nsm_release - Release an NSM handle
30267c6d107SChuck Lever  * @nsm: pointer to handle to be released
30367c6d107SChuck Lever  *
30467c6d107SChuck Lever  */
30567c6d107SChuck Lever void nsm_release(struct nsm_handle *nsm)
30667c6d107SChuck Lever {
30767c6d107SChuck Lever 	if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
30867c6d107SChuck Lever 		list_del(&nsm->sm_link);
30967c6d107SChuck Lever 		spin_unlock(&nsm_lock);
3105cf1c4b1SChuck Lever 		dprintk("lockd: destroyed nsm_handle for %s (%s)\n",
3115cf1c4b1SChuck Lever 				nsm->sm_name, nsm->sm_addrbuf);
31267c6d107SChuck Lever 		kfree(nsm);
31367c6d107SChuck Lever 	}
31467c6d107SChuck Lever }
31567c6d107SChuck Lever 
3161da177e4SLinus Torvalds /*
3171da177e4SLinus Torvalds  * Create NSM client for the local host
3181da177e4SLinus Torvalds  */
3191da177e4SLinus Torvalds static struct rpc_clnt *
3201da177e4SLinus Torvalds nsm_create(void)
3211da177e4SLinus Torvalds {
322e1ec7892SChuck Lever 	struct sockaddr_in	sin = {
323e1ec7892SChuck Lever 		.sin_family	= AF_INET,
324e1ec7892SChuck Lever 		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
325e1ec7892SChuck Lever 		.sin_port	= 0,
326e1ec7892SChuck Lever 	};
327e1ec7892SChuck Lever 	struct rpc_create_args args = {
3280896a725S\"Talpey, Thomas\ 		.protocol	= XPRT_TRANSPORT_UDP,
329e1ec7892SChuck Lever 		.address	= (struct sockaddr *)&sin,
330e1ec7892SChuck Lever 		.addrsize	= sizeof(sin),
331e1ec7892SChuck Lever 		.servername	= "localhost",
332e1ec7892SChuck Lever 		.program	= &nsm_program,
33336e8e668SChuck Lever 		.version	= NSM_VERSION,
334e1ec7892SChuck Lever 		.authflavor	= RPC_AUTH_NULL,
335e1ec7892SChuck Lever 	};
3361da177e4SLinus Torvalds 
337e1ec7892SChuck Lever 	return rpc_create(&args);
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds /*
3411da177e4SLinus Torvalds  * XDR functions for NSM.
3422ca7754dSChuck Lever  *
3432ca7754dSChuck Lever  * See http://www.opengroup.org/ for details on the Network
3442ca7754dSChuck Lever  * Status Monitor wire protocol.
3451da177e4SLinus Torvalds  */
3461da177e4SLinus Torvalds 
34703eb1dcbSChuck Lever static int encode_nsm_string(struct xdr_stream *xdr, const char *string)
348099bd05fSChuck Lever {
34903eb1dcbSChuck Lever 	const u32 len = strlen(string);
35003eb1dcbSChuck Lever 	__be32 *p;
351099bd05fSChuck Lever 
35203eb1dcbSChuck Lever 	if (unlikely(len > SM_MAXSTRLEN))
35303eb1dcbSChuck Lever 		return -EIO;
35403eb1dcbSChuck Lever 	p = xdr_reserve_space(xdr, sizeof(u32) + len);
35503eb1dcbSChuck Lever 	if (unlikely(p == NULL))
35603eb1dcbSChuck Lever 		return -EIO;
35703eb1dcbSChuck Lever 	xdr_encode_opaque(p, string, len);
35803eb1dcbSChuck Lever 	return 0;
359099bd05fSChuck Lever }
360099bd05fSChuck Lever 
36149695174SChuck Lever /*
36249695174SChuck Lever  * "mon_name" specifies the host to be monitored.
36349695174SChuck Lever  */
36403eb1dcbSChuck Lever static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
36549695174SChuck Lever {
36603eb1dcbSChuck Lever 	return encode_nsm_string(xdr, argp->mon_name);
36749695174SChuck Lever }
36849695174SChuck Lever 
369850c95fdSChuck Lever /*
370850c95fdSChuck Lever  * The "my_id" argument specifies the hostname and RPC procedure
371850c95fdSChuck Lever  * to be called when the status manager receives notification
37236e8e668SChuck Lever  * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
373850c95fdSChuck Lever  * has changed.
374850c95fdSChuck Lever  */
37503eb1dcbSChuck Lever static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
376850c95fdSChuck Lever {
37703eb1dcbSChuck Lever 	int status;
37803eb1dcbSChuck Lever 	__be32 *p;
379850c95fdSChuck Lever 
38003eb1dcbSChuck Lever 	status = encode_nsm_string(xdr, utsname()->nodename);
38103eb1dcbSChuck Lever 	if (unlikely(status != 0))
38203eb1dcbSChuck Lever 		return status;
38303eb1dcbSChuck Lever 	p = xdr_reserve_space(xdr, 3 * sizeof(u32));
38403eb1dcbSChuck Lever 	if (unlikely(p == NULL))
38503eb1dcbSChuck Lever 		return -EIO;
386850c95fdSChuck Lever 	*p++ = htonl(argp->prog);
387850c95fdSChuck Lever 	*p++ = htonl(argp->vers);
388850c95fdSChuck Lever 	*p++ = htonl(argp->proc);
38903eb1dcbSChuck Lever 	return 0;
390850c95fdSChuck Lever }
391850c95fdSChuck Lever 
392ea72a7f1SChuck Lever /*
393ea72a7f1SChuck Lever  * The "mon_id" argument specifies the non-private arguments
39436e8e668SChuck Lever  * of an NSMPROC_MON or NSMPROC_UNMON call.
395ea72a7f1SChuck Lever  */
39603eb1dcbSChuck Lever static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
397ea72a7f1SChuck Lever {
39803eb1dcbSChuck Lever 	int status;
399ea72a7f1SChuck Lever 
40003eb1dcbSChuck Lever 	status = encode_mon_name(xdr, argp);
40103eb1dcbSChuck Lever 	if (unlikely(status != 0))
40203eb1dcbSChuck Lever 		return status;
40303eb1dcbSChuck Lever 	return encode_my_id(xdr, argp);
404ea72a7f1SChuck Lever }
405ea72a7f1SChuck Lever 
4060490a54aSChuck Lever /*
4070490a54aSChuck Lever  * The "priv" argument may contain private information required
40836e8e668SChuck Lever  * by the NSMPROC_MON call. This information will be supplied in the
40936e8e668SChuck Lever  * NLMPROC_SM_NOTIFY call.
4100490a54aSChuck Lever  *
4110490a54aSChuck Lever  * Linux provides the raw IP address of the monitored host,
4120490a54aSChuck Lever  * left in network byte order.
4130490a54aSChuck Lever  */
41403eb1dcbSChuck Lever static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
4150490a54aSChuck Lever {
41603eb1dcbSChuck Lever 	__be32 *p;
41703eb1dcbSChuck Lever 
41803eb1dcbSChuck Lever 	p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
41903eb1dcbSChuck Lever 	if (unlikely(p == NULL))
42003eb1dcbSChuck Lever 		return -EIO;
4210490a54aSChuck Lever 	*p++ = argp->addr;
4220490a54aSChuck Lever 	*p++ = 0;
4230490a54aSChuck Lever 	*p++ = 0;
4240490a54aSChuck Lever 	*p++ = 0;
4251da177e4SLinus Torvalds 	return 0;
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
42803eb1dcbSChuck Lever static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p,
42903eb1dcbSChuck Lever 		       const struct nsm_args *argp)
4301da177e4SLinus Torvalds {
43103eb1dcbSChuck Lever 	struct xdr_stream xdr;
43203eb1dcbSChuck Lever 	int status;
43303eb1dcbSChuck Lever 
43403eb1dcbSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
43503eb1dcbSChuck Lever 	status = encode_mon_id(&xdr, argp);
43603eb1dcbSChuck Lever 	if (unlikely(status))
43703eb1dcbSChuck Lever 		return status;
43803eb1dcbSChuck Lever 	return encode_priv(&xdr, argp);
4391da177e4SLinus Torvalds }
4401da177e4SLinus Torvalds 
44103eb1dcbSChuck Lever static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p,
44203eb1dcbSChuck Lever 			 const struct nsm_args *argp)
4431da177e4SLinus Torvalds {
44403eb1dcbSChuck Lever 	struct xdr_stream xdr;
44503eb1dcbSChuck Lever 
44603eb1dcbSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
44703eb1dcbSChuck Lever 	return encode_mon_id(&xdr, argp);
44803eb1dcbSChuck Lever }
44903eb1dcbSChuck Lever 
45003eb1dcbSChuck Lever static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p,
45103eb1dcbSChuck Lever 			    struct nsm_res *resp)
45203eb1dcbSChuck Lever {
45303eb1dcbSChuck Lever 	struct xdr_stream xdr;
45403eb1dcbSChuck Lever 
45503eb1dcbSChuck Lever 	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
45603eb1dcbSChuck Lever 	p = xdr_inline_decode(&xdr, 2 * sizeof(u32));
45703eb1dcbSChuck Lever 	if (unlikely(p == NULL))
45803eb1dcbSChuck Lever 		return -EIO;
4591da177e4SLinus Torvalds 	resp->status = ntohl(*p++);
46003eb1dcbSChuck Lever 	resp->state = ntohl(*p);
46103eb1dcbSChuck Lever 
46203eb1dcbSChuck Lever 	dprintk("lockd: xdr_dec_stat_res status %d state %d\n",
4631da177e4SLinus Torvalds 			resp->status, resp->state);
4641da177e4SLinus Torvalds 	return 0;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds 
46703eb1dcbSChuck Lever static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
46803eb1dcbSChuck Lever 			struct nsm_res *resp)
4691da177e4SLinus Torvalds {
47003eb1dcbSChuck Lever 	struct xdr_stream xdr;
47103eb1dcbSChuck Lever 
47203eb1dcbSChuck Lever 	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
47303eb1dcbSChuck Lever 	p = xdr_inline_decode(&xdr, sizeof(u32));
47403eb1dcbSChuck Lever 	if (unlikely(p == NULL))
47503eb1dcbSChuck Lever 		return -EIO;
47603eb1dcbSChuck Lever 	resp->state = ntohl(*p);
47703eb1dcbSChuck Lever 
47803eb1dcbSChuck Lever 	dprintk("lockd: xdr_dec_stat state %d\n", resp->state);
4791da177e4SLinus Torvalds 	return 0;
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds #define SM_my_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
4832ca7754dSChuck Lever #define SM_my_id_sz	(SM_my_name_sz+3)
4842ca7754dSChuck Lever #define SM_mon_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
4852ca7754dSChuck Lever #define SM_mon_id_sz	(SM_mon_name_sz+SM_my_id_sz)
4860490a54aSChuck Lever #define SM_priv_sz	(XDR_QUADLEN(SM_PRIV_SIZE))
4870490a54aSChuck Lever #define SM_mon_sz	(SM_mon_id_sz+SM_priv_sz)
4881da177e4SLinus Torvalds #define SM_monres_sz	2
4891da177e4SLinus Torvalds #define SM_unmonres_sz	1
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds static struct rpc_procinfo	nsm_procedures[] = {
49236e8e668SChuck Lever [NSMPROC_MON] = {
49336e8e668SChuck Lever 		.p_proc		= NSMPROC_MON,
49403eb1dcbSChuck Lever 		.p_encode	= (kxdrproc_t)xdr_enc_mon,
49503eb1dcbSChuck Lever 		.p_decode	= (kxdrproc_t)xdr_dec_stat_res,
4962bea90d4SChuck Lever 		.p_arglen	= SM_mon_sz,
4972bea90d4SChuck Lever 		.p_replen	= SM_monres_sz,
49836e8e668SChuck Lever 		.p_statidx	= NSMPROC_MON,
499cc0175c1SChuck Lever 		.p_name		= "MONITOR",
5001da177e4SLinus Torvalds 	},
50136e8e668SChuck Lever [NSMPROC_UNMON] = {
50236e8e668SChuck Lever 		.p_proc		= NSMPROC_UNMON,
50303eb1dcbSChuck Lever 		.p_encode	= (kxdrproc_t)xdr_enc_unmon,
50403eb1dcbSChuck Lever 		.p_decode	= (kxdrproc_t)xdr_dec_stat,
5052bea90d4SChuck Lever 		.p_arglen	= SM_mon_id_sz,
5062bea90d4SChuck Lever 		.p_replen	= SM_unmonres_sz,
50736e8e668SChuck Lever 		.p_statidx	= NSMPROC_UNMON,
508cc0175c1SChuck Lever 		.p_name		= "UNMONITOR",
5091da177e4SLinus Torvalds 	},
5101da177e4SLinus Torvalds };
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds static struct rpc_version	nsm_version1 = {
5131da177e4SLinus Torvalds 		.number		= 1,
514e8c96f8cSTobias Klauser 		.nrprocs	= ARRAY_SIZE(nsm_procedures),
5151da177e4SLinus Torvalds 		.procs		= nsm_procedures
5161da177e4SLinus Torvalds };
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds static struct rpc_version *	nsm_version[] = {
5191da177e4SLinus Torvalds 	[1] = &nsm_version1,
5201da177e4SLinus Torvalds };
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds static struct rpc_stat		nsm_stats;
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds static struct rpc_program	nsm_program = {
5251da177e4SLinus Torvalds 		.name		= "statd",
52636e8e668SChuck Lever 		.number		= NSM_PROGRAM,
527e8c96f8cSTobias Klauser 		.nrvers		= ARRAY_SIZE(nsm_version),
5281da177e4SLinus Torvalds 		.version	= nsm_version,
5291da177e4SLinus Torvalds 		.stats		= &nsm_stats
5301da177e4SLinus Torvalds };
531