1 /* 2 * linux/fs/nfs/callback.c 3 * 4 * Copyright (C) 2004 Trond Myklebust 5 * 6 * NFSv4 callback handling 7 */ 8 9 #include <linux/completion.h> 10 #include <linux/ip.h> 11 #include <linux/module.h> 12 #include <linux/smp_lock.h> 13 #include <linux/sunrpc/svc.h> 14 #include <linux/sunrpc/svcsock.h> 15 #include <linux/nfs_fs.h> 16 #include <linux/mutex.h> 17 18 #include <net/inet_sock.h> 19 20 #include "nfs4_fs.h" 21 #include "callback.h" 22 23 #define NFSDBG_FACILITY NFSDBG_CALLBACK 24 25 struct nfs_callback_data { 26 unsigned int users; 27 struct svc_serv *serv; 28 pid_t pid; 29 struct completion started; 30 struct completion stopped; 31 }; 32 33 static struct nfs_callback_data nfs_callback_info; 34 static DEFINE_MUTEX(nfs_callback_mutex); 35 static struct svc_program nfs4_callback_program; 36 37 unsigned int nfs_callback_set_tcpport; 38 unsigned short nfs_callback_tcpport; 39 40 /* 41 * This is the callback kernel thread. 42 */ 43 static void nfs_callback_svc(struct svc_rqst *rqstp) 44 { 45 struct svc_serv *serv = rqstp->rq_server; 46 int err; 47 48 __module_get(THIS_MODULE); 49 lock_kernel(); 50 51 nfs_callback_info.pid = current->pid; 52 daemonize("nfsv4-svc"); 53 /* Process request with signals blocked, but allow SIGKILL. */ 54 allow_signal(SIGKILL); 55 56 complete(&nfs_callback_info.started); 57 58 for(;;) { 59 if (signalled()) { 60 if (nfs_callback_info.users == 0) 61 break; 62 flush_signals(current); 63 } 64 /* 65 * Listen for a request on the socket 66 */ 67 err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT); 68 if (err == -EAGAIN || err == -EINTR) 69 continue; 70 if (err < 0) { 71 printk(KERN_WARNING 72 "%s: terminating on error %d\n", 73 __FUNCTION__, -err); 74 break; 75 } 76 dprintk("%s: request from %u.%u.%u.%u\n", __FUNCTION__, 77 NIPQUAD(rqstp->rq_addr.sin_addr.s_addr)); 78 svc_process(serv, rqstp); 79 } 80 81 svc_exit_thread(rqstp); 82 nfs_callback_info.pid = 0; 83 complete(&nfs_callback_info.stopped); 84 unlock_kernel(); 85 module_put_and_exit(0); 86 } 87 88 /* 89 * Bring up the server process if it is not already up. 90 */ 91 int nfs_callback_up(void) 92 { 93 struct svc_serv *serv; 94 struct svc_sock *svsk; 95 int ret = 0; 96 97 lock_kernel(); 98 mutex_lock(&nfs_callback_mutex); 99 if (nfs_callback_info.users++ || nfs_callback_info.pid != 0) 100 goto out; 101 init_completion(&nfs_callback_info.started); 102 init_completion(&nfs_callback_info.stopped); 103 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE); 104 ret = -ENOMEM; 105 if (!serv) 106 goto out_err; 107 /* FIXME: We don't want to register this socket with the portmapper */ 108 ret = svc_makesock(serv, IPPROTO_TCP, nfs_callback_set_tcpport); 109 if (ret < 0) 110 goto out_destroy; 111 if (!list_empty(&serv->sv_permsocks)) { 112 svsk = list_entry(serv->sv_permsocks.next, 113 struct svc_sock, sk_list); 114 nfs_callback_tcpport = ntohs(inet_sk(svsk->sk_sk)->sport); 115 dprintk ("Callback port = 0x%x\n", nfs_callback_tcpport); 116 } else 117 BUG(); 118 ret = svc_create_thread(nfs_callback_svc, serv); 119 if (ret < 0) 120 goto out_destroy; 121 nfs_callback_info.serv = serv; 122 wait_for_completion(&nfs_callback_info.started); 123 out: 124 mutex_unlock(&nfs_callback_mutex); 125 unlock_kernel(); 126 return ret; 127 out_destroy: 128 svc_destroy(serv); 129 out_err: 130 nfs_callback_info.users--; 131 goto out; 132 } 133 134 /* 135 * Kill the server process if it is not already up. 136 */ 137 int nfs_callback_down(void) 138 { 139 int ret = 0; 140 141 lock_kernel(); 142 mutex_lock(&nfs_callback_mutex); 143 nfs_callback_info.users--; 144 do { 145 if (nfs_callback_info.users != 0 || nfs_callback_info.pid == 0) 146 break; 147 if (kill_proc(nfs_callback_info.pid, SIGKILL, 1) < 0) 148 break; 149 } while (wait_for_completion_timeout(&nfs_callback_info.stopped, 5*HZ) == 0); 150 mutex_unlock(&nfs_callback_mutex); 151 unlock_kernel(); 152 return ret; 153 } 154 155 static int nfs_callback_authenticate(struct svc_rqst *rqstp) 156 { 157 struct in_addr *addr = &rqstp->rq_addr.sin_addr; 158 struct nfs4_client *clp; 159 160 /* Don't talk to strangers */ 161 clp = nfs4_find_client(addr); 162 if (clp == NULL) 163 return SVC_DROP; 164 dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr)); 165 nfs4_put_client(clp); 166 switch (rqstp->rq_authop->flavour) { 167 case RPC_AUTH_NULL: 168 if (rqstp->rq_proc != CB_NULL) 169 return SVC_DENIED; 170 break; 171 case RPC_AUTH_UNIX: 172 break; 173 case RPC_AUTH_GSS: 174 /* FIXME: RPCSEC_GSS handling? */ 175 default: 176 return SVC_DENIED; 177 } 178 return SVC_OK; 179 } 180 181 /* 182 * Define NFS4 callback program 183 */ 184 static struct svc_version *nfs4_callback_version[] = { 185 [1] = &nfs4_callback_version1, 186 }; 187 188 static struct svc_stat nfs4_callback_stats; 189 190 static struct svc_program nfs4_callback_program = { 191 .pg_prog = NFS4_CALLBACK, /* RPC service number */ 192 .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ 193 .pg_vers = nfs4_callback_version, /* version table */ 194 .pg_name = "NFSv4 callback", /* service name */ 195 .pg_class = "nfs", /* authentication class */ 196 .pg_stats = &nfs4_callback_stats, 197 .pg_authenticate = nfs_callback_authenticate, 198 }; 199