2005-04-28 02:32:00 +04:00
/*
* Copyright ( C ) 2005 Red Hat , Inc . All rights reserved .
*
* This file is part of the device - mapper userspace tools .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/*
* dmeventd - dm event daemon to monitor active mapped devices
*
* Author - Heinz Mauelshagen , Red Hat GmbH .
*/
# include "libdevmapper.h"
# include "libdm-event.h"
# include "list.h"
# include <dlfcn.h>
# include <errno.h>
# include <fcntl.h>
# include <libdevmapper.h>
# include <libgen.h>
# include <malloc.h>
# include <pthread.h>
# include <signal.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/file.h>
# include <sys/mman.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <unistd.h>
2005-05-04 01:29:13 +04:00
# include "libmultilog.h"
2005-04-28 02:32:00 +04:00
# define dbg_malloc(x...) malloc(x)
# define dbg_strdup(x...) strdup(x)
# define dbg_free(x...) free(x)
/* List (un)link macros. */
# define LINK(x, head) list_add(&(x)->list, head)
# define LINK_DSO(dso) LINK(dso, &dso_registry)
# define LINK_THREAD(thread) LINK(thread, &thread_registry)
# define UNLINK(x) list_del(&(x)->list)
# define UNLINK_DSO(x) UNLINK(x)
# define UNLINK_THREAD(x) UNLINK(x)
/* Global mutex for list accesses. */
static pthread_mutex_t mutex ;
/* Data kept about a DSO. */
struct dso_data {
struct list list ;
char * dso_name ; /* DSO name (eg, "evms", "dmraid", "lvm2"). */
void * dso_handle ; /* Opaque handle as returned from dlopen(). */
unsigned int ref_count ; /* Library reference count. */
/*
* Event processing .
*
* The DSO can do whatever appropriate steps if an event happens
* such as changing the mapping in case a mirror fails , update
* the application metadata etc .
*/
void ( * process_event ) ( char * device , enum event_type event ) ;
/*
* Device registration .
*
* When an application registers a device for an event , the DSO
* can carry out appropriate steps so that a later call to
* the process_event ( ) function is sane ( eg , read metadata
* and activate a mapping ) .
*/
int ( * register_device ) ( char * device ) ;
/*
* Device unregistration .
*
* In case all devices of a mapping ( eg , RAID10 ) are unregistered
* for events , the DSO can recognize this and carry out appropriate
* steps ( eg , deactivate mapping , metadata update ) .
*/
int ( * unregister_device ) ( char * device ) ;
} ;
static LIST_INIT ( dso_registry ) ;
/* Structure to keep parsed register variables from client message. */
2005-04-28 18:02:30 +04:00
struct message_data {
2005-04-28 02:32:00 +04:00
char * dso_name ; /* Name of DSO. */
char * device_path ; /* Mapped device path. */
union {
char * str ; /* Events string as fetched from message. */
enum event_type field ; /* Events bitfield. */
} events ;
2005-04-28 18:02:30 +04:00
struct daemon_message * msg ; /* Pointer to message buffer. */
2005-04-28 02:32:00 +04:00
} ;
/*
* Housekeeping of thread + device states .
*
* One thread per mapped device which can block on it until an event
* occurs and the event processing function of the DSO gets called .
*/
struct thread_status {
struct list list ;
2005-05-02 15:02:19 +04:00
pthread_t thread ;
2005-04-28 02:32:00 +04:00
struct dso_data * dso_data ; /* DSO this thread accesses. */
char * device_path ; /* Mapped device path. */
enum event_type events ; /* bitfield for event filter. */
enum event_type current_events ; /* bitfield for occured events. */
enum event_type processed_events ; /* bitfield for processed events. */
} ;
static LIST_INIT ( thread_registry ) ;
/* Allocate/free the status structure for a monitoring thread. */
2005-04-28 18:02:30 +04:00
static struct thread_status * alloc_thread_status ( struct message_data * data ,
2005-04-28 02:32:00 +04:00
struct dso_data * dso_data )
{
struct thread_status * ret = ( typeof ( ret ) ) dbg_malloc ( sizeof ( * ret ) ) ;
if ( ret ) {
memset ( ret , 0 , sizeof ( * ret ) ) ;
if ( ( ret - > device_path = dbg_strdup ( data - > device_path ) ) ) {
ret - > dso_data = dso_data ;
ret - > events = data - > events . field ;
} else {
dbg_free ( ret ) ;
ret = NULL ;
}
}
return ret ;
}
static void free_thread_status ( struct thread_status * thread )
{
dbg_free ( thread - > device_path ) ;
dbg_free ( thread ) ;
}
/* Allocate/free DSO data. */
2005-04-28 18:02:30 +04:00
static struct dso_data * alloc_dso_data ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
struct dso_data * ret = ( typeof ( ret ) ) dbg_malloc ( sizeof ( * ret ) ) ;
if ( ret ) {
memset ( ret , 0 , sizeof ( * ret ) ) ;
if ( ! ( ret - > dso_name = dbg_strdup ( data - > dso_name ) ) ) {
dbg_free ( ret ) ;
ret = NULL ;
}
}
return ret ;
}
static void free_dso_data ( struct dso_data * data )
{
dbg_free ( data - > dso_name ) ;
dbg_free ( data ) ;
}
/* Fetch a string off src and duplicate it into *dest. */
2005-04-28 18:02:30 +04:00
/* FIXME: move to separate module to share with the client lib. */
2005-04-28 02:32:00 +04:00
static const char delimiter = ' ' ;
static char * fetch_string ( char * * src )
{
2005-04-29 18:56:35 +04:00
char * p , * ret ;
2005-04-28 02:32:00 +04:00
2005-04-29 18:56:35 +04:00
if ( ( p = strchr ( * src , delimiter ) ) )
2005-04-28 02:32:00 +04:00
* p = 0 ;
2005-04-29 18:56:35 +04:00
if ( ( ret = strdup ( * src ) ) )
* src + = strlen ( ret ) + 1 ;
2005-04-28 02:32:00 +04:00
2005-04-29 18:56:35 +04:00
if ( p )
* p = delimiter ;
2005-04-28 02:32:00 +04:00
return ret ;
}
/* Free message memory. */
2005-04-28 18:02:30 +04:00
static void free_message ( struct message_data * message_data )
2005-04-28 02:32:00 +04:00
{
2005-04-28 18:02:30 +04:00
if ( message_data - > dso_name )
dbg_free ( message_data - > dso_name ) ;
2005-04-28 02:32:00 +04:00
2005-04-28 18:02:30 +04:00
if ( message_data - > device_path )
dbg_free ( message_data - > device_path ) ;
2005-04-28 02:32:00 +04:00
}
/* Parse a register message from the client. */
2005-04-28 18:02:30 +04:00
static int parse_message ( struct message_data * message_data )
2005-04-28 02:32:00 +04:00
{
2005-04-28 18:02:30 +04:00
char * p = message_data - > msg - > msg ;
2005-04-28 02:32:00 +04:00
2005-04-29 18:56:35 +04:00
log_print ( " %s: here \n " , __func__ ) ;
fflush ( stdout ) ;
2005-04-28 02:32:00 +04:00
/*
* Retrieve application identifier , mapped device
* path and events # string from message .
*/
2005-04-28 18:02:30 +04:00
if ( ( message_data - > dso_name = fetch_string ( & p ) ) & &
( message_data - > device_path = fetch_string ( & p ) ) & &
( message_data - > events . str = fetch_string ( & p ) ) ) {
2005-04-29 18:56:35 +04:00
log_print ( " %s: %s %s %s \n " , __func__ , message_data - > dso_name , message_data - > device_path , message_data - > events . str ) ;
2005-04-28 18:02:30 +04:00
if ( message_data - > events . str ) {
enum event_type i = atoi ( message_data - > events . str ) ;
/*
* Free string representaion of events .
* Not needed an more .
*/
dbg_free ( message_data - > events . str ) ;
message_data - > events . field = i ;
}
2005-04-28 02:32:00 +04:00
return 1 ;
}
2005-04-28 18:02:30 +04:00
free_message ( message_data ) ;
2005-04-28 02:32:00 +04:00
return 0 ;
} ;
/* Global mutex to lock access to lists et al. */
static int lock_mutex ( void )
{
return pthread_mutex_lock ( & mutex ) ;
}
static int unlock_mutex ( void )
{
return pthread_mutex_unlock ( & mutex ) ;
}
/* Store pid in pidfile. */
static int storepid ( int lf )
{
int len ;
char pid [ 8 ] ;
if ( ( len = snprintf ( pid , sizeof ( pid ) , " %u \n " , getpid ( ) ) ) < 0 )
return 0 ;
if ( len > sizeof ( pid ) )
len = sizeof ( pid ) ;
if ( write ( lf , pid , len ) ! = len )
return 0 ;
fsync ( lf ) ;
return 1 ;
}
/*
* create + flock file .
*
* Used to synchronize daemon startups .
*/
static int lf = - 1 ;
2005-04-29 17:41:25 +04:00
static char pidfile [ ] = " /var/run/dmeventd.pid " ;
2005-04-28 02:32:00 +04:00
/* Store pid in pidfile. */
static int lock ( void )
{
/* Already locked. */
if ( lf > - 1 )
return 1 ;
if ( ( lf = open ( pidfile , O_CREAT | O_RDWR , 0644 ) ) = = - 1 ) {
log_err ( " opening pid file \n " ) ;
return 0 ;
}
if ( flock ( lf , LOCK_EX | LOCK_NB ) = = - 1 ) {
log_err ( " lock pid file \n " ) ;
close ( lf ) ;
lf = - 1 ;
return 0 ;
}
return 1 ;
}
static void unlock ( void )
{
/* Not locked! */
if ( lf = = - 1 )
return ;
if ( flock ( lf , LOCK_UN ) )
log_err ( " flock unlock %s \n " , pidfile ) ;
if ( close ( lf ) )
log_err ( " close %s \n " , pidfile ) ;
lf = - 1 ;
}
/* Check, if a device exists. */
static int device_exists ( char * device )
{
int f ;
if ( ( f = open ( device , O_RDONLY ) ) = = - 1 )
return 0 ;
close ( f ) ;
return 1 ;
}
/*
* Find an existing thread for a device .
*
* Mutex must be hold when calling this .
*/
2005-04-28 18:02:30 +04:00
static struct thread_status * lookup_thread_status ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
struct thread_status * thread ;
list_iterate_items ( thread , & thread_registry ) {
if ( ! strcmp ( data - > device_path , thread - > device_path ) )
return thread ;
}
return NULL ;
}
/* Cleanup at exit. */
static void exit_dm_lib ( void )
{
dm_lib_release ( ) ;
dm_lib_exit ( ) ;
}
/* Derive error case from target parameter string. */
static int error_detected ( struct thread_status * thread , char * params )
{
size_t len ;
2005-04-28 18:02:30 +04:00
if ( ( len = strlen ( params ) ) & &
params [ len - 1 ] = = ' F ' ) {
2005-04-28 02:32:00 +04:00
thread - > current_events | = DEVICE_ERROR ;
return 1 ;
}
return 0 ;
}
/* Wait on a device until an event occurs. */
static int event_wait ( struct thread_status * thread )
{
int ret = 0 ;
2005-05-02 15:02:19 +04:00
void * next = NULL ;
2005-04-28 02:32:00 +04:00
char * params , * target_type ;
uint64_t start , length ;
struct dm_task * dmt ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_WAITEVENT ) ) )
return 0 ;
if ( ( ret = dm_task_set_name ( dmt , basename ( thread - > device_path ) ) ) & &
( ret = dm_task_set_event_nr ( dmt , 0 ) ) & &
( ret = dm_task_run ( dmt ) ) ) {
do {
/* Retrieve next target. */
params = NULL ;
next = dm_get_next_target ( dmt , next , & start , & length ,
& target_type , & params ) ;
log_print ( " %s: %s \n " , __func__ , params ) ;
2005-04-28 18:02:30 +04:00
if ( ( ret = error_detected ( thread , params ) ) )
2005-04-28 02:32:00 +04:00
break ;
} while ( next ) ;
}
dm_task_destroy ( dmt ) ;
return ret ;
}
/* Register a device with the DSO. */
2005-04-29 17:41:25 +04:00
static int do_register_device ( struct thread_status * thread )
2005-04-28 02:32:00 +04:00
{
return thread - > dso_data - > register_device ( thread - > device_path ) ;
}
/* Unregister a device with the DSO. */
2005-04-29 17:41:25 +04:00
static int do_unregister_device ( struct thread_status * thread )
2005-04-28 02:32:00 +04:00
{
return thread - > dso_data - > unregister_device ( thread - > device_path ) ;
}
/* Process an event the DSO. */
2005-04-29 17:41:25 +04:00
static void do_process_event ( struct thread_status * thread )
2005-04-28 02:32:00 +04:00
{
thread - > dso_data - > process_event ( thread - > device_path ,
thread - > current_events ) ;
}
/* Thread cleanup handler to unregister device. */
static void monitor_unregister ( void * arg )
{
struct thread_status * thread = arg ;
2005-04-29 17:41:25 +04:00
if ( ! do_unregister_device ( thread ) )
2005-04-28 02:32:00 +04:00
log_err ( " %s: %s unregister failed \n " , __func__ ,
thread - > device_path ) ;
}
/* Device monitoring thread. */
2005-04-28 18:02:30 +04:00
static void * monitor_thread ( void * arg )
2005-04-28 02:32:00 +04:00
{
struct thread_status * thread = arg ;
pthread_setcanceltype ( PTHREAD_CANCEL_DEFERRED , NULL ) ;
pthread_cleanup_push ( monitor_unregister , thread ) ;
/* Wait for comm_thread() to finish its task. */
lock_mutex ( ) ;
unlock_mutex ( ) ;
/* Loop forever awaiting/analyzing device events. */
while ( 1 ) {
thread - > current_events = 0 ;
if ( ! event_wait ( thread ) )
continue ;
/* REMOVEME: */
log_print ( " %s: cycle on %s \n " , __func__ , thread - > device_path ) ;
2005-04-28 18:02:30 +04:00
/*
* Check against filter .
*
* If there ' s current events delivered from event_wait ( ) AND
* the device got registered for those events AND
* those events haven ' t been processed yet , call
* the DSO ' s process_event ( ) handler .
*/
2005-04-28 02:32:00 +04:00
if ( thread - > events &
thread - > current_events &
~ thread - > processed_events ) {
2005-04-29 17:41:25 +04:00
do_process_event ( thread ) ;
2005-04-28 02:32:00 +04:00
thread - > processed_events | = thread - > current_events ;
}
}
pthread_cleanup_pop ( 0 ) ;
}
/* Create a device monitoring thread. */
/* FIXME: call this with mutex hold ? */
static int create_thread ( struct thread_status * thread )
{
return pthread_create ( & thread - > thread , NULL , monitor_thread , thread ) ;
}
static int terminate_thread ( struct thread_status * thread )
{
return pthread_cancel ( thread - > thread ) ;
}
/* DSO reference counting. */
static void lib_get ( struct dso_data * data )
{
data - > ref_count + + ;
}
static void lib_put ( struct dso_data * data )
{
if ( ! - - data - > ref_count ) {
dlclose ( data - > dso_handle ) ;
UNLINK_DSO ( data ) ;
free_dso_data ( data ) ;
}
}
/* Find DSO data. */
2005-04-28 18:02:30 +04:00
static struct dso_data * lookup_dso ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
struct dso_data * dso_data , * ret = NULL ;
lock_mutex ( ) ;
list_iterate_items ( dso_data , & dso_registry ) {
if ( ! strcmp ( data - > dso_name , dso_data - > dso_name ) ) {
2005-04-28 18:02:30 +04:00
lib_get ( dso_data ) ;
2005-04-28 02:32:00 +04:00
ret = dso_data ;
break ;
}
}
unlock_mutex ( ) ;
return ret ;
}
/* Lookup DSO symbols we need. */
static int lookup_symbol ( void * dl , struct dso_data * data ,
void * * symbol , const char * name )
{
if ( ( * symbol = dlsym ( dl , name ) ) )
return 1 ;
log_err ( " looking up %s symbol in %s \n " , name , data - > dso_name ) ;
return 0 ;
}
static int lookup_symbols ( void * dl , struct dso_data * data )
{
return lookup_symbol ( dl , data , ( void * ) & data - > process_event ,
" process_event " ) & &
lookup_symbol ( dl , data , ( void * ) & data - > register_device ,
" register_device " ) & &
lookup_symbol ( dl , data , ( void * ) & data - > unregister_device ,
" unregister_device " ) ;
}
/* Create a DSO file name based on its name. */
2005-04-28 18:02:30 +04:00
static char * create_dso_file_name ( char * dso_name )
2005-04-28 02:32:00 +04:00
{
char * ret ;
2005-04-29 17:41:25 +04:00
static char prefix [ ] = " libdmeventd " ;
static char suffix [ ] = " .so " ;
2005-04-28 02:32:00 +04:00
if ( ( ret = dbg_malloc ( strlen ( prefix ) +
strlen ( dso_name ) +
strlen ( suffix ) + 1 ) ) )
sprintf ( ret , " %s%s%s " , prefix , dso_name , suffix ) ;
2005-04-29 17:41:25 +04:00
log_print ( " %s: \" %s \" \n " , __func__ , ret ) ;
2005-04-28 02:32:00 +04:00
return ret ;
}
/* Load an application specific DSO. */
2005-04-28 18:02:30 +04:00
static struct dso_data * load_dso ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
void * dl ;
struct dso_data * ret = NULL ;
char * dso_file ;
2005-04-29 17:41:25 +04:00
log_print ( " %s: \" %s \" \n " , __func__ , data - > dso_name ) ;
2005-04-28 18:02:30 +04:00
if ( ! ( dso_file = create_dso_file_name ( data - > dso_name ) ) )
2005-04-28 02:32:00 +04:00
return NULL ;
if ( ! ( dl = dlopen ( dso_file , RTLD_NOW ) ) )
goto free_dso_file ;
if ( ! ( ret = alloc_dso_data ( data ) ) )
goto close ;
if ( ! ( lookup_symbols ( dl , ret ) ) )
goto free_all ;
/*
* Keep handle to close the library once
* we ' ve got no references to it any more .
*/
ret - > dso_handle = dl ;
lib_get ( ret ) ;
lock_mutex ( ) ;
LINK_DSO ( ret ) ;
unlock_mutex ( ) ;
goto free_dso_file ;
free_all :
free_dso_data ( ret ) ;
close :
dlclose ( dl ) ;
free_dso_file :
dbg_free ( dso_file ) ;
return ret ;
}
/*
* Register for an event .
*
* Only one caller at a time here , because we use a FIFO and lock
* it against multiple accesses .
*/
2005-04-28 18:02:30 +04:00
static int register_for_event ( struct message_data * message_data )
2005-04-28 02:32:00 +04:00
{
int ret = 0 ;
struct thread_status * thread , * thread_new ;
struct dso_data * dso_data ;
2005-04-28 18:02:30 +04:00
if ( ! device_exists ( message_data - > device_path ) ) {
2005-04-28 02:32:00 +04:00
stack ;
ret = - ENODEV ;
goto out ;
}
2005-04-29 18:56:35 +04:00
log_print ( " %s \n " , __func__ ) ;
fflush ( stdout ) ;
2005-04-28 02:32:00 +04:00
2005-04-28 18:02:30 +04:00
if ( ! ( dso_data = lookup_dso ( message_data ) ) & &
! ( dso_data = load_dso ( message_data ) ) ) {
2005-04-28 02:32:00 +04:00
stack ;
ret = - ELIBACC ;
goto out ;
}
/* Preallocate thread status struct to avoid deadlock. */
2005-04-28 18:02:30 +04:00
if ( ! ( thread_new = alloc_thread_status ( message_data , dso_data ) ) ) {
2005-04-28 02:32:00 +04:00
stack ;
ret = - ENOMEM ;
goto out ;
}
2005-04-29 17:41:25 +04:00
if ( ! ( ret = do_register_device ( thread_new ) ) )
2005-04-28 02:32:00 +04:00
goto out ;
lock_mutex ( ) ;
2005-04-28 18:02:30 +04:00
if ( ( thread = lookup_thread_status ( message_data ) ) )
2005-04-28 02:32:00 +04:00
ret = - EPERM ;
else {
thread = thread_new ;
thread_new = NULL ;
/* Try to create the monitoring thread for this device. */
if ( ( ret = - create_thread ( thread ) ) ) {
unlock_mutex ( ) ;
free_thread_status ( thread ) ;
goto out ;
} else
LINK_THREAD ( thread ) ;
}
/* Or event # into events bitfield. */
2005-04-28 18:02:30 +04:00
thread - > events | = message_data - > events . field ;
2005-04-28 02:32:00 +04:00
unlock_mutex ( ) ;
/*
* Deallocate thread status after releasing
* the lock in case we haven ' t used it .
*/
if ( thread_new )
free_thread_status ( thread_new ) ;
out :
2005-04-28 18:02:30 +04:00
free_message ( message_data ) ;
2005-04-28 02:32:00 +04:00
return ret ;
}
/*
* Unregister for an event .
*
* Only one caller at a time here as with register_for_event ( ) .
*/
2005-04-28 18:02:30 +04:00
static int unregister_for_event ( struct message_data * message_data )
2005-04-28 02:32:00 +04:00
{
int ret = 0 ;
struct thread_status * thread ;
/*
* Clear event in bitfield and deactivate
* monitoring thread in case bitfield is 0.
*/
lock_mutex ( ) ;
2005-04-28 18:02:30 +04:00
if ( ! ( thread = lookup_thread_status ( message_data ) ) ) {
2005-04-28 02:32:00 +04:00
unlock_mutex ( ) ;
ret = - ESRCH ;
goto out ;
}
2005-04-28 18:02:30 +04:00
thread - > events & = ~ message_data - > events . field ;
2005-04-28 02:32:00 +04:00
/*
* In case there ' s no events to monitor on this
* device - > unlink and terminate its monitoring thread .
*/
if ( ! thread - > events )
UNLINK_THREAD ( thread ) ;
unlock_mutex ( ) ;
if ( ! thread - > events ) {
/* turn codes negative */
if ( ( ret = - terminate_thread ( thread ) ) ) {
stack ;
} else {
pthread_join ( thread - > thread , NULL ) ;
lib_put ( thread - > dso_data ) ;
free_thread_status ( thread ) ;
lock_mutex ( ) ;
if ( list_empty ( & thread_registry ) )
exit_dm_lib ( ) ;
unlock_mutex ( ) ;
}
}
out :
2005-04-28 18:02:30 +04:00
free_message ( message_data ) ;
2005-04-28 02:32:00 +04:00
return ret ;
}
2005-04-28 18:02:30 +04:00
/*
* Get next registered device .
*
* Only one caller at a time here as with register_for_event ( ) .
*/
2005-04-29 17:41:25 +04:00
static int registered_device ( struct message_data * message_data ,
struct thread_status * thread )
2005-04-28 18:02:30 +04:00
{
struct daemon_message * msg = message_data - > msg ;
snprintf ( msg - > msg , sizeof ( msg - > msg ) , " %s %s %u " ,
2005-04-29 17:41:25 +04:00
thread - > dso_data - > dso_name , thread - > device_path ,
thread - > events ) ;
unlock_mutex ( ) ;
2005-04-28 18:02:30 +04:00
return 0 ;
}
2005-04-29 17:41:25 +04:00
static int get_registered_device ( struct message_data * message_data , int next )
2005-04-28 18:02:30 +04:00
{
2005-04-29 17:41:25 +04:00
int dev , dso , hit = 0 ;
2005-05-02 15:02:19 +04:00
struct thread_status * thread ;
2005-04-28 18:02:30 +04:00
2005-04-29 17:41:25 +04:00
lock_mutex ( ) ;
2005-04-28 18:02:30 +04:00
2005-05-02 15:02:19 +04:00
thread = list_item ( thread_registry . n , struct thread_status ) ;
2005-04-29 17:41:25 +04:00
if ( ! message_data - > dso_name & &
! message_data - > device_path )
goto out ;
list_iterate_items ( thread , & thread_registry ) {
dev = dso = 0 ;
2005-05-03 17:50:42 +04:00
log_print ( " %s: working %s %s %u \n " , __func__ , thread - > dso_data - > dso_name , thread - > device_path , thread - > events ) ;
2005-05-02 15:02:19 +04:00
/* If DSO name equals. */
2005-04-29 17:41:25 +04:00
if ( message_data - > dso_name & &
! strcmp ( message_data - > dso_name ,
thread - > dso_data - > dso_name ) )
dso = 1 ;
2005-05-02 15:02:19 +04:00
/* If dev path equals. */
2005-04-29 17:41:25 +04:00
if ( message_data - > device_path & &
! strcmp ( message_data - > device_path ,
thread - > device_path ) )
dev = 1 ;
2005-05-02 15:02:19 +04:00
/* We've got both DSO name and device patch or either. */
/* FIXME: wrong logic! */
2005-05-03 17:50:42 +04:00
if ( message_data - > dso_name & & message_data - > device_path & &
dso & & dev )
2005-04-29 17:41:25 +04:00
hit = 1 ;
2005-05-03 17:50:42 +04:00
else if ( message_data - > dso_name & & dso )
hit = 1 ;
else if ( message_data - > device_path & &
dev )
hit = 1 ;
if ( hit )
{ log_print ( " %s: HIT %s %s %u \n " , __func__ , thread - > dso_data - > dso_name , thread - > device_path , thread - > events ) ;
2005-04-29 17:41:25 +04:00
break ;
2005-05-03 17:50:42 +04:00
}
2005-04-28 18:02:30 +04:00
}
2005-04-29 17:41:25 +04:00
/*
* If we got a registered device and want the next one - >
* fetch next element off the list .
*/
if ( hit & & next )
thread = list_item ( & thread - > list . n , struct thread_status ) ;
2005-04-28 18:02:30 +04:00
2005-04-29 17:41:25 +04:00
out :
if ( list_empty ( & thread - > list ) | |
2005-05-02 15:02:19 +04:00
& thread - > list = = & thread_registry ) {
2005-04-29 17:41:25 +04:00
unlock_mutex ( ) ;
return - ENOENT ;
}
2005-04-28 18:02:30 +04:00
2005-05-02 15:02:19 +04:00
unlock_mutex ( ) ;
2005-04-29 17:41:25 +04:00
return registered_device ( message_data , thread ) ;
2005-04-28 18:02:30 +04:00
}
2005-04-28 02:32:00 +04:00
/* Initialize a fifos structure with path names. */
static void init_fifos ( struct fifos * fifos )
{
memset ( fifos , 0 , sizeof ( * fifos ) ) ;
fifos - > client_path = FIFO_CLIENT ;
fifos - > server_path = FIFO_SERVER ;
}
/* Open fifos used for client communication. */
static int open_fifos ( struct fifos * fifos )
{
/* Blocks until client is ready to write. */
if ( ( fifos - > server = open ( fifos - > server_path , O_WRONLY ) ) = = - 1 ) {
stack ;
return 0 ;
}
/* Need to open read+write for select() to work. */
if ( ( fifos - > client = open ( fifos - > client_path , O_RDWR ) ) = = - 1 ) {
stack ;
close ( fifos - > server ) ;
return 0 ;
}
return 1 ;
}
/*
* Read message from client making sure that data is available
* and a complete message is read .
*/
static int client_read ( struct fifos * fifos , struct daemon_message * msg )
{
int bytes = 0 , ret = 0 ;
fd_set fds ;
errno = 0 ;
while ( bytes < sizeof ( * msg ) & & errno ! = EOF ) {
do {
/* Watch client read FIFO for input. */
FD_ZERO ( & fds ) ;
FD_SET ( fifos - > client , & fds ) ;
} while ( select ( fifos - > client + 1 , & fds , NULL , NULL , NULL ) ! = 1 ) ;
ret = read ( fifos - > client , msg , sizeof ( * msg ) - bytes ) ;
bytes + = ret > 0 ? ret : 0 ;
}
return bytes = = sizeof ( * msg ) ;
}
/*
* Write a message to the client making sure that it is ready to write .
*/
static int client_write ( struct fifos * fifos , struct daemon_message * msg )
{
2005-04-29 17:41:25 +04:00
int bytes = 0 , ret = 0 ;
2005-04-28 02:32:00 +04:00
fd_set fds ;
2005-04-29 17:41:25 +04:00
errno = 0 ;
while ( bytes < sizeof ( * msg ) & & errno ! = EIO ) {
do {
/* Watch client write FIFO to be ready for output. */
FD_ZERO ( & fds ) ;
FD_SET ( fifos - > server , & fds ) ;
} while ( select ( fifos - > server + 1 , NULL , & fds , NULL , NULL ) ! = 1 ) ;
2005-04-28 02:32:00 +04:00
2005-04-29 17:41:25 +04:00
ret = write ( fifos - > server , msg , sizeof ( * msg ) - bytes ) ;
bytes + = ret > 0 ? ret : 0 ;
}
return bytes = = sizeof ( * msg ) ;
2005-04-28 02:32:00 +04:00
}
/* Process a request passed from the communication thread. */
2005-04-28 18:02:30 +04:00
static int do_process_request ( struct daemon_message * msg )
2005-04-28 02:32:00 +04:00
{
int ret ;
2005-04-28 18:02:30 +04:00
static struct message_data message_data ;
2005-04-29 17:41:25 +04:00
log_print ( " %s: \" %s \" \n " , __func__ , msg - > msg ) ;
2005-04-28 18:02:30 +04:00
/* Parse the message. */
message_data . msg = msg ;
if ( msg - > opcode . cmd ! = CMD_ACTIVE & &
! parse_message ( & message_data ) ) {
stack ;
2005-04-29 18:56:35 +04:00
fflush ( stdout ) ;
2005-04-28 18:02:30 +04:00
return - EINVAL ;
2005-04-29 18:56:35 +04:00
}
2005-04-28 02:32:00 +04:00
2005-04-29 18:56:35 +04:00
fflush ( stdout ) ;
2005-04-29 17:41:25 +04:00
2005-04-28 02:32:00 +04:00
/* Check the request type. */
switch ( msg - > opcode . cmd ) {
case CMD_ACTIVE :
ret = 0 ;
break ;
case CMD_REGISTER_FOR_EVENT :
2005-04-28 18:02:30 +04:00
ret = register_for_event ( & message_data ) ;
2005-04-28 02:32:00 +04:00
break ;
case CMD_UNREGISTER_FOR_EVENT :
2005-04-28 18:02:30 +04:00
ret = unregister_for_event ( & message_data ) ;
break ;
2005-04-29 17:41:25 +04:00
case CMD_GET_REGISTERED_DEVICE :
ret = get_registered_device ( & message_data , 0 ) ;
break ;
2005-04-28 18:02:30 +04:00
case CMD_GET_NEXT_REGISTERED_DEVICE :
2005-04-29 17:41:25 +04:00
ret = get_registered_device ( & message_data , 1 ) ;
2005-04-28 02:32:00 +04:00
break ;
2005-04-30 02:12:09 +04:00
default :
ret = - EINVAL ;
break ;
2005-04-28 02:32:00 +04:00
}
return ret ;
}
2005-04-28 18:02:30 +04:00
/* Only one caller at a time. */
2005-04-28 02:32:00 +04:00
static void process_request ( struct fifos * fifos , struct daemon_message * msg )
{
/* Read the request from the client. */
memset ( msg , 0 , sizeof ( * msg ) ) ;
if ( ! client_read ( fifos , msg ) ) {
stack ;
return ;
}
2005-04-28 18:02:30 +04:00
msg - > opcode . status = do_process_request ( msg ) ;
2005-04-28 02:32:00 +04:00
memset ( & msg - > msg , 0 , sizeof ( msg - > msg ) ) ;
if ( ! client_write ( fifos , msg ) )
stack ;
}
/* Communication thread. */
static void comm_thread ( struct fifos * fifos )
{
struct daemon_message msg ;
/* Open fifos (must be created by client). */
if ( ! open_fifos ( fifos ) ) {
stack ;
return ;
}
/* Loop forever and handle client requests sequentially. */
while ( 1 )
process_request ( fifos , & msg ) ;
}
/* Fork into the background and detach from our parent process. */
static int daemonize ( void )
{
pid_t pid ;
if ( ( pid = fork ( ) ) = = - 1 ) {
log_err ( " %s: fork " , __func__ ) ;
return 0 ;
} else if ( pid > 0 ) /* Parent. */
return 2 ;
log_print ( " daemonizing 2nd... \n " ) ;
setsid ( ) ;
if ( chdir ( " / " ) ) {
log_err ( " %s: chdir / " , __func__ ) ;
return 0 ;
}
/* REMOVEME: */
return 1 ;
log_print ( " daemonizing 3rd... \n " ) ;
/* Detach ourself. */
if ( close ( STDIN_FILENO ) = = - 1 | |
close ( STDOUT_FILENO ) = = - 1 | |
close ( STDERR_FILENO ) = = - 1 )
return 0 ;
log_print ( " daemonized \n " ) ;
return 1 ;
}
/* Init thread signal handling. */
2005-05-02 15:02:19 +04:00
# define HANGUP SIGHUP
static void init_thread_signals ( int hup )
2005-04-28 02:32:00 +04:00
{
sigset_t sigset ;
sigfillset ( & sigset ) ;
2005-05-02 15:02:19 +04:00
if ( hup )
sigdelset ( & sigset , HANGUP ) ;
pthread_sigmask ( SIG_SETMASK , & sigset , NULL ) ;
2005-04-28 02:32:00 +04:00
}
int main ( void )
{
struct fifos fifos ;
2005-05-04 01:29:13 +04:00
2005-05-02 15:02:19 +04:00
/* Make sure, parent accepts HANGUP signal. */
init_thread_signals ( 1 ) ;
2005-04-28 02:32:00 +04:00
switch ( daemonize ( ) ) {
case 1 : /* Child. */
/* Try to lock pidfile. */
if ( ! lock ( ) ) {
fprintf ( stderr , " daemon already running \n " ) ;
break ;
}
2005-05-02 15:02:19 +04:00
init_thread_signals ( 0 ) ;
kill ( getppid ( ) , HANGUP ) ;
2005-04-29 02:47:52 +04:00
2005-05-04 01:29:13 +04:00
multilog_clear_logging ( ) ;
2005-05-09 22:44:35 +04:00
multilog_add_type ( threaded_syslog , NULL ) ;
multilog_init_verbose ( threaded_syslog , _LOG_DEBUG ) ;
2005-04-29 02:47:52 +04:00
2005-04-28 02:32:00 +04:00
init_fifos ( & fifos ) ;
pthread_mutex_init ( & mutex , NULL ) ;
if ( ! storepid ( lf ) ) {
stack ;
2005-05-02 15:02:19 +04:00
exit ( EXIT_FAILURE ) ;
2005-04-28 02:32:00 +04:00
}
if ( mlockall ( MCL_FUTURE ) = = - 1 ) {
stack ;
2005-05-02 15:02:19 +04:00
exit ( EXIT_FAILURE ) ;
2005-04-28 02:32:00 +04:00
}
/* Communication thread runs forever... */
comm_thread ( & fifos ) ;
/* We should never get here. */
munlockall ( ) ;
pthread_mutex_destroy ( & mutex ) ;
case 0 : /* Error (either on daemonize() or on comm_thread() return. */
unlock ( ) ;
2005-05-02 15:02:19 +04:00
exit ( EXIT_FAILURE ) ;
2005-04-28 02:32:00 +04:00
break ;
case 2 : /* Parent. */
wait ( NULL ) ;
break ;
}
2005-05-02 15:02:19 +04:00
exit ( EXIT_SUCCESS ) ;
2005-04-28 02:32:00 +04:00
}
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only . This must remain at the end
* of the file .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - file - style : " linux "
* End :
*/