2005-04-17 02:20:36 +04:00
/*
* linux / fs / nfs / callback . c
*
* Copyright ( C ) 2004 Trond Myklebust
*
* NFSv4 callback handling
*/
# 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>
2006-03-26 13:37:12 +04:00
# include <linux/mutex.h>
2005-12-27 07:43:12 +03:00
# include <net/inet_sock.h>
2005-06-22 21:16:21 +04:00
# include "nfs4_fs.h"
2005-04-17 02:20:36 +04:00
# include "callback.h"
2006-08-23 04:06:10 +04:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
# 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 ;
2006-03-26 13:37:12 +04:00
static DEFINE_MUTEX ( nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
static struct svc_program nfs4_callback_program ;
2006-01-03 11:55:41 +03:00
unsigned int nfs_callback_set_tcpport ;
2005-04-17 02:20:36 +04:00
unsigned short nfs_callback_tcpport ;
2006-08-23 04:06:07 +04:00
static const int nfs_set_port_min = 0 ;
static const int nfs_set_port_max = 65535 ;
static int param_set_port ( const char * val , struct kernel_param * kp )
{
char * endp ;
int num = simple_strtol ( val , & endp , 0 ) ;
if ( endp = = val | | * endp | | num < nfs_set_port_min | | num > nfs_set_port_max )
return - EINVAL ;
* ( ( int * ) kp - > arg ) = num ;
return 0 ;
}
module_param_call ( callback_tcpport , param_set_port , param_get_int ,
& nfs_callback_set_tcpport , 0644 ) ;
2005-04-17 02:20:36 +04:00
/*
* This is the callback kernel thread .
*/
static void nfs_callback_svc ( struct svc_rqst * rqstp )
{
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 ) ;
2006-03-20 21:44:49 +03:00
for ( ; ; ) {
2007-02-12 11:53:32 +03:00
char buf [ RPC_MAX_ADDRBUFLEN ] ;
2006-03-20 21:44:49 +03:00
if ( signalled ( ) ) {
if ( nfs_callback_info . users = = 0 )
break ;
flush_signals ( current ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Listen for a request on the socket
*/
2006-10-02 13:17:50 +04:00
err = svc_recv ( rqstp , MAX_SCHEDULE_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
if ( err = = - EAGAIN | | err = = - EINTR )
continue ;
if ( err < 0 ) {
printk ( KERN_WARNING
" %s: terminating on error %d \n " ,
__FUNCTION__ , - err ) ;
break ;
}
2007-02-12 11:53:32 +03:00
dprintk ( " %s: request from %s \n " , __FUNCTION__ ,
svc_print_addr ( rqstp , buf , sizeof ( buf ) ) ) ;
2006-10-02 13:17:50 +04:00
svc_process ( rqstp ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-20 21:44:46 +03:00
svc_exit_thread ( rqstp ) ;
2005-04-17 02:20:36 +04:00
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 ;
int ret = 0 ;
lock_kernel ( ) ;
2006-03-26 13:37:12 +04:00
mutex_lock ( & nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( nfs_callback_info . users + + | | nfs_callback_info . pid ! = 0 )
goto out ;
init_completion ( & nfs_callback_info . started ) ;
init_completion ( & nfs_callback_info . stopped ) ;
2006-10-02 13:17:44 +04:00
serv = svc_create ( & nfs4_callback_program , NFS4_CALLBACK_BUFSIZE , NULL ) ;
2005-04-17 02:20:36 +04:00
ret = - ENOMEM ;
if ( ! serv )
goto out_err ;
2007-02-12 11:53:29 +03:00
ret = svc_makesock ( serv , IPPROTO_TCP , nfs_callback_set_tcpport ,
SVC_SOCK_ANONYMOUS ) ;
if ( ret < = 0 )
2005-04-17 02:20:36 +04:00
goto out_destroy ;
2007-02-12 11:53:29 +03:00
nfs_callback_tcpport = ret ;
dprintk ( " Callback port = 0x%x \n " , nfs_callback_tcpport ) ;
2005-04-17 02:20:36 +04:00
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 :
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return ret ;
out_destroy :
2007-02-12 11:53:29 +03:00
dprintk ( " Couldn't create callback socket or server thread; err = %d \n " ,
ret ) ;
2005-04-17 02:20:36 +04:00
svc_destroy ( serv ) ;
out_err :
nfs_callback_info . users - - ;
goto out ;
}
/*
* Kill the server process if it is not already up .
*/
2006-08-23 04:06:08 +04:00
void nfs_callback_down ( void )
2005-04-17 02:20:36 +04:00
{
lock_kernel ( ) ;
2006-03-26 13:37:12 +04:00
mutex_lock ( & nfs_callback_mutex ) ;
2006-03-20 21:44:49 +03:00
nfs_callback_info . users - - ;
do {
if ( nfs_callback_info . users ! = 0 | | nfs_callback_info . pid = = 0 )
break ;
if ( kill_proc ( nfs_callback_info . pid , SIGKILL , 1 ) < 0 )
break ;
} while ( wait_for_completion_timeout ( & nfs_callback_info . stopped , 5 * HZ ) = = 0 ) ;
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
}
static int nfs_callback_authenticate ( struct svc_rqst * rqstp )
{
2007-02-12 11:53:34 +03:00
struct sockaddr_in * addr = svc_addr_in ( rqstp ) ;
2006-08-23 04:06:08 +04:00
struct nfs_client * clp ;
2007-02-12 11:53:32 +03:00
char buf [ RPC_MAX_ADDRBUFLEN ] ;
2005-04-17 02:20:36 +04:00
/* Don't talk to strangers */
2006-08-23 04:06:10 +04:00
clp = nfs_find_client ( addr , 4 ) ;
2005-04-17 02:20:36 +04:00
if ( clp = = NULL )
return SVC_DROP ;
2007-02-12 11:53:32 +03:00
dprintk ( " %s: %s NFSv4 callback! \n " , __FUNCTION__ ,
svc_print_addr ( rqstp , buf , sizeof ( buf ) ) ) ;
2006-08-23 04:06:10 +04:00
nfs_put_client ( clp ) ;
2007-02-12 11:53:32 +03:00
2005-04-17 02:20:36 +04:00
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
*/
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 ,
} ;