2005-04-17 02:20:36 +04:00
/*
* linux / fs / nfs / callback . c
*
* Copyright ( C ) 2004 Trond Myklebust
*
* NFSv4 callback handling
*/
# include <linux/config.h>
# include <linux/completion.h>
# include <linux/ip.h>
# include <linux/module.h>
# include <linux/smp_lock.h>
# include <linux/sunrpc/svc.h>
# include <linux/sunrpc/svcsock.h>
# include <linux/nfs_fs.h>
2005-06-22 21:16:21 +04:00
# include "nfs4_fs.h"
2005-04-17 02:20:36 +04:00
# include "callback.h"
# define NFSDBG_FACILITY NFSDBG_CALLBACK
struct nfs_callback_data {
unsigned int users ;
struct svc_serv * serv ;
pid_t pid ;
struct completion started ;
struct completion stopped ;
} ;
static struct nfs_callback_data nfs_callback_info ;
static DECLARE_MUTEX ( nfs_callback_sema ) ;
static struct svc_program nfs4_callback_program ;
unsigned short nfs_callback_tcpport ;
/*
* This is the callback kernel thread .
*/
static void nfs_callback_svc ( struct svc_rqst * rqstp )
{
struct svc_serv * serv = rqstp - > rq_server ;
int err ;
__module_get ( THIS_MODULE ) ;
lock_kernel ( ) ;
nfs_callback_info . pid = current - > pid ;
daemonize ( " nfsv4-svc " ) ;
/* Process request with signals blocked, but allow SIGKILL. */
allow_signal ( SIGKILL ) ;
complete ( & nfs_callback_info . started ) ;
while ( nfs_callback_info . users ! = 0 | | ! signalled ( ) ) {
/*
* Listen for a request on the socket
*/
err = svc_recv ( serv , rqstp , MAX_SCHEDULE_TIMEOUT ) ;
if ( err = = - EAGAIN | | err = = - EINTR )
continue ;
if ( err < 0 ) {
printk ( KERN_WARNING
" %s: terminating on error %d \n " ,
__FUNCTION__ , - err ) ;
break ;
}
dprintk ( " %s: request from %u.%u.%u.%u \n " , __FUNCTION__ ,
NIPQUAD ( rqstp - > rq_addr . sin_addr . s_addr ) ) ;
svc_process ( serv , rqstp ) ;
}
nfs_callback_info . pid = 0 ;
complete ( & nfs_callback_info . stopped ) ;
unlock_kernel ( ) ;
module_put_and_exit ( 0 ) ;
}
/*
* Bring up the server process if it is not already up .
*/
int nfs_callback_up ( void )
{
struct svc_serv * serv ;
struct svc_sock * svsk ;
int ret = 0 ;
lock_kernel ( ) ;
down ( & nfs_callback_sema ) ;
if ( nfs_callback_info . users + + | | nfs_callback_info . pid ! = 0 )
goto out ;
init_completion ( & nfs_callback_info . started ) ;
init_completion ( & nfs_callback_info . stopped ) ;
serv = svc_create ( & nfs4_callback_program , NFS4_CALLBACK_BUFSIZE ) ;
ret = - ENOMEM ;
if ( ! serv )
goto out_err ;
/* FIXME: We don't want to register this socket with the portmapper */
ret = svc_makesock ( serv , IPPROTO_TCP , 0 ) ;
if ( ret < 0 )
goto out_destroy ;
if ( ! list_empty ( & serv - > sv_permsocks ) ) {
svsk = list_entry ( serv - > sv_permsocks . next ,
struct svc_sock , sk_list ) ;
nfs_callback_tcpport = ntohs ( inet_sk ( svsk - > sk_sk ) - > sport ) ;
dprintk ( " Callback port = 0x%x \n " , nfs_callback_tcpport ) ;
} else
BUG ( ) ;
ret = svc_create_thread ( nfs_callback_svc , serv ) ;
if ( ret < 0 )
goto out_destroy ;
nfs_callback_info . serv = serv ;
wait_for_completion ( & nfs_callback_info . started ) ;
out :
up ( & nfs_callback_sema ) ;
unlock_kernel ( ) ;
return ret ;
out_destroy :
svc_destroy ( serv ) ;
out_err :
nfs_callback_info . users - - ;
goto out ;
}
/*
* Kill the server process if it is not already up .
*/
int nfs_callback_down ( void )
{
int ret = 0 ;
lock_kernel ( ) ;
down ( & nfs_callback_sema ) ;
if ( - - nfs_callback_info . users | | nfs_callback_info . pid = = 0 )
goto out ;
kill_proc ( nfs_callback_info . pid , SIGKILL , 1 ) ;
wait_for_completion ( & nfs_callback_info . stopped ) ;
out :
up ( & nfs_callback_sema ) ;
unlock_kernel ( ) ;
return ret ;
}
static int nfs_callback_authenticate ( struct svc_rqst * rqstp )
{
struct in_addr * addr = & rqstp - > rq_addr . sin_addr ;
struct nfs4_client * clp ;
/* Don't talk to strangers */
clp = nfs4_find_client ( addr ) ;
if ( clp = = NULL )
return SVC_DROP ;
dprintk ( " %s: %u.%u.%u.%u NFSv4 callback! \n " , __FUNCTION__ , NIPQUAD ( addr ) ) ;
nfs4_put_client ( clp ) ;
switch ( rqstp - > rq_authop - > flavour ) {
case RPC_AUTH_NULL :
if ( rqstp - > rq_proc ! = CB_NULL )
return SVC_DENIED ;
break ;
case RPC_AUTH_UNIX :
break ;
case RPC_AUTH_GSS :
/* FIXME: RPCSEC_GSS handling? */
default :
return SVC_DENIED ;
}
return SVC_OK ;
}
/*
* Define NFS4 callback program
*/
extern struct svc_version nfs4_callback_version1 ;
static struct svc_version * nfs4_callback_version [ ] = {
[ 1 ] = & nfs4_callback_version1 ,
} ;
static struct svc_stat nfs4_callback_stats ;
static struct svc_program nfs4_callback_program = {
. pg_prog = NFS4_CALLBACK , /* RPC service number */
. pg_nvers = ARRAY_SIZE ( nfs4_callback_version ) , /* Number of entries */
. pg_vers = nfs4_callback_version , /* version table */
. pg_name = " NFSv4 callback " , /* service name */
. pg_class = " nfs " , /* authentication class */
. pg_stats = & nfs4_callback_stats ,
. pg_authenticate = nfs_callback_authenticate ,
} ;