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; 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* 521da177e4SLinus Torvalds * Local NSM state 531da177e4SLinus Torvalds */ 54460f5cacSOlaf Kirch int nsm_local_state; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds /* 5736e8e668SChuck Lever * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls 581da177e4SLinus Torvalds */ 591da177e4SLinus Torvalds static int 609502c522SOlaf Kirch nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) 611da177e4SLinus Torvalds { 621da177e4SLinus Torvalds struct rpc_clnt *clnt; 631da177e4SLinus Torvalds int status; 64a4846750SChuck Lever struct nsm_args args = { 65a4846750SChuck Lever .addr = nsm_addr_in(nsm)->sin_addr.s_addr, 66a4846750SChuck Lever .prog = NLM_PROGRAM, 67a4846750SChuck Lever .vers = 3, 68a4846750SChuck Lever .proc = NLMPROC_NSM_NOTIFY, 6929ed1407SChuck Lever .mon_name = nsm->sm_mon_name, 70a4846750SChuck Lever }; 71dead28daSChuck Lever struct rpc_message msg = { 72dead28daSChuck Lever .rpc_argp = &args, 73dead28daSChuck Lever .rpc_resp = res, 74dead28daSChuck Lever }; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds clnt = nsm_create(); 771da177e4SLinus Torvalds if (IS_ERR(clnt)) { 781da177e4SLinus Torvalds status = PTR_ERR(clnt); 795acf4315SChuck Lever dprintk("lockd: failed to create NSM upcall transport, " 805acf4315SChuck Lever "status=%d\n", status); 811da177e4SLinus Torvalds goto out; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds memset(res, 0, sizeof(*res)); 851da177e4SLinus Torvalds 86dead28daSChuck Lever msg.rpc_proc = &clnt->cl_procinfo[proc]; 87dead28daSChuck Lever status = rpc_call_sync(clnt, &msg, 0); 881da177e4SLinus Torvalds if (status < 0) 895acf4315SChuck Lever dprintk("lockd: NSM upcall RPC failed, status=%d\n", 901da177e4SLinus Torvalds status); 911da177e4SLinus Torvalds else 921da177e4SLinus Torvalds status = 0; 9390c5755fSTrond Myklebust rpc_shutdown_client(clnt); 941da177e4SLinus Torvalds out: 951da177e4SLinus Torvalds return status; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 981e49323cSChuck Lever /** 991e49323cSChuck Lever * nsm_monitor - Notify a peer in case we reboot 1001e49323cSChuck Lever * @host: pointer to nlm_host of peer to notify 1011e49323cSChuck Lever * 1021e49323cSChuck Lever * If this peer is not already monitored, this function sends an 1031e49323cSChuck Lever * upcall to the local rpc.statd to record the name/address of 1041e49323cSChuck Lever * the peer to notify in case we reboot. 1051e49323cSChuck Lever * 1061e49323cSChuck Lever * Returns zero if the peer is monitored by the local rpc.statd; 1071e49323cSChuck Lever * otherwise a negative errno value is returned. 1081da177e4SLinus Torvalds */ 1091e49323cSChuck Lever int nsm_monitor(const struct nlm_host *host) 1101da177e4SLinus Torvalds { 1118dead0dbSOlaf Kirch struct nsm_handle *nsm = host->h_nsmhandle; 1121da177e4SLinus Torvalds struct nsm_res res; 1131da177e4SLinus Torvalds int status; 1141da177e4SLinus Torvalds 1159fee4902SChuck Lever dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name); 1168dead0dbSOlaf Kirch 1178dead0dbSOlaf Kirch if (nsm->sm_monitored) 118977faf39SOlaf Kirch return 0; 1191da177e4SLinus Torvalds 12029ed1407SChuck Lever /* 12129ed1407SChuck Lever * Choose whether to record the caller_name or IP address of 12229ed1407SChuck Lever * this peer in the local rpc.statd's database. 12329ed1407SChuck Lever */ 12429ed1407SChuck Lever nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf; 12529ed1407SChuck Lever 12636e8e668SChuck Lever status = nsm_mon_unmon(nsm, NSMPROC_MON, &res); 1275d254b11SChuck Lever if (res.status != 0) 1285d254b11SChuck Lever status = -EIO; 1295d254b11SChuck Lever if (status < 0) 1309fee4902SChuck Lever printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name); 1311da177e4SLinus Torvalds else 1328dead0dbSOlaf Kirch nsm->sm_monitored = 1; 1331da177e4SLinus Torvalds return status; 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 136356c3eb4SChuck Lever /** 137356c3eb4SChuck Lever * nsm_unmonitor - Unregister peer notification 138356c3eb4SChuck Lever * @host: pointer to nlm_host of peer to stop monitoring 139356c3eb4SChuck Lever * 140356c3eb4SChuck Lever * If this peer is monitored, this function sends an upcall to 141356c3eb4SChuck Lever * tell the local rpc.statd not to send this peer a notification 142356c3eb4SChuck Lever * when we reboot. 1431da177e4SLinus Torvalds */ 144356c3eb4SChuck Lever void nsm_unmonitor(const struct nlm_host *host) 1451da177e4SLinus Torvalds { 1468dead0dbSOlaf Kirch struct nsm_handle *nsm = host->h_nsmhandle; 1471da177e4SLinus Torvalds struct nsm_res res; 148356c3eb4SChuck Lever int status; 1491da177e4SLinus Torvalds 1509502c522SOlaf Kirch if (atomic_read(&nsm->sm_count) == 1 1519502c522SOlaf Kirch && nsm->sm_monitored && !nsm->sm_sticky) { 1529fee4902SChuck Lever dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name); 1539502c522SOlaf Kirch 15436e8e668SChuck Lever status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res); 1550c7aef45SChuck Lever if (res.status != 0) 1560c7aef45SChuck Lever status = -EIO; 1571da177e4SLinus Torvalds if (status < 0) 1589502c522SOlaf Kirch printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", 1599fee4902SChuck Lever nsm->sm_name); 1609502c522SOlaf Kirch else 1618dead0dbSOlaf Kirch nsm->sm_monitored = 0; 162977faf39SOlaf Kirch } 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds /* 1661da177e4SLinus Torvalds * Create NSM client for the local host 1671da177e4SLinus Torvalds */ 1681da177e4SLinus Torvalds static struct rpc_clnt * 1691da177e4SLinus Torvalds nsm_create(void) 1701da177e4SLinus Torvalds { 171e1ec7892SChuck Lever struct sockaddr_in sin = { 172e1ec7892SChuck Lever .sin_family = AF_INET, 173e1ec7892SChuck Lever .sin_addr.s_addr = htonl(INADDR_LOOPBACK), 174e1ec7892SChuck Lever .sin_port = 0, 175e1ec7892SChuck Lever }; 176e1ec7892SChuck Lever struct rpc_create_args args = { 1770896a725S\"Talpey, Thomas\ .protocol = XPRT_TRANSPORT_UDP, 178e1ec7892SChuck Lever .address = (struct sockaddr *)&sin, 179e1ec7892SChuck Lever .addrsize = sizeof(sin), 180e1ec7892SChuck Lever .servername = "localhost", 181e1ec7892SChuck Lever .program = &nsm_program, 18236e8e668SChuck Lever .version = NSM_VERSION, 183e1ec7892SChuck Lever .authflavor = RPC_AUTH_NULL, 184e1ec7892SChuck Lever }; 1851da177e4SLinus Torvalds 186e1ec7892SChuck Lever return rpc_create(&args); 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds /* 1901da177e4SLinus Torvalds * XDR functions for NSM. 1912ca7754dSChuck Lever * 1922ca7754dSChuck Lever * See http://www.opengroup.org/ for details on the Network 1932ca7754dSChuck Lever * Status Monitor wire protocol. 1941da177e4SLinus Torvalds */ 1951da177e4SLinus Torvalds 196*03eb1dcbSChuck Lever static int encode_nsm_string(struct xdr_stream *xdr, const char *string) 197099bd05fSChuck Lever { 198*03eb1dcbSChuck Lever const u32 len = strlen(string); 199*03eb1dcbSChuck Lever __be32 *p; 200099bd05fSChuck Lever 201*03eb1dcbSChuck Lever if (unlikely(len > SM_MAXSTRLEN)) 202*03eb1dcbSChuck Lever return -EIO; 203*03eb1dcbSChuck Lever p = xdr_reserve_space(xdr, sizeof(u32) + len); 204*03eb1dcbSChuck Lever if (unlikely(p == NULL)) 205*03eb1dcbSChuck Lever return -EIO; 206*03eb1dcbSChuck Lever xdr_encode_opaque(p, string, len); 207*03eb1dcbSChuck Lever return 0; 208099bd05fSChuck Lever } 209099bd05fSChuck Lever 21049695174SChuck Lever /* 21149695174SChuck Lever * "mon_name" specifies the host to be monitored. 21249695174SChuck Lever */ 213*03eb1dcbSChuck Lever static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp) 21449695174SChuck Lever { 215*03eb1dcbSChuck Lever return encode_nsm_string(xdr, argp->mon_name); 21649695174SChuck Lever } 21749695174SChuck Lever 218850c95fdSChuck Lever /* 219850c95fdSChuck Lever * The "my_id" argument specifies the hostname and RPC procedure 220850c95fdSChuck Lever * to be called when the status manager receives notification 22136e8e668SChuck Lever * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name" 222850c95fdSChuck Lever * has changed. 223850c95fdSChuck Lever */ 224*03eb1dcbSChuck Lever static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp) 225850c95fdSChuck Lever { 226*03eb1dcbSChuck Lever int status; 227*03eb1dcbSChuck Lever __be32 *p; 228850c95fdSChuck Lever 229*03eb1dcbSChuck Lever status = encode_nsm_string(xdr, utsname()->nodename); 230*03eb1dcbSChuck Lever if (unlikely(status != 0)) 231*03eb1dcbSChuck Lever return status; 232*03eb1dcbSChuck Lever p = xdr_reserve_space(xdr, 3 * sizeof(u32)); 233*03eb1dcbSChuck Lever if (unlikely(p == NULL)) 234*03eb1dcbSChuck Lever return -EIO; 235850c95fdSChuck Lever *p++ = htonl(argp->prog); 236850c95fdSChuck Lever *p++ = htonl(argp->vers); 237850c95fdSChuck Lever *p++ = htonl(argp->proc); 238*03eb1dcbSChuck Lever return 0; 239850c95fdSChuck Lever } 240850c95fdSChuck Lever 241ea72a7f1SChuck Lever /* 242ea72a7f1SChuck Lever * The "mon_id" argument specifies the non-private arguments 24336e8e668SChuck Lever * of an NSMPROC_MON or NSMPROC_UNMON call. 244ea72a7f1SChuck Lever */ 245*03eb1dcbSChuck Lever static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp) 246ea72a7f1SChuck Lever { 247*03eb1dcbSChuck Lever int status; 248ea72a7f1SChuck Lever 249*03eb1dcbSChuck Lever status = encode_mon_name(xdr, argp); 250*03eb1dcbSChuck Lever if (unlikely(status != 0)) 251*03eb1dcbSChuck Lever return status; 252*03eb1dcbSChuck Lever return encode_my_id(xdr, argp); 253ea72a7f1SChuck Lever } 254ea72a7f1SChuck Lever 2550490a54aSChuck Lever /* 2560490a54aSChuck Lever * The "priv" argument may contain private information required 25736e8e668SChuck Lever * by the NSMPROC_MON call. This information will be supplied in the 25836e8e668SChuck Lever * NLMPROC_SM_NOTIFY call. 2590490a54aSChuck Lever * 2600490a54aSChuck Lever * Linux provides the raw IP address of the monitored host, 2610490a54aSChuck Lever * left in network byte order. 2620490a54aSChuck Lever */ 263*03eb1dcbSChuck Lever static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp) 2640490a54aSChuck Lever { 265*03eb1dcbSChuck Lever __be32 *p; 266*03eb1dcbSChuck Lever 267*03eb1dcbSChuck Lever p = xdr_reserve_space(xdr, SM_PRIV_SIZE); 268*03eb1dcbSChuck Lever if (unlikely(p == NULL)) 269*03eb1dcbSChuck Lever return -EIO; 2700490a54aSChuck Lever *p++ = argp->addr; 2710490a54aSChuck Lever *p++ = 0; 2720490a54aSChuck Lever *p++ = 0; 2730490a54aSChuck Lever *p++ = 0; 2741da177e4SLinus Torvalds return 0; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 277*03eb1dcbSChuck Lever static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p, 278*03eb1dcbSChuck Lever const struct nsm_args *argp) 2791da177e4SLinus Torvalds { 280*03eb1dcbSChuck Lever struct xdr_stream xdr; 281*03eb1dcbSChuck Lever int status; 282*03eb1dcbSChuck Lever 283*03eb1dcbSChuck Lever xdr_init_encode(&xdr, &req->rq_snd_buf, p); 284*03eb1dcbSChuck Lever status = encode_mon_id(&xdr, argp); 285*03eb1dcbSChuck Lever if (unlikely(status)) 286*03eb1dcbSChuck Lever return status; 287*03eb1dcbSChuck Lever return encode_priv(&xdr, argp); 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 290*03eb1dcbSChuck Lever static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p, 291*03eb1dcbSChuck Lever const struct nsm_args *argp) 2921da177e4SLinus Torvalds { 293*03eb1dcbSChuck Lever struct xdr_stream xdr; 294*03eb1dcbSChuck Lever 295*03eb1dcbSChuck Lever xdr_init_encode(&xdr, &req->rq_snd_buf, p); 296*03eb1dcbSChuck Lever return encode_mon_id(&xdr, argp); 297*03eb1dcbSChuck Lever } 298*03eb1dcbSChuck Lever 299*03eb1dcbSChuck Lever static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p, 300*03eb1dcbSChuck Lever struct nsm_res *resp) 301*03eb1dcbSChuck Lever { 302*03eb1dcbSChuck Lever struct xdr_stream xdr; 303*03eb1dcbSChuck Lever 304*03eb1dcbSChuck Lever xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); 305*03eb1dcbSChuck Lever p = xdr_inline_decode(&xdr, 2 * sizeof(u32)); 306*03eb1dcbSChuck Lever if (unlikely(p == NULL)) 307*03eb1dcbSChuck Lever return -EIO; 3081da177e4SLinus Torvalds resp->status = ntohl(*p++); 309*03eb1dcbSChuck Lever resp->state = ntohl(*p); 310*03eb1dcbSChuck Lever 311*03eb1dcbSChuck Lever dprintk("lockd: xdr_dec_stat_res status %d state %d\n", 3121da177e4SLinus Torvalds resp->status, resp->state); 3131da177e4SLinus Torvalds return 0; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 316*03eb1dcbSChuck Lever static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p, 317*03eb1dcbSChuck Lever struct nsm_res *resp) 3181da177e4SLinus Torvalds { 319*03eb1dcbSChuck Lever struct xdr_stream xdr; 320*03eb1dcbSChuck Lever 321*03eb1dcbSChuck Lever xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); 322*03eb1dcbSChuck Lever p = xdr_inline_decode(&xdr, sizeof(u32)); 323*03eb1dcbSChuck Lever if (unlikely(p == NULL)) 324*03eb1dcbSChuck Lever return -EIO; 325*03eb1dcbSChuck Lever resp->state = ntohl(*p); 326*03eb1dcbSChuck Lever 327*03eb1dcbSChuck Lever dprintk("lockd: xdr_dec_stat state %d\n", resp->state); 3281da177e4SLinus Torvalds return 0; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds #define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN)) 3322ca7754dSChuck Lever #define SM_my_id_sz (SM_my_name_sz+3) 3332ca7754dSChuck Lever #define SM_mon_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN)) 3342ca7754dSChuck Lever #define SM_mon_id_sz (SM_mon_name_sz+SM_my_id_sz) 3350490a54aSChuck Lever #define SM_priv_sz (XDR_QUADLEN(SM_PRIV_SIZE)) 3360490a54aSChuck Lever #define SM_mon_sz (SM_mon_id_sz+SM_priv_sz) 3371da177e4SLinus Torvalds #define SM_monres_sz 2 3381da177e4SLinus Torvalds #define SM_unmonres_sz 1 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds static struct rpc_procinfo nsm_procedures[] = { 34136e8e668SChuck Lever [NSMPROC_MON] = { 34236e8e668SChuck Lever .p_proc = NSMPROC_MON, 343*03eb1dcbSChuck Lever .p_encode = (kxdrproc_t)xdr_enc_mon, 344*03eb1dcbSChuck Lever .p_decode = (kxdrproc_t)xdr_dec_stat_res, 3452bea90d4SChuck Lever .p_arglen = SM_mon_sz, 3462bea90d4SChuck Lever .p_replen = SM_monres_sz, 34736e8e668SChuck Lever .p_statidx = NSMPROC_MON, 348cc0175c1SChuck Lever .p_name = "MONITOR", 3491da177e4SLinus Torvalds }, 35036e8e668SChuck Lever [NSMPROC_UNMON] = { 35136e8e668SChuck Lever .p_proc = NSMPROC_UNMON, 352*03eb1dcbSChuck Lever .p_encode = (kxdrproc_t)xdr_enc_unmon, 353*03eb1dcbSChuck Lever .p_decode = (kxdrproc_t)xdr_dec_stat, 3542bea90d4SChuck Lever .p_arglen = SM_mon_id_sz, 3552bea90d4SChuck Lever .p_replen = SM_unmonres_sz, 35636e8e668SChuck Lever .p_statidx = NSMPROC_UNMON, 357cc0175c1SChuck Lever .p_name = "UNMONITOR", 3581da177e4SLinus Torvalds }, 3591da177e4SLinus Torvalds }; 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds static struct rpc_version nsm_version1 = { 3621da177e4SLinus Torvalds .number = 1, 363e8c96f8cSTobias Klauser .nrprocs = ARRAY_SIZE(nsm_procedures), 3641da177e4SLinus Torvalds .procs = nsm_procedures 3651da177e4SLinus Torvalds }; 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds static struct rpc_version * nsm_version[] = { 3681da177e4SLinus Torvalds [1] = &nsm_version1, 3691da177e4SLinus Torvalds }; 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds static struct rpc_stat nsm_stats; 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds static struct rpc_program nsm_program = { 3741da177e4SLinus Torvalds .name = "statd", 37536e8e668SChuck Lever .number = NSM_PROGRAM, 376e8c96f8cSTobias Klauser .nrvers = ARRAY_SIZE(nsm_version), 3771da177e4SLinus Torvalds .version = nsm_version, 3781da177e4SLinus Torvalds .stats = &nsm_stats 3791da177e4SLinus Torvalds }; 380