1/*
2 * linux/fs/lockd/mon.c
3 *
4 * The kernel statd client.
5 *
6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7 */
8
9#include <linux/types.h>
10#include <linux/utsname.h>
11#include <linux/kernel.h>
12#include <linux/sunrpc/clnt.h>
13#include <linux/sunrpc/svc.h>
14#include <linux/lockd/lockd.h>
15#include <linux/lockd/sm_inter.h>
16
17
18#define NLMDBG_FACILITY		NLMDBG_MONITOR
19
20static struct rpc_clnt *	nsm_create(void);
21
22static struct rpc_program	nsm_program;
23
24/*
25 * Local NSM state
26 */
27int				nsm_local_state;
28
29/*
30 * Common procedure for SM_MON/SM_UNMON calls
31 */
32static int
33nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
34{
35	struct rpc_clnt	*clnt;
36	int		status;
37	struct nsm_args	args;
38	struct rpc_message msg = {
39		.rpc_argp	= &args,
40		.rpc_resp	= res,
41	};
42
43	clnt = nsm_create();
44	if (IS_ERR(clnt)) {
45		status = PTR_ERR(clnt);
46		goto out;
47	}
48
49	memset(&args, 0, sizeof(args));
50	args.mon_name = nsm->sm_name;
51	args.addr = nsm->sm_addr.sin_addr.s_addr;
52	args.prog = NLM_PROGRAM;
53	args.vers = 3;
54	args.proc = NLMPROC_NSM_NOTIFY;
55	memset(res, 0, sizeof(*res));
56
57	msg.rpc_proc = &clnt->cl_procinfo[proc];
58	status = rpc_call_sync(clnt, &msg, 0);
59	if (status < 0)
60		printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
61			status);
62	else
63		status = 0;
64 out:
65	return status;
66}
67
68/*
69 * Set up monitoring of a remote host
70 */
71int
72nsm_monitor(struct nlm_host *host)
73{
74	struct nsm_handle *nsm = host->h_nsmhandle;
75	struct nsm_res	res;
76	int		status;
77
78	dprintk("lockd: nsm_monitor(%s)\n", host->h_name);
79	BUG_ON(nsm == NULL);
80
81	if (nsm->sm_monitored)
82		return 0;
83
84	status = nsm_mon_unmon(nsm, SM_MON, &res);
85
86	if (status < 0 || res.status != 0)
87		printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name);
88	else
89		nsm->sm_monitored = 1;
90	return status;
91}
92
93/*
94 * Cease to monitor remote host
95 */
96int
97nsm_unmonitor(struct nlm_host *host)
98{
99	struct nsm_handle *nsm = host->h_nsmhandle;
100	struct nsm_res	res;
101	int		status = 0;
102
103	if (nsm == NULL)
104		return 0;
105	host->h_nsmhandle = NULL;
106
107	if (atomic_read(&nsm->sm_count) == 1
108	 && nsm->sm_monitored && !nsm->sm_sticky) {
109		dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name);
110
111		status = nsm_mon_unmon(nsm, SM_UNMON, &res);
112		if (status < 0)
113			printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
114					host->h_name);
115		else
116			nsm->sm_monitored = 0;
117	}
118	nsm_release(nsm);
119	return status;
120}
121
122/*
123 * Create NSM client for the local host
124 */
125static struct rpc_clnt *
126nsm_create(void)
127{
128	struct sockaddr_in	sin = {
129		.sin_family	= AF_INET,
130		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
131		.sin_port	= 0,
132	};
133	struct rpc_create_args args = {
134		.protocol	= IPPROTO_UDP,
135		.address	= (struct sockaddr *)&sin,
136		.addrsize	= sizeof(sin),
137		.servername	= "localhost",
138		.program	= &nsm_program,
139		.version	= SM_VERSION,
140		.authflavor	= RPC_AUTH_NULL,
141		.flags		= (RPC_CLNT_CREATE_ONESHOT),
142	};
143
144	return rpc_create(&args);
145}
146
147/*
148 * XDR functions for NSM.
149 */
150
151static __be32 *
152xdr_encode_common(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
153{
154	char	buffer[20], *name;
155
156	/*
157	 * Use the dotted-quad IP address of the remote host as
158	 * identifier. Linux statd always looks up the canonical
159	 * hostname first for whatever remote hostname it receives,
160	 * so this works alright.
161	 */
162	if (nsm_use_hostnames) {
163		name = argp->mon_name;
164	} else {
165		sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr));
166		name = buffer;
167	}
168	if (!(p = xdr_encode_string(p, name))
169	 || !(p = xdr_encode_string(p, utsname()->nodename)))
170		return ERR_PTR(-EIO);
171	*p++ = htonl(argp->prog);
172	*p++ = htonl(argp->vers);
173	*p++ = htonl(argp->proc);
174
175	return p;
176}
177
178static int
179xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
180{
181	p = xdr_encode_common(rqstp, p, argp);
182	if (IS_ERR(p))
183		return PTR_ERR(p);
184
185	/* Surprise - there may even be room for an IPv6 address now */
186	*p++ = argp->addr;
187	*p++ = 0;
188	*p++ = 0;
189	*p++ = 0;
190	rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
191	return 0;
192}
193
194static int
195xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
196{
197	p = xdr_encode_common(rqstp, p, argp);
198	if (IS_ERR(p))
199		return PTR_ERR(p);
200	rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
201	return 0;
202}
203
204static int
205xdr_decode_stat_res(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
206{
207	resp->status = ntohl(*p++);
208	resp->state = ntohl(*p++);
209	dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
210			resp->status, resp->state);
211	return 0;
212}
213
214static int
215xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
216{
217	resp->state = ntohl(*p++);
218	return 0;
219}
220
221#define SM_my_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
222#define SM_my_id_sz	(3+1+SM_my_name_sz)
223#define SM_mon_id_sz	(1+XDR_QUADLEN(20)+SM_my_id_sz)
224#define SM_mon_sz	(SM_mon_id_sz+4)
225#define SM_monres_sz	2
226#define SM_unmonres_sz	1
227
228static struct rpc_procinfo	nsm_procedures[] = {
229[SM_MON] = {
230		.p_proc		= SM_MON,
231		.p_encode	= (kxdrproc_t) xdr_encode_mon,
232		.p_decode	= (kxdrproc_t) xdr_decode_stat_res,
233		.p_arglen	= SM_mon_sz,
234		.p_replen	= SM_monres_sz,
235		.p_statidx	= SM_MON,
236		.p_name		= "MONITOR",
237	},
238[SM_UNMON] = {
239		.p_proc		= SM_UNMON,
240		.p_encode	= (kxdrproc_t) xdr_encode_unmon,
241		.p_decode	= (kxdrproc_t) xdr_decode_stat,
242		.p_arglen	= SM_mon_id_sz,
243		.p_replen	= SM_unmonres_sz,
244		.p_statidx	= SM_UNMON,
245		.p_name		= "UNMONITOR",
246	},
247};
248
249static struct rpc_version	nsm_version1 = {
250		.number		= 1,
251		.nrprocs	= ARRAY_SIZE(nsm_procedures),
252		.procs		= nsm_procedures
253};
254
255static struct rpc_version *	nsm_version[] = {
256	[1] = &nsm_version1,
257};
258
259static struct rpc_stat		nsm_stats;
260
261static struct rpc_program	nsm_program = {
262		.name		= "statd",
263		.number		= SM_PROGRAM,
264		.nrvers		= ARRAY_SIZE(nsm_version),
265		.version	= nsm_version,
266		.stats		= &nsm_stats
267};
268