2005-04-28 02:32:00 +04:00
/*
2007-01-08 18:18:52 +03:00
* Copyright ( C ) 2005 - 2007 Red Hat , Inc . All rights reserved .
2005-04-28 02:32:00 +04:00
*
* 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
*/
2006-05-10 23:38:25 +04:00
# define _GNU_SOURCE
# define _FILE_OFFSET_BITS 64
2007-01-12 00:54:53 +03:00
# include "configure.h"
2005-04-28 02:32:00 +04:00
# include "libdevmapper.h"
2005-12-02 18:39:16 +03:00
# include "libdevmapper-event.h"
2005-04-28 02:32:00 +04:00
# include "list.h"
2005-12-02 18:39:16 +03:00
# include "dmeventd.h"
//#include "libmultilog.h"
# include "log.h"
2005-04-28 02:32:00 +04:00
# include <dlfcn.h>
# include <errno.h>
# include <fcntl.h>
# include <libgen.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>
2007-01-08 18:35:08 +03:00
# include <sys/resource.h>
2005-04-28 02:32:00 +04:00
# include <unistd.h>
2005-12-02 18:39:16 +03:00
# include <stdarg.h>
2007-01-12 00:54:53 +03:00
# include <arpa/inet.h> /* for htonl, ntohl */
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
# ifdef linux
# include <malloc.h>
# endif
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
/* FIXME We use syslog for now, because multilog is not yet implemented */
2007-01-08 18:18:52 +03:00
# include <syslog.h>
2007-01-12 00:54:53 +03:00
static volatile sig_atomic_t _exit_now = 0 ; /* set to '1' when signal is given to exit */
static volatile sig_atomic_t _thread_registries_empty = 1 ; /* registries are empty initially */
2007-01-08 18:18:52 +03:00
2005-04-28 02:32:00 +04:00
/* List (un)link macros. */
2005-12-02 18:39:16 +03:00
# define LINK(x, head) list_add(head, &(x)->list)
2007-01-08 18:18:52 +03:00
# define LINK_DSO(dso) LINK(dso, &_dso_registry)
# define LINK_THREAD(thread) LINK(thread, &_thread_registry)
2005-04-28 02:32:00 +04:00
# define UNLINK(x) list_del(&(x)->list)
# define UNLINK_DSO(x) UNLINK(x)
# define UNLINK_THREAD(x) UNLINK(x)
2005-12-02 18:39:16 +03:00
# define DAEMON_NAME "dmeventd"
2005-04-28 02:32:00 +04:00
/* Global mutex for list accesses. */
2007-01-08 18:18:52 +03:00
static pthread_mutex_t _global_mutex ;
# define DM_THREAD_RUNNING 0
# define DM_THREAD_SHUTDOWN 1
# define DM_THREAD_DONE 2
2005-04-28 02:32:00 +04:00
/* Data kept about a DSO. */
struct dso_data {
struct list list ;
2007-01-12 00:54:53 +03:00
char * dso_name ; /* DSO name (eg, "evms", "dmraid", "lvm2"). */
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
void * dso_handle ; /* Opaque handle as returned from dlopen(). */
unsigned int ref_count ; /* Library reference count. */
2005-04-28 02:32:00 +04:00
/*
* Event processing .
*
2007-01-12 00:54:53 +03:00
* 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 .
*
* This function gets a dm_task that is a result of
* DM_DEVICE_WAITEVENT ioctl ( results equivalent to
* DM_DEVICE_STATUS ) . It should not destroy it .
* The caller must dispose of the task .
2005-04-28 02:32:00 +04:00
*/
2007-01-12 00:54:53 +03:00
void ( * process_event ) ( struct dm_task * dmt , enum dm_event_type event ) ;
2005-04-28 02:32:00 +04:00
/*
* 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 ) .
*/
2007-01-12 00:54:53 +03:00
int ( * register_device ) ( const char * device , const char * uuid , int major ,
int minor ) ;
2005-04-28 02:32:00 +04:00
/*
* 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 ) .
*/
2007-01-12 00:54:53 +03:00
int ( * unregister_device ) ( const char * device , const char * uuid ,
int major , int minor ) ;
2005-04-28 02:32:00 +04:00
} ;
2007-01-08 18:18:52 +03:00
static LIST_INIT ( _dso_registry ) ;
2005-04-28 02:32:00 +04:00
/* 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. */
2007-01-12 00:54:53 +03:00
char * device_uuid ; /* Mapped device path. */
2005-04-28 02:32:00 +04:00
union {
char * str ; /* Events string as fetched from message. */
2005-12-02 18:39:16 +03:00
enum dm_event_type field ; /* Events bitfield. */
2005-04-28 02:32:00 +04:00
} events ;
2005-12-02 18:39:16 +03:00
union {
char * str ;
uint32_t secs ;
} timeout ;
struct dm_event_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 {
2007-01-08 18:18:52 +03:00
struct list list ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
pthread_t thread ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
struct dso_data * dso_data ; /* DSO this thread accesses. */
struct {
char * uuid ;
char * name ;
int major , minor ;
} device ;
2006-12-20 17:35:02 +03:00
uint32_t event_nr ; /* event number */
int processing ; /* Set when event is being processed */
2007-01-08 18:18:52 +03:00
int status ; /* running/shutdown/done */
2005-12-02 18:39:16 +03:00
enum dm_event_type events ; /* bitfield for event filter. */
2007-01-12 00:54:53 +03:00
enum dm_event_type current_events ; /* bitfield for occured events. */
struct dm_task * current_task ;
2005-12-02 18:39:16 +03:00
time_t next_time ;
uint32_t timeout ;
struct list timeout_list ;
2005-04-28 02:32:00 +04:00
} ;
2007-01-08 18:18:52 +03:00
static LIST_INIT ( _thread_registry ) ;
static LIST_INIT ( _thread_registry_unused ) ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
static int _timeout_running ;
2005-12-02 18:39:16 +03:00
static LIST_INIT ( timeout_registry ) ;
2007-01-08 18:18:52 +03:00
static pthread_mutex_t _timeout_mutex = PTHREAD_MUTEX_INITIALIZER ;
static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER ;
2005-12-02 18:39:16 +03:00
2005-04-28 02:32:00 +04:00
/* 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 )
{
2006-01-31 17:50:38 +03:00
struct thread_status * ret = ( typeof ( ret ) ) dm_malloc ( sizeof ( * ret ) ) ;
2005-04-28 02:32:00 +04:00
if ( ret ) {
2005-12-02 18:39:16 +03:00
if ( ! memset ( ret , 0 , sizeof ( * ret ) ) | |
2007-01-12 00:54:53 +03:00
! ( ret - > device . uuid = dm_strdup ( data - > device_uuid ) ) ) {
2006-01-31 17:50:38 +03:00
dm_free ( ret ) ;
2005-04-28 02:32:00 +04:00
ret = NULL ;
2005-12-02 18:39:16 +03:00
} else {
2007-01-12 00:54:53 +03:00
ret - > current_task = NULL ;
ret - > device . name = NULL ;
ret - > device . major = ret - > device . minor = 0 ;
2005-12-02 18:39:16 +03:00
ret - > dso_data = dso_data ;
2007-01-12 00:54:53 +03:00
ret - > events = data - > events . field ;
ret - > timeout = data - > timeout . secs ;
2005-12-02 18:39:16 +03:00
list_init ( & ret - > timeout_list ) ;
2005-04-28 02:32:00 +04:00
}
}
return ret ;
}
static void free_thread_status ( struct thread_status * thread )
{
2007-01-12 00:54:53 +03:00
dm_free ( thread - > device . uuid ) ;
dm_free ( thread - > device . name ) ;
2006-01-31 17:50:38 +03:00
dm_free ( thread ) ;
2005-04-28 02:32:00 +04:00
}
/* 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
{
2006-01-31 17:50:38 +03:00
struct dso_data * ret = ( typeof ( ret ) ) dm_malloc ( sizeof ( * ret ) ) ;
2005-04-28 02:32:00 +04:00
2006-12-20 17:35:02 +03:00
if ( ! ret )
return NULL ;
if ( ! memset ( ret , 0 , sizeof ( * ret ) ) | |
! ( ret - > dso_name = dm_strdup ( data - > dso_name ) ) ) {
dm_free ( ret ) ;
return NULL ;
2005-04-28 02:32:00 +04:00
}
return ret ;
}
static void free_dso_data ( struct dso_data * data )
{
2006-01-31 17:50:38 +03:00
dm_free ( data - > dso_name ) ;
dm_free ( data ) ;
2005-04-28 02:32:00 +04:00
}
2005-12-02 18:39:16 +03:00
/*
* Fetch a string off src and duplicate it into * ptr .
2007-01-12 00:54:53 +03:00
* Pay attention to zero - length strings .
2005-12-02 18:39:16 +03:00
*/
2007-01-12 00:54:53 +03:00
/* FIXME? move to libdevmapper to share with the client lib (need to
make delimiter a parameter then ) */
2005-04-28 02:32:00 +04:00
static const char delimiter = ' ' ;
2005-12-02 18:39:16 +03:00
static int fetch_string ( char * * ptr , char * * src )
2005-04-28 02:32:00 +04:00
{
2005-12-02 18:39:16 +03:00
int ret = 0 ;
char * p ;
size_t len ;
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 ;
2006-01-31 17:50:38 +03:00
if ( ( * ptr = dm_strdup ( * src ) ) ) {
2005-12-02 18:39:16 +03:00
if ( ( len = strlen ( * ptr ) ) )
* src + = len ;
else {
2006-01-31 17:50:38 +03:00
dm_free ( * ptr ) ;
2005-12-02 18:39:16 +03:00
* ptr = NULL ;
}
( * src ) + + ;
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 )
2006-01-31 17:50:38 +03:00
dm_free ( message_data - > dso_name ) ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
if ( message_data - > device_uuid )
dm_free ( message_data - > device_uuid ) ;
2007-01-08 18:18:52 +03:00
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
{
2007-01-08 18:18:52 +03:00
int ret = 0 ;
char * p = message_data - > msg - > data ;
struct dm_event_daemon_message * msg = message_data - > msg ;
if ( ! msg - > data )
return 0 ;
2005-04-28 02:32:00 +04:00
/*
* Retrieve application identifier , mapped device
* path and events # string from message .
*/
2005-12-02 18:39:16 +03:00
if ( fetch_string ( & message_data - > dso_name , & p ) & &
2007-01-12 00:54:53 +03:00
fetch_string ( & message_data - > device_uuid , & p ) & &
2005-12-02 18:39:16 +03:00
fetch_string ( & message_data - > events . str , & p ) & &
fetch_string ( & message_data - > timeout . str , & p ) ) {
2005-04-28 18:02:30 +04:00
if ( message_data - > events . str ) {
2005-12-02 18:39:16 +03:00
enum dm_event_type i = atoi ( message_data - > events . str ) ;
2005-04-28 18:02:30 +04:00
/*
* Free string representaion of events .
* Not needed an more .
*/
2006-01-31 17:50:38 +03:00
dm_free ( message_data - > events . str ) ;
2005-04-28 18:02:30 +04:00
message_data - > events . field = i ;
}
2005-12-02 18:39:16 +03:00
if ( message_data - > timeout . str ) {
uint32_t secs = atoi ( message_data - > timeout . str ) ;
2006-01-31 17:50:38 +03:00
dm_free ( message_data - > timeout . str ) ;
2005-12-02 18:39:16 +03:00
message_data - > timeout . secs = secs ? secs :
2007-01-12 00:54:53 +03:00
DM_EVENT_DEFAULT_TIMEOUT ;
2005-12-02 18:39:16 +03:00
}
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
ret = 1 ;
2005-04-28 02:32:00 +04:00
}
2007-01-08 18:18:52 +03:00
dm_free ( msg - > data ) ;
msg - > data = NULL ;
msg - > size = 0 ;
return ret ;
2005-04-28 02:32:00 +04:00
} ;
/* Global mutex to lock access to lists et al. */
static int lock_mutex ( void )
{
2007-01-08 18:18:52 +03:00
return pthread_mutex_lock ( & _global_mutex ) ;
2005-04-28 02:32:00 +04:00
}
static int unlock_mutex ( void )
{
2007-01-08 18:18:52 +03:00
return pthread_mutex_unlock ( & _global_mutex ) ;
2005-04-28 02:32:00 +04:00
}
/* 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 ;
2006-01-31 17:50:38 +03:00
if ( len > ( int ) sizeof ( pid ) )
len = ( int ) sizeof ( pid ) ;
2005-04-28 02:32:00 +04:00
2006-01-31 17:50:38 +03:00
if ( write ( lf , pid , ( size_t ) len ) ! = len )
2005-04-28 02:32:00 +04:00
return 0 ;
fsync ( lf ) ;
return 1 ;
}
/* Check, if a device exists. */
2007-01-12 00:54:53 +03:00
static int fill_device_data ( struct thread_status * ts )
2005-04-28 02:32:00 +04:00
{
2007-01-12 00:54:53 +03:00
struct dm_task * dmt ;
struct dm_info dmi ;
2006-02-03 22:44:59 +03:00
2007-01-12 00:54:53 +03:00
if ( ! ts - > device . uuid )
2006-02-03 22:44:59 +03:00
return 0 ;
2007-01-12 00:54:53 +03:00
ts - > device . name = NULL ;
ts - > device . major = ts - > device . minor = 0 ;
2006-02-03 22:44:59 +03:00
2007-01-12 00:54:53 +03:00
dmt = dm_task_create ( DM_DEVICE_INFO ) ;
if ( ! dmt )
2006-02-03 22:44:59 +03:00
return 0 ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
dm_task_set_uuid ( dmt , ts - > device . uuid ) ;
if ( ! dm_task_run ( dmt ) )
goto fail ;
ts - > device . name = dm_strdup ( dm_task_get_name ( dmt ) ) ;
if ( ! ts - > device . name )
goto fail ;
if ( ! dm_task_get_info ( dmt , & dmi ) )
goto fail ;
ts - > device . major = dmi . major ;
ts - > device . minor = dmi . minor ;
dm_task_destroy ( dmt ) ;
return 1 ;
fail :
dm_task_destroy ( dmt ) ;
dm_free ( ts - > device . name ) ;
return 0 ;
2005-04-28 02:32:00 +04:00
}
/*
* Find an existing thread for a device .
*
2006-01-27 23:52:21 +03:00
* Mutex must be held when calling this .
2005-04-28 02:32:00 +04:00
*/
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 ;
2007-01-08 18:18:52 +03:00
list_iterate_items ( thread , & _thread_registry )
2007-01-12 00:54:53 +03:00
if ( ! strcmp ( data - > device_uuid , thread - > device . uuid ) )
return thread ;
2005-04-28 02:32:00 +04:00
return NULL ;
}
/* Cleanup at exit. */
static void exit_dm_lib ( void )
{
dm_lib_release ( ) ;
dm_lib_exit ( ) ;
}
2005-12-02 18:39:16 +03:00
static void exit_timeout ( void * unused )
{
2007-01-08 18:18:52 +03:00
_timeout_running = 0 ;
pthread_mutex_unlock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
}
/* Wake up monitor threads every so often. */
static void * timeout_thread ( void * unused )
{
struct timespec timeout ;
time_t curr_time ;
timeout . tv_nsec = 0 ;
pthread_cleanup_push ( exit_timeout , NULL ) ;
2007-01-08 18:18:52 +03:00
pthread_mutex_lock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
while ( ! list_empty ( & timeout_registry ) ) {
struct thread_status * thread ;
2007-01-12 00:54:53 +03:00
timeout . tv_sec = ( time_t ) - 1 ;
2005-12-02 18:39:16 +03:00
curr_time = time ( NULL ) ;
2007-01-12 00:54:53 +03:00
list_iterate_items_gen ( thread , & timeout_registry , timeout_list ) {
2005-12-02 18:39:16 +03:00
if ( thread - > next_time < curr_time ) {
thread - > next_time = curr_time + thread - > timeout ;
pthread_kill ( thread - > thread , SIGALRM ) ;
}
if ( thread - > next_time < timeout . tv_sec )
timeout . tv_sec = thread - > next_time ;
}
2007-01-12 00:54:53 +03:00
pthread_cond_timedwait ( & _timeout_cond , & _timeout_mutex ,
& timeout ) ;
2005-12-02 18:39:16 +03:00
}
pthread_cleanup_pop ( 1 ) ;
return NULL ;
}
static int register_for_timeout ( struct thread_status * thread )
{
int ret = 0 ;
2007-01-08 18:18:52 +03:00
pthread_mutex_lock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
thread - > next_time = time ( NULL ) + thread - > timeout ;
if ( list_empty ( & thread - > timeout_list ) ) {
list_add ( & timeout_registry , & thread - > timeout_list ) ;
2007-01-08 18:18:52 +03:00
if ( _timeout_running )
pthread_cond_signal ( & _timeout_cond ) ;
2005-12-02 18:39:16 +03:00
}
2007-01-08 18:18:52 +03:00
if ( ! _timeout_running ) {
2005-12-02 18:39:16 +03:00
pthread_t timeout_id ;
if ( ! ( ret = - pthread_create ( & timeout_id , NULL ,
timeout_thread , NULL ) ) )
2007-01-08 18:18:52 +03:00
_timeout_running = 1 ;
2005-12-02 18:39:16 +03:00
}
2007-01-08 18:18:52 +03:00
pthread_mutex_unlock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
return ret ;
}
static void unregister_for_timeout ( struct thread_status * thread )
{
2007-01-08 18:18:52 +03:00
pthread_mutex_lock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
if ( ! list_empty ( & thread - > timeout_list ) ) {
list_del ( & thread - > timeout_list ) ;
list_init ( & thread - > timeout_list ) ;
}
2007-01-08 18:18:52 +03:00
pthread_mutex_unlock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
}
static void no_intr_log ( int level , const char * file , int line ,
2007-01-12 00:54:53 +03:00
const char * f , . . . )
2005-12-02 18:39:16 +03:00
{
va_list ap ;
if ( errno = = EINTR )
return ;
if ( level > _LOG_WARN )
return ;
va_start ( ap , f ) ;
if ( level < _LOG_WARN )
vfprintf ( stderr , f , ap ) ;
else
vprintf ( f , ap ) ;
va_end ( ap ) ;
if ( level < _LOG_WARN )
fprintf ( stderr , " \n " ) ;
else
fprintf ( stdout , " \n " ) ;
}
static sigset_t unblock_sigalrm ( void )
{
sigset_t set , old ;
sigemptyset ( & set ) ;
sigaddset ( & set , SIGALRM ) ;
pthread_sigmask ( SIG_UNBLOCK , & set , & old ) ;
return old ;
}
2007-01-08 18:18:52 +03:00
# define DM_WAIT_RETRY 0
# define DM_WAIT_INTR 1
# define DM_WAIT_FATAL 2
2005-04-28 02:32:00 +04:00
/* Wait on a device until an event occurs. */
2007-01-12 00:54:53 +03:00
static int event_wait ( struct thread_status * thread , struct dm_task * * task )
2005-04-28 02:32:00 +04:00
{
2005-12-02 18:39:16 +03:00
sigset_t set ;
2007-01-08 18:18:52 +03:00
int ret = DM_WAIT_RETRY ;
2005-04-28 02:32:00 +04:00
struct dm_task * dmt ;
2005-12-02 18:39:16 +03:00
struct dm_info info ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
* task = 0 ;
2005-04-28 02:32:00 +04:00
if ( ! ( dmt = dm_task_create ( DM_DEVICE_WAITEVENT ) ) )
2007-01-08 18:18:52 +03:00
return DM_WAIT_RETRY ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
thread - > current_task = dmt ;
if ( ! dm_task_set_uuid ( dmt , thread - > device . uuid ) | |
! dm_task_set_event_nr ( dmt , thread - > event_nr ) )
2005-12-02 18:39:16 +03:00
goto out ;
/*
* This is so that you can break out of waiting on an event ,
* either for a timeout event , or to cancel the thread .
*/
set = unblock_sigalrm ( ) ;
dm_log_init ( no_intr_log ) ;
errno = 0 ;
2007-01-12 00:54:53 +03:00
if ( dm_task_run ( dmt ) ) {
2005-12-02 18:39:16 +03:00
thread - > current_events | = DM_EVENT_DEVICE_ERROR ;
2007-01-08 18:18:52 +03:00
ret = DM_WAIT_INTR ;
2005-12-02 18:39:16 +03:00
if ( ( ret = dm_task_get_info ( dmt , & info ) ) )
thread - > event_nr = info . event_nr ;
} else if ( thread - > events & DM_EVENT_TIMEOUT & & errno = = EINTR ) {
thread - > current_events | = DM_EVENT_TIMEOUT ;
2007-01-08 18:18:52 +03:00
ret = DM_WAIT_INTR ;
2006-12-20 17:35:02 +03:00
} else {
syslog ( LOG_NOTICE , " dm_task_run failed, errno = %d, %s " ,
errno , strerror ( errno ) ) ;
if ( errno = = ENXIO ) {
2007-01-12 00:54:53 +03:00
syslog ( LOG_ERR , " %s disappeared, detaching " ,
thread - > device . name ) ;
2007-01-08 18:18:52 +03:00
ret = DM_WAIT_FATAL ;
2006-12-20 17:35:02 +03:00
}
2005-04-28 02:32:00 +04:00
}
2005-12-02 18:39:16 +03:00
pthread_sigmask ( SIG_SETMASK , & set , NULL ) ;
dm_log_init ( NULL ) ;
2007-01-12 00:54:53 +03:00
out :
if ( ret = = DM_WAIT_FATAL | | ret = = DM_WAIT_RETRY ) {
dm_task_destroy ( dmt ) ;
thread - > current_task = NULL ;
} else
* task = dmt ;
2005-04-28 02:32:00 +04:00
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
{
2007-01-12 00:54:53 +03:00
return thread - > dso_data - > register_device ( thread - > device . name ,
thread - > device . uuid ,
thread - > device . major ,
thread - > device . minor ) ;
2005-04-28 02:32:00 +04:00
}
/* 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
{
2007-01-12 00:54:53 +03:00
return thread - > dso_data - > unregister_device ( thread - > device . name ,
thread - > device . uuid ,
thread - > device . major ,
thread - > device . minor ) ;
2005-04-28 02:32:00 +04:00
}
2005-12-02 18:39:16 +03:00
/* Process an event in the DSO. */
2007-01-12 00:54:53 +03:00
static void do_process_event ( struct thread_status * thread , struct dm_task * task )
2005-04-28 02:32:00 +04:00
{
2007-01-12 00:54:53 +03:00
thread - > dso_data - > process_event ( task , thread - > current_events ) ;
2005-04-28 02:32:00 +04:00
}
/* 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 ) )
2007-01-08 18:18:52 +03:00
syslog ( LOG_ERR , " %s: %s unregister failed \n " , __func__ ,
2007-01-12 00:54:53 +03:00
thread - > device . name ) ;
if ( thread - > current_task )
dm_task_destroy ( thread - > current_task ) ;
thread - > current_task = NULL ;
2005-04-28 02:32:00 +04:00
}
/* 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 ;
2006-12-20 17:35:02 +03:00
int wait_error = 0 ;
2007-01-12 00:54:53 +03:00
struct dm_task * task ;
2005-04-28 02:32:00 +04:00
pthread_setcanceltype ( PTHREAD_CANCEL_DEFERRED , NULL ) ;
pthread_cleanup_push ( monitor_unregister , thread ) ;
2006-01-27 23:52:21 +03:00
/* Wait for do_process_request() to finish its task. */
2005-04-28 02:32:00 +04:00
lock_mutex ( ) ;
2007-01-08 18:18:52 +03:00
thread - > status = DM_THREAD_RUNNING ;
2005-04-28 02:32:00 +04:00
unlock_mutex ( ) ;
/* Loop forever awaiting/analyzing device events. */
while ( 1 ) {
thread - > current_events = 0 ;
2007-01-12 00:54:53 +03:00
wait_error = event_wait ( thread , & task ) ;
2007-01-08 18:18:52 +03:00
if ( wait_error = = DM_WAIT_RETRY )
2005-04-28 02:32:00 +04:00
continue ;
2007-01-08 18:18:52 +03:00
if ( wait_error = = DM_WAIT_FATAL )
2006-12-20 17:35:02 +03:00
break ;
2007-01-12 00:54:53 +03:00
/*
* We know that wait succeeded and stored a
* pointer to dm_task with device status into task .
*/
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 .
*/
2007-01-08 18:18:52 +03:00
lock_mutex ( ) ;
if ( thread - > status = = DM_THREAD_SHUTDOWN ) {
unlock_mutex ( ) ;
break ;
}
unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
if ( thread - > events & thread - > current_events ) {
2006-01-27 23:50:01 +03:00
lock_mutex ( ) ;
thread - > processing = 1 ;
unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
do_process_event ( thread , task ) ;
dm_task_destroy ( task ) ;
thread - > current_task = NULL ;
2006-01-27 23:50:01 +03:00
lock_mutex ( ) ;
thread - > processing = 0 ;
unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
} else {
dm_task_destroy ( task ) ;
thread - > current_task = NULL ;
2005-04-28 02:32:00 +04:00
}
}
2007-01-08 18:18:52 +03:00
lock_mutex ( ) ;
thread - > status = DM_THREAD_DONE ;
unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
pthread_cleanup_pop ( 1 ) ;
2007-01-08 18:18:52 +03:00
return NULL ;
2005-04-28 02:32:00 +04:00
}
/* Create a device monitoring thread. */
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 )
{
2005-12-02 18:39:16 +03:00
int ret ;
if ( ( ret = pthread_cancel ( thread - > thread ) ) )
return ret ;
return pthread_kill ( thread - > thread , SIGALRM ) ;
2005-04-28 02:32:00 +04:00
}
/* 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 ( ) ;
2007-01-08 18:18:52 +03:00
list_iterate_items ( dso_data , & _dso_registry )
2007-01-12 00:54:53 +03:00
if ( ! strcmp ( data - > dso_name , dso_data - > dso_name ) ) {
lib_get ( dso_data ) ;
ret = dso_data ;
break ;
}
2005-04-28 02:32:00 +04:00
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 ;
return 0 ;
}
static int lookup_symbols ( void * dl , struct dso_data * data )
{
2007-01-12 00:54:53 +03:00
return lookup_symbol ( dl , data , ( void * ) & data - > process_event ,
2005-04-28 02:32:00 +04:00
" process_event " ) & &
2007-01-12 00:54:53 +03:00
lookup_symbol ( dl , data , ( void * ) & data - > register_device ,
" register_device " ) & &
lookup_symbol ( dl , data , ( void * ) & data - > unregister_device ,
" unregister_device " ) ;
2005-04-28 02:32:00 +04:00
}
/* 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 ;
2007-01-12 00:54:53 +03:00
if ( ! ( dl = dlopen ( data - > dso_name , RTLD_NOW ) ) ) {
2007-01-08 18:18:52 +03:00
const char * dlerr = dlerror ( ) ;
2007-01-12 00:54:53 +03:00
syslog ( LOG_ERR , " dmeventd %s dlopen failed: %s " , data - > dso_name ,
dlerr ) ;
data - > msg - > size =
dm_saprintf ( & ( data - > msg - > data ) , " %s dlopen failed: %s " ,
data - > dso_name , dlerr ) ;
2006-02-03 22:41:34 +03:00
return NULL ;
2005-12-02 18:39:16 +03:00
}
2005-04-28 02:32:00 +04:00
2006-02-03 22:41:34 +03:00
if ( ! ( ret = alloc_dso_data ( data ) ) ) {
dlclose ( dl ) ;
return NULL ;
}
2005-04-28 02:32:00 +04:00
2006-02-03 22:41:34 +03:00
if ( ! ( lookup_symbols ( dl , ret ) ) ) {
free_dso_data ( ret ) ;
dlclose ( dl ) ;
return NULL ;
}
2005-04-28 02:32:00 +04:00
/*
* 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 ( ) ;
return ret ;
}
2005-12-02 18:39:16 +03:00
/* Return success on daemon active check. */
static int active ( struct message_data * message_data )
{
return 0 ;
}
2005-04-28 02:32:00 +04:00
/*
* Register for an event .
*
2005-12-02 18:39:16 +03:00
* Only one caller at a time here , because we use
* a FIFO and lock it against multiple accesses .
2005-04-28 02:32:00 +04:00
*/
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 ;
2005-12-02 18:39:16 +03:00
struct thread_status * thread , * thread_new = NULL ;
2005-04-28 02:32:00 +04:00
struct dso_data * dso_data ;
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 ;
2005-12-02 18:39:16 +03:00
# ifdef ELIBACC
2005-04-28 02:32:00 +04:00
ret = - ELIBACC ;
2005-12-02 18:39:16 +03:00
# else
ret = - ENODEV ;
# endif
2005-04-28 02:32:00 +04:00
goto out ;
}
2007-01-12 00:54:53 +03:00
2005-04-28 02:32:00 +04:00
/* 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 ;
}
2007-01-12 00:54:53 +03:00
if ( ! fill_device_data ( thread_new ) ) {
stack ;
ret = - ENODEV ;
goto out ;
}
2005-04-28 02:32:00 +04:00
lock_mutex ( ) ;
2005-12-02 18:39:16 +03:00
if ( ! ( thread = lookup_thread_status ( message_data ) ) ) {
unlock_mutex ( ) ;
if ( ! ( ret = do_register_device ( thread_new ) ) )
goto out ;
2005-04-28 02:32:00 +04:00
thread = thread_new ;
thread_new = NULL ;
/* Try to create the monitoring thread for this device. */
2005-12-02 18:39:16 +03:00
lock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
if ( ( ret = - create_thread ( thread ) ) ) {
unlock_mutex ( ) ;
2005-12-02 18:39:16 +03:00
do_unregister_device ( thread ) ;
2005-04-28 02:32:00 +04:00
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
2007-01-12 00:54:53 +03:00
unlock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
/* FIXME - If you fail to register for timeout events, you
still monitor all the other events . Is this the right
action for newly created devices ? Also , you are still
on the timeout registry , so if a timeout thread is
successfully started up later , you will start receiving
DM_EVENT_TIMEOUT events */
if ( thread - > events & DM_EVENT_TIMEOUT )
ret = - register_for_timeout ( thread ) ;
2007-01-12 00:54:53 +03:00
out :
2005-04-28 02:32:00 +04:00
/*
* Deallocate thread status after releasing
* the lock in case we haven ' t used it .
*/
if ( thread_new )
free_thread_status ( thread_new ) ;
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 ( ) ;
2005-12-02 18:39:16 +03:00
ret = - ENODEV ;
2005-04-28 02:32:00 +04:00
goto out ;
}
2005-04-28 18:02:30 +04:00
thread - > events & = ~ message_data - > events . field ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
if ( ! ( thread - > events & DM_EVENT_TIMEOUT ) )
unregister_for_timeout ( thread ) ;
2005-04-28 02:32:00 +04:00
/*
2005-12-02 18:39:16 +03:00
* In case there ' s no events to monitor on this device - >
* unlink and terminate its monitoring thread .
2005-04-28 02:32:00 +04:00
*/
if ( ! thread - > events ) {
2006-01-27 23:50:01 +03:00
UNLINK_THREAD ( thread ) ;
2007-01-08 18:18:52 +03:00
LINK ( thread , & _thread_registry_unused ) ;
2005-04-28 02:32:00 +04:00
}
2006-01-27 23:50:01 +03:00
unlock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
out :
2005-04-28 02:32:00 +04:00
return ret ;
}
2005-04-28 18:02:30 +04:00
/*
2005-12-02 18:39:16 +03:00
* Get registered device .
2005-04-28 18:02:30 +04:00
*
* 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
{
2005-12-02 18:39:16 +03:00
struct dm_event_daemon_message * msg = message_data - > msg ;
2005-04-28 18:02:30 +04:00
2007-01-08 18:18:52 +03:00
const char * fmt = " %s %s %u " ;
const char * dso = thread - > dso_data - > dso_name ;
2007-01-12 00:54:53 +03:00
const char * dev = thread - > device . uuid ;
unsigned events = ( ( thread - > status = = DM_THREAD_RUNNING )
& & ( thread - > events ) ) ? thread - > events : thread - >
events | DM_EVENT_REGISTRATION_PENDING ;
2007-01-08 18:18:52 +03:00
if ( msg - > data )
dm_free ( msg - > data ) ;
2007-01-12 00:54:53 +03:00
msg - > size = dm_saprintf ( & ( msg - > data ) , fmt , dso , dev , events ) ;
2005-04-29 17:41:25 +04:00
unlock_mutex ( ) ;
2005-04-28 18:02:30 +04:00
return 0 ;
}
2007-01-12 00:54:53 +03:00
static int want_registered_device ( char * dso_name , char * device_uuid ,
2005-12-02 18:39:16 +03:00
struct thread_status * thread )
2005-04-28 18:02:30 +04:00
{
2005-12-02 18:39:16 +03:00
/* If DSO names and device paths are equal. */
2007-01-12 00:54:53 +03:00
if ( dso_name & & device_uuid )
2005-12-02 18:39:16 +03:00
return ! strcmp ( dso_name , thread - > dso_data - > dso_name ) & &
2007-01-12 00:54:53 +03:00
! strcmp ( device_uuid , thread - > device . uuid ) ;
2005-12-02 18:39:16 +03:00
/* If DSO names are equal. */
if ( dso_name )
return ! strcmp ( dso_name , thread - > dso_data - > dso_name ) ;
2007-01-12 00:54:53 +03:00
2005-12-02 18:39:16 +03:00
/* If device paths are equal. */
2007-01-12 00:54:53 +03:00
if ( device_uuid )
return ! strcmp ( device_uuid , thread - > device . uuid ) ;
2005-04-28 18:02:30 +04:00
2005-12-02 18:39:16 +03:00
return 1 ;
}
2005-04-28 18:02:30 +04:00
2005-12-02 18:39:16 +03:00
static int _get_registered_device ( struct message_data * message_data , int next )
{
int hit = 0 ;
struct thread_status * thread ;
2005-05-02 15:02:19 +04:00
2005-12-02 18:39:16 +03:00
lock_mutex ( ) ;
2005-04-29 17:41:25 +04:00
2005-12-02 18:39:16 +03:00
/* Iterate list of threads checking if we want a particular one. */
2007-01-08 18:18:52 +03:00
list_iterate_items ( thread , & _thread_registry )
2007-01-12 00:54:53 +03:00
if ( ( hit = want_registered_device ( message_data - > dso_name ,
message_data - > device_uuid ,
thread ) ) )
break ;
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 - >
2005-12-02 18:39:16 +03:00
* fetch next conforming element off the list .
2005-04-29 17:41:25 +04:00
*/
2006-12-20 17:35:02 +03:00
if ( ! hit | | ! next )
goto out ;
2005-04-28 18:02:30 +04:00
2006-12-20 17:35:02 +03:00
do {
2007-01-08 18:18:52 +03:00
if ( list_end ( & _thread_registry , & thread - > list ) )
2006-12-20 17:35:02 +03:00
goto out ;
2007-01-12 00:54:53 +03:00
thread = list_item ( thread - > list . n , struct thread_status ) ;
} while ( ! want_registered_device ( message_data - > dso_name , NULL , thread ) ) ;
2006-12-20 17:35:02 +03:00
return registered_device ( message_data , thread ) ;
2005-04-28 18:02:30 +04:00
2007-01-12 00:54:53 +03:00
out :
2005-05-02 15:02:19 +04:00
unlock_mutex ( ) ;
2005-12-02 18:39:16 +03:00
return - ENOENT ;
}
static int get_registered_device ( struct message_data * message_data )
{
return _get_registered_device ( message_data , 0 ) ;
}
static int get_next_registered_device ( struct message_data * message_data )
{
return _get_registered_device ( message_data , 1 ) ;
2005-04-28 18:02:30 +04:00
}
2005-12-02 18:39:16 +03:00
static int set_timeout ( struct message_data * message_data )
{
struct thread_status * thread ;
lock_mutex ( ) ;
if ( ( thread = lookup_thread_status ( message_data ) ) )
2007-01-12 00:54:53 +03:00
thread - > timeout = message_data - > timeout . secs ;
2005-12-02 18:39:16 +03:00
unlock_mutex ( ) ;
return thread ? 0 : - ENODEV ;
}
static int get_timeout ( struct message_data * message_data )
{
struct thread_status * thread ;
struct dm_event_daemon_message * msg = message_data - > msg ;
2007-01-08 18:18:52 +03:00
if ( msg - > data )
dm_free ( msg - > data ) ;
2005-12-02 18:39:16 +03:00
lock_mutex ( ) ;
2007-01-08 18:18:52 +03:00
if ( ( thread = lookup_thread_status ( message_data ) ) ) {
2007-01-12 00:54:53 +03:00
msg - > size =
dm_saprintf ( & ( msg - > data ) , " % " PRIu32 , thread - > timeout ) ;
2007-01-08 18:18:52 +03:00
} else {
msg - > data = NULL ;
msg - > size = 0 ;
}
2005-12-02 18:39:16 +03:00
unlock_mutex ( ) ;
return thread ? 0 : - ENODEV ;
}
2005-04-28 02:32:00 +04:00
/* Initialize a fifos structure with path names. */
2007-01-08 18:18:52 +03:00
static void init_fifos ( struct dm_event_fifos * fifos )
2005-04-28 02:32:00 +04:00
{
2007-01-08 18:18:52 +03:00
memset ( fifos , 0 , sizeof ( * fifos ) ) ;
2005-12-02 18:39:16 +03:00
2007-01-08 18:18:52 +03:00
fifos - > client_path = DM_EVENT_FIFO_CLIENT ;
fifos - > server_path = DM_EVENT_FIFO_SERVER ;
2005-04-28 02:32:00 +04:00
}
/* Open fifos used for client communication. */
2005-12-02 18:39:16 +03:00
static int open_fifos ( struct dm_event_fifos * fifos )
2005-04-28 02:32:00 +04:00
{
2007-01-08 18:18:52 +03:00
/* Create fifos */
if ( ( ( mkfifo ( fifos - > client_path , 0600 ) = = - 1 ) & & errno ! = EEXIST ) | |
( ( mkfifo ( fifos - > server_path , 0600 ) = = - 1 ) & & errno ! = EEXIST ) ) {
syslog ( LOG_ERR , " %s: Failed to create a fifo. \n " , __func__ ) ;
2005-04-28 02:32:00 +04:00
stack ;
2007-01-08 18:18:52 +03:00
return - errno ;
}
2007-01-12 00:54:53 +03:00
struct stat st ;
/* Warn about wrong permissions if applicable */
if ( ( ! stat ( fifos - > client_path , & st ) ) & & ( st . st_mode & 0777 ) ! = 0600 )
syslog ( LOG_WARNING , " Fixing wrong permissions on %s " ,
fifos - > client_path ) ;
if ( ( ! stat ( fifos - > server_path , & st ) ) & & ( st . st_mode & 0777 ) ! = 0600 )
syslog ( LOG_WARNING , " Fixing wrong permissions on %s " ,
fifos - > server_path ) ;
2007-01-08 18:18:52 +03:00
/* If they were already there, make sure permissions are ok. */
if ( chmod ( fifos - > client_path , 0600 ) ) {
syslog ( LOG_ERR , " Unable to set correct file permissions on %s " ,
2007-01-12 00:54:53 +03:00
fifos - > client_path ) ;
2007-01-08 18:18:52 +03:00
return - errno ;
}
if ( chmod ( fifos - > server_path , 0600 ) ) {
syslog ( LOG_ERR , " Unable to set correct file permissions on %s " ,
2007-01-12 00:54:53 +03:00
fifos - > server_path ) ;
2007-01-08 18:18:52 +03:00
return - errno ;
}
/* Need to open read+write or we will block or fail */
if ( ( fifos - > server = open ( fifos - > server_path , O_RDWR ) ) < 0 ) {
stack ;
return - errno ;
2005-04-28 02:32:00 +04:00
}
/* Need to open read+write for select() to work. */
2007-01-08 18:18:52 +03:00
if ( ( fifos - > client = open ( fifos - > client_path , O_RDWR ) ) < 0 ) {
2005-04-28 02:32:00 +04:00
stack ;
close ( fifos - > server ) ;
2007-01-08 18:18:52 +03:00
return - errno ;
2005-04-28 02:32:00 +04:00
}
2005-12-02 18:39:16 +03:00
return 0 ;
2005-04-28 02:32:00 +04:00
}
/*
* Read message from client making sure that data is available
2006-03-10 00:33:59 +03:00
* and a complete message is read . Must not block indefinitely .
2005-04-28 02:32:00 +04:00
*/
2007-01-12 00:54:53 +03:00
static int client_read ( struct dm_event_fifos * fifos ,
struct dm_event_daemon_message * msg )
2005-04-28 02:32:00 +04:00
{
2006-03-10 00:33:59 +03:00
struct timeval t ;
2006-01-31 17:50:38 +03:00
unsigned bytes = 0 ;
int ret = 0 ;
2005-04-28 02:32:00 +04:00
fd_set fds ;
2007-01-08 18:18:52 +03:00
int header = 1 ;
size_t size = 2 * sizeof ( uint32_t ) ; /* status + size */
char * buf = alloca ( size ) ;
msg - > data = NULL ;
2005-04-28 02:32:00 +04:00
errno = 0 ;
2007-01-08 18:18:52 +03:00
while ( bytes < size & & errno ! = EOF ) {
2006-03-10 00:33:59 +03:00
/* Watch client read FIFO for input. */
FD_ZERO ( & fds ) ;
FD_SET ( fifos - > client , & fds ) ;
t . tv_sec = 1 ;
t . tv_usec = 0 ;
2007-01-12 00:54:53 +03:00
ret = select ( fifos - > client + 1 , & fds , NULL , NULL , & t ) ;
2006-03-10 00:33:59 +03:00
2007-01-12 00:54:53 +03:00
if ( ! ret & & ! bytes ) /* nothing to read */
2006-03-10 00:33:59 +03:00
return 0 ;
2007-01-12 00:54:53 +03:00
if ( ! ret ) /* trying to finish read */
2006-03-10 00:33:59 +03:00
continue ;
2007-01-12 00:54:53 +03:00
if ( ret < 0 ) /* error */
2006-03-10 00:33:59 +03:00
return 0 ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
ret = read ( fifos - > client , buf + bytes , size - bytes ) ;
2005-04-28 02:32:00 +04:00
bytes + = ret > 0 ? ret : 0 ;
2007-01-12 00:54:53 +03:00
if ( bytes = = 2 * sizeof ( uint32_t ) & & header ) {
msg - > cmd = ntohl ( * ( ( uint32_t * ) buf ) ) ;
msg - > size = ntohl ( * ( ( uint32_t * ) buf + 1 ) ) ;
2007-01-08 18:18:52 +03:00
buf = msg - > data = dm_malloc ( msg - > size ) ;
size = msg - > size ;
bytes = 0 ;
header = 0 ;
}
2005-04-28 02:32:00 +04:00
}
2007-01-08 18:18:52 +03:00
if ( bytes ! = size ) {
if ( msg - > data )
dm_free ( msg - > data ) ;
msg - > data = NULL ;
2007-01-12 00:54:53 +03:00
msg - > size = 0 ;
2007-01-08 18:18:52 +03:00
}
return bytes = = size ;
2005-04-28 02:32:00 +04:00
}
/*
* Write a message to the client making sure that it is ready to write .
*/
2007-01-12 00:54:53 +03:00
static int client_write ( struct dm_event_fifos * fifos ,
struct dm_event_daemon_message * msg )
2005-04-28 02:32:00 +04:00
{
2006-01-31 17:50:38 +03:00
unsigned bytes = 0 ;
int ret = 0 ;
2005-04-28 02:32:00 +04:00
fd_set fds ;
2007-01-12 00:54:53 +03:00
size_t size = 2 * sizeof ( uint32_t ) + msg - > size ;
2007-01-08 18:18:52 +03:00
char * buf = alloca ( size ) ;
* ( ( uint32_t * ) buf ) = htonl ( msg - > cmd ) ;
* ( ( uint32_t * ) buf + 1 ) = htonl ( msg - > size ) ;
2007-01-12 00:54:53 +03:00
if ( msg - > data )
memcpy ( buf + 2 * sizeof ( uint32_t ) , msg - > data , msg - > size ) ;
2007-01-08 18:18:52 +03:00
2005-04-29 17:41:25 +04:00
errno = 0 ;
2007-01-08 18:18:52 +03:00
while ( bytes < size & & errno ! = EIO ) {
2005-04-29 17:41:25 +04:00
do {
/* Watch client write FIFO to be ready for output. */
FD_ZERO ( & fds ) ;
FD_SET ( fifos - > server , & fds ) ;
2007-01-12 00:54:53 +03:00
} while ( select ( fifos - > server + 1 , NULL , & fds , NULL , NULL ) ! =
1 ) ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
ret = write ( fifos - > server , buf + bytes , size - bytes ) ;
2005-04-29 17:41:25 +04:00
bytes + = ret > 0 ? ret : 0 ;
}
2007-01-08 18:18:52 +03:00
return bytes = = size ;
2005-04-28 02:32:00 +04:00
}
2005-12-02 18:39:16 +03:00
/*
* Handle a client request .
*
* We put the request handling functions into
* a list because of the growing number .
*/
static int handle_request ( struct dm_event_daemon_message * msg ,
struct message_data * message_data )
{
static struct {
unsigned int cmd ;
2007-01-12 00:54:53 +03:00
int ( * f ) ( struct message_data * ) ;
2005-12-02 18:39:16 +03:00
} requests [ ] = {
2007-01-12 00:54:53 +03:00
{ DM_EVENT_CMD_REGISTER_FOR_EVENT , register_for_event } ,
{ DM_EVENT_CMD_UNREGISTER_FOR_EVENT , unregister_for_event } ,
{ DM_EVENT_CMD_GET_REGISTERED_DEVICE , get_registered_device } ,
{ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE ,
get_next_registered_device } ,
{ DM_EVENT_CMD_SET_TIMEOUT , set_timeout } ,
{ DM_EVENT_CMD_GET_TIMEOUT , get_timeout } ,
{ DM_EVENT_CMD_ACTIVE , active } ,
2005-12-02 18:39:16 +03:00
} , * req ;
2006-12-20 17:35:02 +03:00
for ( req = requests ; req < requests + sizeof ( requests ) ; req + + )
2007-01-08 18:18:52 +03:00
if ( req - > cmd = = msg - > cmd )
2005-12-02 18:39:16 +03:00
return req - > f ( message_data ) ;
return - EINVAL ;
}
2005-04-28 02:32:00 +04:00
/* Process a request passed from the communication thread. */
2005-12-02 18:39:16 +03:00
static int do_process_request ( struct dm_event_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 ;
/* Parse the message. */
2005-12-02 18:39:16 +03:00
memset ( & message_data , 0 , sizeof ( message_data ) ) ;
2005-04-28 18:02:30 +04:00
message_data . msg = msg ;
2007-01-12 00:54:53 +03:00
if ( msg - > cmd ! = DM_EVENT_CMD_ACTIVE & & ! parse_message ( & message_data ) ) {
2005-04-28 18:02:30 +04:00
stack ;
2005-04-30 02:12:09 +04:00
ret = - EINVAL ;
2006-12-20 17:35:02 +03:00
} else
2005-12-02 18:39:16 +03:00
ret = handle_request ( msg , & message_data ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
free_message ( & message_data ) ;
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-12-02 18:39:16 +03:00
static void process_request ( struct dm_event_fifos * fifos )
2005-04-28 02:32:00 +04:00
{
2005-12-02 18:39:16 +03:00
struct dm_event_daemon_message msg ;
2006-03-10 00:33:59 +03:00
memset ( & msg , 0 , sizeof ( msg ) ) ;
/*
2007-01-12 00:54:53 +03:00
* Read the request from the client ( client_read , client_write
* give true on success and false on failure ) .
2006-03-10 00:33:59 +03:00
*/
if ( ! client_read ( fifos , & msg ) )
2005-04-28 02:32:00 +04:00
return ;
2007-01-08 18:18:52 +03:00
msg . cmd = do_process_request ( & msg ) ;
if ( ! msg . data ) {
msg . data = dm_strdup ( strerror ( - msg . cmd ) ) ;
2007-01-12 00:54:53 +03:00
if ( msg . data )
msg . size = strlen ( msg . data ) + 1 ;
else {
msg . size = 0 ;
stack ;
}
2007-01-08 18:18:52 +03:00
}
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
if ( ! client_write ( fifos , & msg ) )
2005-04-28 02:32:00 +04:00
stack ;
2007-01-08 18:18:52 +03:00
if ( msg . data )
dm_free ( msg . data ) ;
2005-04-28 02:32:00 +04:00
}
2006-01-27 23:50:01 +03:00
static void cleanup_unused_threads ( void )
{
int ret ;
struct list * l ;
struct thread_status * thread ;
lock_mutex ( ) ;
2007-01-08 18:18:52 +03:00
while ( ( l = list_first ( & _thread_registry_unused ) ) ) {
2006-01-27 23:50:01 +03:00
thread = list_item ( l , struct thread_status ) ;
if ( thread - > processing ) {
2007-01-12 00:54:53 +03:00
goto out ; /* cleanup on the next round */
2006-01-27 23:50:01 +03:00
}
2007-01-08 18:18:52 +03:00
if ( thread - > status = = DM_THREAD_RUNNING ) {
thread - > status = DM_THREAD_SHUTDOWN ;
goto out ;
} else if ( thread - > status = = DM_THREAD_SHUTDOWN ) {
if ( ! thread - > events ) {
/* turn codes negative -- should we be returning this? */
ret = terminate_thread ( thread ) ;
if ( ret = = ESRCH ) {
thread - > status = DM_THREAD_DONE ;
} else if ( ret ) {
2007-01-12 00:54:53 +03:00
syslog ( LOG_ERR ,
" Unable to terminate thread: %s \n " ,
2007-01-08 18:18:52 +03:00
strerror ( - ret ) ) ;
stack ;
}
goto out ;
} else {
list_del ( l ) ;
2007-01-12 00:54:53 +03:00
syslog ( LOG_ERR ,
" thread can't be on unused list unless !thread->events " ) ;
2007-01-08 18:18:52 +03:00
thread - > status = DM_THREAD_RUNNING ;
LINK_THREAD ( thread ) ;
2006-01-27 23:50:01 +03:00
}
2007-01-08 18:18:52 +03:00
} else if ( thread - > status = = DM_THREAD_DONE ) {
list_del ( l ) ;
pthread_join ( thread - > thread , NULL ) ;
lib_put ( thread - > dso_data ) ;
free_thread_status ( thread ) ;
2006-01-27 23:50:01 +03:00
}
}
2007-01-12 00:54:53 +03:00
out :
2006-01-27 23:50:01 +03:00
unlock_mutex ( ) ;
}
2005-12-02 18:39:16 +03:00
static void sig_alarm ( int signum )
2005-04-28 02:32:00 +04:00
{
2005-12-02 18:39:16 +03:00
pthread_testcancel ( ) ;
}
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
/* Init thread signal handling. */
static void init_thread_signals ( void )
{
2006-05-11 23:08:02 +04:00
sigset_t my_sigset ;
2005-12-02 18:39:16 +03:00
struct sigaction act ;
2007-01-12 00:54:53 +03:00
2005-12-02 18:39:16 +03:00
memset ( & act , 0 , sizeof ( act ) ) ;
act . sa_handler = sig_alarm ;
sigaction ( SIGALRM , & act , NULL ) ;
2006-05-11 23:08:02 +04:00
sigfillset ( & my_sigset ) ;
2007-01-08 18:18:52 +03:00
/* These are used for exiting */
sigdelset ( & my_sigset , SIGTERM ) ;
sigdelset ( & my_sigset , SIGINT ) ;
sigdelset ( & my_sigset , SIGHUP ) ;
sigdelset ( & my_sigset , SIGQUIT ) ;
2006-05-11 23:08:02 +04:00
pthread_sigmask ( SIG_BLOCK , & my_sigset , NULL ) ;
2005-04-28 02:32:00 +04:00
}
2007-01-08 18:18:52 +03:00
/*
* exit_handler
* @ sig
*
* Set the global variable which the process should
* be watching to determine when to exit .
*/
static void exit_handler ( int sig )
2005-04-28 02:32:00 +04:00
{
2007-01-08 18:18:52 +03:00
/*
* We exit when ' _exit_now ' is set .
* That is , when a signal has been received .
*
* We can not simply set ' _exit_now ' unless all
* threads are done processing .
*/
if ( ! _thread_registries_empty ) {
syslog ( LOG_ERR , " There are still devices being monitored. " ) ;
syslog ( LOG_ERR , " Refusing to exit. " ) ;
} else
_exit_now = 1 ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
}
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
static int lock_pidfile ( void )
{
int lf ;
2007-01-12 00:54:53 +03:00
char pidfile [ ] = DMEVENTD_PIDFILE ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
if ( ( lf = open ( pidfile , O_CREAT | O_RDWR , 0644 ) ) < 0 )
2007-01-08 18:18:52 +03:00
exit ( EXIT_OPEN_PID_FAILURE ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
if ( flock ( lf , LOCK_EX | LOCK_NB ) < 0 )
2007-01-08 18:18:52 +03:00
exit ( EXIT_LOCKFILE_INUSE ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
if ( ! storepid ( lf ) )
2007-01-08 18:18:52 +03:00
exit ( EXIT_FAILURE ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
return 0 ;
2005-04-28 02:32:00 +04:00
}
2007-01-08 18:18:52 +03:00
static void daemonize ( void )
2005-04-28 02:32:00 +04:00
{
2007-01-08 18:18:52 +03:00
int status ;
int pid ;
int fd ;
struct rlimit rlim ;
struct timeval tval ;
sigset_t my_sigset ;
sigemptyset ( & my_sigset ) ;
if ( sigprocmask ( SIG_SETMASK , & my_sigset , NULL ) < 0 ) {
fprintf ( stderr , " Unable to restore signals. " ) ;
exit ( EXIT_FAILURE ) ;
}
signal ( SIGTERM , & exit_handler ) ;
2005-05-02 15:02:19 +04:00
2007-01-08 18:18:52 +03:00
pid = fork ( ) ;
2005-05-02 15:02:19 +04:00
2007-01-08 18:18:52 +03:00
if ( pid < 0 )
exit ( EXIT_FAILURE ) ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
if ( pid ) {
/* Wait for response from child */
while ( ! waitpid ( pid , & status , WNOHANG ) & & ! _exit_now ) {
tval . tv_sec = 0 ;
2007-01-12 00:54:53 +03:00
tval . tv_usec = 250000 ; /* .25 sec */
2007-01-08 18:18:52 +03:00
select ( 0 , NULL , NULL , NULL , & tval ) ;
}
2007-01-12 00:54:53 +03:00
if ( _exit_now ) /* Child has signaled it is ok - we can exit now */
2007-01-08 18:18:52 +03:00
exit ( EXIT_SUCCESS ) ;
/* Problem with child. Determine what it is by exit code */
switch ( WEXITSTATUS ( status ) ) {
case EXIT_LOCKFILE_INUSE :
break ;
case EXIT_DESC_CLOSE_FAILURE :
break ;
case EXIT_DESC_OPEN_FAILURE :
break ;
case EXIT_OPEN_PID_FAILURE :
break ;
case EXIT_FIFO_FAILURE :
break ;
case EXIT_CHDIR_FAILURE :
break ;
default :
break ;
}
2007-01-12 00:54:53 +03:00
exit ( EXIT_FAILURE ) ; /* Redundant */
2007-01-08 18:18:52 +03:00
}
setsid ( ) ;
if ( chdir ( " / " ) )
exit ( EXIT_CHDIR_FAILURE ) ;
if ( getrlimit ( RLIMIT_NOFILE , & rlim ) < 0 )
2007-01-12 00:54:53 +03:00
fd = 256 ; /* just have to guess */
2007-01-08 18:18:52 +03:00
else
fd = rlim . rlim_cur ;
for ( - - fd ; fd > = 0 ; fd - - )
close ( fd ) ;
if ( ( open ( " /dev/null " , O_RDONLY ) < 0 ) | |
( open ( " /dev/null " , O_WRONLY ) < 0 ) | |
( open ( " /dev/null " , O_WRONLY ) < 0 ) )
exit ( EXIT_DESC_OPEN_FAILURE ) ;
openlog ( " dmeventd " , LOG_PID , LOG_DAEMON ) ;
2007-01-12 00:54:53 +03:00
lock_pidfile ( ) ; /* exits if failure */
2007-01-08 18:18:52 +03:00
/* Set the rest of the signals to cause '_exit_now' to be set */
signal ( SIGINT , & exit_handler ) ;
signal ( SIGHUP , & exit_handler ) ;
signal ( SIGQUIT , & exit_handler ) ;
}
int main ( int argc , char * argv [ ] )
{
int ret ;
struct dm_event_fifos fifos ;
//struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON};
daemonize ( ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
init_thread_signals ( ) ;
2005-04-29 02:47:52 +04:00
2005-12-02 18:39:16 +03:00
//multilog_clear_logging();
//multilog_add_type(std_syslog, &logdata);
//multilog_init_verbose(std_syslog, _LOG_DEBUG);
//multilog_async(1);
2005-04-29 02:47:52 +04:00
2007-01-08 18:18:52 +03:00
init_fifos ( & fifos ) ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:35:08 +03:00
pthread_mutex_init ( & _global_mutex , NULL ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
# ifdef MCL_CURRENT
if ( mlockall ( MCL_CURRENT | MCL_FUTURE ) = = - 1 )
exit ( EXIT_FAILURE ) ;
# endif
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
if ( ( ret = open_fifos ( & fifos ) ) )
2007-01-08 18:18:52 +03:00
exit ( EXIT_FIFO_FAILURE ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
/* Signal parent, letting them know we are ready to go. */
2007-01-08 18:18:52 +03:00
kill ( getppid ( ) , SIGTERM ) ;
syslog ( LOG_INFO , " dmeventd ready for processing. " ) ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
while ( ! _exit_now ) {
2005-12-02 18:39:16 +03:00
process_request ( & fifos ) ;
2006-01-27 23:50:01 +03:00
cleanup_unused_threads ( ) ;
2007-01-12 00:54:53 +03:00
if ( ! list_empty ( & _thread_registry )
| | ! list_empty ( & _thread_registry_unused ) )
2007-01-08 18:18:52 +03:00
_thread_registries_empty = 0 ;
else
_thread_registries_empty = 1 ;
}
2006-01-27 23:50:01 +03:00
exit_dm_lib ( ) ;
2005-12-02 18:39:16 +03:00
# ifdef MCL_CURRENT
munlockall ( ) ;
# endif
2007-01-08 18:35:08 +03:00
pthread_mutex_destroy ( & _global_mutex ) ;
2005-04-28 02:32:00 +04:00
2007-01-08 18:18:52 +03:00
syslog ( LOG_INFO , " dmeventd shutting down. " ) ;
closelog ( ) ;
2005-05-02 15:02:19 +04:00
exit ( EXIT_SUCCESS ) ;
2005-04-28 02:32:00 +04:00
}