xref: /openbmc/linux/fs/lockd/mon.c (revision 0c7aef4569f8680951b7dee01dddffb9d2f809ff)
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
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds static struct rpc_clnt *	nsm_create(void);
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds static struct rpc_program	nsm_program;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /*
261da177e4SLinus Torvalds  * Local NSM state
271da177e4SLinus Torvalds  */
28460f5cacSOlaf Kirch int				nsm_local_state;
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds /*
311da177e4SLinus Torvalds  * Common procedure for SM_MON/SM_UNMON calls
321da177e4SLinus Torvalds  */
331da177e4SLinus Torvalds static int
349502c522SOlaf Kirch nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
371da177e4SLinus Torvalds 	int		status;
38a4846750SChuck Lever 	struct nsm_args args = {
39a4846750SChuck Lever 		.addr		= nsm_addr_in(nsm)->sin_addr.s_addr,
40a4846750SChuck Lever 		.prog		= NLM_PROGRAM,
41a4846750SChuck Lever 		.vers		= 3,
42a4846750SChuck Lever 		.proc		= NLMPROC_NSM_NOTIFY,
4329ed1407SChuck Lever 		.mon_name	= nsm->sm_mon_name,
44a4846750SChuck Lever 	};
45dead28daSChuck Lever 	struct rpc_message msg = {
46dead28daSChuck Lever 		.rpc_argp	= &args,
47dead28daSChuck Lever 		.rpc_resp	= res,
48dead28daSChuck Lever 	};
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	clnt = nsm_create();
511da177e4SLinus Torvalds 	if (IS_ERR(clnt)) {
521da177e4SLinus Torvalds 		status = PTR_ERR(clnt);
535acf4315SChuck Lever 		dprintk("lockd: failed to create NSM upcall transport, "
545acf4315SChuck Lever 				"status=%d\n", status);
551da177e4SLinus Torvalds 		goto out;
561da177e4SLinus Torvalds 	}
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	memset(res, 0, sizeof(*res));
591da177e4SLinus Torvalds 
60dead28daSChuck Lever 	msg.rpc_proc = &clnt->cl_procinfo[proc];
61dead28daSChuck Lever 	status = rpc_call_sync(clnt, &msg, 0);
621da177e4SLinus Torvalds 	if (status < 0)
635acf4315SChuck Lever 		dprintk("lockd: NSM upcall RPC failed, status=%d\n",
641da177e4SLinus Torvalds 				status);
651da177e4SLinus Torvalds 	else
661da177e4SLinus Torvalds 		status = 0;
6790c5755fSTrond Myklebust 	rpc_shutdown_client(clnt);
681da177e4SLinus Torvalds  out:
691da177e4SLinus Torvalds 	return status;
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds 
721e49323cSChuck Lever /**
731e49323cSChuck Lever  * nsm_monitor - Notify a peer in case we reboot
741e49323cSChuck Lever  * @host: pointer to nlm_host of peer to notify
751e49323cSChuck Lever  *
761e49323cSChuck Lever  * If this peer is not already monitored, this function sends an
771e49323cSChuck Lever  * upcall to the local rpc.statd to record the name/address of
781e49323cSChuck Lever  * the peer to notify in case we reboot.
791e49323cSChuck Lever  *
801e49323cSChuck Lever  * Returns zero if the peer is monitored by the local rpc.statd;
811e49323cSChuck Lever  * otherwise a negative errno value is returned.
821da177e4SLinus Torvalds  */
831e49323cSChuck Lever int nsm_monitor(const struct nlm_host *host)
841da177e4SLinus Torvalds {
858dead0dbSOlaf Kirch 	struct nsm_handle *nsm = host->h_nsmhandle;
861da177e4SLinus Torvalds 	struct nsm_res	res;
871da177e4SLinus Torvalds 	int		status;
881da177e4SLinus Torvalds 
899fee4902SChuck Lever 	dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
908dead0dbSOlaf Kirch 
918dead0dbSOlaf Kirch 	if (nsm->sm_monitored)
92977faf39SOlaf Kirch 		return 0;
931da177e4SLinus Torvalds 
9429ed1407SChuck Lever 	/*
9529ed1407SChuck Lever 	 * Choose whether to record the caller_name or IP address of
9629ed1407SChuck Lever 	 * this peer in the local rpc.statd's database.
9729ed1407SChuck Lever 	 */
9829ed1407SChuck Lever 	nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
9929ed1407SChuck Lever 
1009502c522SOlaf Kirch 	status = nsm_mon_unmon(nsm, SM_MON, &res);
1015d254b11SChuck Lever 	if (res.status != 0)
1025d254b11SChuck Lever 		status = -EIO;
1035d254b11SChuck Lever 	if (status < 0)
1049fee4902SChuck Lever 		printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
1051da177e4SLinus Torvalds 	else
1068dead0dbSOlaf Kirch 		nsm->sm_monitored = 1;
1071da177e4SLinus Torvalds 	return status;
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
110356c3eb4SChuck Lever /**
111356c3eb4SChuck Lever  * nsm_unmonitor - Unregister peer notification
112356c3eb4SChuck Lever  * @host: pointer to nlm_host of peer to stop monitoring
113356c3eb4SChuck Lever  *
114356c3eb4SChuck Lever  * If this peer is monitored, this function sends an upcall to
115356c3eb4SChuck Lever  * tell the local rpc.statd not to send this peer a notification
116356c3eb4SChuck Lever  * when we reboot.
1171da177e4SLinus Torvalds  */
118356c3eb4SChuck Lever void nsm_unmonitor(const struct nlm_host *host)
1191da177e4SLinus Torvalds {
1208dead0dbSOlaf Kirch 	struct nsm_handle *nsm = host->h_nsmhandle;
1211da177e4SLinus Torvalds 	struct nsm_res	res;
122356c3eb4SChuck Lever 	int status;
1231da177e4SLinus Torvalds 
1249502c522SOlaf Kirch 	if (atomic_read(&nsm->sm_count) == 1
1259502c522SOlaf Kirch 	 && nsm->sm_monitored && !nsm->sm_sticky) {
1269fee4902SChuck Lever 		dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
1279502c522SOlaf Kirch 
1289502c522SOlaf Kirch 		status = nsm_mon_unmon(nsm, SM_UNMON, &res);
129*0c7aef45SChuck Lever 		if (res.status != 0)
130*0c7aef45SChuck Lever 			status = -EIO;
1311da177e4SLinus Torvalds 		if (status < 0)
1329502c522SOlaf Kirch 			printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
1339fee4902SChuck Lever 					nsm->sm_name);
1349502c522SOlaf Kirch 		else
1358dead0dbSOlaf Kirch 			nsm->sm_monitored = 0;
136977faf39SOlaf Kirch 	}
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds /*
1401da177e4SLinus Torvalds  * Create NSM client for the local host
1411da177e4SLinus Torvalds  */
1421da177e4SLinus Torvalds static struct rpc_clnt *
1431da177e4SLinus Torvalds nsm_create(void)
1441da177e4SLinus Torvalds {
145e1ec7892SChuck Lever 	struct sockaddr_in	sin = {
146e1ec7892SChuck Lever 		.sin_family	= AF_INET,
147e1ec7892SChuck Lever 		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
148e1ec7892SChuck Lever 		.sin_port	= 0,
149e1ec7892SChuck Lever 	};
150e1ec7892SChuck Lever 	struct rpc_create_args args = {
1510896a725S\"Talpey, Thomas\ 		.protocol	= XPRT_TRANSPORT_UDP,
152e1ec7892SChuck Lever 		.address	= (struct sockaddr *)&sin,
153e1ec7892SChuck Lever 		.addrsize	= sizeof(sin),
154e1ec7892SChuck Lever 		.servername	= "localhost",
155e1ec7892SChuck Lever 		.program	= &nsm_program,
156e1ec7892SChuck Lever 		.version	= SM_VERSION,
157e1ec7892SChuck Lever 		.authflavor	= RPC_AUTH_NULL,
158e1ec7892SChuck Lever 	};
1591da177e4SLinus Torvalds 
160e1ec7892SChuck Lever 	return rpc_create(&args);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds /*
1641da177e4SLinus Torvalds  * XDR functions for NSM.
1652ca7754dSChuck Lever  *
1662ca7754dSChuck Lever  * See http://www.opengroup.org/ for details on the Network
1672ca7754dSChuck Lever  * Status Monitor wire protocol.
1681da177e4SLinus Torvalds  */
1691da177e4SLinus Torvalds 
170099bd05fSChuck Lever static __be32 *xdr_encode_nsm_string(__be32 *p, char *string)
171099bd05fSChuck Lever {
172099bd05fSChuck Lever 	size_t len = strlen(string);
173099bd05fSChuck Lever 
174099bd05fSChuck Lever 	if (len > SM_MAXSTRLEN)
175099bd05fSChuck Lever 		len = SM_MAXSTRLEN;
176099bd05fSChuck Lever 	return xdr_encode_opaque(p, string, len);
177099bd05fSChuck Lever }
178099bd05fSChuck Lever 
17949695174SChuck Lever /*
18049695174SChuck Lever  * "mon_name" specifies the host to be monitored.
18149695174SChuck Lever  */
18249695174SChuck Lever static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp)
18349695174SChuck Lever {
18429ed1407SChuck Lever 	return xdr_encode_nsm_string(p, argp->mon_name);
18549695174SChuck Lever }
18649695174SChuck Lever 
187850c95fdSChuck Lever /*
188850c95fdSChuck Lever  * The "my_id" argument specifies the hostname and RPC procedure
189850c95fdSChuck Lever  * to be called when the status manager receives notification
190850c95fdSChuck Lever  * (via the SM_NOTIFY call) that the state of host "mon_name"
191850c95fdSChuck Lever  * has changed.
192850c95fdSChuck Lever  */
193850c95fdSChuck Lever static __be32 *xdr_encode_my_id(__be32 *p, struct nsm_args *argp)
194850c95fdSChuck Lever {
195850c95fdSChuck Lever 	p = xdr_encode_nsm_string(p, utsname()->nodename);
196850c95fdSChuck Lever 	if (!p)
197850c95fdSChuck Lever 		return ERR_PTR(-EIO);
198850c95fdSChuck Lever 
199850c95fdSChuck Lever 	*p++ = htonl(argp->prog);
200850c95fdSChuck Lever 	*p++ = htonl(argp->vers);
201850c95fdSChuck Lever 	*p++ = htonl(argp->proc);
202850c95fdSChuck Lever 
203850c95fdSChuck Lever 	return p;
204850c95fdSChuck Lever }
205850c95fdSChuck Lever 
206ea72a7f1SChuck Lever /*
207ea72a7f1SChuck Lever  * The "mon_id" argument specifies the non-private arguments
208ea72a7f1SChuck Lever  * of an SM_MON or SM_UNMON call.
209ea72a7f1SChuck Lever  */
210ea72a7f1SChuck Lever static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp)
211ea72a7f1SChuck Lever {
212ea72a7f1SChuck Lever 	p = xdr_encode_mon_name(p, argp);
213ea72a7f1SChuck Lever 	if (!p)
214ea72a7f1SChuck Lever 		return ERR_PTR(-EIO);
215ea72a7f1SChuck Lever 
216ea72a7f1SChuck Lever 	return xdr_encode_my_id(p, argp);
217ea72a7f1SChuck Lever }
218ea72a7f1SChuck Lever 
2190490a54aSChuck Lever /*
2200490a54aSChuck Lever  * The "priv" argument may contain private information required
2210490a54aSChuck Lever  * by the SM_MON call. This information will be supplied in the
2220490a54aSChuck Lever  * SM_NOTIFY call.
2230490a54aSChuck Lever  *
2240490a54aSChuck Lever  * Linux provides the raw IP address of the monitored host,
2250490a54aSChuck Lever  * left in network byte order.
2260490a54aSChuck Lever  */
2270490a54aSChuck Lever static __be32 *xdr_encode_priv(__be32 *p, struct nsm_args *argp)
2280490a54aSChuck Lever {
2290490a54aSChuck Lever 	*p++ = argp->addr;
2300490a54aSChuck Lever 	*p++ = 0;
2310490a54aSChuck Lever 	*p++ = 0;
2320490a54aSChuck Lever 	*p++ = 0;
2330490a54aSChuck Lever 
2340490a54aSChuck Lever 	return p;
2350490a54aSChuck Lever }
2360490a54aSChuck Lever 
2371da177e4SLinus Torvalds static int
23852921e02SAl Viro xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
2391da177e4SLinus Torvalds {
2402ca7754dSChuck Lever 	p = xdr_encode_mon_id(p, argp);
2411da177e4SLinus Torvalds 	if (IS_ERR(p))
2421da177e4SLinus Torvalds 		return PTR_ERR(p);
2439502c522SOlaf Kirch 
2440490a54aSChuck Lever 	p = xdr_encode_priv(p, argp);
2450490a54aSChuck Lever 	if (IS_ERR(p))
2460490a54aSChuck Lever 		return PTR_ERR(p);
2470490a54aSChuck Lever 
2481da177e4SLinus Torvalds 	rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
2491da177e4SLinus Torvalds 	return 0;
2501da177e4SLinus Torvalds }
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds static int
25352921e02SAl Viro xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
2541da177e4SLinus Torvalds {
2552ca7754dSChuck Lever 	p = xdr_encode_mon_id(p, argp);
2561da177e4SLinus Torvalds 	if (IS_ERR(p))
2571da177e4SLinus Torvalds 		return PTR_ERR(p);
2581da177e4SLinus Torvalds 	rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
2591da177e4SLinus Torvalds 	return 0;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds static int
26352921e02SAl Viro xdr_decode_stat_res(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
2641da177e4SLinus Torvalds {
2651da177e4SLinus Torvalds 	resp->status = ntohl(*p++);
2661da177e4SLinus Torvalds 	resp->state = ntohl(*p++);
2671da177e4SLinus Torvalds 	dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
2681da177e4SLinus Torvalds 			resp->status, resp->state);
2691da177e4SLinus Torvalds 	return 0;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds static int
27352921e02SAl Viro xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	resp->state = ntohl(*p++);
2761da177e4SLinus Torvalds 	return 0;
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds #define SM_my_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
2802ca7754dSChuck Lever #define SM_my_id_sz	(SM_my_name_sz+3)
2812ca7754dSChuck Lever #define SM_mon_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
2822ca7754dSChuck Lever #define SM_mon_id_sz	(SM_mon_name_sz+SM_my_id_sz)
2830490a54aSChuck Lever #define SM_priv_sz	(XDR_QUADLEN(SM_PRIV_SIZE))
2840490a54aSChuck Lever #define SM_mon_sz	(SM_mon_id_sz+SM_priv_sz)
2851da177e4SLinus Torvalds #define SM_monres_sz	2
2861da177e4SLinus Torvalds #define SM_unmonres_sz	1
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds static struct rpc_procinfo	nsm_procedures[] = {
2891da177e4SLinus Torvalds [SM_MON] = {
2901da177e4SLinus Torvalds 		.p_proc		= SM_MON,
2911da177e4SLinus Torvalds 		.p_encode	= (kxdrproc_t) xdr_encode_mon,
2921da177e4SLinus Torvalds 		.p_decode	= (kxdrproc_t) xdr_decode_stat_res,
2932bea90d4SChuck Lever 		.p_arglen	= SM_mon_sz,
2942bea90d4SChuck Lever 		.p_replen	= SM_monres_sz,
295cc0175c1SChuck Lever 		.p_statidx	= SM_MON,
296cc0175c1SChuck Lever 		.p_name		= "MONITOR",
2971da177e4SLinus Torvalds 	},
2981da177e4SLinus Torvalds [SM_UNMON] = {
2991da177e4SLinus Torvalds 		.p_proc		= SM_UNMON,
3001da177e4SLinus Torvalds 		.p_encode	= (kxdrproc_t) xdr_encode_unmon,
3011da177e4SLinus Torvalds 		.p_decode	= (kxdrproc_t) xdr_decode_stat,
3022bea90d4SChuck Lever 		.p_arglen	= SM_mon_id_sz,
3032bea90d4SChuck Lever 		.p_replen	= SM_unmonres_sz,
304cc0175c1SChuck Lever 		.p_statidx	= SM_UNMON,
305cc0175c1SChuck Lever 		.p_name		= "UNMONITOR",
3061da177e4SLinus Torvalds 	},
3071da177e4SLinus Torvalds };
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds static struct rpc_version	nsm_version1 = {
3101da177e4SLinus Torvalds 		.number		= 1,
311e8c96f8cSTobias Klauser 		.nrprocs	= ARRAY_SIZE(nsm_procedures),
3121da177e4SLinus Torvalds 		.procs		= nsm_procedures
3131da177e4SLinus Torvalds };
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds static struct rpc_version *	nsm_version[] = {
3161da177e4SLinus Torvalds 	[1] = &nsm_version1,
3171da177e4SLinus Torvalds };
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds static struct rpc_stat		nsm_stats;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds static struct rpc_program	nsm_program = {
3221da177e4SLinus Torvalds 		.name		= "statd",
3231da177e4SLinus Torvalds 		.number		= SM_PROGRAM,
324e8c96f8cSTobias Klauser 		.nrvers		= ARRAY_SIZE(nsm_version),
3251da177e4SLinus Torvalds 		.version	= nsm_version,
3261da177e4SLinus Torvalds 		.stats		= &nsm_stats
3271da177e4SLinus Torvalds };
328