2014-02-24 16:23:49 +04:00
/*
* Unix SMB / CIFS implementation .
* Samba internal messaging functions
* Copyright ( C ) 2013 by Volker Lendecke
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "lib/util/data_blob.h"
# include "lib/util/debug.h"
# include "lib/unix_msg/unix_msg.h"
# include "system/filesys.h"
2014-07-17 15:57:51 +04:00
# include "lib/messages_dgm.h"
2014-02-24 16:23:49 +04:00
# include "lib/param/param.h"
# include "poll_funcs/poll_funcs_tevent.h"
# include "unix_msg/unix_msg.h"
2014-09-10 11:58:00 +04:00
struct sun_path_buf {
/*
* This will carry enough for a socket path
*/
char buf [ sizeof ( struct sockaddr_un ) ] ;
} ;
2014-02-24 16:23:49 +04:00
struct messaging_dgm_context {
2014-07-25 15:03:11 +04:00
pid_t pid ;
2014-05-05 10:45:52 +04:00
struct poll_funcs * msg_callbacks ;
void * tevent_handle ;
2014-02-24 16:23:49 +04:00
struct unix_msg_ctx * dgm_ctx ;
2014-09-10 12:12:23 +04:00
struct sun_path_buf cache_dir ;
2014-02-24 16:23:49 +04:00
int lockfile_fd ;
2014-07-17 13:44:41 +04:00
2014-07-27 14:29:26 +04:00
void ( * recv_cb ) ( const uint8_t * msg ,
size_t msg_len ,
2014-07-17 13:44:41 +04:00
void * private_data ) ;
void * recv_cb_private_data ;
2014-07-20 16:51:47 +04:00
bool * have_dgm_context ;
2014-02-24 16:23:49 +04:00
} ;
2014-09-10 18:13:18 +04:00
static struct messaging_dgm_context * global_dgm_context ;
2014-02-24 16:23:49 +04:00
static void messaging_dgm_recv ( struct unix_msg_ctx * ctx ,
uint8_t * msg , size_t msg_len ,
void * private_data ) ;
2014-09-10 11:58:00 +04:00
static int messaging_dgm_lockfile_name ( struct sun_path_buf * buf ,
const char * cache_dir ,
pid_t pid )
2014-08-02 15:26:44 +04:00
{
2014-09-10 11:58:00 +04:00
int ret ;
ret = snprintf ( buf - > buf , sizeof ( buf - > buf ) , " %s/lck/%u " , cache_dir ,
( unsigned ) pid ) ;
if ( ret > = sizeof ( buf - > buf ) ) {
return ENAMETOOLONG ;
}
return 0 ;
2014-08-02 15:26:44 +04:00
}
2014-02-24 16:23:49 +04:00
static int messaging_dgm_context_destructor ( struct messaging_dgm_context * c ) ;
2014-09-10 12:00:05 +04:00
static int messaging_dgm_lockfile_create ( const char * cache_dir ,
2014-07-17 15:47:32 +04:00
uid_t dir_owner , pid_t pid ,
2014-02-24 16:23:49 +04:00
int * plockfile_fd , uint64_t unique )
{
2014-06-03 00:29:44 +04:00
fstring buf ;
2014-09-10 11:58:00 +04:00
struct sun_path_buf dir ;
struct sun_path_buf lockfile_name ;
2014-02-24 16:23:49 +04:00
int lockfile_fd ;
2014-07-25 14:42:19 +04:00
struct flock lck ;
2014-02-24 16:23:49 +04:00
int unique_len , ret ;
ssize_t written ;
bool ok ;
2014-09-10 11:58:00 +04:00
ret = messaging_dgm_lockfile_name ( & lockfile_name , cache_dir , pid ) ;
if ( ret ! = 0 ) {
return ret ;
2014-02-24 16:23:49 +04:00
}
2014-09-10 11:58:00 +04:00
/* shorter than lockfile_name, can't overflow */
snprintf ( dir . buf , sizeof ( dir . buf ) , " %s/lck " , cache_dir ) ;
ok = directory_create_or_exist_strict ( dir . buf , dir_owner , 0755 ) ;
2014-02-24 16:23:49 +04:00
if ( ! ok ) {
ret = errno ;
DEBUG ( 1 , ( " %s: Could not create lock directory: %s \n " ,
__func__ , strerror ( ret ) ) ) ;
return ret ;
}
/* no O_EXCL, existence check is via the fcntl lock */
2014-09-10 11:58:00 +04:00
lockfile_fd = open ( lockfile_name . buf , O_NONBLOCK | O_CREAT | O_WRONLY ,
0644 ) ;
2014-02-24 16:23:49 +04:00
if ( lockfile_fd = = - 1 ) {
ret = errno ;
DEBUG ( 1 , ( " %s: open failed: %s \n " , __func__ , strerror ( errno ) ) ) ;
2014-09-10 11:58:00 +04:00
return ret ;
2014-02-24 16:23:49 +04:00
}
2014-07-25 14:42:19 +04:00
lck = ( struct flock ) {
. l_type = F_WRLCK ,
. l_whence = SEEK_SET
} ;
2014-02-24 16:23:49 +04:00
ret = fcntl ( lockfile_fd , F_SETLK , & lck ) ;
if ( ret = = - 1 ) {
ret = errno ;
DEBUG ( 1 , ( " %s: fcntl failed: %s \n " , __func__ , strerror ( ret ) ) ) ;
goto fail_close ;
}
2014-08-02 15:57:43 +04:00
unique_len = snprintf ( buf , sizeof ( buf ) , " %ju \n " , ( uintmax_t ) unique ) ;
2014-02-24 16:23:49 +04:00
/* shorten a potentially preexisting file */
ret = ftruncate ( lockfile_fd , unique_len ) ;
if ( ret = = - 1 ) {
ret = errno ;
DEBUG ( 1 , ( " %s: ftruncate failed: %s \n " , __func__ ,
strerror ( ret ) ) ) ;
goto fail_unlink ;
}
written = write ( lockfile_fd , buf , unique_len ) ;
if ( written ! = unique_len ) {
ret = errno ;
DEBUG ( 1 , ( " %s: write failed: %s \n " , __func__ , strerror ( ret ) ) ) ;
goto fail_unlink ;
}
* plockfile_fd = lockfile_fd ;
return 0 ;
fail_unlink :
2014-09-10 11:58:00 +04:00
unlink ( lockfile_name . buf ) ;
2014-02-24 16:23:49 +04:00
fail_close :
close ( lockfile_fd ) ;
return ret ;
}
2014-09-10 12:00:05 +04:00
static int messaging_dgm_lockfile_remove ( const char * cache_dir , pid_t pid )
2014-02-24 16:23:49 +04:00
{
2014-09-10 11:58:00 +04:00
struct sun_path_buf lockfile_name ;
2014-02-24 16:23:49 +04:00
int ret ;
2014-09-10 11:58:00 +04:00
ret = messaging_dgm_lockfile_name ( & lockfile_name , cache_dir , pid ) ;
if ( ret ! = 0 ) {
return ret ;
2014-02-24 16:23:49 +04:00
}
2014-09-10 11:58:00 +04:00
ret = unlink ( lockfile_name . buf ) ;
2014-02-24 16:23:49 +04:00
if ( ret = = - 1 ) {
ret = errno ;
2014-05-30 19:31:33 +04:00
DEBUG ( 10 , ( " %s: unlink(%s) failed: %s \n " , __func__ ,
2014-09-10 11:58:00 +04:00
lockfile_name . buf , strerror ( ret ) ) ) ;
2014-02-24 16:23:49 +04:00
}
2014-05-30 19:31:33 +04:00
2014-02-24 16:23:49 +04:00
return ret ;
}
2014-09-10 18:13:18 +04:00
int messaging_dgm_init ( struct tevent_context * ev ,
2014-07-17 13:56:39 +04:00
struct server_id pid ,
2014-07-17 15:38:36 +04:00
const char * cache_dir ,
2014-07-17 15:47:32 +04:00
uid_t dir_owner ,
2014-07-27 14:29:26 +04:00
void ( * recv_cb ) ( const uint8_t * msg ,
2014-07-17 13:44:41 +04:00
size_t msg_len ,
void * private_data ) ,
2014-09-10 18:13:18 +04:00
void * recv_cb_private_data )
2014-02-24 16:23:49 +04:00
{
struct messaging_dgm_context * ctx ;
int ret ;
bool ok ;
2014-09-10 12:12:23 +04:00
struct sun_path_buf socket_dir ;
2014-06-01 22:57:21 +04:00
struct sockaddr_un socket_address ;
size_t sockname_len ;
2014-02-24 16:23:49 +04:00
uint64_t cookie ;
2014-07-20 16:51:47 +04:00
static bool have_dgm_context = false ;
if ( have_dgm_context ) {
return EEXIST ;
}
2014-02-24 16:23:49 +04:00
2014-09-10 18:13:18 +04:00
ctx = talloc_zero ( NULL , struct messaging_dgm_context ) ;
2014-02-24 16:23:49 +04:00
if ( ctx = = NULL ) {
goto fail_nomem ;
}
2014-07-25 15:03:11 +04:00
ctx - > pid = pid . pid ;
2014-07-17 13:44:41 +04:00
ctx - > recv_cb = recv_cb ;
ctx - > recv_cb_private_data = recv_cb_private_data ;
2014-09-10 12:12:23 +04:00
ret = snprintf ( socket_dir . buf , sizeof ( socket_dir . buf ) ,
" %s/msg " , cache_dir ) ;
if ( ret > = sizeof ( socket_dir . buf ) ) {
TALLOC_FREE ( ctx ) ;
return ENAMETOOLONG ;
2014-02-24 16:23:49 +04:00
}
2014-06-01 22:57:21 +04:00
2014-09-10 12:12:23 +04:00
/* shorter than socket_dir, can't overflow */
strlcpy ( ctx - > cache_dir . buf , cache_dir , sizeof ( ctx - > cache_dir . buf ) ) ;
2014-06-01 22:57:21 +04:00
socket_address = ( struct sockaddr_un ) { . sun_family = AF_UNIX } ;
sockname_len = snprintf ( socket_address . sun_path ,
sizeof ( socket_address . sun_path ) ,
2014-09-10 12:12:23 +04:00
" %s/%u " , socket_dir . buf , ( unsigned ) pid . pid ) ;
2014-06-01 22:57:21 +04:00
if ( sockname_len > = sizeof ( socket_address . sun_path ) ) {
2014-07-17 15:23:46 +04:00
TALLOC_FREE ( ctx ) ;
2014-06-10 19:21:10 +04:00
return ENAMETOOLONG ;
2014-02-24 16:23:49 +04:00
}
2014-09-10 12:00:05 +04:00
ret = messaging_dgm_lockfile_create ( cache_dir , dir_owner , pid . pid ,
2014-02-24 16:23:49 +04:00
& ctx - > lockfile_fd , pid . unique_id ) ;
if ( ret ! = 0 ) {
DEBUG ( 1 , ( " %s: messaging_dgm_create_lockfile failed: %s \n " ,
__func__ , strerror ( ret ) ) ) ;
2014-07-17 15:23:46 +04:00
TALLOC_FREE ( ctx ) ;
2014-06-10 19:21:10 +04:00
return ret ;
2014-02-24 16:23:49 +04:00
}
2014-05-05 10:45:52 +04:00
ctx - > msg_callbacks = poll_funcs_init_tevent ( ctx ) ;
if ( ctx - > msg_callbacks = = NULL ) {
2014-06-10 18:57:05 +04:00
goto fail_nomem ;
2014-05-05 10:45:52 +04:00
}
ctx - > tevent_handle = poll_funcs_tevent_register (
2014-07-17 13:52:28 +04:00
ctx , ctx - > msg_callbacks , ev ) ;
2014-05-05 10:45:52 +04:00
if ( ctx - > tevent_handle = = NULL ) {
2014-06-10 18:57:05 +04:00
goto fail_nomem ;
2014-05-05 10:45:52 +04:00
}
2014-02-24 16:23:49 +04:00
2014-09-10 12:12:23 +04:00
ok = directory_create_or_exist_strict ( socket_dir . buf , dir_owner , 0700 ) ;
2014-02-24 16:23:49 +04:00
if ( ! ok ) {
DEBUG ( 1 , ( " Could not create socket directory \n " ) ) ;
2014-07-17 15:23:46 +04:00
TALLOC_FREE ( ctx ) ;
2014-06-10 19:21:10 +04:00
return EACCES ;
2014-02-24 16:23:49 +04:00
}
2014-06-01 22:57:21 +04:00
unlink ( socket_address . sun_path ) ;
2014-02-24 16:23:49 +04:00
generate_random_buffer ( ( uint8_t * ) & cookie , sizeof ( cookie ) ) ;
2014-06-01 22:57:21 +04:00
ret = unix_msg_init ( & socket_address , ctx - > msg_callbacks , 1024 , cookie ,
2014-02-24 16:23:49 +04:00
messaging_dgm_recv , ctx , & ctx - > dgm_ctx ) ;
if ( ret ! = 0 ) {
DEBUG ( 1 , ( " unix_msg_init failed: %s \n " , strerror ( ret ) ) ) ;
2014-07-17 15:23:46 +04:00
TALLOC_FREE ( ctx ) ;
2014-06-10 19:21:10 +04:00
return ret ;
2014-02-24 16:23:49 +04:00
}
talloc_set_destructor ( ctx , messaging_dgm_context_destructor ) ;
2014-07-20 16:51:47 +04:00
ctx - > have_dgm_context = & have_dgm_context ;
2014-09-10 18:13:18 +04:00
global_dgm_context = ctx ;
2014-06-10 19:21:10 +04:00
return 0 ;
2014-02-24 16:23:49 +04:00
fail_nomem :
2014-07-17 15:23:46 +04:00
TALLOC_FREE ( ctx ) ;
2014-06-10 19:21:10 +04:00
return ENOMEM ;
2014-02-24 16:23:49 +04:00
}
static int messaging_dgm_context_destructor ( struct messaging_dgm_context * c )
{
/*
* First delete the socket to avoid races . The lockfile is the
* indicator that we ' re still around .
*/
unix_msg_free ( c - > dgm_ctx ) ;
2014-07-25 15:03:11 +04:00
if ( getpid ( ) = = c - > pid ) {
2014-09-10 12:12:23 +04:00
( void ) messaging_dgm_lockfile_remove ( c - > cache_dir . buf , c - > pid ) ;
2014-02-24 16:23:49 +04:00
}
close ( c - > lockfile_fd ) ;
2014-07-20 16:51:47 +04:00
if ( c - > have_dgm_context ! = NULL ) {
* c - > have_dgm_context = false ;
}
2014-02-24 16:23:49 +04:00
return 0 ;
}
2014-09-10 18:13:18 +04:00
void messaging_dgm_destroy ( void )
{
TALLOC_FREE ( global_dgm_context ) ;
}
int messaging_dgm_send ( pid_t pid , const struct iovec * iov , int iovlen )
2014-02-24 16:23:49 +04:00
{
2014-09-10 18:13:18 +04:00
struct messaging_dgm_context * ctx = global_dgm_context ;
2014-06-01 22:57:21 +04:00
struct sockaddr_un dst ;
ssize_t dst_pathlen ;
2014-02-24 16:23:49 +04:00
int ret ;
2014-09-10 18:13:18 +04:00
if ( ctx = = NULL ) {
return ENOTCONN ;
}
2014-06-01 22:57:21 +04:00
dst = ( struct sockaddr_un ) { . sun_family = AF_UNIX } ;
2014-02-24 16:23:49 +04:00
2014-06-01 22:57:21 +04:00
dst_pathlen = snprintf ( dst . sun_path , sizeof ( dst . sun_path ) ,
2014-09-10 12:12:23 +04:00
" %s/msg/%u " , ctx - > cache_dir . buf , ( unsigned ) pid ) ;
2014-06-01 22:57:21 +04:00
if ( dst_pathlen > = sizeof ( dst . sun_path ) ) {
2014-06-04 18:36:57 +04:00
return ENAMETOOLONG ;
2014-02-24 16:23:49 +04:00
}
2014-07-27 14:29:26 +04:00
DEBUG ( 10 , ( " %s: Sending message to %u \n " , __func__ , ( unsigned ) pid ) ) ;
2014-02-24 16:23:49 +04:00
2014-07-27 14:29:26 +04:00
ret = unix_msg_send ( ctx - > dgm_ctx , & dst , iov , iovlen ) ;
2014-02-24 16:23:49 +04:00
2014-06-04 18:36:57 +04:00
return ret ;
2014-02-24 16:23:49 +04:00
}
static void messaging_dgm_recv ( struct unix_msg_ctx * ctx ,
uint8_t * msg , size_t msg_len ,
void * private_data )
{
struct messaging_dgm_context * dgm_ctx = talloc_get_type_abort (
private_data , struct messaging_dgm_context ) ;
2014-07-27 14:29:26 +04:00
dgm_ctx - > recv_cb ( msg , msg_len , dgm_ctx - > recv_cb_private_data ) ;
2014-02-24 16:23:49 +04:00
}
2014-09-10 18:13:18 +04:00
int messaging_dgm_cleanup ( pid_t pid )
2014-02-24 16:23:49 +04:00
{
2014-09-10 18:13:18 +04:00
struct messaging_dgm_context * ctx = global_dgm_context ;
2014-09-10 11:58:00 +04:00
struct sun_path_buf lockfile_name , socket_name ;
2014-02-24 16:23:49 +04:00
int fd , ret ;
struct flock lck = { } ;
2014-09-10 18:13:18 +04:00
if ( ctx = = NULL ) {
return ENOTCONN ;
}
2014-09-10 12:12:23 +04:00
ret = messaging_dgm_lockfile_name ( & lockfile_name , ctx - > cache_dir . buf ,
pid ) ;
2014-09-10 11:58:00 +04:00
if ( ret ! = 0 ) {
return ret ;
2014-02-24 16:23:49 +04:00
}
2014-09-10 11:58:00 +04:00
/* same length as lockfile_name, can't overflow */
snprintf ( socket_name . buf , sizeof ( socket_name . buf ) , " %s/msg/%u " ,
2014-09-10 12:12:23 +04:00
ctx - > cache_dir . buf , ( unsigned ) pid ) ;
2014-09-10 11:58:00 +04:00
fd = open ( lockfile_name . buf , O_NONBLOCK | O_WRONLY , 0 ) ;
2014-02-24 16:23:49 +04:00
if ( fd = = - 1 ) {
2014-06-04 18:42:46 +04:00
ret = errno ;
2014-08-18 15:58:05 +04:00
if ( ret ! = ENOENT ) {
DEBUG ( 10 , ( " %s: open(%s) failed: %s \n " , __func__ ,
2014-09-10 11:58:00 +04:00
lockfile_name . buf , strerror ( ret ) ) ) ;
2014-08-18 15:58:05 +04:00
}
2014-06-04 18:42:46 +04:00
return ret ;
2014-02-24 16:23:49 +04:00
}
lck . l_type = F_WRLCK ;
lck . l_whence = SEEK_SET ;
lck . l_start = 0 ;
lck . l_len = 0 ;
ret = fcntl ( fd , F_SETLK , & lck ) ;
if ( ret ! = 0 ) {
2014-06-04 18:42:46 +04:00
ret = errno ;
2014-02-24 16:23:49 +04:00
DEBUG ( 10 , ( " %s: Could not get lock: %s \n " , __func__ ,
2014-06-04 18:42:46 +04:00
strerror ( ret ) ) ) ;
2014-02-24 16:23:49 +04:00
close ( fd ) ;
2014-06-04 18:42:46 +04:00
return ret ;
2014-02-24 16:23:49 +04:00
}
2014-09-10 11:58:00 +04:00
( void ) unlink ( socket_name . buf ) ;
( void ) unlink ( lockfile_name . buf ) ;
2014-02-24 16:23:49 +04:00
( void ) close ( fd ) ;
2014-06-04 18:42:46 +04:00
return 0 ;
2014-02-24 16:23:49 +04:00
}
2014-04-11 00:07:11 +04:00
2014-09-10 18:13:18 +04:00
int messaging_dgm_wipe ( void )
2014-04-11 00:07:11 +04:00
{
2014-09-10 18:13:18 +04:00
struct messaging_dgm_context * ctx = global_dgm_context ;
2014-09-10 11:58:00 +04:00
struct sun_path_buf msgdir_name ;
2014-04-11 00:07:11 +04:00
DIR * msgdir ;
struct dirent * dp ;
pid_t our_pid = getpid ( ) ;
2014-06-04 18:47:05 +04:00
int ret ;
2014-04-11 00:07:11 +04:00
2014-09-10 18:13:18 +04:00
if ( ctx = = NULL ) {
return ENOTCONN ;
}
2014-04-11 00:07:11 +04:00
/*
* We scan the socket directory and not the lock directory . Otherwise
* we would race against messaging_dgm_lockfile_create ' s open ( O_CREAT )
* and fcntl ( SETLK ) .
*/
2014-09-10 11:58:00 +04:00
ret = snprintf ( msgdir_name . buf , sizeof ( msgdir_name . buf ) ,
2014-09-10 12:12:23 +04:00
" %s/msg " , ctx - > cache_dir . buf ) ;
2014-09-10 11:58:00 +04:00
if ( ret > = sizeof ( msgdir_name . buf ) ) {
return ENAMETOOLONG ;
2014-04-11 00:07:11 +04:00
}
2014-09-10 11:58:00 +04:00
msgdir = opendir ( msgdir_name . buf ) ;
2014-04-11 00:07:11 +04:00
if ( msgdir = = NULL ) {
2014-06-04 18:47:05 +04:00
ret = errno ;
return ret ;
2014-04-11 00:07:11 +04:00
}
while ( ( dp = readdir ( msgdir ) ) ! = NULL ) {
unsigned long pid ;
pid = strtoul ( dp - > d_name , NULL , 10 ) ;
if ( pid = = 0 ) {
/*
* . and . . and other malformed entries
*/
continue ;
}
if ( pid = = our_pid ) {
/*
* fcntl ( F_GETLK ) will succeed for ourselves , we hold
* that lock ourselves .
*/
continue ;
}
2014-09-10 18:13:18 +04:00
ret = messaging_dgm_cleanup ( pid ) ;
2014-04-11 00:07:11 +04:00
DEBUG ( 10 , ( " messaging_dgm_cleanup(%lu) returned %s \n " ,
2014-06-04 18:42:46 +04:00
pid , ret ? strerror ( ret ) : " ok " ) ) ;
2014-04-11 00:07:11 +04:00
}
closedir ( msgdir ) ;
2014-06-04 18:47:05 +04:00
return 0 ;
2014-04-11 00:07:11 +04:00
}
2014-05-06 11:11:17 +04:00
void * messaging_dgm_register_tevent_context ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev )
{
2014-09-10 18:13:18 +04:00
struct messaging_dgm_context * ctx = global_dgm_context ;
if ( ctx = = NULL ) {
return NULL ;
}
2014-05-06 11:11:17 +04:00
return poll_funcs_tevent_register ( mem_ctx , ctx - > msg_callbacks , ev ) ;
}