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"
2004-11-02 15:15:17 +03:00
# include "events.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"
2004-10-17 14:04:49 +04:00
/* change the message version with any incompatible changes in the protocol */
# define MESSAGING_VERSION 1
2004-11-03 13:09:48 +03:00
/* the number of microseconds to backoff in retrying to send a message */
# define MESSAGING_BACKOFF 250000
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 ;
char * path ;
struct dispatch_fn * dispatch ;
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 {
2004-10-27 02:45:33 +04:00
struct messaging_context * msg ;
2004-10-17 14:04:49 +04:00
struct socket_context * sock ;
struct fd_event * fde ;
const char * path ;
struct {
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 ;
} header ;
DATA_BLOB data ;
uint32_t ndone ;
} ;
/*
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 ;
2004-10-17 14:04:49 +04:00
if ( d - > msg_type = = rec - > header . msg_type ) {
d - > fn ( msg , d - > private , d - > msg_type , rec - > header . from , & rec - > data ) ;
}
}
/* we don't free the record itself here as there may
be more messages from this client */
data_blob_free ( & rec - > data ) ;
rec - > header . length = 0 ;
rec - > ndone = 0 ;
}
/*
handle IO for a single message
*/
static void messaging_recv_handler ( struct event_context * ev , struct fd_event * fde ,
2005-02-03 14:25:52 +03:00
uint16_t flags , void * private )
2004-10-17 14:04:49 +04:00
{
2005-02-03 05:35:52 +03:00
struct messaging_rec * rec = talloc_get_type ( private , struct messaging_rec ) ;
2004-10-27 02:45:33 +04:00
struct messaging_context * msg = rec - > msg ;
2004-10-17 14:04:49 +04:00
NTSTATUS status ;
if ( rec - > ndone < sizeof ( rec - > header ) ) {
/* receive the header */
2004-10-28 08:00:43 +04:00
size_t nread ;
status = socket_recv ( rec - > sock ,
rec - > ndone + ( char * ) & rec - > header ,
sizeof ( rec - > header ) - rec - > ndone , & nread , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
talloc_free ( rec ) ;
return ;
}
2004-10-28 08:00:43 +04:00
if ( nread = = 0 ) {
2004-10-17 14:04:49 +04:00
return ;
}
2004-10-28 08:00:43 +04:00
rec - > ndone + = nread ;
2004-10-17 14:04:49 +04:00
if ( rec - > ndone = = sizeof ( rec - > header ) ) {
if ( rec - > header . version ! = MESSAGING_VERSION ) {
DEBUG ( 0 , ( " meessage with wrong version %u \n " ,
rec - > header . version ) ) ;
talloc_free ( rec ) ;
}
rec - > data = data_blob_talloc ( rec , NULL , rec - > header . length ) ;
if ( rec - > data . length ! = rec - > header . length ) {
DEBUG ( 0 , ( " Unable to allocate message of size %u \n " ,
rec - > header . length ) ) ;
talloc_free ( rec ) ;
}
}
}
if ( rec - > ndone > = sizeof ( rec - > header ) & &
rec - > ndone < sizeof ( rec - > header ) + rec - > header . length ) {
/* receive the body, if any */
2004-10-28 08:00:43 +04:00
size_t nread ;
status = socket_recv ( rec - > sock ,
rec - > data . data + ( rec - > ndone - sizeof ( rec - > header ) ) ,
sizeof ( rec - > header ) + rec - > header . length - rec - > ndone ,
& nread , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
talloc_free ( rec ) ;
return ;
}
2004-10-28 08:00:43 +04:00
if ( nread = = 0 ) {
2004-10-17 14:04:49 +04:00
return ;
}
2004-10-28 08:00:43 +04:00
rec - > ndone + = nread ;
2004-10-17 14:04:49 +04:00
}
if ( rec - > ndone = = sizeof ( rec - > header ) + rec - > header . length ) {
/* we've got the whole message */
messaging_dispatch ( msg , rec ) ;
}
}
/*
handle a new incoming connection
*/
static void messaging_listen_handler ( struct event_context * ev , struct fd_event * fde ,
2005-02-03 14:25:52 +03:00
uint16_t flags , void * private )
2004-10-17 14:04:49 +04:00
{
2005-02-03 05:35:52 +03:00
struct messaging_context * msg = talloc_get_type ( private ,
struct messaging_context ) ;
2004-10-17 14:04:49 +04:00
struct messaging_rec * rec ;
NTSTATUS status ;
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 " ) ;
}
2004-10-28 11:55:33 +04:00
status = socket_accept ( msg - > sock , & rec - > sock ) ;
2004-10-17 14:04:49 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
smb_panic ( " Unable to accept messaging_rec " ) ;
}
talloc_steal ( rec , rec - > sock ) ;
2005-02-03 05:35:52 +03:00
rec - > msg = msg ;
rec - > ndone = 0 ;
2004-10-17 14:04:49 +04:00
rec - > header . length = 0 ;
2005-02-03 05:35:52 +03:00
rec - > path = msg - > path ;
rec - > fde = event_add_fd ( msg - > event . ev , rec , socket_get_fd ( rec - > sock ) ,
EVENT_FD_READ , messaging_recv_handler , rec ) ;
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 ) ;
}
}
}
/*
handle IO for sending a message
*/
static void messaging_send_handler ( struct event_context * ev , struct fd_event * fde ,
2005-02-03 14:25:52 +03:00
uint16_t flags , void * private )
2004-10-17 14:04:49 +04:00
{
2005-02-03 05:35:52 +03:00
struct messaging_rec * rec = talloc_get_type ( private , struct messaging_rec ) ;
2004-10-17 14:04:49 +04:00
NTSTATUS status ;
if ( rec - > ndone < sizeof ( rec - > header ) ) {
/* send the header */
size_t nsent ;
DATA_BLOB blob ;
2004-12-04 16:56:25 +03:00
blob . data = rec - > ndone + ( uint8_t * ) & rec - > header ;
2004-10-17 14:04:49 +04:00
blob . length = sizeof ( rec - > header ) - rec - > ndone ;
2004-10-28 08:00:43 +04:00
status = socket_send ( rec - > sock , & blob , & nsent , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
talloc_free ( rec ) ;
return ;
}
if ( nsent = = 0 ) {
return ;
}
rec - > ndone + = nsent ;
}
if ( rec - > ndone > = sizeof ( rec - > header ) & &
rec - > ndone < sizeof ( rec - > header ) + rec - > header . length ) {
/* send the body, if any */
DATA_BLOB blob ;
size_t nsent ;
blob . data = rec - > data . data + ( rec - > ndone - sizeof ( rec - > header ) ) ;
blob . length = rec - > header . length - ( rec - > ndone - sizeof ( rec - > header ) ) ;
2004-10-28 08:00:43 +04:00
status = socket_send ( rec - > sock , & blob , & nsent , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
talloc_free ( rec ) ;
return ;
}
rec - > ndone + = nsent ;
}
if ( rec - > ndone = = sizeof ( rec - > header ) + rec - > header . length ) {
/* we've done the whole message */
talloc_free ( rec ) ;
}
}
2004-11-03 13:09:48 +03:00
/*
wrapper around socket_connect with raised privileges
*/
static NTSTATUS try_connect ( struct messaging_rec * rec )
{
NTSTATUS status ;
void * priv = root_privileges ( ) ;
status = socket_connect ( rec - > sock , NULL , 0 , rec - > path , 0 , 0 ) ;
talloc_free ( priv ) ;
return status ;
}
2004-10-18 02:28:26 +04:00
/*
when the servers listen queue is full we use this to backoff the message
*/
2004-11-03 13:09:48 +03:00
static void messaging_backoff_handler ( struct event_context * ev , struct timed_event * te ,
2005-02-03 05:35:52 +03:00
struct timeval t , void * private )
2004-10-18 02:28:26 +04:00
{
2005-02-03 05:35:52 +03:00
struct messaging_rec * rec = talloc_get_type ( private , struct messaging_rec ) ;
2004-10-27 02:45:33 +04:00
struct messaging_context * msg = rec - > msg ;
2004-10-18 02:28:26 +04:00
NTSTATUS status ;
2004-11-03 13:09:48 +03:00
status = try_connect ( rec ) ;
2004-10-18 02:28:26 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
/* backoff again */
2005-02-03 05:35:52 +03:00
event_add_timed ( msg - > event . ev , rec ,
timeval_add ( & t , 0 , MESSAGING_BACKOFF ) ,
messaging_backoff_handler , rec ) ;
2004-10-18 02:28:26 +04:00
return ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " messaging: Lost message from %u to %u of type %u after backoff - %s \n " ,
rec - > header . from , rec - > header . to , rec - > header . msg_type , nt_errstr ( status ) ) ) ;
talloc_free ( rec ) ;
return ;
}
2005-02-03 05:35:52 +03:00
rec - > fde = event_add_fd ( msg - > event . ev , rec , socket_get_fd ( rec - > sock ) ,
EVENT_FD_WRITE , messaging_send_handler , rec ) ;
2004-10-18 02:28:26 +04:00
}
2004-10-17 14:04:49 +04:00
/*
Send a message to a particular server
*/
2005-01-30 03:54:57 +03: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-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 ;
}
rec - > msg = msg ;
rec - > header . version = MESSAGING_VERSION ;
rec - > header . msg_type = msg_type ;
rec - > header . from = msg - > server_id ;
rec - > header . to = server ;
rec - > header . length = data ? data - > length : 0 ;
if ( rec - > header . length ! = 0 ) {
rec - > data = data_blob_talloc ( rec , data - > data , data - > length ) ;
} else {
rec - > data = data_blob ( NULL , 0 ) ;
}
rec - > ndone = 0 ;
status = socket_create ( " unix " , SOCKET_TYPE_STREAM , & rec - > sock , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( rec ) ;
return status ;
}
talloc_steal ( rec , rec - > sock ) ;
rec - > path = messaging_path ( rec , server ) ;
2004-11-03 13:09:48 +03:00
status = try_connect ( rec ) ;
2004-10-18 02:28:26 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
/* backoff on this message - the servers listen queue is full */
2005-02-03 05:35:52 +03:00
event_add_timed ( msg - > event . ev , rec ,
timeval_current_ofs ( 0 , MESSAGING_BACKOFF ) ,
messaging_backoff_handler , rec ) ;
2004-10-18 02:28:26 +04:00
return NT_STATUS_OK ;
}
2004-10-17 14:04:49 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( rec ) ;
return status ;
}
2005-02-03 05:35:52 +03:00
rec - > fde = event_add_fd ( msg - > event . ev , rec , socket_get_fd ( rec - > sock ) ,
EVENT_FD_WRITE , messaging_send_handler , rec ) ;
2004-10-17 17:33:03 +04:00
2004-10-17 14:04:49 +04:00
return NT_STATUS_OK ;
}
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-01-30 03:54:57 +03: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-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 */
2004-10-29 12:38:59 +04:00
msg - > path = smbd_tmp_path ( msg , " messaging " ) ;
2004-10-17 14:04:49 +04:00
mkdir ( msg - > path , 0700 ) ;
talloc_free ( msg - > path ) ;
msg - > server_id = server_id ;
msg - > dispatch = NULL ;
msg - > path = messaging_path ( msg , server_id ) ;
status = socket_create ( " unix " , SOCKET_TYPE_STREAM , & msg - > sock , 0 ) ;
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-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 ) ,
EVENT_FD_READ , messaging_listen_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 ;
}