2004-10-17 14:04:49 +04:00
/*
Unix SMB / CIFS implementation .
Samba internal messaging functions
Copyright ( C ) Andrew Tridgell 2004
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2005-02-03 14:56:03 +03:00
# include "lib/events/events.h"
2005-02-10 08:09:35 +03:00
# include "system/filesys.h"
2004-11-02 03:24:21 +03:00
# include "system/time.h"
2004-11-02 09:42:15 +03:00
# include "messages.h"
# include "dlinklist.h"
2005-02-10 09:59:29 +03:00
# include "lib/socket/socket.h"
2004-10-17 14:04:49 +04:00
/* change the message version with any incompatible changes in the protocol */
# define MESSAGING_VERSION 1
2004-10-27 02:45:33 +04:00
struct messaging_context {
2005-01-30 03:54:57 +03:00
uint32_t server_id ;
2004-10-17 14:04:49 +04:00
struct socket_context * sock ;
2005-05-01 22:49:07 +04:00
const char * path ;
2004-10-17 14:04:49 +04:00
struct dispatch_fn * dispatch ;
2005-06-03 08:21:25 +04:00
struct messaging_rec * pending ;
2004-10-17 14:04:49 +04:00
struct {
struct event_context * ev ;
struct fd_event * fde ;
} event ;
} ;
/* we have a linked list of dispatch handlers that this messaging
server can deal with */
struct dispatch_fn {
struct dispatch_fn * next , * prev ;
uint32_t msg_type ;
void * private ;
2004-10-27 02:45:33 +04:00
void ( * fn ) ( struct messaging_context * msg , void * private ,
2005-01-30 03:54:57 +03:00
uint32_t msg_type , uint32_t server_id , DATA_BLOB * data ) ;
2004-10-17 14:04:49 +04:00
} ;
/* an individual message */
struct messaging_rec {
2005-06-03 08:21:25 +04:00
struct messaging_rec * next , * prev ;
2004-10-27 02:45:33 +04:00
struct messaging_context * msg ;
2004-10-17 14:04:49 +04:00
const char * path ;
2005-06-03 10:04:34 +04:00
struct messaging_header {
2004-10-17 14:04:49 +04:00
uint32_t version ;
uint32_t msg_type ;
2005-01-30 03:54:57 +03:00
uint32_t from ;
uint32_t to ;
2004-10-17 14:04:49 +04:00
uint32_t length ;
2005-06-03 10:04:34 +04:00
} * header ;
2004-10-17 14:04:49 +04:00
2005-06-03 10:04:34 +04:00
DATA_BLOB packet ;
2004-10-17 14:04:49 +04:00
} ;
2005-06-03 08:21:25 +04:00
2004-10-17 14:04:49 +04:00
/*
A useful function for testing the message system .
*/
2004-10-27 02:45:33 +04:00
static void ping_message ( struct messaging_context * msg , void * private ,
2005-01-30 03:54:57 +03:00
uint32_t msg_type , uint32_t src , DATA_BLOB * data )
2004-10-17 14:04:49 +04:00
{
DEBUG ( 1 , ( " INFO: Received PING message from server %u [%.*s] \n " ,
( uint_t ) src , data - > length , data - > data ? ( const char * ) data - > data : " " ) ) ;
2004-10-27 02:45:33 +04:00
messaging_send ( msg , src , MSG_PONG , data ) ;
2004-10-17 14:04:49 +04:00
}
/*
return the path to a messaging socket
*/
2005-01-30 03:54:57 +03:00
static char * messaging_path ( TALLOC_CTX * mem_ctx , uint32_t server_id )
2004-10-17 14:04:49 +04:00
{
char * name = talloc_asprintf ( mem_ctx , " messaging/msg.%u " , ( unsigned ) server_id ) ;
char * ret ;
2004-10-29 12:38:59 +04:00
ret = smbd_tmp_path ( mem_ctx , name ) ;
2004-10-17 14:04:49 +04:00
talloc_free ( name ) ;
return ret ;
}
/*
dispatch a fully received message
*/
2004-10-27 02:45:33 +04:00
static void messaging_dispatch ( struct messaging_context * msg , struct messaging_rec * rec )
2004-10-17 14:04:49 +04:00
{
2004-10-18 15:47:13 +04:00
struct dispatch_fn * d , * next ;
for ( d = msg - > dispatch ; d ; d = next ) {
next = d - > next ;
2005-06-03 10:04:34 +04:00
if ( d - > msg_type = = rec - > header - > msg_type ) {
DATA_BLOB data ;
data . data = rec - > packet . data + sizeof ( * rec - > header ) ;
data . length = rec - > header - > length ;
d - > fn ( msg , d - > private , d - > msg_type , rec - > header - > from , & data ) ;
2004-10-17 14:04:49 +04:00
}
}
2005-06-03 10:04:34 +04:00
rec - > header - > length = 0 ;
2004-10-17 14:04:49 +04:00
}
/*
2005-06-03 08:21:25 +04:00
try to send the message
2004-10-17 14:04:49 +04:00
*/
2005-06-03 08:21:25 +04:00
static NTSTATUS try_send ( struct messaging_rec * rec )
{
struct messaging_context * msg = rec - > msg ;
size_t nsent ;
void * priv ;
NTSTATUS status ;
/* we send with privileges so messages work from any context */
priv = root_privileges ( ) ;
2005-06-03 10:04:34 +04:00
status = socket_sendto ( msg - > sock , & rec - > packet , & nsent , 0 , rec - > path , 0 ) ;
2005-06-03 08:21:25 +04:00
talloc_free ( priv ) ;
return status ;
}
/*
handle a socket write event
*/
static void messaging_send_handler ( struct messaging_context * msg )
{
while ( msg - > pending ) {
struct messaging_rec * rec = msg - > pending ;
NTSTATUS status ;
status = try_send ( rec ) ;
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
break ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " messaging: Lost message from %u to %u of type %u - %s \n " ,
2005-06-03 10:04:34 +04:00
rec - > header - > from , rec - > header - > to , rec - > header - > msg_type ,
2005-06-03 08:21:25 +04:00
nt_errstr ( status ) ) ) ;
}
DLIST_REMOVE ( msg - > pending , rec ) ;
talloc_free ( rec ) ;
}
if ( msg - > pending = = NULL ) {
EVENT_FD_NOT_WRITEABLE ( msg - > event . fde ) ;
}
}
/*
handle a new incoming packet
*/
static void messaging_recv_handler ( struct messaging_context * msg )
2004-10-17 14:04:49 +04:00
{
2005-05-01 22:49:07 +04:00
struct messaging_rec * rec ;
2004-10-17 14:04:49 +04:00
NTSTATUS status ;
2005-06-03 10:04:34 +04:00
DATA_BLOB packet ;
2005-05-01 22:49:07 +04:00
size_t msize ;
2004-10-17 14:04:49 +04:00
2005-06-03 10:04:34 +04:00
/* see how many bytes are in the next packet */
2005-06-03 17:20:45 +04:00
status = socket_pending ( msg - > sock , & msize ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " socket_pending failed in messaging - %s \n " ,
nt_errstr ( status ) ) ) ;
2005-06-03 10:04:34 +04:00
return ;
}
2005-06-03 17:20:45 +04:00
packet = data_blob_talloc ( msg , NULL , msize ) ;
2005-06-03 10:04:34 +04:00
if ( packet . data = = NULL ) {
/* assume this is temporary and retry */
return ;
}
2005-06-03 17:20:45 +04:00
status = socket_recv ( msg - > sock , packet . data , msize , & msize , 0 ) ;
2005-05-01 22:49:07 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-03 10:04:34 +04:00
data_blob_free ( & packet ) ;
2005-05-01 22:49:07 +04:00
return ;
2004-10-17 14:04:49 +04:00
}
2005-06-03 10:04:34 +04:00
if ( msize < sizeof ( * rec - > header ) ) {
2005-05-01 22:49:07 +04:00
DEBUG ( 0 , ( " messaging: bad message of size %d \n " , msize ) ) ;
2005-06-03 10:04:34 +04:00
data_blob_free ( & packet ) ;
2005-05-01 22:49:07 +04:00
return ;
2004-10-17 14:04:49 +04:00
}
2005-01-27 10:08:20 +03:00
rec = talloc ( msg , struct messaging_rec ) ;
2004-10-17 14:04:49 +04:00
if ( rec = = NULL ) {
smb_panic ( " Unable to allocate messaging_rec " ) ;
}
2005-06-03 10:04:34 +04:00
talloc_steal ( rec , packet . data ) ;
2005-02-03 05:35:52 +03:00
rec - > msg = msg ;
rec - > path = msg - > path ;
2005-06-03 10:04:34 +04:00
rec - > header = ( struct messaging_header * ) packet . data ;
rec - > packet = packet ;
2005-05-01 22:49:07 +04:00
2005-06-03 10:04:34 +04:00
if ( msize ! = sizeof ( * rec - > header ) + rec - > header - > length ) {
2005-05-01 22:49:07 +04:00
DEBUG ( 0 , ( " messaging: bad message header size %d should be %d \n " ,
2005-06-03 10:04:34 +04:00
rec - > header - > length , msize - sizeof ( * rec - > header ) ) ) ;
2005-05-01 22:49:07 +04:00
talloc_free ( rec ) ;
return ;
}
messaging_dispatch ( msg , rec ) ;
talloc_free ( rec ) ;
2004-10-17 14:04:49 +04:00
}
2005-06-03 08:21:25 +04:00
/*
handle a socket event
*/
static void messaging_handler ( struct event_context * ev , struct fd_event * fde ,
uint16_t flags , void * private )
{
struct messaging_context * msg = talloc_get_type ( private ,
struct messaging_context ) ;
if ( flags & EVENT_FD_WRITE ) {
messaging_send_handler ( msg ) ;
}
if ( flags & EVENT_FD_READ ) {
messaging_recv_handler ( msg ) ;
}
}
2004-10-17 14:04:49 +04:00
/*
Register a dispatch function for a particular message type .
*/
2004-10-27 02:45:33 +04:00
void messaging_register ( struct messaging_context * msg , void * private ,
2004-10-17 14:04:49 +04:00
uint32_t msg_type ,
2005-01-30 03:54:57 +03:00
void ( * fn ) ( struct messaging_context * , void * , uint32_t , uint32_t , DATA_BLOB * ) )
2004-10-17 14:04:49 +04:00
{
struct dispatch_fn * d ;
2005-01-27 10:08:20 +03:00
d = talloc ( msg , struct dispatch_fn ) ;
2004-10-17 14:04:49 +04:00
d - > msg_type = msg_type ;
d - > private = private ;
d - > fn = fn ;
DLIST_ADD ( msg - > dispatch , d ) ;
}
/*
De - register the function for a particular message type .
*/
2004-10-27 02:45:33 +04:00
void messaging_deregister ( struct messaging_context * msg , uint32_t msg_type , void * private )
2004-10-17 14:04:49 +04:00
{
struct dispatch_fn * d , * next ;
for ( d = msg - > dispatch ; d ; d = next ) {
next = d - > next ;
2004-10-18 11:40:17 +04:00
if ( d - > msg_type = = msg_type & &
d - > private = = private ) {
2004-10-17 14:04:49 +04:00
DLIST_REMOVE ( msg - > dispatch , d ) ;
talloc_free ( d ) ;
}
}
}
/*
Send a message to a particular server
*/
2005-06-03 08:21:25 +04:00
NTSTATUS messaging_send ( struct messaging_context * msg , uint32_t server ,
uint32_t msg_type , DATA_BLOB * data )
2004-10-17 14:04:49 +04:00
{
struct messaging_rec * rec ;
NTSTATUS status ;
2005-06-03 10:04:34 +04:00
size_t dlength = data ? data - > length : 0 ;
2004-10-17 14:04:49 +04:00
2005-01-27 10:08:20 +03:00
rec = talloc ( msg , struct messaging_rec ) ;
2004-10-17 14:04:49 +04:00
if ( rec = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2005-06-03 10:04:34 +04:00
rec - > packet = data_blob_talloc ( rec , NULL , sizeof ( * rec - > header ) + dlength ) ;
if ( rec - > packet . data = = NULL ) {
talloc_free ( rec ) ;
return NT_STATUS_NO_MEMORY ;
}
rec - > msg = msg ;
rec - > header = ( struct messaging_header * ) rec - > packet . data ;
rec - > header - > version = MESSAGING_VERSION ;
rec - > header - > msg_type = msg_type ;
rec - > header - > from = msg - > server_id ;
rec - > header - > to = server ;
rec - > header - > length = dlength ;
if ( dlength ! = 0 ) {
memcpy ( rec - > packet . data + sizeof ( * rec - > header ) ,
data - > data , dlength ) ;
2004-10-17 14:04:49 +04:00
}
rec - > path = messaging_path ( rec , server ) ;
2005-06-03 08:21:25 +04:00
status = try_send ( rec ) ;
2004-10-18 02:28:26 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
2005-06-03 08:21:25 +04:00
if ( msg - > pending = = NULL ) {
EVENT_FD_WRITEABLE ( msg - > event . fde ) ;
}
DLIST_ADD ( msg - > pending , rec ) ;
2004-10-18 02:28:26 +04:00
return NT_STATUS_OK ;
}
2005-06-03 08:21:25 +04:00
talloc_free ( rec ) ;
2004-10-17 17:33:03 +04:00
2005-06-03 08:21:25 +04:00
return status ;
2004-10-17 14:04:49 +04:00
}
2004-11-03 13:09:48 +03:00
/*
Send a message to a particular server , with the message containing a single pointer
*/
2005-01-30 03:54:57 +03:00
NTSTATUS messaging_send_ptr ( struct messaging_context * msg , uint32_t server ,
2004-11-03 13:09:48 +03:00
uint32_t msg_type , void * ptr )
{
DATA_BLOB blob ;
blob . data = ( void * ) & ptr ;
blob . length = sizeof ( void * ) ;
return messaging_send ( msg , server , msg_type , & blob ) ;
}
2004-10-17 14:04:49 +04:00
/*
destroy the messaging context
*/
2004-10-27 02:45:33 +04:00
static int messaging_destructor ( void * ptr )
2004-10-17 14:04:49 +04:00
{
2004-10-27 02:45:33 +04:00
struct messaging_context * msg = ptr ;
2004-10-25 07:30:39 +04:00
unlink ( msg - > path ) ;
2004-10-17 14:04:49 +04:00
return 0 ;
}
/*
create the listening socket and setup the dispatcher
*/
2005-05-01 22:49:07 +04:00
struct messaging_context * messaging_init ( TALLOC_CTX * mem_ctx , uint32_t server_id ,
struct event_context * ev )
2004-10-17 14:04:49 +04:00
{
2004-10-27 02:45:33 +04:00
struct messaging_context * msg ;
2004-10-17 14:04:49 +04:00
NTSTATUS status ;
2005-05-01 22:49:07 +04:00
char * path ;
2004-10-17 14:04:49 +04:00
2005-01-27 10:08:20 +03:00
msg = talloc ( mem_ctx , struct messaging_context ) ;
2004-10-17 14:04:49 +04:00
if ( msg = = NULL ) {
return NULL ;
}
/* create the messaging directory if needed */
2005-05-01 22:49:07 +04:00
path = smbd_tmp_path ( msg , " messaging " ) ;
mkdir ( path , 0700 ) ;
talloc_free ( path ) ;
2004-10-17 14:04:49 +04:00
2005-05-01 22:49:07 +04:00
msg - > path = messaging_path ( msg , server_id ) ;
2004-10-17 14:04:49 +04:00
msg - > server_id = server_id ;
msg - > dispatch = NULL ;
2005-06-03 08:21:25 +04:00
msg - > pending = NULL ;
2004-10-17 14:04:49 +04:00
2005-05-01 22:49:07 +04:00
status = socket_create ( " unix " , SOCKET_TYPE_DGRAM , & msg - > sock , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( msg ) ;
return NULL ;
}
/* by stealing here we ensure that the socket is cleaned up (and even
deleted ) on exit */
talloc_steal ( msg , msg - > sock ) ;
status = socket_listen ( msg - > sock , msg - > path , 0 , 50 , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Unable to setup messaging listener for '%s' \n " , msg - > path ) ) ;
talloc_free ( msg ) ;
return NULL ;
}
2005-06-03 08:21:25 +04:00
/* it needs to be non blocking for sends */
set_blocking ( socket_get_fd ( msg - > sock ) , False ) ;
2005-01-30 03:54:57 +03:00
msg - > event . ev = talloc_reference ( msg , ev ) ;
2005-02-03 05:35:52 +03:00
msg - > event . fde = event_add_fd ( ev , msg , socket_get_fd ( msg - > sock ) ,
2005-06-03 08:21:25 +04:00
EVENT_FD_READ , messaging_handler , msg ) ;
2004-10-17 14:04:49 +04:00
talloc_set_destructor ( msg , messaging_destructor ) ;
messaging_register ( msg , NULL , MSG_PING , ping_message ) ;
return msg ;
}