2005-04-28 02:32:00 +04:00
/*
2015-10-09 22:57:48 +03:00
* Copyright ( C ) 2005 - 2015 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
*/
2008-11-04 02:01:21 +03:00
# include "dm-logging.h"
2005-04-28 02:32:00 +04:00
2015-07-06 19:30:18 +03:00
# include "libdevmapper-event.h"
# include "dmeventd.h"
2015-10-10 19:32:53 +03:00
# include "tool.h"
2005-04-28 02:32:00 +04:00
# include <dlfcn.h>
# include <pthread.h>
# include <sys/file.h>
2007-01-16 21:04:15 +03:00
# include <sys/stat.h>
2005-04-28 02:32:00 +04:00
# include <sys/wait.h>
2007-01-16 21:04:15 +03:00
# include <sys/time.h>
# include <sys/resource.h>
2008-07-09 17:26:07 +04:00
# include <signal.h>
2007-01-12 00:54:53 +03:00
# include <arpa/inet.h> /* for htonl, ntohl */
2013-06-15 04:28:54 +04:00
# include <fcntl.h> /* for musl libc */
2005-04-28 02:32:00 +04:00
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2011-07-28 17:03:37 +04:00
/*
* Kernel version 2.6 .36 and higher has
* new OOM killer adjustment interface .
*/
# define OOM_ADJ_FILE_OLD " / proc / self / oom_adj"
# define OOM_ADJ_FILE " / proc / self / oom_score_adj"
2007-01-22 18:03:57 +03:00
2007-01-19 20:22:17 +03:00
/* From linux/oom.h */
2011-07-28 17:03:37 +04:00
/* Old interface */
2007-01-19 20:22:17 +03:00
# define OOM_DISABLE (-17)
# define OOM_ADJUST_MIN (-16)
2011-07-28 17:03:37 +04:00
/* New interface */
# define OOM_SCORE_ADJ_MIN (-1000)
2007-01-19 20:22:17 +03:00
2011-07-28 17:06:50 +04:00
/* Systemd on-demand activation support */
2014-03-14 18:57:28 +04:00
# define SD_RUNTIME_UNIT_FILE_DIR DEFAULT_DM_RUN_DIR " / systemd / system / "
2012-03-14 19:51:51 +04:00
# define SD_ACTIVATION_ENV_VAR_NAME "SD_ACTIVATION"
2011-07-28 17:06:50 +04:00
# define SD_LISTEN_PID_ENV_VAR_NAME "LISTEN_PID"
# define SD_LISTEN_FDS_ENV_VAR_NAME "LISTEN_FDS"
# define SD_LISTEN_FDS_START 3
# define SD_FD_FIFO_SERVER SD_LISTEN_FDS_START
# define SD_FD_FIFO_CLIENT (SD_LISTEN_FDS_START + 1)
2005-12-02 18:39:16 +03:00
# endif
2005-04-28 02:32:00 +04:00
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 */
2007-01-08 18:18:52 +03:00
2005-04-28 02:32:00 +04:00
/* List (un)link macros. */
2008-11-04 01:14:30 +03:00
# define LINK(x, head) dm_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
2008-11-04 01:14:30 +03:00
# define UNLINK(x) dm_list_del(&(x)->list)
2005-04-28 02:32:00 +04:00
# 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
2007-01-16 23:27:07 +03:00
/*
2007-01-19 20:22:17 +03:00
Global mutex for thread list access . Has to be held when :
2007-01-16 23:27:07 +03:00
- iterating thread list
- adding or removing elements from thread list
- changing or reading thread_status ' s fields :
processing , status , events
Use _lock_mutex ( ) and _unlock_mutex ( ) to hold / release it
*/
2007-01-08 18:18:52 +03:00
static pthread_mutex_t _global_mutex ;
2007-01-23 20:38:39 +03:00
/*
There are three states a thread can attain ( see struct
thread_status , field int status ) :
- DM_THREAD_RUNNING : thread has started up and is either working or
waiting for events . . . transitions to either SHUTDOWN or DONE
- DM_THREAD_SHUTDOWN : thread is still doing something , but it is
supposed to terminate ( and transition to DONE ) as soon as it
finishes whatever it was doing at the point of flipping state to
SHUTDOWN . . . the thread is still on the thread list
- DM_THREAD_DONE : thread has terminated and has been moved over to
unused thread list , cleanup pending
*/
2007-01-08 18:18:52 +03:00
# define DM_THREAD_RUNNING 0
# define DM_THREAD_SHUTDOWN 1
# define DM_THREAD_DONE 2
2005-04-28 02:32:00 +04:00
2007-01-16 23:27:07 +03:00
# define THREAD_STACK_SIZE (300*1024)
2007-01-16 23:13:04 +03:00
2015-10-13 16:14:11 +03:00
static int _debug_level = 0 ;
static int _use_syslog = 1 ;
2011-07-28 17:06:50 +04:00
static int _systemd_activation = 0 ;
2010-05-14 18:56:39 +04:00
static int _foreground = 0 ;
2010-10-20 19:12:12 +04:00
static int _restart = 0 ;
static char * * _initial_registrations = 0 ;
2010-05-14 18:56:39 +04:00
2014-11-04 21:56:20 +03:00
/* FIXME Make configurable at runtime */
2015-10-10 19:32:53 +03:00
__attribute__ ( ( format ( printf , 4 , 5 ) ) )
static void _dmeventd_log ( int level , const char * file , int line ,
const char * format , . . . )
2014-11-04 21:56:20 +03:00
{
va_list ap ;
2015-10-10 19:32:53 +03:00
va_start ( ap , format ) ;
dm_event_log ( " dm " , level , file , line , 0 , format , ap ) ;
2014-11-04 21:56:20 +03:00
va_end ( ap ) ;
}
2015-10-13 21:57:59 +03:00
# ifdef DEBUG
# define DEBUGLOG log_debug
2014-11-04 21:56:20 +03:00
static const char * decode_cmd ( uint32_t cmd )
{
switch ( cmd ) {
2015-06-15 15:35:33 +03:00
case DM_EVENT_CMD_ACTIVE : return " ACTIVE " ;
case DM_EVENT_CMD_REGISTER_FOR_EVENT : return " REGISTER_FOR_EVENT " ;
case DM_EVENT_CMD_UNREGISTER_FOR_EVENT : return " UNREGISTER_FOR_EVENT " ;
case DM_EVENT_CMD_GET_REGISTERED_DEVICE : return " GET_REGISTERED_DEVICE " ;
case DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE : return " GET_NEXT_REGISTERED_DEVICE " ;
case DM_EVENT_CMD_SET_TIMEOUT : return " SET_TIMEOUT " ;
case DM_EVENT_CMD_GET_TIMEOUT : return " GET_TIMEOUT " ;
case DM_EVENT_CMD_HELLO : return " HELLO " ;
case DM_EVENT_CMD_DIE : return " DIE " ;
case DM_EVENT_CMD_GET_STATUS : return " GET_STATUS " ;
case DM_EVENT_CMD_GET_PARAMETERS : return " GET_PARAMETERS " ;
default : return " unknown " ;
2014-11-04 21:56:20 +03:00
}
}
# else
# define DEBUGLOG(fmt, args...) do { } while (0)
# endif
2005-04-28 02:32:00 +04:00
/* Data kept about a DSO. */
struct dso_data {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2005-04-28 02:32:00 +04:00
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-23 20:38:39 +03:00
void ( * process_event ) ( struct dm_task * dmt , enum dm_event_mask event , void * * user ) ;
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 ,
2007-01-23 20:38:39 +03:00
int minor , void * * user ) ;
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 ,
2007-01-23 20:38:39 +03:00
int major , int minor , void * * user ) ;
2005-04-28 02:32:00 +04:00
} ;
2008-11-04 01:14:30 +03:00
static DM_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 {
2007-02-02 20:08:51 +03:00
char * id ;
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. */
2013-04-19 22:44:20 +04:00
char * events_str ; /* Events string as fetched from message. */
enum dm_event_mask events_field ; /* Events bitfield. */
char * timeout_str ;
uint32_t timeout_secs ;
2005-12-02 18:39:16 +03:00
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 {
2008-11-04 01:14:30 +03:00
struct dm_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-23 20:38:39 +03:00
int status ; /* see DM_THREAD_{RUNNING,SHUTDOWN,DONE}
constants above */
2007-01-15 21:21:01 +03:00
enum dm_event_mask events ; /* bitfield for event filter. */
enum dm_event_mask current_events ; /* bitfield for occured events. */
2007-01-12 00:54:53 +03:00
struct dm_task * current_task ;
2005-12-02 18:39:16 +03:00
time_t next_time ;
uint32_t timeout ;
2008-11-04 01:14:30 +03:00
struct dm_list timeout_list ;
2007-01-23 20:38:39 +03:00
void * dso_private ; /* dso per-thread status variable */
2005-04-28 02:32:00 +04:00
} ;
2008-11-04 01:14:30 +03:00
static DM_LIST_INIT ( _thread_registry ) ;
static DM_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 ;
2008-11-04 01:14:30 +03:00
static DM_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. */
2012-06-21 23:20:01 +04:00
static struct thread_status * _alloc_thread_status ( const struct message_data * data ,
2007-01-15 21:58:40 +03:00
struct dso_data * dso_data )
2005-04-28 02:32:00 +04:00
{
2014-04-17 11:59:54 +04:00
struct thread_status * ret ;
2005-04-28 02:32:00 +04:00
2014-04-17 11:59:54 +04:00
if ( ! ( ret = dm_zalloc ( sizeof ( * ret ) ) ) )
2007-01-15 21:58:40 +03:00
return NULL ;
2007-03-16 17:36:14 +03:00
if ( ! ( ret - > device . uuid = dm_strdup ( data - > device_uuid ) ) ) {
2007-01-15 21:58:40 +03:00
dm_free ( ret ) ;
return NULL ;
2005-04-28 02:32:00 +04:00
}
2007-01-15 21:58:40 +03:00
ret - > dso_data = dso_data ;
2013-04-19 22:44:20 +04:00
ret - > events = data - > events_field ;
ret - > timeout = data - > timeout_secs ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & ret - > timeout_list ) ;
2007-01-15 21:58:40 +03:00
2005-04-28 02:32:00 +04:00
return ret ;
}
2010-03-30 18:37:28 +04:00
static void _lib_put ( struct dso_data * data ) ;
2007-01-15 21:58:40 +03:00
static void _free_thread_status ( struct thread_status * thread )
2005-04-28 02:32:00 +04:00
{
2010-03-30 18:37:28 +04:00
_lib_put ( thread - > dso_data ) ;
2007-04-23 19:06:03 +04:00
if ( thread - > current_task )
dm_task_destroy ( thread - > current_task ) ;
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. */
2007-01-15 21:58:40 +03:00
static struct dso_data * _alloc_dso_data ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
2010-10-01 01:06:50 +04:00
struct dso_data * ret = ( typeof ( ret ) ) dm_zalloc ( sizeof ( * ret ) ) ;
2005-04-28 02:32:00 +04:00
2006-12-20 17:35:02 +03:00
if ( ! ret )
return NULL ;
2007-03-16 17:36:14 +03:00
if ( ! ( ret - > dso_name = dm_strdup ( data - > dso_name ) ) ) {
2006-12-20 17:35:02 +03:00
dm_free ( ret ) ;
return NULL ;
2005-04-28 02:32:00 +04:00
}
return ret ;
}
2013-08-01 00:23:13 +04:00
/*
* Create a device monitoring thread .
* N . B . Error codes returned are positive .
*/
2007-01-16 23:13:04 +03:00
static int _pthread_create_smallstack ( pthread_t * t , void * ( * fun ) ( void * ) , void * arg )
{
2013-08-01 00:23:13 +04:00
int r ;
pthread_t tmp ;
2007-01-16 23:13:04 +03:00
pthread_attr_t attr ;
2013-08-01 00:23:13 +04:00
/*
* From pthread_attr_init man page :
* POSIX .1 - 2001 documents an ENOMEM error for pthread_attr_init ( ) ; on
* Linux these functions always succeed ( but portable and future - proof
* applications should nevertheless handle a possible error return ) .
*/
if ( ( r = pthread_attr_init ( & attr ) ) ! = 0 )
return r ;
2007-01-16 23:27:07 +03:00
/*
* We use a smaller stack since it gets preallocated in its entirety
*/
2007-01-16 23:13:04 +03:00
pthread_attr_setstacksize ( & attr , THREAD_STACK_SIZE ) ;
2013-08-01 00:23:13 +04:00
/*
* If no - one will be waiting , we need to detach .
*/
if ( ! t ) {
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED ) ;
t = & tmp ;
}
r = pthread_create ( t , & attr , fun , arg ) ;
pthread_attr_destroy ( & attr ) ;
return r ;
2007-01-16 23:13:04 +03:00
}
2007-01-15 21:58:40 +03:00
static void _free_dso_data ( struct dso_data * data )
2005-04-28 02:32:00 +04:00
{
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 ) */
2007-01-17 00:13:07 +03:00
static int _fetch_string ( char * * ptr , char * * src , const int delimiter )
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. */
2007-01-15 21:58:40 +03:00
static void _free_message ( struct message_data * message_data )
2005-04-28 02:32:00 +04:00
{
2010-08-16 22:19:46 +04:00
dm_free ( message_data - > id ) ;
dm_free ( message_data - > dso_name ) ;
dm_free ( message_data - > device_uuid ) ;
2013-04-19 22:44:20 +04:00
dm_free ( message_data - > events_str ) ;
dm_free ( message_data - > timeout_str ) ;
2005-04-28 02:32:00 +04:00
}
/* Parse a register message from the client. */
2007-01-15 21:58:40 +03: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 ;
struct dm_event_daemon_message * msg = message_data - > msg ;
2012-06-21 23:20:01 +04:00
char * p = msg - > data ;
2007-01-08 18:18:52 +03:00
if ( ! msg - > data )
return 0 ;
2005-04-28 02:32:00 +04:00
/*
* Retrieve application identifier , mapped device
* path and events # string from message .
*/
2007-02-02 20:08:51 +03:00
if ( _fetch_string ( & message_data - > id , & p , ' ' ) & &
_fetch_string ( & message_data - > dso_name , & p , ' ' ) & &
2007-01-15 21:58:40 +03:00
_fetch_string ( & message_data - > device_uuid , & p , ' ' ) & &
2013-04-19 22:44:20 +04:00
_fetch_string ( & message_data - > events_str , & p , ' ' ) & &
_fetch_string ( & message_data - > timeout_str , & p , ' ' ) ) {
if ( message_data - > events_str )
message_data - > events_field =
atoi ( message_data - > events_str ) ;
if ( message_data - > timeout_str )
message_data - > timeout_secs =
atoi ( message_data - > timeout_str )
? : DM_EVENT_DEFAULT_TIMEOUT ;
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 ;
2012-06-21 23:20:01 +04:00
2007-01-08 18:18:52 +03:00
return ret ;
2012-06-21 23:20:01 +04:00
}
2005-04-28 02:32:00 +04:00
2007-01-16 23:27:07 +03:00
/* Global mutex to lock access to lists et al. See _global_mutex
above . */
2007-01-15 21:58:40 +03:00
static int _lock_mutex ( void )
2005-04-28 02:32:00 +04:00
{
2007-01-08 18:18:52 +03:00
return pthread_mutex_lock ( & _global_mutex ) ;
2005-04-28 02:32:00 +04:00
}
2007-01-15 21:58:40 +03:00
static int _unlock_mutex ( void )
2005-04-28 02:32:00 +04:00
{
2007-01-08 18:18:52 +03:00
return pthread_mutex_unlock ( & _global_mutex ) ;
2005-04-28 02:32:00 +04:00
}
/* Check, if a device exists. */
2007-01-15 21:58:40 +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 ;
2014-04-18 13:37:17 +04:00
int ret = 0 ;
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 ;
2014-04-17 11:59:54 +04:00
if ( ! ( dmt = dm_task_create ( DM_DEVICE_INFO ) ) )
2006-02-03 22:44:59 +03:00
return 0 ;
2005-04-28 02:32:00 +04:00
2011-02-28 22:47:22 +03:00
if ( ! dm_task_set_uuid ( dmt , ts - > device . uuid ) )
goto fail ;
2007-01-12 00:54:53 +03:00
if ( ! dm_task_run ( dmt ) )
goto fail ;
2014-04-17 11:59:54 +04:00
dm_free ( ts - > device . name ) ;
if ( ! ( ts - > device . name = dm_strdup ( dm_task_get_name ( dmt ) ) ) )
2007-01-12 00:54:53 +03:00
goto fail ;
if ( ! dm_task_get_info ( dmt , & dmi ) )
goto fail ;
ts - > device . major = dmi . major ;
ts - > device . minor = dmi . minor ;
2014-04-18 13:37:17 +04:00
ret = 1 ;
fail :
2007-01-12 00:54:53 +03:00
dm_task_destroy ( dmt ) ;
2013-04-19 22:49:57 +04:00
2014-04-18 13:37:17 +04:00
return ret ;
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
*/
2007-01-15 21:58:40 +03:00
static struct thread_status * _lookup_thread_status ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
struct thread_status * thread ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( thread , & _thread_registry )
2012-06-21 23:20:01 +04:00
if ( ! strcmp ( data - > device_uuid , thread - > device . uuid ) )
return thread ;
2005-04-28 02:32:00 +04:00
return NULL ;
}
2010-10-20 19:12:12 +04:00
static int _get_status ( struct message_data * message_data )
{
struct dm_event_daemon_message * msg = message_data - > msg ;
struct thread_status * thread ;
2014-04-18 15:29:38 +04:00
int i = 0 , j ;
int ret = - ENOMEM ;
int count ;
2014-03-10 12:44:26 +04:00
int size = 0 , current ;
2014-04-18 15:29:38 +04:00
size_t len ;
char * * buffers ;
2010-10-20 19:12:12 +04:00
char * message ;
_lock_mutex ( ) ;
2014-04-18 15:29:38 +04:00
count = dm_list_size ( & _thread_registry ) ;
buffers = alloca ( sizeof ( char * ) * count ) ;
2010-10-20 19:12:12 +04:00
dm_list_iterate_items ( thread , & _thread_registry ) {
if ( ( current = dm_asprintf ( buffers + i , " 0:%d %s %s %u % " PRIu32 " ; " ,
i , thread - > dso_data - > dso_name ,
thread - > device . uuid , thread - > events ,
thread - > timeout ) ) < 0 ) {
_unlock_mutex ( ) ;
goto out ;
}
2014-04-18 15:34:44 +04:00
+ + i ;
size + = current ; /* count with trailing '\0' */
2010-10-20 19:12:12 +04:00
}
_unlock_mutex ( ) ;
2014-04-18 15:34:44 +04:00
len = strlen ( message_data - > id ) ;
msg - > size = size + len + 1 ;
dm_free ( msg - > data ) ;
if ( ! ( msg - > data = dm_malloc ( msg - > size ) ) )
2010-10-20 19:12:12 +04:00
goto out ;
2014-04-18 15:34:44 +04:00
memcpy ( msg - > data , message_data - > id , len ) ;
message = msg - > data + len ;
* message + + = ' ' ;
2010-10-20 19:12:12 +04:00
for ( j = 0 ; j < i ; + + j ) {
2014-04-18 15:34:44 +04:00
len = strlen ( buffers [ j ] ) ;
memcpy ( message , buffers [ j ] , len ) ;
message + = len ;
2010-10-20 19:12:12 +04:00
}
ret = 0 ;
out :
for ( j = 0 ; j < i ; + + j )
dm_free ( buffers [ j ] ) ;
2014-03-10 12:44:26 +04:00
return ret ;
2010-10-20 19:12:12 +04:00
}
2014-02-04 20:01:54 +04:00
static int _get_parameters ( struct message_data * message_data ) {
struct dm_event_daemon_message * msg = message_data - > msg ;
2014-04-18 16:26:36 +04:00
int size ;
2014-02-04 20:01:54 +04:00
dm_free ( msg - > data ) ;
2014-04-18 16:26:36 +04:00
if ( ( size = dm_asprintf ( & msg - > data , " %s pid=%d daemon=%s exec_method=%s " ,
message_data - > id , getpid ( ) ,
_foreground ? " no " : " yes " ,
_systemd_activation ? " systemd " : " direct " ) ) < 0 ) {
stack ;
return - ENOMEM ;
}
2014-02-04 20:01:54 +04:00
2014-04-18 16:26:36 +04:00
msg - > size = ( uint32_t ) size ;
2014-02-04 20:01:54 +04:00
2014-04-18 16:26:36 +04:00
return 0 ;
2014-02-04 20:01:54 +04:00
}
2005-04-28 02:32:00 +04:00
/* Cleanup at exit. */
2007-01-15 21:58:40 +03:00
static void _exit_dm_lib ( void )
2005-04-28 02:32:00 +04:00
{
dm_lib_release ( ) ;
dm_lib_exit ( ) ;
}
2010-07-09 19:34:40 +04:00
static void _exit_timeout ( void * unused __attribute__ ( ( unused ) ) )
2005-12-02 18:39:16 +03:00
{
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. */
2010-07-09 19:34:40 +04:00
static void * _timeout_thread ( void * unused __attribute__ ( ( unused ) ) )
2005-12-02 18:39:16 +03:00
{
2012-06-21 23:20:01 +04:00
struct thread_status * thread ;
2005-12-02 18:39:16 +03:00
struct timespec timeout ;
time_t curr_time ;
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Timeout thread starting. " ) ;
2007-01-15 21:58:40 +03:00
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
2008-11-04 01:14:30 +03:00
while ( ! dm_list_empty ( & _timeout_registry ) ) {
2007-01-22 18:03:57 +03:00
timeout . tv_sec = 0 ;
2015-10-13 10:39:18 +03:00
timeout . tv_nsec = 0 ;
2005-12-02 18:39:16 +03:00
curr_time = time ( NULL ) ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items_gen ( thread , & _timeout_registry , timeout_list ) {
2007-01-22 18:03:57 +03:00
if ( thread - > next_time < = curr_time ) {
2005-12-02 18:39:16 +03:00
thread - > next_time = curr_time + thread - > timeout ;
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Sending SIGALRM to Thr %x for timeout. " , ( int ) thread - > thread ) ;
2005-12-02 18:39:16 +03:00
pthread_kill ( thread - > thread , SIGALRM ) ;
}
2007-01-22 18:03:57 +03:00
if ( thread - > next_time < timeout . tv_sec | | ! timeout . tv_sec )
2005-12-02 18:39:16 +03:00
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
}
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Timeout thread finished. " ) ;
2005-12-02 18:39:16 +03:00
pthread_cleanup_pop ( 1 ) ;
return NULL ;
}
2007-01-15 21:58:40 +03:00
static int _register_for_timeout ( struct thread_status * thread )
2005-12-02 18:39:16 +03:00
{
int ret = 0 ;
2007-01-08 18:18:52 +03:00
pthread_mutex_lock ( & _timeout_mutex ) ;
2005-12-02 18:39:16 +03:00
2008-11-04 01:14:30 +03:00
if ( dm_list_empty ( & thread - > timeout_list ) ) {
2014-04-18 16:30:00 +04:00
thread - > next_time = time ( NULL ) + thread - > timeout ;
2008-11-04 01:14:30 +03:00
dm_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
}
2013-08-01 00:23:13 +04:00
if ( ! _timeout_running & &
! ( ret = _pthread_create_smallstack ( NULL , _timeout_thread , NULL ) ) )
_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 ;
}
2007-01-15 21:58:40 +03:00
static void _unregister_for_timeout ( struct thread_status * thread )
2005-12-02 18:39:16 +03:00
{
2007-01-08 18:18:52 +03:00
pthread_mutex_lock ( & _timeout_mutex ) ;
2008-11-04 01:14:30 +03:00
if ( ! dm_list_empty ( & thread - > timeout_list ) ) {
dm_list_del ( & thread - > timeout_list ) ;
dm_list_init ( & thread - > timeout_list ) ;
2014-03-10 12:40:32 +04:00
if ( dm_list_empty ( & _timeout_registry ) )
2014-04-18 13:27:07 +04:00
/* No more work -> wakeup to finish quickly */
2014-03-10 12:40:32 +04:00
pthread_cond_signal ( & _timeout_cond ) ;
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
}
2007-01-15 21:58:40 +03:00
static sigset_t _unblock_sigalrm ( void )
2005-12-02 18:39:16 +03:00
{
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-15 21:58:40 +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 ;
2015-05-26 17:13:49 +03:00
int ioctl_errno ;
2005-04-28 02:32:00 +04:00
2007-01-12 00:54:53 +03:00
* task = 0 ;
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Preparing waitevent task for %s " , thread - > device . uuid ) ;
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 ;
2014-03-10 12:47:44 +04:00
/*
* Check if there are already some waiting events ,
* in this case the logging is unmodified .
* TODO : audit libdm thread usage
*/
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Starting waitevent task for %s " , thread - > device . uuid ) ;
2005-12-02 18:39:16 +03:00
/*
* This is so that you can break out of waiting on an event ,
* either for a timeout event , or to cancel the thread .
*/
2007-01-15 21:58:40 +03:00
set = _unblock_sigalrm ( ) ;
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 ;
2006-12-20 17:35:02 +03:00
} else {
2015-05-26 17:13:49 +03:00
ioctl_errno = dm_task_get_errno ( dmt ) ;
if ( thread - > events & DM_EVENT_TIMEOUT & & ioctl_errno = = EINTR ) {
thread - > current_events | = DM_EVENT_TIMEOUT ;
ret = DM_WAIT_INTR ;
} else if ( thread - > status = = DM_THREAD_SHUTDOWN & & ioctl_errno = = EINTR )
2007-01-08 18:18:52 +03:00
ret = DM_WAIT_FATAL ;
2015-05-26 17:13:49 +03:00
else {
if ( ioctl_errno = = ENXIO ) {
2015-10-09 22:57:48 +03:00
log_error ( " %s disappeared, detaching. " ,
thread - > device . name ) ;
2015-05-26 17:13:49 +03:00
ret = DM_WAIT_FATAL ;
2015-10-09 22:57:48 +03:00
} else
log_sys_error ( " dm_task_run " , " " ) ;
2006-12-20 17:35:02 +03:00
}
2005-04-28 02:32:00 +04:00
}
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Completed waitevent task for %s " , thread - > device . uuid ) ;
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
pthread_sigmask ( SIG_SETMASK , & set , 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. */
2007-01-15 21:58:40 +03: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 ,
2007-01-23 20:38:39 +03:00
thread - > device . minor ,
& ( thread - > dso_private ) ) ;
2005-04-28 02:32:00 +04:00
}
/* Unregister a device with the DSO. */
2007-01-15 21:58:40 +03: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 ,
2007-01-23 20:38:39 +03:00
thread - > device . minor ,
& ( thread - > dso_private ) ) ;
2005-04-28 02:32:00 +04:00
}
2005-12-02 18:39:16 +03:00
/* Process an event in the DSO. */
2007-01-15 21:58:40 +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-23 20:38:39 +03:00
thread - > dso_data - > process_event ( task , thread - > current_events , & ( thread - > dso_private ) ) ;
2005-04-28 02:32:00 +04:00
}
/* Thread cleanup handler to unregister device. */
2007-01-15 21:58:40 +03:00
static void _monitor_unregister ( void * arg )
2005-04-28 02:32:00 +04:00
{
2007-01-16 23:27:07 +03:00
struct thread_status * thread = arg , * thread_iter ;
2005-04-28 02:32:00 +04:00
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " _monitor_unregister thread cleanup handler running " ) ;
2007-01-15 21:58:40 +03:00
if ( ! _do_unregister_device ( thread ) )
2015-10-09 22:57:48 +03:00
log_error ( " %s: %s unregister failed. " , __func__ ,
thread - > device . name ) ;
2013-06-30 18:04:14 +04:00
if ( thread - > current_task ) {
2007-01-12 00:54:53 +03:00
dm_task_destroy ( thread - > current_task ) ;
2013-06-30 18:04:14 +04:00
thread - > current_task = NULL ;
}
2007-01-16 23:27:07 +03:00
_lock_mutex ( ) ;
if ( thread - > events & DM_EVENT_TIMEOUT ) {
/* _unregister_for_timeout locks another mutex, we
don ' t want to deadlock so we release our mutex for
a bit */
_unlock_mutex ( ) ;
_unregister_for_timeout ( thread ) ;
_lock_mutex ( ) ;
}
/* we may have been relinked to unused registry since we were
called , so check that */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( thread_iter , & _thread_registry_unused )
2007-01-16 23:27:07 +03:00
if ( thread_iter = = thread ) {
thread - > status = DM_THREAD_DONE ;
_unlock_mutex ( ) ;
return ;
}
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Marking Thr %x as DONE and unused. " , ( int ) thread - > thread ) ;
2007-01-16 23:27:07 +03:00
thread - > status = DM_THREAD_DONE ;
UNLINK_THREAD ( thread ) ;
LINK ( thread , & _thread_registry_unused ) ;
_unlock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
}
2007-01-22 18:03:57 +03:00
static struct dm_task * _get_device_status ( struct thread_status * ts )
{
struct dm_task * dmt = dm_task_create ( DM_DEVICE_STATUS ) ;
if ( ! dmt )
return NULL ;
2011-08-31 12:23:05 +04:00
if ( ! dm_task_set_uuid ( dmt , ts - > device . uuid ) ) {
dm_task_destroy ( dmt ) ;
return NULL ;
}
2007-01-22 18:03:57 +03:00
if ( ! dm_task_run ( dmt ) ) {
dm_task_destroy ( dmt ) ;
return NULL ;
}
return dmt ;
}
2005-04-28 02:32:00 +04:00
/* Device monitoring thread. */
2007-01-15 21:58:40 +03:00
static void * _monitor_thread ( void * arg )
2005-04-28 02:32:00 +04:00
{
struct thread_status * thread = arg ;
2014-03-10 12:44:26 +04:00
int wait_error ;
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 ) ;
2007-01-15 21:58:40 +03:00
pthread_cleanup_push ( _monitor_unregister , thread ) ;
2005-04-28 02:32:00 +04:00
2006-01-27 23:52:21 +03:00
/* Wait for do_process_request() to finish its task. */
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
2007-01-08 18:18:52 +03:00
thread - > status = DM_THREAD_RUNNING ;
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
/* Loop forever awaiting/analyzing device events. */
while ( 1 ) {
thread - > current_events = 0 ;
2007-01-15 21:58:40 +03:00
wait_error = _event_wait ( thread , & task ) ;
2012-03-22 20:00:25 +04:00
if ( wait_error = = DM_WAIT_RETRY ) {
usleep ( 100 ) ; /* avoid busy loop */
2005-04-28 02:32:00 +04:00
continue ;
2012-03-22 20:00:25 +04:00
}
2005-04-28 02:32:00 +04:00
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-22 18:03:57 +03:00
/* Timeout occurred, task is not filled properly.
* We get device status here for processing it in DSO .
*/
if ( wait_error = = DM_WAIT_INTR & &
thread - > current_events & DM_EVENT_TIMEOUT ) {
dm_task_destroy ( task ) ;
task = _get_device_status ( thread ) ;
/* FIXME: syslog fail here ? */
if ( ! ( thread - > current_task = task ) )
continue ;
}
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 .
*
2007-01-15 21:58:40 +03:00
* If there ' s current events delivered from _event_wait ( ) AND
2005-04-28 18:02:30 +04:00
* the device got registered for those events AND
* those events haven ' t been processed yet , call
* the DSO ' s process_event ( ) handler .
*/
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
2007-01-08 18:18:52 +03:00
if ( thread - > status = = DM_THREAD_SHUTDOWN ) {
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2007-01-08 18:18:52 +03:00
break ;
}
2007-01-12 00:54:53 +03:00
if ( thread - > events & thread - > current_events ) {
2006-01-27 23:50:01 +03:00
thread - > processing = 1 ;
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
2007-01-15 21:58:40 +03:00
_do_process_event ( thread , task ) ;
2007-01-12 00:54:53 +03:00
dm_task_destroy ( task ) ;
thread - > current_task = NULL ;
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
2006-01-27 23:50:01 +03:00
thread - > processing = 0 ;
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
} else {
2013-06-30 18:07:26 +04:00
_unlock_mutex ( ) ;
2007-01-12 00:54:53 +03:00
dm_task_destroy ( task ) ;
thread - > current_task = NULL ;
2005-04-28 02:32:00 +04:00
}
}
2015-10-10 17:36:18 +03:00
DEBUGLOG ( " Finished _monitor_thread. " ) ;
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. */
2007-01-15 21:58:40 +03:00
static int _create_thread ( struct thread_status * thread )
2005-04-28 02:32:00 +04:00
{
2007-01-16 23:13:04 +03:00
return _pthread_create_smallstack ( & thread - > thread , _monitor_thread , thread ) ;
2005-04-28 02:32:00 +04:00
}
2007-01-15 21:58:40 +03:00
static int _terminate_thread ( struct thread_status * thread )
2005-04-28 02:32:00 +04:00
{
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Sending SIGALRM to terminate Thr %x. " , ( int ) thread - > thread ) ;
2005-12-02 18:39:16 +03:00
return pthread_kill ( thread - > thread , SIGALRM ) ;
2005-04-28 02:32:00 +04:00
}
2007-01-16 23:27:07 +03:00
/* DSO reference counting. Call with _global_mutex locked! */
2007-01-15 21:58:40 +03:00
static void _lib_get ( struct dso_data * data )
2005-04-28 02:32:00 +04:00
{
data - > ref_count + + ;
}
2007-01-15 21:58:40 +03:00
static void _lib_put ( struct dso_data * data )
2005-04-28 02:32:00 +04:00
{
if ( ! - - data - > ref_count ) {
dlclose ( data - > dso_handle ) ;
UNLINK_DSO ( data ) ;
2007-01-15 21:58:40 +03:00
_free_dso_data ( data ) ;
2005-04-28 02:32:00 +04:00
}
}
/* Find DSO data. */
2007-01-15 21:58:40 +03: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 ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( dso_data , & _dso_registry )
2014-04-18 13:27:07 +04: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
return ret ;
}
/* Lookup DSO symbols we need. */
2007-01-22 18:03:57 +03:00
static int _lookup_symbol ( void * dl , void * * symbol , const char * name )
2005-04-28 02:32:00 +04:00
{
if ( ( * symbol = dlsym ( dl , name ) ) )
return 1 ;
return 0 ;
}
2015-10-08 12:59:11 +03:00
static int _lookup_symbols ( void * dl , struct dso_data * data )
2005-04-28 02:32:00 +04:00
{
2007-01-22 18:03:57 +03:00
return _lookup_symbol ( dl , ( void * ) & data - > process_event ,
2005-04-28 02:32:00 +04:00
" process_event " ) & &
2007-01-22 18:03:57 +03:00
_lookup_symbol ( dl , ( void * ) & data - > register_device ,
2007-01-12 00:54:53 +03:00
" register_device " ) & &
2007-01-22 18:03:57 +03:00
_lookup_symbol ( dl , ( void * ) & data - > unregister_device ,
2007-01-12 00:54:53 +03:00
" unregister_device " ) ;
2005-04-28 02:32:00 +04:00
}
/* Load an application specific DSO. */
2007-01-15 21:58:40 +03:00
static struct dso_data * _load_dso ( struct message_data * data )
2005-04-28 02:32:00 +04:00
{
void * dl ;
2014-04-18 13:27:07 +04:00
struct dso_data * ret ;
2005-04-28 02:32:00 +04:00
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 ( ) ;
2015-10-09 22:57:48 +03:00
log_error ( " dmeventd %s dlopen failed: %s. " ,
data - > dso_name , dlerr ) ;
2007-01-12 00:54:53 +03:00
data - > msg - > size =
2007-02-02 20:08:51 +03:00
dm_asprintf ( & ( data - > msg - > data ) , " %s %s dlopen failed: %s " ,
data - > id , 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
2007-01-15 21:58:40 +03:00
if ( ! ( ret = _alloc_dso_data ( data ) ) ) {
2006-02-03 22:41:34 +03:00
dlclose ( dl ) ;
return NULL ;
}
2005-04-28 02:32:00 +04:00
2015-10-08 12:59:11 +03:00
if ( ! ( _lookup_symbols ( dl , ret ) ) ) {
2007-01-15 21:58:40 +03:00
_free_dso_data ( ret ) ;
2006-02-03 22:41:34 +03:00
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 ;
2007-01-15 21:58:40 +03:00
_lib_get ( ret ) ;
2005-04-28 02:32:00 +04:00
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
LINK_DSO ( ret ) ;
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
return ret ;
}
2005-12-02 18:39:16 +03:00
/* Return success on daemon active check. */
2007-01-15 21:58:40 +03:00
static int _active ( struct message_data * message_data )
2005-12-02 18:39:16 +03:00
{
return 0 ;
}
2005-04-28 02:32:00 +04:00
/*
* Unregister for an event .
*
* Only one caller at a time here as with register_for_event ( ) .
*/
2007-01-15 21:58:40 +03: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.
*/
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
2007-01-15 21:58:40 +03:00
if ( ! ( thread = _lookup_thread_status ( message_data ) ) ) {
_unlock_mutex ( ) ;
2005-12-02 18:39:16 +03:00
ret = - ENODEV ;
2005-04-28 02:32:00 +04:00
goto out ;
}
2007-01-16 23:27:07 +03:00
if ( thread - > status = = DM_THREAD_DONE ) {
/* the thread has terminated while we were not
watching */
_unlock_mutex ( ) ;
return 0 ;
}
2013-04-19 22:44:20 +04:00
thread - > events & = ~ message_data - > events_field ;
2005-04-28 02:32:00 +04:00
2013-06-30 18:06:39 +04:00
if ( ! ( thread - > events & DM_EVENT_TIMEOUT ) ) {
_unlock_mutex ( ) ;
2007-01-15 21:58:40 +03:00
_unregister_for_timeout ( thread ) ;
2013-06-30 18:06:39 +04:00
_lock_mutex ( ) ;
}
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 ) {
2014-11-04 21:56:20 +03:00
DEBUGLOG ( " Marking Thr %x unused (no events). " , ( int ) thread - > thread ) ;
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
}
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2005-04-28 02:32:00 +04:00
2015-10-10 17:36:18 +03:00
DEBUGLOG ( " Unregistered uuid:%s. " , thread - > device . uuid ) ;
2007-01-12 00:54:53 +03:00
out :
2005-04-28 02:32:00 +04:00
return ret ;
}
2015-10-13 10:43:09 +03:00
/*
* Register for an event .
*
* Only one caller at a time here , because we use
* a FIFO and lock it against multiple accesses .
*/
static int _register_for_event ( struct message_data * message_data )
{
int ret = 0 ;
struct thread_status * thread ;
struct dso_data * dso_data ;
if ( ! ( dso_data = _lookup_dso ( message_data ) ) & &
! ( dso_data = _load_dso ( message_data ) ) ) {
stack ;
# ifdef ELIBACC
ret = - ELIBACC ;
# else
ret = - ENODEV ;
# endif
return ret ;
}
_lock_mutex ( ) ;
if ( ( thread = _lookup_thread_status ( message_data ) ) )
/* Or event # into events bitfield. */
thread - > events | = message_data - > events_field ;
_unlock_mutex ( ) ;
if ( ! thread ) {
if ( ! ( thread = _alloc_thread_status ( message_data , dso_data ) ) ) {
stack ;
return - ENOMEM ;
}
if ( ! _fill_device_data ( thread ) ) {
ret = - ENODEV ;
goto_out ;
}
if ( ! _do_register_device ( thread ) ) {
ret = - ENOMEM ;
goto_out ;
}
if ( ( ret = - _create_thread ( thread ) ) ) {
_do_unregister_device ( thread ) ;
goto_out ;
}
_lock_mutex ( ) ;
if ( _lookup_thread_status ( message_data ) ) {
DEBUGLOG ( " Race, uuid already registered, marking Thr %x unused. " ,
( int ) thread - > thread ) ;
thread - > status = DM_THREAD_SHUTDOWN ;
thread - > events = 0 ;
LINK ( thread , & _thread_registry_unused ) ;
_unlock_mutex ( ) ;
ret = - EEXIST ; /* race ? */
goto_out ;
}
LINK_THREAD ( thread ) ;
_unlock_mutex ( ) ;
}
/* If creation of timeout thread fails (as it may), we fail
here completely . The client is responsible for either
retrying later or trying to register without timeout
events . However , if timeout thread cannot be started , it
usually means we are so starved on resources that we are
almost as good as dead already . . . */
if ( ( thread - > events & DM_EVENT_TIMEOUT ) & &
( ret = - _register_for_timeout ( thread ) ) )
_unregister_for_event ( message_data ) ;
return ret ;
out :
_free_thread_status ( thread ) ;
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 ( ) .
*/
2007-01-15 21:58:40 +03:00
static int _registered_device ( struct message_data * message_data ,
2005-04-29 17:41:25 +04:00
struct thread_status * thread )
2005-04-28 18:02:30 +04:00
{
2012-03-03 03:01:10 +04:00
int r ;
2013-11-22 15:54:59 +04:00
struct dm_event_daemon_message * msg = message_data - > msg ;
2013-06-30 18:04:14 +04:00
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
2010-08-16 22:19:46 +04:00
dm_free ( msg - > data ) ;
2007-01-08 18:18:52 +03:00
2013-11-22 15:54:59 +04:00
if ( ( r = dm_asprintf ( & ( msg - > data ) , " %s %s %s %u " ,
message_data - > id ,
thread - > dso_data - > dso_name ,
2014-04-18 17:12:59 +04:00
thread - > device . uuid , events ) ) < 0 )
2012-03-03 03:01:10 +04:00
return - ENOMEM ;
2005-04-29 17:41:25 +04:00
2012-03-03 03:01:10 +04:00
msg - > size = ( uint32_t ) r ;
2015-10-10 17:36:18 +03:00
DEBUGLOG ( " Registered %s. " , msg - > data ) ;
2005-04-28 18:02:30 +04:00
return 0 ;
}
2007-01-15 21:58:40 +03:00
static int _want_registered_device ( char * dso_name , char * device_uuid ,
2014-11-04 19:55:42 +03:00
struct thread_status * thread )
2005-04-28 18:02:30 +04:00
{
2015-10-10 17:36:18 +03:00
DEBUGLOG ( " Looking for dso:%s uuid:%s. " , dso_name , device_uuid ) ;
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 ) & &
2014-11-04 19:55:42 +03:00
! strcmp ( device_uuid , thread - > device . uuid ) ;
2005-12-02 18:39:16 +03:00
/* If DSO names are equal. */
if ( dso_name )
2014-11-04 19:55:42 +03:00
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 )
2014-11-04 19:55:42 +03:00
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
2007-01-15 21:58:40 +03:00
static int _get_registered_dev ( struct message_data * message_data , int next )
2005-12-02 18:39:16 +03:00
{
2007-01-17 02:03:13 +03:00
struct thread_status * thread , * hit = NULL ;
2012-03-03 03:01:10 +04:00
int ret = - ENOENT ;
2005-05-02 15:02:19 +04:00
2015-10-10 17:36:18 +03:00
DEBUGLOG ( " Get%s dso:%s uuid:%s. " , next ? " " : " Next " ,
message_data - > dso_name ,
message_data - > device_uuid ) ;
2007-01-15 21:58:40 +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. */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( thread , & _thread_registry )
2007-01-17 02:03:13 +03:00
if ( _want_registered_device ( message_data - > dso_name ,
message_data - > device_uuid ,
thread ) ) {
hit = 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
*/
2012-03-03 03:01:10 +04:00
if ( hit & & ! next )
goto reg ;
2007-01-17 02:03:13 +03:00
2014-11-04 19:58:14 +03:00
/*
* If we didn ' t get a match , try the threads waiting to be deleted .
* FIXME Do something similar if ' next ' is set .
*/
if ( ! hit & & ! next )
dm_list_iterate_items ( thread , & _thread_registry_unused )
if ( _want_registered_device ( message_data - > dso_name ,
message_data - > device_uuid , thread ) ) {
hit = thread ;
goto reg ;
}
2015-10-10 17:36:18 +03:00
if ( ! hit ) {
DEBUGLOG ( " Get%s not registered " , next ? " " : " Next " ) ;
2006-12-20 17:35:02 +03:00
goto out ;
2015-10-10 17:36:18 +03:00
}
2005-04-28 18:02:30 +04:00
2007-01-23 20:38:39 +03:00
while ( 1 ) {
2008-11-04 01:14:30 +03:00
if ( dm_list_end ( & _thread_registry , & thread - > list ) )
2006-12-20 17:35:02 +03:00
goto out ;
2007-01-12 00:54:53 +03:00
2008-11-04 01:14:30 +03:00
thread = dm_list_item ( thread - > list . n , struct thread_status ) ;
2007-01-23 20:38:39 +03:00
if ( _want_registered_device ( message_data - > dso_name , NULL , thread ) ) {
hit = thread ;
break ;
}
}
2006-12-20 17:35:02 +03:00
2012-03-03 03:01:10 +04:00
reg :
ret = _registered_device ( message_data , hit ) ;
2005-04-28 18:02:30 +04:00
2007-01-12 00:54:53 +03:00
out :
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2012-03-03 03:01:10 +04:00
return ret ;
2005-12-02 18:39:16 +03:00
}
2007-01-15 21:58:40 +03:00
static int _get_registered_device ( struct message_data * message_data )
2005-12-02 18:39:16 +03:00
{
2007-01-15 21:58:40 +03:00
return _get_registered_dev ( message_data , 0 ) ;
2005-12-02 18:39:16 +03:00
}
2007-01-15 21:58:40 +03:00
static int _get_next_registered_device ( struct message_data * message_data )
2005-12-02 18:39:16 +03:00
{
2007-01-15 21:58:40 +03:00
return _get_registered_dev ( message_data , 1 ) ;
2005-04-28 18:02:30 +04:00
}
2007-01-15 21:58:40 +03:00
static int _set_timeout ( struct message_data * message_data )
2005-12-02 18:39:16 +03:00
{
struct thread_status * thread ;
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
if ( ( thread = _lookup_thread_status ( message_data ) ) )
2013-04-19 22:44:20 +04:00
thread - > timeout = message_data - > timeout_secs ;
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2005-12-02 18:39:16 +03:00
return thread ? 0 : - ENODEV ;
}
2007-01-15 21:58:40 +03:00
static int _get_timeout ( struct message_data * message_data )
2005-12-02 18:39:16 +03:00
{
struct thread_status * thread ;
struct dm_event_daemon_message * msg = message_data - > msg ;
2010-08-16 22:19:46 +04:00
dm_free ( msg - > data ) ;
2007-01-08 18:18:52 +03:00
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
if ( ( thread = _lookup_thread_status ( message_data ) ) ) {
2014-04-18 13:27:07 +04:00
msg - > size = dm_asprintf ( & ( msg - > data ) , " %s % " PRIu32 ,
message_data - > id , thread - > timeout ) ;
2014-04-18 17:12:59 +04:00
} else
2007-01-08 18:18:52 +03:00
msg - > data = NULL ;
2014-04-18 17:12:59 +04:00
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2005-12-02 18:39:16 +03:00
return thread ? 0 : - ENODEV ;
}
2005-04-28 02:32:00 +04:00
/* Open fifos used for client communication. */
2007-01-15 21:58:40 +03:00
static int _open_fifos ( struct dm_event_fifos * fifos )
2005-04-28 02:32:00 +04:00
{
2011-03-30 00:30:05 +04:00
struct stat st ;
2010-12-13 13:43:56 +03:00
/* Create client fifo. */
( void ) dm_prepare_selinux_context ( fifos - > client_path , S_IFIFO ) ;
if ( ( mkfifo ( fifos - > client_path , 0600 ) = = - 1 ) & & errno ! = EEXIST ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " client mkfifo " , fifos - > client_path ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
2007-01-08 18:18:52 +03:00
}
2010-12-13 13:43:56 +03:00
/* Create server fifo. */
( void ) dm_prepare_selinux_context ( fifos - > server_path , S_IFIFO ) ;
if ( ( mkfifo ( fifos - > server_path , 0600 ) = = - 1 ) & & errno ! = EEXIST ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " server mkfifo " , fifos - > server_path ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
2010-12-13 13:43:56 +03:00
}
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2007-01-12 00:54:53 +03:00
/* Warn about wrong permissions if applicable */
if ( ( ! stat ( fifos - > client_path , & st ) ) & & ( st . st_mode & 0777 ) ! = 0600 )
2015-10-09 22:57:48 +03:00
log_warn ( " WARNING: Fixing wrong permissions on %s: %s. \n " ,
fifos - > client_path , strerror ( errno ) ) ;
2007-01-12 00:54:53 +03:00
if ( ( ! stat ( fifos - > server_path , & st ) ) & & ( st . st_mode & 0777 ) ! = 0600 )
2015-10-09 22:57:48 +03:00
log_warn ( " WARNING: Fixing wrong permissions on %s: %s. \n " ,
fifos - > server_path , strerror ( errno ) ) ;
2007-01-12 00:54:53 +03:00
2007-01-08 18:18:52 +03:00
/* If they were already there, make sure permissions are ok. */
if ( chmod ( fifos - > client_path , 0600 ) ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " chmod " , fifos - > client_path ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
2007-01-08 18:18:52 +03:00
}
if ( chmod ( fifos - > server_path , 0600 ) ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " chmod " , fifos - > server_path ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
2007-01-08 18:18:52 +03:00
}
/* Need to open read+write or we will block or fail */
if ( ( fifos - > server = open ( fifos - > server_path , O_RDWR ) ) < 0 ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " server open " , fifos - > server_path ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
}
if ( fcntl ( fifos - > server , F_SETFD , FD_CLOEXEC ) < 0 ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " fcntl(FD_CLOEXEC) " , fifos - > server_path ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
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 ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " client open " , fifos - > client_path ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
}
if ( fcntl ( fifos - > client , F_SETFD , FD_CLOEXEC ) < 0 ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " fcntl(FD_CLOEXEC) " , fifos - > client_path ) ;
2013-01-15 17:59:54 +04:00
goto fail ;
2005-04-28 02:32:00 +04:00
}
2012-03-02 02:06:18 +04:00
return 1 ;
2013-01-15 17:59:54 +04:00
fail :
if ( fifos - > server > = 0 & & close ( fifos - > server ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " server close " , fifos - > server_path ) ;
2013-01-15 17:59:54 +04:00
if ( fifos - > client > = 0 & & close ( fifos - > client ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " client close " , fifos - > client_path ) ;
2013-01-15 17:59:54 +04: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-15 21:58:40 +03:00
static int _client_read ( struct dm_event_fifos * fifos ,
2007-01-12 00:54:53 +03:00
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
size_t size = 2 * sizeof ( uint32_t ) ; /* status + size */
2010-10-25 15:57:06 +04:00
uint32_t * header = alloca ( size ) ;
char * buf = ( char * ) header ;
2007-01-08 18:18:52 +03:00
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 ;
2010-10-25 15:57:06 +04:00
if ( header & & ( bytes = = 2 * sizeof ( uint32_t ) ) ) {
msg - > cmd = ntohl ( header [ 0 ] ) ;
2015-10-19 11:59:09 +03:00
size = msg - > size = ntohl ( header [ 1 ] ) ;
2007-01-08 18:18:52 +03:00
bytes = 0 ;
2015-10-19 11:59:09 +03:00
if ( ! size )
break ; /* No data -> error */
buf = msg - > data = dm_malloc ( msg - > size ) ;
if ( ! buf )
break ; /* No mem -> error */
2007-01-08 18:18:52 +03:00
header = 0 ;
}
2005-04-28 02:32:00 +04:00
}
2007-01-08 18:18:52 +03:00
if ( bytes ! = size ) {
2010-08-16 22:19:46 +04:00
dm_free ( msg - > data ) ;
2007-01-08 18:18:52 +03:00
msg - > data = NULL ;
2014-03-10 12:44:26 +04:00
return 0 ;
2007-01-08 18:18:52 +03:00
}
2014-03-10 12:44:26 +04:00
return 1 ;
2005-04-28 02:32:00 +04:00
}
/*
* Write a message to the client making sure that it is ready to write .
*/
2007-01-15 21:58:40 +03:00
static int _client_write ( struct dm_event_fifos * fifos ,
2007-01-12 00:54:53 +03:00
struct dm_event_daemon_message * msg )
2005-04-28 02:32:00 +04:00
{
2014-04-18 16:42:39 +04:00
uint32_t temp [ 2 ] ;
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 ;
2014-04-18 16:42:39 +04:00
size_t size = 2 * sizeof ( uint32_t ) + ( ( msg - > data ) ? msg - > size : 0 ) ;
uint32_t * header = dm_malloc ( size ) ;
2010-10-25 15:57:06 +04:00
char * buf = ( char * ) header ;
2007-01-08 18:18:52 +03:00
2014-04-18 16:42:39 +04:00
if ( ! header ) {
/* Reply with ENOMEM message */
header = temp ;
size = sizeof ( temp ) ;
header [ 0 ] = htonl ( - ENOMEM ) ;
header [ 1 ] = 0 ;
} else {
header [ 0 ] = htonl ( msg - > cmd ) ;
header [ 1 ] = htonl ( ( msg - > data ) ? msg - > size : 0 ) ;
if ( msg - > data )
memcpy ( buf + 2 * sizeof ( uint32_t ) , msg - > data , msg - > size ) ;
}
2007-01-08 18:18:52 +03:00
2014-04-18 16:43:13 +04:00
while ( bytes < size ) {
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 ) ;
2014-04-18 16:43:13 +04:00
} while ( select ( fifos - > server + 1 , NULL , & fds , NULL , NULL ) ! = 1 ) ;
2005-04-28 02:32:00 +04:00
2014-04-18 16:43:13 +04:00
if ( ( ret = write ( fifos - > server , buf + bytes , size - bytes ) ) > 0 )
bytes + = ret ;
else if ( errno = = EIO )
break ;
2005-04-29 17:41:25 +04:00
}
2014-04-18 16:42:39 +04:00
if ( header ! = temp )
dm_free ( header ) ;
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 .
*/
2007-01-15 21:58:40 +03:00
static int _handle_request ( struct dm_event_daemon_message * msg ,
2005-12-02 18:39:16 +03:00
struct message_data * message_data )
{
2014-04-18 16:45:45 +04:00
switch ( msg - > cmd ) {
case DM_EVENT_CMD_REGISTER_FOR_EVENT :
return _register_for_event ( message_data ) ;
case DM_EVENT_CMD_UNREGISTER_FOR_EVENT :
return _unregister_for_event ( message_data ) ;
case DM_EVENT_CMD_GET_REGISTERED_DEVICE :
return _get_registered_device ( message_data ) ;
case DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
return _get_next_registered_device ( message_data ) ;
case DM_EVENT_CMD_SET_TIMEOUT :
return _set_timeout ( message_data ) ;
case DM_EVENT_CMD_GET_TIMEOUT :
return _get_timeout ( message_data ) ;
case DM_EVENT_CMD_ACTIVE :
return _active ( message_data ) ;
case DM_EVENT_CMD_GET_STATUS :
return _get_status ( message_data ) ;
/* dmeventd parameters of running dmeventd,
* returns ' pid = < pid > daemon = < no / yes > exec_method = < direct / systemd > '
* pid - pidfile of running dmeventd
* daemon - running as a daemon or not ( foreground ) ?
* exec_method - " direct " if executed directly or
* " systemd " if executed via systemd
*/
case DM_EVENT_CMD_GET_PARAMETERS :
return _get_parameters ( message_data ) ;
default :
return - EINVAL ;
}
2005-12-02 18:39:16 +03:00
}
2005-04-28 02:32:00 +04:00
/* Process a request passed from the communication thread. */
2007-01-15 21:58:40 +03:00
static int _do_process_request ( struct dm_event_daemon_message * msg )
2005-04-28 02:32:00 +04:00
{
int ret ;
2007-02-02 20:08:51 +03:00
char * answer ;
2012-06-21 23:19:28 +04:00
struct message_data message_data = { . msg = msg } ;
2005-04-28 18:02:30 +04:00
/* Parse the message. */
2010-10-20 19:12:12 +04:00
if ( msg - > cmd = = DM_EVENT_CMD_HELLO | | msg - > cmd = = DM_EVENT_CMD_DIE ) {
2007-02-02 20:08:51 +03:00
ret = 0 ;
2007-04-23 19:06:03 +04:00
answer = msg - > data ;
2007-02-02 20:08:51 +03:00
if ( answer ) {
2011-04-04 20:11:09 +04:00
msg - > size = dm_asprintf ( & ( msg - > data ) , " %s %s %d " , answer ,
2014-04-18 17:12:59 +04:00
( msg - > cmd = = DM_EVENT_CMD_DIE ) ? " DYING " : " HELLO " ,
2012-06-21 23:20:01 +04:00
DM_EVENT_PROTOCOL_VERSION ) ;
2007-02-02 20:08:51 +03:00
dm_free ( answer ) ;
2014-04-18 17:12:59 +04:00
}
2007-02-02 20:08:51 +03:00
} else 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
2007-01-15 21:58:40 +03:00
ret = _handle_request ( msg , & message_data ) ;
2005-04-28 02:32:00 +04:00
2007-02-02 20:08:51 +03:00
msg - > cmd = ret ;
if ( ! msg - > data )
msg - > size = dm_asprintf ( & ( msg - > data ) , " %s %s " , message_data . id , strerror ( - ret ) ) ;
2007-01-15 21:58:40 +03:00
_free_message ( & message_data ) ;
2005-12-02 18:39:16 +03:00
2005-04-28 02:32:00 +04:00
return ret ;
}
2005-04-28 18:02:30 +04:00
/* Only one caller at a time. */
2007-01-15 21:58:40 +03:00
static void _process_request ( struct dm_event_fifos * fifos )
2005-04-28 02:32:00 +04:00
{
2014-03-10 12:44:26 +04:00
int die ;
2012-06-21 23:19:28 +04:00
struct dm_event_daemon_message msg = { 0 } ;
2015-10-13 21:57:59 +03:00
# ifdef DEBUG
2015-10-09 23:16:33 +03:00
int cmd ;
2015-10-13 21:57:59 +03:00
# endif
2006-03-10 00:33:59 +03:00
/*
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
*/
2007-01-15 21:58:40 +03:00
if ( ! _client_read ( fifos , & msg ) )
2005-04-28 02:32:00 +04:00
return ;
2015-10-13 21:57:59 +03:00
# ifdef DEBUG
2015-10-09 23:16:33 +03:00
cmd = msg . cmd ;
2015-10-13 21:57:59 +03:00
# endif
2015-10-13 12:19:59 +03:00
DEBUGLOG ( " >>> CMD:%s (0x%x) processing... " , decode_cmd ( cmd ) , cmd ) ;
2015-06-15 15:35:33 +03:00
2014-03-10 12:44:26 +04:00
die = ( msg . cmd = = DM_EVENT_CMD_DIE ) ? 1 : 0 ;
2010-10-20 19:12:12 +04:00
2007-02-02 20:08:51 +03:00
/* _do_process_request fills in msg (if memory allows for
data , otherwise just cmd and size = 0 ) */
_do_process_request ( & msg ) ;
2005-04-28 02:32:00 +04:00
2007-01-15 21:58:40 +03:00
if ( ! _client_write ( fifos , & msg ) )
2005-04-28 02:32:00 +04:00
stack ;
2007-01-08 18:18:52 +03:00
2010-08-16 22:19:46 +04:00
dm_free ( msg . data ) ;
2012-03-03 02:57:25 +04:00
2015-10-13 12:19:59 +03:00
DEBUGLOG ( " <<< CMD:%s (0x%x) completed (=%d). " , decode_cmd ( cmd ) , cmd , msg . cmd ) ;
2014-11-04 21:56:20 +03:00
2014-02-06 20:53:03 +04:00
if ( die ) {
if ( unlink ( DMEVENTD_PIDFILE ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " unlink " , DMEVENTD_PIDFILE ) ;
2014-02-06 14:44:57 +04:00
_exit ( 0 ) ;
2014-02-06 20:53:03 +04:00
}
2005-04-28 02:32:00 +04:00
}
2010-10-26 12:54:37 +04:00
static void _process_initial_registrations ( void )
2010-10-20 19:12:12 +04:00
{
2013-06-30 18:04:14 +04:00
int i ;
2010-10-20 19:12:12 +04:00
char * reg ;
2013-06-30 18:04:14 +04:00
struct dm_event_daemon_message msg = { 0 } ;
2010-10-20 19:12:12 +04:00
2013-06-30 18:04:14 +04:00
for ( i = 0 ; ( reg = _initial_registrations [ i ] ) ; + + i ) {
2010-10-20 19:12:12 +04:00
msg . cmd = DM_EVENT_CMD_REGISTER_FOR_EVENT ;
2012-03-02 02:54:17 +04:00
if ( ( msg . size = strlen ( reg ) ) ) {
msg . data = reg ;
_do_process_request ( & msg ) ;
}
2010-10-20 19:12:12 +04:00
}
}
2007-01-15 21:58:40 +03:00
static void _cleanup_unused_threads ( void )
2006-01-27 23:50:01 +03:00
{
int ret ;
2008-11-04 01:14:30 +03:00
struct dm_list * l ;
2006-01-27 23:50:01 +03:00
struct thread_status * thread ;
2012-03-01 13:50:04 +04:00
int join_ret = 0 ;
2006-01-27 23:50:01 +03:00
2007-01-15 21:58:40 +03:00
_lock_mutex ( ) ;
2008-11-04 01:14:30 +03:00
while ( ( l = dm_list_first ( & _thread_registry_unused ) ) ) {
thread = dm_list_item ( l , struct thread_status ) ;
2007-01-16 01:37:40 +03:00
if ( thread - > processing )
break ; /* cleanup on the next round */
2007-01-08 18:18:52 +03:00
if ( thread - > status = = DM_THREAD_RUNNING ) {
thread - > status = DM_THREAD_SHUTDOWN ;
2007-01-16 01:37:40 +03:00
break ;
2007-01-19 20:22:17 +03:00
}
2007-01-16 01:37:40 +03:00
if ( thread - > status = = DM_THREAD_SHUTDOWN ) {
2007-01-08 18:18:52 +03:00
if ( ! thread - > events ) {
/* turn codes negative -- should we be returning this? */
2007-01-15 21:58:40 +03:00
ret = _terminate_thread ( thread ) ;
2007-01-08 18:18:52 +03:00
if ( ret = = ESRCH ) {
thread - > status = DM_THREAD_DONE ;
} else if ( ret ) {
2015-10-09 22:57:48 +03:00
log_error ( " Unable to terminate thread: %s " ,
strerror ( ret ) ) ;
2007-01-08 18:18:52 +03:00
}
2007-01-16 01:37:40 +03:00
break ;
2007-01-19 20:22:17 +03:00
}
2007-01-16 01:37:40 +03:00
2008-11-04 01:14:30 +03:00
dm_list_del ( l ) ;
2015-10-09 22:57:48 +03:00
log_error ( " thread can't be on unused list unless !thread->events " ) ;
2007-01-16 01:37:40 +03:00
thread - > status = DM_THREAD_RUNNING ;
LINK_THREAD ( thread ) ;
continue ;
}
if ( thread - > status = = DM_THREAD_DONE ) {
2008-11-04 01:14:30 +03:00
dm_list_del ( l ) ;
2013-06-30 18:07:26 +04:00
_unlock_mutex ( ) ;
2015-10-10 17:36:18 +03:00
DEBUGLOG ( " Destroying Thr %x. " , ( int ) thread - > thread ) ;
2012-03-01 13:50:04 +04:00
join_ret = pthread_join ( thread - > thread , NULL ) ;
2007-01-15 21:58:40 +03:00
_free_thread_status ( thread ) ;
2013-06-30 18:07:26 +04:00
_lock_mutex ( ) ;
2006-01-27 23:50:01 +03:00
}
}
2007-01-16 01:37:40 +03:00
2007-01-15 21:58:40 +03:00
_unlock_mutex ( ) ;
2012-03-01 13:50:04 +04:00
if ( join_ret )
2015-10-09 22:57:48 +03:00
log_error ( " Failed pthread_join: %s. " , strerror ( join_ret ) ) ;
2006-01-27 23:50:01 +03:00
}
2010-07-09 19:34:40 +04:00
static void _sig_alarm ( int signum __attribute__ ( ( unused ) ) )
2005-04-28 02:32:00 +04:00
{
2015-06-15 15:39:17 +03:00
/* empty SIG_IGN */ ;
2005-12-02 18:39:16 +03:00
}
2005-04-28 02:32:00 +04:00
2005-12-02 18:39:16 +03:00
/* Init thread signal handling. */
2007-01-15 21:58:40 +03:00
static void _init_thread_signals ( void )
2005-12-02 18:39:16 +03:00
{
2006-05-11 23:08:02 +04:00
sigset_t my_sigset ;
2012-06-21 23:19:28 +04:00
struct sigaction act = { . sa_handler = _sig_alarm } ;
2007-01-12 00:54:53 +03:00
2005-12-02 18:39:16 +03:00
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 .
*/
2010-07-09 19:34:40 +04:00
static void _exit_handler ( int sig __attribute__ ( ( unused ) ) )
2005-04-28 02:32:00 +04:00
{
2013-06-30 17:53:42 +04:00
_exit_now = 1 ;
2005-12-02 18:39:16 +03:00
}
2005-04-28 02:32:00 +04:00
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2011-07-28 17:03:37 +04:00
static int _set_oom_adj ( const char * oom_adj_path , int val )
{
FILE * fp ;
if ( ! ( fp = fopen ( oom_adj_path , " w " ) ) ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " open " , oom_adj_path ) ;
2011-07-28 17:03:37 +04:00
return 0 ;
}
fprintf ( fp , " %i " , val ) ;
if ( dm_fclose ( fp ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " fclose " , oom_adj_path ) ;
2011-07-28 17:03:37 +04:00
return 1 ;
}
2007-01-19 20:22:17 +03:00
/*
* Protection against OOM killer if kernel supports it
*/
2011-09-14 13:53:32 +04:00
static int _protect_against_oom_killer ( void )
2007-01-16 23:13:04 +03:00
{
2007-01-17 17:45:10 +03:00
struct stat st ;
2007-01-19 20:22:17 +03:00
if ( stat ( OOM_ADJ_FILE , & st ) = = - 1 ) {
2011-07-28 17:03:37 +04:00
if ( errno ! = ENOENT )
2015-10-09 22:57:48 +03:00
log_sys_error ( " stat " , OOM_ADJ_FILE ) ;
2007-01-16 23:13:04 +03:00
2011-07-28 17:03:37 +04:00
/* Try old oom_adj interface as a fallback */
if ( stat ( OOM_ADJ_FILE_OLD , & st ) = = - 1 ) {
2015-10-09 22:57:48 +03:00
log_sys_error ( " stat " , OOM_ADJ_FILE_OLD ) ;
2011-07-28 17:03:37 +04:00
return 1 ;
}
2007-01-16 23:13:04 +03:00
2011-07-28 17:03:37 +04:00
return _set_oom_adj ( OOM_ADJ_FILE_OLD , OOM_DISABLE ) | |
_set_oom_adj ( OOM_ADJ_FILE_OLD , OOM_ADJUST_MIN ) ;
}
2007-01-16 23:13:04 +03:00
2011-07-28 17:03:37 +04:00
return _set_oom_adj ( OOM_ADJ_FILE , OOM_SCORE_ADJ_MIN ) ;
2007-01-16 23:13:04 +03:00
}
2012-02-27 15:13:51 +04:00
static int _handle_preloaded_fifo ( int fd , const char * path )
{
struct stat st_fd , st_path ;
int flags ;
if ( ( flags = fcntl ( fd , F_GETFD ) ) < 0 )
return 0 ;
if ( flags & FD_CLOEXEC )
return 0 ;
if ( fstat ( fd , & st_fd ) < 0 | | ! S_ISFIFO ( st_fd . st_mode ) )
return 0 ;
if ( stat ( path , & st_path ) < 0 | |
st_path . st_dev ! = st_fd . st_dev | |
st_path . st_ino ! = st_fd . st_ino )
return 0 ;
if ( fcntl ( fd , F_SETFD , flags | FD_CLOEXEC ) < 0 )
return 0 ;
return 1 ;
}
static int _systemd_handover ( struct dm_event_fifos * fifos )
{
const char * e ;
char * p ;
unsigned long env_pid , env_listen_fds ;
int r = 0 ;
2012-03-14 19:51:51 +04:00
/* SD_ACTIVATION must be set! */
if ( ! ( e = getenv ( SD_ACTIVATION_ENV_VAR_NAME ) ) | | strcmp ( e , " 1 " ) )
goto out ;
2012-02-27 15:13:51 +04:00
/* LISTEN_PID must be equal to our PID! */
if ( ! ( e = getenv ( SD_LISTEN_PID_ENV_VAR_NAME ) ) )
goto out ;
errno = 0 ;
env_pid = strtoul ( e , & p , 10 ) ;
if ( errno | | ! p | | * p | | env_pid < = 0 | |
getpid ( ) ! = ( pid_t ) env_pid )
goto out ;
/* LISTEN_FDS must be 2 and the fds must be FIFOSs! */
if ( ! ( e = getenv ( SD_LISTEN_FDS_ENV_VAR_NAME ) ) )
goto out ;
errno = 0 ;
env_listen_fds = strtoul ( e , & p , 10 ) ;
if ( errno | | ! p | | * p | | env_listen_fds ! = 2 )
goto out ;
/* Check and handle the FIFOs passed in */
r = ( _handle_preloaded_fifo ( SD_FD_FIFO_SERVER , DM_EVENT_FIFO_SERVER ) & &
_handle_preloaded_fifo ( SD_FD_FIFO_CLIENT , DM_EVENT_FIFO_CLIENT ) ) ;
if ( r ) {
fifos - > server = SD_FD_FIFO_SERVER ;
fifos - > server_path = DM_EVENT_FIFO_SERVER ;
fifos - > client = SD_FD_FIFO_CLIENT ;
fifos - > client_path = DM_EVENT_FIFO_CLIENT ;
}
out :
2012-03-14 19:51:51 +04:00
unsetenv ( SD_ACTIVATION_ENV_VAR_NAME ) ;
2012-02-27 15:13:51 +04:00
unsetenv ( SD_LISTEN_PID_ENV_VAR_NAME ) ;
unsetenv ( SD_LISTEN_FDS_ENV_VAR_NAME ) ;
return r ;
}
2014-02-04 20:01:54 +04:00
2007-01-22 18:03:57 +03:00
# endif
2007-01-16 23:13:04 +03:00
2012-03-15 12:45:55 +04:00
static void _remove_files_on_exit ( void )
2010-07-13 17:51:01 +04:00
{
2010-12-20 17:08:46 +03:00
if ( unlink ( DMEVENTD_PIDFILE ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " unlink " , DMEVENTD_PIDFILE ) ;
2012-03-15 12:45:55 +04:00
if ( ! _systemd_activation ) {
if ( unlink ( DM_EVENT_FIFO_CLIENT ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " unlink " , DM_EVENT_FIFO_CLIENT ) ;
2012-03-15 12:45:55 +04:00
if ( unlink ( DM_EVENT_FIFO_SERVER ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " unlink " , DM_EVENT_FIFO_SERVER ) ;
2012-03-15 12:45:55 +04:00
}
2010-07-13 17:51:01 +04:00
}
2007-01-15 21:58:40 +03:00
static void _daemonize ( void )
2005-04-28 02:32:00 +04:00
{
2007-01-19 20:22:17 +03:00
int child_status ;
2007-01-08 18:18:52 +03:00
int fd ;
2007-01-19 20:22:17 +03:00
pid_t pid ;
2007-01-08 18:18:52 +03:00
struct rlimit rlim ;
struct timeval tval ;
sigset_t my_sigset ;
sigemptyset ( & my_sigset ) ;
if ( sigprocmask ( SIG_SETMASK , & my_sigset , NULL ) < 0 ) {
2007-01-25 17:16:20 +03:00
fprintf ( stderr , " Unable to restore signals. \n " ) ;
2007-01-08 18:18:52 +03:00
exit ( EXIT_FAILURE ) ;
}
2007-01-15 21:58:40 +03:00
signal ( SIGTERM , & _exit_handler ) ;
2005-05-02 15:02:19 +04:00
2007-01-19 20:22:17 +03:00
switch ( pid = fork ( ) ) {
case - 1 :
2015-10-09 22:57:48 +03:00
log_sys_error ( " fork " , " " ) ;
2007-01-08 18:18:52 +03:00
exit ( EXIT_FAILURE ) ;
2005-04-28 02:32:00 +04:00
2007-01-19 20:22:17 +03:00
case 0 : /* Child */
break ;
default :
2007-01-08 18:18:52 +03:00
/* Wait for response from child */
2007-01-19 20:22:17 +03:00
while ( ! waitpid ( pid , & child_status , WNOHANG ) & & ! _exit_now ) {
2007-01-08 18:18:52 +03:00
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 */
2007-01-19 20:22:17 +03:00
switch ( WEXITSTATUS ( child_status ) ) {
2007-01-08 18:18:52 +03:00
case EXIT_DESC_CLOSE_FAILURE :
case EXIT_DESC_OPEN_FAILURE :
case EXIT_FIFO_FAILURE :
case EXIT_CHDIR_FAILURE :
default :
2007-01-19 21:08:36 +03:00
fprintf ( stderr , " Child exited with code %d \n " , WEXITSTATUS ( child_status ) ) ;
2007-01-08 18:18:52 +03:00
break ;
}
2007-01-19 21:08:36 +03:00
exit ( WEXITSTATUS ( child_status ) ) ;
2007-01-08 18:18:52 +03:00
}
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 ;
2011-07-28 17:06:50 +04:00
for ( - - fd ; fd > = 0 ; fd - - ) {
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2011-07-28 17:06:50 +04:00
/* Do not close fds preloaded by systemd! */
if ( _systemd_activation & &
( fd = = SD_FD_FIFO_SERVER | | fd = = SD_FD_FIFO_CLIENT ) )
continue ;
2012-02-27 15:13:51 +04:00
# endif
2012-03-02 01:12:37 +04:00
( void ) close ( fd ) ;
2011-07-28 17:06:50 +04:00
}
2007-01-08 18:18:52 +03:00
if ( ( open ( " /dev/null " , O_RDONLY ) < 0 ) | |
( open ( " /dev/null " , O_WRONLY ) < 0 ) | |
( open ( " /dev/null " , O_WRONLY ) < 0 ) )
exit ( EXIT_DESC_OPEN_FAILURE ) ;
2007-01-19 20:22:17 +03:00
setsid ( ) ;
}
2007-01-08 18:18:52 +03:00
2014-02-03 19:07:06 +04:00
static int _reinstate_registrations ( struct dm_event_fifos * fifos )
{
static const char _failed_parsing_msg [ ] = " Failed to parse existing event registration. \n " ;
static const char * _delim = " " ;
struct dm_event_daemon_message msg = { 0 } ;
char * endp , * dso_name , * dev_name , * mask , * timeout ;
unsigned long mask_value , timeout_value ;
int i , ret ;
ret = daemon_talk ( fifos , & msg , DM_EVENT_CMD_HELLO , NULL , NULL , 0 , 0 ) ;
dm_free ( msg . data ) ;
msg . data = NULL ;
if ( ret ) {
fprintf ( stderr , " Failed to communicate with new instance of dmeventd. \n " ) ;
return 0 ;
}
for ( i = 0 ; _initial_registrations [ i ] ; + + i ) {
if ( ! ( strtok ( _initial_registrations [ i ] , _delim ) ) | |
! ( dso_name = strtok ( NULL , _delim ) ) | |
! ( dev_name = strtok ( NULL , _delim ) ) | |
! ( mask = strtok ( NULL , _delim ) ) | |
! ( timeout = strtok ( NULL , _delim ) ) ) {
fprintf ( stderr , _failed_parsing_msg ) ;
continue ;
}
errno = 0 ;
mask_value = strtoul ( mask , & endp , 10 ) ;
if ( errno | | ! endp | | * endp ) {
fprintf ( stderr , _failed_parsing_msg ) ;
continue ;
}
errno = 0 ;
timeout_value = strtoul ( timeout , & endp , 10 ) ;
if ( errno | | ! endp | | * endp ) {
fprintf ( stderr , _failed_parsing_msg ) ;
continue ;
}
if ( daemon_talk ( fifos , & msg , DM_EVENT_CMD_REGISTER_FOR_EVENT ,
dso_name ,
dev_name ,
( enum dm_event_mask ) mask_value ,
timeout_value ) )
fprintf ( stderr , " Failed to reinstate monitoring for device %s. \n " , dev_name ) ;
}
return 1 ;
}
2015-10-08 12:59:11 +03:00
static void _restart_dmeventd ( void )
2010-10-20 19:12:12 +04:00
{
2013-11-28 15:21:06 +04:00
struct dm_event_fifos fifos = {
. server = - 1 ,
. client = - 1 ,
/* FIXME Make these either configurable or depend directly on dmeventd_path */
. client_path = DM_EVENT_FIFO_CLIENT ,
. server_path = DM_EVENT_FIFO_SERVER
} ;
2013-06-15 00:00:44 +04:00
struct dm_event_daemon_message msg = { 0 } ;
2010-10-20 19:12:12 +04:00
int i , count = 0 ;
char * message ;
2011-04-04 20:11:09 +04:00
int version ;
2014-02-03 19:07:06 +04:00
const char * e ;
2010-10-20 19:12:12 +04:00
/* Get the list of registrations from the running daemon. */
if ( ! init_fifos ( & fifos ) ) {
2011-03-02 15:49:13 +03:00
fprintf ( stderr , " WARNING: Could not initiate communication with existing dmeventd. \n " ) ;
2012-03-14 15:16:00 +04:00
exit ( EXIT_FAILURE ) ;
2010-10-20 19:12:12 +04:00
}
2011-04-04 20:11:09 +04:00
if ( ! dm_event_get_version ( & fifos , & version ) ) {
2011-03-02 15:49:13 +03:00
fprintf ( stderr , " WARNING: Could not communicate with existing dmeventd. \n " ) ;
2013-11-25 17:24:25 +04:00
goto bad ;
2010-10-20 19:12:12 +04:00
}
2011-04-04 20:11:09 +04:00
if ( version < 1 ) {
fprintf ( stderr , " WARNING: The running dmeventd instance is too old. \n "
2014-11-04 21:56:20 +03:00
" Protocol version %d (required: 1). Action cancelled. \n " ,
version ) ;
2013-11-25 17:24:25 +04:00
goto bad ;
2011-04-04 20:11:09 +04:00
}
2013-11-25 17:24:25 +04:00
if ( daemon_talk ( & fifos , & msg , DM_EVENT_CMD_GET_STATUS , " - " , " - " , 0 , 0 ) )
goto bad ;
2010-10-20 19:12:12 +04:00
2014-04-18 17:17:57 +04:00
message = strchr ( msg . data , ' ' ) + 1 ;
for ( i = 0 ; msg . data [ i ] ; + + i )
2010-10-20 19:12:12 +04:00
if ( msg . data [ i ] = = ' ; ' ) {
msg . data [ i ] = 0 ;
+ + count ;
}
2012-02-13 15:18:45 +04:00
if ( ! ( _initial_registrations = dm_malloc ( sizeof ( char * ) * ( count + 1 ) ) ) ) {
fprintf ( stderr , " Memory allocation registration failed. \n " ) ;
2013-11-25 17:24:25 +04:00
goto bad ;
2012-02-13 15:18:45 +04:00
}
2010-10-20 19:12:12 +04:00
for ( i = 0 ; i < count ; + + i ) {
2012-02-13 15:18:45 +04:00
if ( ! ( _initial_registrations [ i ] = dm_strdup ( message ) ) ) {
fprintf ( stderr , " Memory allocation for message failed. \n " ) ;
2013-11-25 17:24:25 +04:00
goto bad ;
2012-02-13 15:18:45 +04:00
}
2010-10-20 19:12:12 +04:00
message + = strlen ( message ) + 1 ;
}
2014-04-18 13:27:07 +04:00
_initial_registrations [ count ] = NULL ;
2010-10-20 19:12:12 +04:00
2014-02-04 20:01:54 +04:00
if ( version > = 2 ) {
if ( daemon_talk ( & fifos , & msg , DM_EVENT_CMD_GET_PARAMETERS , " - " , " - " , 0 , 0 ) ) {
fprintf ( stderr , " Failed to acquire parameters from old dmeventd. \n " ) ;
goto bad ;
}
if ( strstr ( msg . data , " exec_method=systemd " ) )
_systemd_activation = 1 ;
}
# ifdef __linux__
/*
* If the protocol version is old , just assume that if systemd is running ,
* the dmeventd is also run as a systemd service via fifo activation .
*/
if ( version < 2 ) {
/* This check is copied from sd-daemon.c. */
struct stat st ;
2014-03-14 18:57:28 +04:00
if ( ! lstat ( SD_RUNTIME_UNIT_FILE_DIR , & st ) & & ! ! S_ISDIR ( st . st_mode ) )
2014-02-04 20:01:54 +04:00
_systemd_activation = 1 ;
}
# endif
2010-10-20 19:12:12 +04:00
if ( daemon_talk ( & fifos , & msg , DM_EVENT_CMD_DIE , " - " , " - " , 0 , 0 ) ) {
fprintf ( stderr , " Old dmeventd refused to die. \n " ) ;
2013-11-25 17:24:25 +04:00
goto bad ;
2010-10-20 19:12:12 +04:00
}
2014-02-04 20:01:54 +04:00
if ( ! _systemd_activation & &
( ( e = getenv ( SD_ACTIVATION_ENV_VAR_NAME ) ) & & strcmp ( e , " 1 " ) ) )
_systemd_activation = 1 ;
2012-04-24 16:25:12 +04:00
for ( i = 0 ; i < 10 ; + + i ) {
2014-02-03 19:07:06 +04:00
if ( ( access ( DMEVENTD_PIDFILE , F_OK ) = = - 1 ) & & ( errno = = ENOENT ) )
break ;
2012-04-24 16:25:12 +04:00
usleep ( 10 ) ;
}
2014-02-06 20:53:03 +04:00
if ( ! _systemd_activation ) {
fini_fifos ( & fifos ) ;
return ;
}
2014-02-03 19:07:06 +04:00
/* Reopen fifos. */
2010-10-20 19:12:12 +04:00
fini_fifos ( & fifos ) ;
2014-02-03 19:07:06 +04:00
if ( ! init_fifos ( & fifos ) ) {
fprintf ( stderr , " Could not initiate communication with new instance of dmeventd. \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2013-11-25 17:24:25 +04:00
2014-02-03 19:07:06 +04:00
if ( ! _reinstate_registrations ( & fifos ) ) {
fprintf ( stderr , " Failed to reinstate monitoring with new instance of dmeventd. \n " ) ;
goto bad ;
}
fini_fifos ( & fifos ) ;
exit ( EXIT_SUCCESS ) ;
2013-11-25 17:24:25 +04:00
bad :
fini_fifos ( & fifos ) ;
exit ( EXIT_FAILURE ) ;
2010-10-20 19:12:12 +04:00
}
2015-10-08 12:59:11 +03:00
static void _usage ( char * prog , FILE * file )
2007-01-19 20:22:17 +03:00
{
2010-11-29 15:15:41 +03:00
fprintf ( file , " Usage: \n "
2015-10-13 16:14:11 +03:00
" %s [-d [-d [-d]]] [-f] [-h] [-l] [-R] [-V] [-?] \n \n "
2010-11-29 15:15:41 +03:00
" -d Log debug messages to syslog (-d, -dd, -ddd) \n "
2011-12-22 19:50:38 +04:00
" -f Don't fork, run in the foreground \n "
2015-10-08 12:59:11 +03:00
" -h Show this help information \n "
2015-10-13 16:14:11 +03:00
" -l Log to stdout,stderr instead of syslog \n "
2015-10-08 12:59:11 +03:00
" -? Show this help information on stderr \n "
2011-12-22 19:50:38 +04:00
" -R Restart dmeventd \n "
" -V Show version of dmeventd \n \n " , prog ) ;
2007-01-08 18:18:52 +03:00
}
int main ( int argc , char * argv [ ] )
{
2007-01-19 20:22:17 +03:00
signed char opt ;
2013-09-05 15:14:19 +04:00
struct dm_event_fifos fifos = {
. client = - 1 ,
. server = - 1 ,
. client_path = DM_EVENT_FIFO_CLIENT ,
. server_path = DM_EVENT_FIFO_SERVER
} ;
2013-06-30 17:53:42 +04:00
int nothreads ;
2007-01-08 18:18:52 +03:00
//struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON};
2007-01-19 20:22:17 +03:00
opterr = 0 ;
optind = 0 ;
2015-10-13 16:14:11 +03:00
while ( ( opt = getopt ( argc , argv , " ?fhVdlR " ) ) ! = EOF ) {
2007-01-19 20:22:17 +03:00
switch ( opt ) {
case ' h ' :
2015-10-08 12:59:11 +03:00
_usage ( argv [ 0 ] , stdout ) ;
2013-05-29 14:54:42 +04:00
exit ( EXIT_SUCCESS ) ;
2007-01-19 20:22:17 +03:00
case ' ? ' :
2015-10-08 12:59:11 +03:00
_usage ( argv [ 0 ] , stderr ) ;
2013-05-29 14:54:42 +04:00
exit ( EXIT_SUCCESS ) ;
2010-10-20 19:12:12 +04:00
case ' R ' :
_restart + + ;
break ;
2010-05-14 18:56:39 +04:00
case ' f ' :
_foreground + + ;
break ;
2007-01-19 20:22:17 +03:00
case ' d ' :
2015-10-13 16:14:11 +03:00
_debug_level + + ;
break ;
case ' l ' :
_use_syslog = 0 ;
2007-01-19 20:22:17 +03:00
break ;
case ' V ' :
printf ( " dmeventd version: %s \n " , DM_LIB_VERSION ) ;
2013-05-29 14:54:42 +04:00
exit ( EXIT_SUCCESS ) ;
2007-01-19 20:22:17 +03:00
}
}
2015-10-13 16:14:11 +03:00
if ( ! _foreground & & ! _use_syslog ) {
printf ( " WARNING: Ignoring logging to stdout, needs options -f \n " ) ;
_use_syslog = 1 ;
}
2010-03-30 18:40:30 +04:00
/*
* Switch to C locale to avoid reading large locale - archive file
* used by some glibc ( on some distributions it takes over 100 MB ) .
* Daemon currently needs to use mlockall ( ) .
*/
2013-01-22 14:25:02 +04:00
if ( setenv ( " LC_ALL " , " C " , 1 ) )
perror ( " Cannot set LC_ALL to C " ) ;
2010-03-30 18:40:30 +04:00
2010-10-20 19:12:12 +04:00
if ( _restart )
2015-10-08 12:59:11 +03:00
_restart_dmeventd ( ) ;
2010-10-20 19:12:12 +04:00
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2011-07-28 17:06:50 +04:00
_systemd_activation = _systemd_handover ( & fifos ) ;
2012-02-27 15:13:51 +04:00
# endif
2011-07-28 17:06:50 +04:00
2010-05-14 18:56:39 +04:00
if ( ! _foreground )
2007-01-19 20:22:17 +03:00
_daemonize ( ) ;
2005-04-28 02:32:00 +04:00
2015-10-13 16:14:11 +03:00
if ( _use_syslog )
openlog ( " dmeventd " , LOG_PID , LOG_DAEMON ) ;
2007-01-19 20:22:17 +03:00
2015-10-13 16:14:11 +03:00
dm_event_log_set ( _debug_level , _use_syslog ) ;
2015-10-10 19:32:53 +03:00
dm_log_init ( _dmeventd_log ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( DMEVENTD_PIDFILE , S_IFREG ) ;
2010-07-13 17:51:01 +04:00
if ( dm_create_lockfile ( DMEVENTD_PIDFILE ) = = 0 )
exit ( EXIT_FAILURE ) ;
2012-03-15 12:45:55 +04:00
atexit ( _remove_files_on_exit ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2007-01-19 20:22:17 +03:00
/* Set the rest of the signals to cause '_exit_now' to be set */
2012-03-15 12:45:55 +04:00
signal ( SIGTERM , & _exit_handler ) ;
2007-01-19 20:22:17 +03:00
signal ( SIGINT , & _exit_handler ) ;
signal ( SIGHUP , & _exit_handler ) ;
signal ( SIGQUIT , & _exit_handler ) ;
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2011-07-28 17:06:50 +04:00
/* Systemd has adjusted oom killer for us already */
if ( ! _systemd_activation & & ! _protect_against_oom_killer ( ) )
2015-10-09 22:57:48 +03:00
log_error ( " Failed to protect against OOM killer. " ) ;
2007-01-22 18:03:57 +03:00
# endif
2007-01-16 23:13:04 +03:00
2007-01-15 21:58:40 +03:00
_init_thread_signals ( ) ;
2005-04-29 02:47:52 +04:00
2007-01-08 18:35:08 +03:00
pthread_mutex_init ( & _global_mutex , NULL ) ;
2005-04-28 02:32:00 +04:00
2012-03-02 02:06:18 +04:00
if ( ! _systemd_activation & & ! _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. */
2010-05-14 18:56:39 +04:00
if ( ! _foreground )
2010-03-31 16:01:49 +04:00
kill ( getppid ( ) , SIGTERM ) ;
2015-10-09 22:57:48 +03:00
log_notice ( " dmeventd ready for processing. " ) ;
2005-04-28 02:32:00 +04:00
2010-10-20 19:12:12 +04:00
if ( _initial_registrations )
_process_initial_registrations ( ) ;
2013-06-30 17:53:42 +04:00
for ( ; ; ) {
if ( _exit_now ) {
_exit_now = 0 ;
/*
* When ' _exit_now ' is set , signal has been received ,
* but can not simply exit unless all
* threads are done processing .
*/
_lock_mutex ( ) ;
nothreads = ( dm_list_empty ( & _thread_registry ) & &
dm_list_empty ( & _thread_registry_unused ) ) ;
_unlock_mutex ( ) ;
if ( nothreads )
break ;
2015-10-09 22:57:48 +03:00
log_error ( " There are still devices being monitored. " ) ;
log_error ( " Refusing to exit. " ) ;
2013-06-30 17:53:42 +04:00
}
2007-01-15 21:58:40 +03:00
_process_request ( & fifos ) ;
_cleanup_unused_threads ( ) ;
2007-01-08 18:18:52 +03:00
}
2006-01-27 23:50:01 +03:00
2007-01-08 18:35:08 +03:00
pthread_mutex_destroy ( & _global_mutex ) ;
2005-04-28 02:32:00 +04:00
2015-10-09 22:57:48 +03:00
log_notice ( " dmeventd shutting down. " ) ;
2015-10-13 16:14:11 +03:00
if ( close ( fifos . client ) )
log_sys_error ( " client close " , fifos . client_path ) ;
if ( close ( fifos . server ) )
log_sys_error ( " server close " , fifos . server_path ) ;
if ( _use_syslog )
closelog ( ) ;
_exit_dm_lib ( ) ;
2007-01-19 20:22:17 +03:00
2005-05-02 15:02:19 +04:00
exit ( EXIT_SUCCESS ) ;
2005-04-28 02:32:00 +04:00
}