2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2001 Lennert Buytenhek ( buytenh @ gnu . org )
2007-10-16 01:27:34 -07:00
* Copyright ( C ) 2001 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2005-04-16 15:20:36 -07:00
* Licensed under the GPL
*/
# include <errno.h>
2007-10-16 01:27:34 -07:00
# include <string.h>
# include <unistd.h>
2005-04-16 15:20:36 -07:00
# include <sys/socket.h>
# include <sys/uio.h>
# include <sys/un.h>
# include "mconsole.h"
static struct mconsole_command commands [ ] = {
2007-10-16 01:27:34 -07:00
/*
* With uts namespaces , uts information becomes process - specific , so
2006-07-10 04:45:16 -07:00
* we need a process context . If we try handling this in interrupt
* context , we may hit an exiting process without a valid uts
* namespace .
*/
{ " version " , mconsole_version , MCONSOLE_PROC } ,
2005-04-16 15:20:36 -07:00
{ " halt " , mconsole_halt , MCONSOLE_PROC } ,
{ " reboot " , mconsole_reboot , MCONSOLE_PROC } ,
{ " config " , mconsole_config , MCONSOLE_PROC } ,
{ " remove " , mconsole_remove , MCONSOLE_PROC } ,
2005-09-30 11:59:00 -07:00
{ " sysrq " , mconsole_sysrq , MCONSOLE_INTR } ,
2005-04-16 15:20:36 -07:00
{ " help " , mconsole_help , MCONSOLE_INTR } ,
{ " cad " , mconsole_cad , MCONSOLE_INTR } ,
{ " stop " , mconsole_stop , MCONSOLE_PROC } ,
{ " go " , mconsole_go , MCONSOLE_INTR } ,
{ " log " , mconsole_log , MCONSOLE_INTR } ,
{ " proc " , mconsole_proc , MCONSOLE_PROC } ,
2007-10-16 01:27:34 -07:00
{ " stack " , mconsole_stack , MCONSOLE_INTR } ,
2005-04-16 15:20:36 -07:00
} ;
/* Initialized in mconsole_init, which is an initcall */
char mconsole_socket_name [ 256 ] ;
2008-04-28 02:13:56 -07:00
static int mconsole_reply_v0 ( struct mc_request * req , char * reply )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:34 -07:00
struct iovec iov ;
struct msghdr msg ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:34 -07:00
iov . iov_base = reply ;
iov . iov_len = strlen ( reply ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:34 -07:00
msg . msg_name = & ( req - > origin ) ;
msg . msg_namelen = req - > originlen ;
msg . msg_iov = & iov ;
msg . msg_iovlen = 1 ;
msg . msg_control = NULL ;
msg . msg_controllen = 0 ;
msg . msg_flags = 0 ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:34 -07:00
return sendmsg ( req - > originating_fd , & msg , 0 ) ;
2005-04-16 15:20:36 -07:00
}
static struct mconsole_command * mconsole_parse ( struct mc_request * req )
{
struct mconsole_command * cmd ;
int i ;
2007-10-16 01:27:34 -07:00
for ( i = 0 ; i < ARRAY_SIZE ( commands ) ; i + + ) {
2005-04-16 15:20:36 -07:00
cmd = & commands [ i ] ;
2007-10-16 01:27:34 -07:00
if ( ! strncmp ( req - > request . data , cmd - > command ,
strlen ( cmd - > command ) ) ) {
2006-09-25 23:33:00 -07:00
return cmd ;
2005-04-16 15:20:36 -07:00
}
}
2006-09-25 23:33:00 -07:00
return NULL ;
2005-04-16 15:20:36 -07:00
}
# define MIN(a,b) ((a)<(b) ? (a):(b))
# define STRINGX(x) #x
# define STRING(x) STRINGX(x)
int mconsole_get_request ( int fd , struct mc_request * req )
{
int len ;
req - > originlen = sizeof ( req - > origin ) ;
2008-02-04 22:31:04 -08:00
req - > len = recvfrom ( fd , & req - > request , sizeof ( req - > request ) , 0 ,
( struct sockaddr * ) req - > origin , & req - > originlen ) ;
2005-04-16 15:20:36 -07:00
if ( req - > len < 0 )
return 0 ;
req - > originating_fd = fd ;
2007-10-16 01:27:34 -07:00
if ( req - > request . magic ! = MCONSOLE_MAGIC ) {
2005-04-16 15:20:36 -07:00
/* Unversioned request */
2007-10-16 01:27:34 -07:00
len = MIN ( sizeof ( req - > request . data ) - 1 ,
2005-04-16 15:20:36 -07:00
strlen ( ( char * ) & req - > request ) ) ;
memmove ( req - > request . data , & req - > request , len ) ;
req - > request . data [ len ] = ' \0 ' ;
req - > request . magic = MCONSOLE_MAGIC ;
req - > request . version = 0 ;
req - > request . len = len ;
mconsole_reply_v0 ( req , " ERR Version 0 mconsole clients are "
" not supported by this driver " ) ;
2007-10-16 01:27:34 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:34 -07:00
if ( req - > request . len > = MCONSOLE_MAX_DATA ) {
2005-04-16 15:20:36 -07:00
mconsole_reply ( req , " Request too large " , 1 , 0 ) ;
2007-10-16 01:27:34 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:34 -07:00
if ( req - > request . version ! = MCONSOLE_VERSION ) {
mconsole_reply ( req , " This driver only supports version "
STRING ( MCONSOLE_VERSION ) " clients " , 1 , 0 ) ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:34 -07:00
2005-04-16 15:20:36 -07:00
req - > request . data [ req - > request . len ] = ' \0 ' ;
req - > cmd = mconsole_parse ( req ) ;
2007-10-16 01:27:34 -07:00
if ( req - > cmd = = NULL ) {
2005-04-16 15:20:36 -07:00
mconsole_reply ( req , " Unknown command " , 1 , 0 ) ;
2007-10-16 01:27:34 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:34 -07:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:03 -08:00
int mconsole_reply_len ( struct mc_request * req , const char * str , int total ,
int err , int more )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:34 -07:00
/*
* XXX This is a stack consumption problem . It ' d be nice to
2006-09-29 01:58:52 -07:00
* make it global and serialize access to it , but there are a
* ton of callers to this function .
*/
2005-04-16 15:20:36 -07:00
struct mconsole_reply reply ;
2006-01-06 00:19:03 -08:00
int len , n ;
2005-04-16 15:20:36 -07:00
do {
reply . err = err ;
/* err can only be true on the first packet */
err = 0 ;
len = MIN ( total , MCONSOLE_MAX_DATA - 1 ) ;
2007-10-16 01:27:34 -07:00
if ( len = = total ) reply . more = more ;
2005-04-16 15:20:36 -07:00
else reply . more = 1 ;
memcpy ( reply . data , str , len ) ;
reply . data [ len ] = ' \0 ' ;
total - = len ;
str + = len ;
reply . len = len + 1 ;
len = sizeof ( reply ) + reply . len - sizeof ( reply . data ) ;
n = sendto ( req - > originating_fd , & reply , len , 0 ,
( struct sockaddr * ) req - > origin , req - > originlen ) ;
2007-10-16 01:27:34 -07:00
if ( n < 0 )
return - errno ;
} while ( total > 0 ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:03 -08:00
int mconsole_reply ( struct mc_request * req , const char * str , int err , int more )
{
return mconsole_reply_len ( req , str , strlen ( str ) , err , more ) ;
}
2005-04-16 15:20:36 -07:00
int mconsole_unlink_socket ( void )
{
unlink ( mconsole_socket_name ) ;
return 0 ;
}
static int notify_sock = - 1 ;
int mconsole_notify ( char * sock_name , int type , const void * data , int len )
{
struct sockaddr_un target ;
struct mconsole_notify packet ;
int n , err = 0 ;
lock_notify ( ) ;
2007-10-16 01:27:34 -07:00
if ( notify_sock < 0 ) {
2005-04-16 15:20:36 -07:00
notify_sock = socket ( PF_UNIX , SOCK_DGRAM , 0 ) ;
2007-10-16 01:27:34 -07:00
if ( notify_sock < 0 ) {
2005-04-16 15:20:36 -07:00
err = - errno ;
2007-10-16 01:27:34 -07:00
printk ( UM_KERN_ERR " mconsole_notify - socket failed, "
" errno = %d \n " , errno ) ;
2005-04-16 15:20:36 -07:00
}
}
unlock_notify ( ) ;
2007-10-16 01:27:34 -07:00
if ( err )
return err ;
2005-04-16 15:20:36 -07:00
target . sun_family = AF_UNIX ;
strcpy ( target . sun_path , sock_name ) ;
packet . magic = MCONSOLE_MAGIC ;
packet . version = MCONSOLE_VERSION ;
packet . type = type ;
len = ( len > sizeof ( packet . data ) ) ? sizeof ( packet . data ) : len ;
packet . len = len ;
memcpy ( packet . data , data , len ) ;
err = 0 ;
len = sizeof ( packet ) + packet . len - sizeof ( packet . data ) ;
2007-10-16 01:27:34 -07:00
n = sendto ( notify_sock , & packet , len , 0 , ( struct sockaddr * ) & target ,
2005-04-16 15:20:36 -07:00
sizeof ( target ) ) ;
2007-10-16 01:27:34 -07:00
if ( n < 0 ) {
2005-04-16 15:20:36 -07:00
err = - errno ;
2007-10-16 01:27:34 -07:00
printk ( UM_KERN_ERR " mconsole_notify - sendto failed, "
" errno = %d \n " , errno ) ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:34 -07:00
return err ;
2005-04-16 15:20:36 -07:00
}