2014-10-22 00:31:17 +02:00
/*
2020-08-20 13:41:42 +02:00
* Copyright ( c ) 2014 - 2020 Albert S . < adhocify @ quitesimple . org >
2014-10-22 00:31:17 +02:00
*
* Permission to use , copy , modify , and distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
2013-04-21 22:36:59 +02:00
# define _GNU_SOURCE
# include <stdio.h>
# include <stdlib.h>
# include <sys/inotify.h>
# include <sys/types.h>
2014-12-20 20:11:51 +01:00
# include <sys/wait.h>
2013-04-21 22:36:59 +02:00
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <signal.h>
# include <libgen.h>
# include <inttypes.h>
# include <time.h>
# include <stdbool.h>
# include <stdarg.h>
# include <errno.h>
2013-06-12 22:38:52 +02:00
# include <fnmatch.h>
2013-09-21 20:45:10 +02:00
# include <getopt.h>
2016-05-23 17:54:02 +02:00
# include <ftw.h>
2014-04-11 19:46:09 +02:00
# include <linux/limits.h>
# define BUF_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1) * 1024
2013-06-12 22:38:52 +02:00
# define STREQ(s1,s2) ( strcmp(s1,s2) == 0 )
2020-08-20 22:42:05 +02:00
# define SCRIPT_PLACE_SPECIFIER "{}" //same as EVENTFILE_PLACEHOLDER for backwards compatibility
# define EVENTFILE_PLACEHOLDER "%eventfilepath%"
# define EVENTSTR_PLACEHOLDER "%eventmaskstr%"
2013-08-29 14:41:55 +02:00
struct watchlistentry
2013-04-21 22:36:59 +02:00
{
int ifd ;
char * path ;
bool isdir ;
2013-08-29 14:41:55 +02:00
struct watchlistentry * next ;
2013-04-21 22:36:59 +02:00
} ;
2017-07-27 22:10:49 +02:00
2013-08-29 14:41:55 +02:00
struct watchlistentry * watchlist_head = NULL ;
struct watchlistentry * * watchlist = & watchlist_head ;
2013-06-12 22:38:52 +02:00
struct ignorelist
{
char * ignore ;
struct ignorelist * next ;
} ;
struct ignorelist * ignorelist_head = NULL ;
struct ignorelist * * ignorelist_current = & ignorelist_head ;
2013-04-21 22:36:59 +02:00
2019-04-27 23:00:12 +02:00
/* Write-once globals. Set from process_arguments*/
2013-12-16 21:13:07 +01:00
bool silent = false ;
2014-10-10 15:52:01 +02:00
bool noenv = false ;
2014-04-11 19:46:09 +02:00
bool fromstdin = false ;
bool forkbombcheck = true ;
bool daemonize = false ;
2014-12-20 20:11:51 +01:00
bool exit_with_child = false ;
2019-04-27 23:00:12 +02:00
int awaited_child_exit_code = - 1 ;
2020-08-15 23:19:14 +02:00
bool negate_child_exit_code = false ;
2014-12-20 20:11:51 +01:00
2013-12-16 21:13:07 +01:00
uint32_t mask = 0 ;
char * prog = NULL ;
char * path_logfile = NULL ;
2014-10-10 15:52:01 +02:00
char * * script_arguments = NULL ; //options to be passed to script we are calling
size_t n_script_arguments = 0 ;
2013-04-21 22:36:59 +02:00
void * xmalloc ( size_t size )
{
void * m = malloc ( size ) ;
if ( m = = NULL )
{
perror ( " malloc " ) ;
exit ( EXIT_FAILURE ) ;
}
return m ;
}
char * xstrdup ( const char * s )
{
char * tmp = strdup ( s ) ;
if ( tmp = = NULL )
{
perror ( " strdup " ) ;
exit ( EXIT_FAILURE ) ;
}
return tmp ;
}
char * xrealpath ( const char * path , char * resolved_path )
{
char * tmp = realpath ( path , resolved_path ) ;
if ( tmp = = NULL )
{
2020-08-15 22:33:35 +02:00
char * errorstr = strerror ( errno ) ;
fprintf ( stderr , " realpath on %s failed: %s \n " , path , errorstr ) ;
2013-04-21 22:36:59 +02:00
exit ( EXIT_FAILURE ) ;
}
return tmp ;
}
char * ndirname ( const char * path )
{
2015-10-12 22:40:36 +02:00
if ( path = = NULL )
{
2013-08-29 14:41:55 +02:00
return xstrdup ( " . " ) ;
2015-10-12 22:40:36 +02:00
}
2013-04-21 22:36:59 +02:00
char * c = strdupa ( path ) ;
return xstrdup ( dirname ( c ) ) ;
}
char * find_ifd_path ( int ifd )
{
2013-08-29 14:41:55 +02:00
for ( struct watchlistentry * lkp = watchlist_head ; lkp ! = NULL ; lkp = lkp - > next )
2015-10-12 22:40:36 +02:00
{
2013-06-12 22:38:52 +02:00
if ( lkp - > ifd = = ifd )
2015-10-12 22:40:36 +02:00
{
2013-06-12 22:38:52 +02:00
return lkp - > path ;
2015-10-12 22:40:36 +02:00
}
}
2013-04-21 22:36:59 +02:00
return NULL ;
}
2013-08-29 14:41:55 +02:00
bool is_ignored ( const char * filename )
2013-06-12 22:38:52 +02:00
{
for ( struct ignorelist * l = ignorelist_head ; l ! = NULL ; l = l - > next )
2015-10-12 22:40:36 +02:00
{
2013-08-29 14:41:55 +02:00
if ( fnmatch ( l - > ignore , filename , 0 ) = = 0 )
2015-10-12 22:40:36 +02:00
{
2013-06-12 22:38:52 +02:00
return true ;
2015-10-12 22:40:36 +02:00
}
}
2013-06-12 22:38:52 +02:00
return false ;
}
2013-08-29 14:41:55 +02:00
bool path_is_directory ( const char * path )
{
struct stat sb ;
int r = stat ( path , & sb ) ;
if ( r = = - 1 )
{
perror ( " stat " ) ;
return false ;
}
return S_ISDIR ( sb . st_mode ) ;
}
2013-12-16 21:13:07 +01:00
static inline bool file_exists ( const char * path )
2013-08-29 14:41:55 +02:00
{
2013-12-16 21:13:07 +01:00
return access ( path , F_OK ) = = 0 ;
2013-08-29 14:41:55 +02:00
}
2015-05-11 12:33:10 +02:00
void add_to_ignore_list ( const char * str )
2013-06-12 22:38:52 +02:00
{
* ignorelist_current = xmalloc ( sizeof ( struct ignorelist ) ) ;
( * ignorelist_current ) - > ignore = xstrdup ( str ) ;
ignorelist_current = & ( * ignorelist_current ) - > next ;
}
2013-04-21 22:36:59 +02:00
void logwrite ( const char * format , . . . )
{
2015-10-12 22:40:36 +02:00
if ( silent )
{
2014-03-05 18:22:12 +01:00
return ;
2015-10-12 22:40:36 +02:00
}
2013-04-21 22:36:59 +02:00
va_list args ;
va_start ( args , format ) ;
2013-06-12 22:38:52 +02:00
vfprintf ( stdout , format , args ) ;
fflush ( stdout ) ;
2013-04-21 22:36:59 +02:00
va_end ( args ) ;
}
void logerror ( const char * format , . . . )
{
va_list args ;
va_start ( args , format ) ;
char * prefix = " Error: " ;
char * tmp = alloca ( strlen ( format ) + strlen ( prefix ) + 1 ) ;
strcpy ( tmp , prefix ) ;
strcat ( tmp , format ) ;
vfprintf ( stderr , tmp , args ) ;
fflush ( stderr ) ;
va_end ( args ) ;
2013-06-12 22:38:52 +02:00
2013-04-21 22:36:59 +02:00
}
2016-05-23 18:04:53 +02:00
void watchqueue_add_path ( const char * pathname )
2013-04-21 22:36:59 +02:00
{
2013-08-29 14:41:55 +02:00
* watchlist = xmalloc ( sizeof ( struct watchlistentry ) ) ;
struct watchlistentry * e = * watchlist ;
2013-04-21 22:36:59 +02:00
char * path = xrealpath ( pathname , NULL ) ;
2013-08-29 14:41:55 +02:00
e - > ifd = 0 ;
e - > path = path ;
e - > isdir = path_is_directory ( pathname ) ;
e - > next = NULL ;
watchlist = & e - > next ;
2013-04-21 22:36:59 +02:00
}
2016-05-23 17:54:02 +02:00
2014-04-11 19:46:09 +02:00
void create_watches ( int fd , uint32_t mask )
2013-04-21 22:36:59 +02:00
{
2013-08-29 14:41:55 +02:00
for ( struct watchlistentry * lkp = watchlist_head ; lkp ! = NULL ; lkp = lkp - > next )
2013-04-21 22:36:59 +02:00
{
int ret = inotify_add_watch ( fd , lkp - > path , mask ) ;
if ( ret = = - 1 )
{
perror ( " inotify_add_watch " ) ;
exit ( EXIT_FAILURE ) ;
}
lkp - > ifd = ret ;
}
}
bool redirect_stdout ( const char * outfile )
{
int fd = open ( outfile , O_CREAT | O_WRONLY | O_APPEND , S_IRUSR | S_IWUSR ) ;
if ( fd = = - 1 )
{
perror ( " open " ) ;
return false ;
}
if ( dup2 ( fd , 1 ) = = - 1 | | dup2 ( fd , 2 ) = = - 1 )
{
perror ( " dup2 " ) ;
return false ;
}
return true ;
}
2020-08-20 22:42:05 +02:00
const char * mask_to_names ( int mask )
{
static char ret [ 1024 ] ;
size_t n = sizeof ( ret ) - 1 ;
if ( mask & IN_ATTRIB )
{
strncat ( ret , " IN_ATTRIB, " , n ) ;
}
if ( mask & IN_OPEN )
{
strncat ( ret , " IN_OPEN, " , n ) ;
}
if ( mask & IN_CLOSE )
{
strncat ( ret , " IN_CLOSE, " , n ) ;
}
if ( mask & IN_CLOSE_NOWRITE )
{
strncat ( ret , " IN_CLOSE, " , n ) ;
}
if ( mask & IN_CLOSE_WRITE )
{
strncat ( ret , " IN_CLOSE_WRITE, " , n ) ;
}
if ( mask & IN_CREATE )
{
strncat ( ret , " IN_CREATE, " , n ) ;
}
if ( mask & IN_DELETE )
{
strncat ( ret , " IN_DELETE, " , n ) ;
}
if ( mask & IN_DELETE_SELF )
{
strncat ( ret , " IN_DELETE_SELF, " , n ) ;
}
if ( mask & IN_MODIFY )
{
strncat ( ret , " IN_MODIFY, " , n ) ;
}
if ( mask & IN_MOVE )
{
strncat ( ret , " IN_MOVE, " , n ) ;
}
if ( mask & IN_MOVE_SELF )
{
strncat ( ret , " IN_MOVE_SELF, " , n ) ;
}
if ( mask & IN_MOVED_FROM )
{
strncat ( ret , " IN_MOVED_FROM, " , n ) ;
}
if ( mask & IN_MOVED_TO )
{
strncat ( ret , " IN_MOVED_TO, " , n ) ;
}
for ( int i = n ; i > = 0 ; - - i )
{
if ( ret [ i ] = = ' , ' )
{
ret [ i ] = 0 ;
break ;
}
}
ret [ 1023 ] = 0 ;
return ret ;
}
2013-12-16 21:13:07 +01:00
bool run_prog ( const char * eventfile , uint32_t eventmask )
2013-04-21 22:36:59 +02:00
{
pid_t pid = fork ( ) ;
if ( pid = = 0 )
{
2013-12-16 21:13:07 +01:00
if ( path_logfile )
2013-04-21 22:36:59 +02:00
{
2015-10-12 22:40:36 +02:00
if ( ! redirect_stdout ( path_logfile ) )
{
2013-04-21 22:36:59 +02:00
return false ;
2015-10-12 22:40:36 +02:00
}
2013-04-21 22:36:59 +02:00
}
2013-06-12 22:38:52 +02:00
2014-10-10 15:52:01 +02:00
if ( ! noenv )
{
char envvar [ 30 ] ;
2020-08-15 23:31:41 +02:00
snprintf ( envvar , sizeof ( envvar ) , " ADHOCIFYEVENT=% " PRIu32 , eventmask ) ;
2014-10-10 15:52:01 +02:00
putenv ( envvar ) ;
}
2014-10-21 23:41:53 +02:00
for ( unsigned int i = 0 ; i < n_script_arguments ; i + + )
2014-10-10 15:52:01 +02:00
{
char * argument = script_arguments [ i ] ;
2020-08-20 22:42:05 +02:00
if ( argument ! = NULL )
{
if ( STREQ ( argument , SCRIPT_PLACE_SPECIFIER ) | | STREQ ( argument , EVENTFILE_PLACEHOLDER ) )
{
script_arguments [ i ] = eventfile ;
}
if ( STREQ ( argument , EVENTSTR_PLACEHOLDER ) )
{
script_arguments [ i ] = mask_to_names ( eventmask ) ;
}
}
2014-10-10 15:52:01 +02:00
}
2020-08-20 11:58:18 +02:00
execvp ( prog , script_arguments ) ;
logerror ( " Exec of %s failed: %s \n " , prog , strerror ( errno ) ) ;
2020-08-20 13:20:20 +02:00
int exitcode = ( errno = = ENOENT ) ? 127 : EXIT_FAILURE ;
exit ( exitcode ) ;
2013-04-21 22:36:59 +02:00
}
if ( pid = = - 1 )
2013-06-12 22:38:52 +02:00
{
2013-04-21 22:36:59 +02:00
perror ( " fork " ) ;
2013-06-12 22:38:52 +02:00
return false ;
}
2013-04-21 22:36:59 +02:00
return true ;
}
2020-08-20 22:42:05 +02:00
uint32_t name_to_mask ( const char * name )
2013-04-21 22:36:59 +02:00
{
2013-06-12 22:38:52 +02:00
if ( STREQ ( name , " IN_CLOSE_WRITE " ) )
2013-04-21 22:36:59 +02:00
return IN_CLOSE_WRITE ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_OPEN " ) )
2013-04-21 22:36:59 +02:00
return IN_OPEN ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_MODIFY " ) )
2013-04-21 22:36:59 +02:00
return IN_MODIFY ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_DELETE " ) )
2013-04-21 22:36:59 +02:00
return IN_DELETE ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_ATTRIB " ) )
2013-04-21 22:36:59 +02:00
return IN_ATTRIB ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_CLOSE_NOWRITE " ) )
2013-04-21 22:36:59 +02:00
return IN_CLOSE_NOWRITE ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_MOVED_FROM " ) )
2013-04-21 22:36:59 +02:00
return IN_MOVED_FROM ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_MOVED_TO " ) )
2013-04-21 22:36:59 +02:00
return IN_MOVED_TO ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_CREATE " ) )
2013-04-21 22:36:59 +02:00
return IN_CREATE ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_DELETE_SELF " ) )
2013-04-21 22:36:59 +02:00
return IN_DELETE_SELF ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_MOVE_SELF " ) )
2015-01-06 20:19:00 +01:00
return IN_MOVE_SELF ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_ALL_EVENTS " ) )
2013-04-21 22:36:59 +02:00
return IN_ALL_EVENTS ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_CLOSE " ) )
2013-04-21 22:36:59 +02:00
return IN_CLOSE ;
2013-06-12 22:38:52 +02:00
else if ( STREQ ( name , " IN_MOVE " ) )
2013-04-21 22:36:59 +02:00
return IN_MOVE ;
else
return 0 ;
}
2013-08-29 14:41:55 +02:00
void check_forkbomb ( const char * path_logfile , const char * path_prog )
2013-04-21 22:36:59 +02:00
{
2013-08-29 14:41:55 +02:00
char * dir_log = ndirname ( path_logfile ) ;
char * dir_prog = ndirname ( path_prog ) ;
2013-04-21 22:36:59 +02:00
2013-08-29 14:41:55 +02:00
struct watchlistentry * lkp = watchlist_head ;
2013-04-21 22:36:59 +02:00
while ( lkp )
{
if ( lkp - > isdir )
{
char * dir_lkpPath = lkp - > path ;
2013-08-29 14:41:55 +02:00
if ( STREQ ( dir_lkpPath , dir_log ) | | STREQ ( dir_lkpPath , dir_prog ) )
2013-04-21 22:36:59 +02:00
{
2020-08-07 23:12:07 +02:00
logerror ( " Don't place your logfiles or command in a directory you are watching for events. Pass -b to bypass this check. \n " ) ;
2013-04-21 22:36:59 +02:00
exit ( EXIT_FAILURE ) ;
}
}
lkp = lkp - > next ;
}
free ( dir_log ) ;
free ( dir_prog ) ;
}
2013-06-12 22:38:52 +02:00
void queue_watches_from_stdin ( )
2013-04-21 22:36:59 +02:00
{
char * line = NULL ;
2013-09-30 19:45:48 +02:00
size_t n = 0 ;
2013-04-21 22:36:59 +02:00
ssize_t r ;
while ( ( r = getline ( & line , & n , stdin ) ) ! = - 1 )
{
2013-09-21 20:45:10 +02:00
if ( line [ r - 1 ] = = ' \n ' )
line [ r - 1 ] = 0 ;
2016-05-23 18:04:53 +02:00
watchqueue_add_path ( line ) ;
2013-04-21 22:36:59 +02:00
}
}
char * get_eventfile_abspath ( struct inotify_event * event )
{
char * wdpath = find_ifd_path ( event - > wd ) ;
2015-10-12 22:40:36 +02:00
if ( wdpath = = NULL )
{
2013-04-21 22:36:59 +02:00
return NULL ;
2015-10-12 22:40:36 +02:00
}
2017-07-28 08:55:53 +02:00
char * result = NULL ;
2017-08-12 16:10:47 +02:00
if ( ( event - > len ) > 0 )
2013-04-21 22:36:59 +02:00
{
2017-07-28 08:55:53 +02:00
if ( asprintf ( & result , " %s/%s " , wdpath , event - > name ) = = - 1 )
{
return NULL ;
}
2013-04-21 22:36:59 +02:00
}
2017-07-28 08:55:53 +02:00
else
{
result = strdup ( wdpath ) ;
}
return result ;
2013-04-21 22:36:59 +02:00
}
2013-06-12 22:38:52 +02:00
2013-12-16 21:13:07 +01:00
void handle_event ( struct inotify_event * event )
2013-06-12 22:38:52 +02:00
{
if ( event - > mask & mask )
{
char * eventfile_abspath = get_eventfile_abspath ( event ) ;
if ( eventfile_abspath = = NULL )
{
2016-08-21 15:48:59 +02:00
logerror ( " Could not get absolute path for event. Watch descriptor %i \n " , event - > wd ) ;
2013-06-12 22:38:52 +02:00
exit ( EXIT_FAILURE ) ;
}
2013-06-12 22:55:30 +02:00
2013-08-29 14:41:55 +02:00
if ( is_ignored ( eventfile_abspath ) )
2014-03-05 18:22:12 +01:00
{
free ( eventfile_abspath ) ;
2013-08-29 14:41:55 +02:00
return ;
2014-03-05 18:22:12 +01:00
}
2020-08-20 13:33:33 +02:00
logwrite ( " Starting execution of command %s \n " , prog ) ;
2013-12-16 21:13:07 +01:00
bool r = run_prog ( eventfile_abspath , event - > mask ) ;
2013-06-12 22:38:52 +02:00
if ( ! r )
{
2020-08-20 13:33:33 +02:00
logerror ( " Execution of command %s failed \n " , prog ) ;
2013-06-12 22:38:52 +02:00
exit ( EXIT_FAILURE ) ;
}
fflush ( stdout ) ;
fflush ( stderr ) ;
free ( eventfile_abspath ) ;
}
}
2013-12-16 21:13:07 +01:00
static inline char * get_cwd ( )
2013-08-29 14:41:55 +02:00
{
return getcwd ( NULL , 0 ) ;
}
void print_usage ( )
{
2020-08-07 23:12:07 +02:00
printf ( " adhocify [OPTIONS] command [arguments for command] - Monitor for inotify events and launch commands \n \n " ) ;
printf ( " --daemon, -d run as a daemon \n " ) ;
printf ( " --path, -w adds the specified path to the watchlist \n " ) ;
printf ( " --logfile, -o path to write output of adhocify and stdout/stderr of launched commands to \n " ) ;
printf ( " --mask, -m inotify event to watch for (see inotify(7)). Can be specified multiple times to watch for several events \n " ) ;
printf ( " --no-env, -a if specified, the inotify event which occured won't be passed to the command as an environment variable \n " ) ;
printf ( " --silent, -q surpress any output created by adhocify itself \n " ) ;
printf ( " --stdin, -s Read the paths which must be added to the watchlist from stdin. Each path must be in a seperate line \n " ) ;
printf ( " --no-forkbomb-check, -b Disable fork bomb detection \n " ) ;
printf ( " --ignore, -i Shell wildcard pattern (see glob(7)) to ignore events on files for which the pattern matches \n " ) ;
printf ( " --exit-with-child, -e Exit when the commands exits. You can also specify a return code (e. g. -e=1 to exit only on errors) \n " ) ; printf ( " \n If your command should know the file the event occured on, use the {} placeholder when you specify the arguments (like xargs) \n " ) ;
2013-08-29 14:41:55 +02:00
}
2013-06-12 22:38:52 +02:00
2013-09-21 20:45:10 +02:00
static struct option long_options [ ] =
{
{ " daemon " , no_argument , 0 , ' d ' } ,
{ " logfile " , required_argument , 0 , ' o ' } ,
{ " mask " , required_argument , 0 , ' m ' } ,
{ " path " , required_argument , 0 , ' w ' } ,
{ " no-env " , no_argument , 0 , ' a ' } ,
{ " stdin " , no_argument , 0 , ' s ' } ,
{ " no-forkbomb-check " , no_argument , 0 , ' b ' } ,
{ " ignore " , required_argument , 0 , ' i ' } ,
{ " silent " , no_argument , 0 , ' q ' } ,
2014-12-20 20:11:51 +01:00
{ " help " , no_argument , 0 , ' h ' } ,
{ " exit-with-child " , optional_argument , 0 , ' e ' }
2013-09-21 20:45:10 +02:00
} ;
2014-10-21 23:41:53 +02:00
//fills global n_script_arguments and script_arguments var
void fill_script_arguments ( size_t n_args , char * args [ ] )
{
n_script_arguments = n_args + 2 ; //2 = argv0 and terminating NULL
char * * arguments = xmalloc ( n_script_arguments * sizeof ( char * ) ) ;
const char * argv0 = memrchr ( prog , ' / ' , strlen ( prog ) ) ;
argv0 = ( argv0 = = NULL ) ? prog : argv0 + 1 ;
arguments [ 0 ] = argv0 ;
const int begin_offset = 1 ;
for ( unsigned int i = 0 ; i < n_args ; i + + )
{
char * argument = args [ i ] ;
arguments [ i + begin_offset ] = strdup ( argument ) ;
}
arguments [ n_args + begin_offset ] = NULL ;
script_arguments = arguments ;
}
2013-09-21 20:45:10 +02:00
2014-04-11 19:46:09 +02:00
void parse_options ( int argc , char * * argv )
2013-04-21 22:36:59 +02:00
{
char * watchpath = NULL ;
2014-03-05 18:22:12 +01:00
int option ;
2013-09-21 20:45:10 +02:00
int option_index ;
2014-04-11 19:46:09 +02:00
uint32_t optmask = 0 ;
2017-07-28 08:55:53 +02:00
while ( ( option = getopt_long ( argc , argv , " absdo:w:m:l:i:e:: " , long_options , & option_index ) ) ! = - 1 )
2013-04-21 22:36:59 +02:00
{
switch ( option )
{
case ' d ' :
2014-04-11 19:46:09 +02:00
daemonize = true ;
2013-04-21 22:36:59 +02:00
break ;
case ' o ' :
2013-08-29 14:41:55 +02:00
path_logfile = optarg ;
2013-04-21 22:36:59 +02:00
break ;
case ' m ' :
2020-08-20 22:42:05 +02:00
optmask = name_to_mask ( optarg ) ;
2013-04-21 22:36:59 +02:00
if ( optmask = = 0 ) {
2020-08-20 00:20:52 +02:00
logerror ( " Not supported inotify event: %s \n " , optarg ) ;
2013-04-21 22:36:59 +02:00
exit ( EXIT_FAILURE ) ;
}
mask | = optmask ;
break ;
case ' w ' :
watchpath = optarg ;
2016-05-23 18:04:53 +02:00
watchqueue_add_path ( watchpath ) ;
2013-04-21 22:36:59 +02:00
break ;
case ' a ' :
2014-10-10 15:52:01 +02:00
noenv = true ;
2013-04-21 22:36:59 +02:00
break ;
case ' s ' :
fromstdin = true ;
break ;
case ' b ' :
forkbombcheck = false ;
break ;
2013-06-12 22:38:52 +02:00
case ' i ' :
2015-05-11 12:33:10 +02:00
add_to_ignore_list ( optarg ) ;
2013-06-12 22:38:52 +02:00
break ;
case ' q ' :
silent = true ;
break ;
2013-08-29 14:41:55 +02:00
case ' h ' :
print_usage ( ) ;
exit ( EXIT_SUCCESS ) ;
2014-12-20 20:11:51 +01:00
case ' e ' :
exit_with_child = true ;
if ( optarg )
{
2020-08-15 23:19:14 +02:00
if ( * optarg = = ' ! ' )
{
negate_child_exit_code = true ;
+ + optarg ;
}
if ( * optarg = = ' \0 ' )
{
logerror ( " Please specify the exit code \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2019-04-27 23:00:12 +02:00
awaited_child_exit_code = atoi ( optarg ) ;
2014-12-20 20:11:51 +01:00
}
2013-08-29 14:41:55 +02:00
break ;
2013-04-21 22:36:59 +02:00
}
}
2013-08-29 14:41:55 +02:00
2014-10-10 15:52:01 +02:00
if ( optind = = argc )
2013-04-21 22:36:59 +02:00
{
2014-10-10 15:52:01 +02:00
print_usage ( ) ;
2020-08-07 23:12:07 +02:00
logerror ( " missing command path \n " ) ;
2013-04-21 22:36:59 +02:00
exit ( EXIT_FAILURE ) ;
}
2014-10-10 15:52:01 +02:00
prog = argv [ optind + + ] ;
2017-03-19 09:53:04 +01:00
if ( optind < = argc )
2014-10-10 15:52:01 +02:00
{
2014-10-21 23:41:53 +02:00
fill_script_arguments ( argc - optind , & argv [ optind ] ) ;
2014-10-10 15:52:01 +02:00
}
2014-04-11 19:46:09 +02:00
}
void process_options ( )
{
if ( fromstdin )
2015-10-12 22:40:36 +02:00
{
2014-04-11 19:46:09 +02:00
queue_watches_from_stdin ( ) ;
2015-10-12 22:40:36 +02:00
}
2020-08-07 23:15:40 +02:00
2014-04-11 19:46:09 +02:00
if ( watchlist_head = = NULL )
2015-10-12 22:40:36 +02:00
{
2016-05-23 18:04:53 +02:00
watchqueue_add_path ( get_cwd ( ) ) ;
2015-10-12 22:40:36 +02:00
}
if ( mask = = 0 )
{
2014-04-11 19:46:09 +02:00
mask | = IN_CLOSE_WRITE ;
2015-10-12 22:40:36 +02:00
}
2014-04-11 19:46:09 +02:00
2013-08-29 14:41:55 +02:00
if ( path_logfile )
2015-10-12 22:40:36 +02:00
{
2013-08-29 14:41:55 +02:00
path_logfile = xrealpath ( path_logfile , NULL ) ;
2015-10-12 22:40:36 +02:00
}
2013-04-21 22:36:59 +02:00
if ( forkbombcheck )
{
2020-08-20 11:58:18 +02:00
char * path_prog = realpath ( prog , NULL ) ;
if ( path_prog ! = NULL )
{
check_forkbomb ( path_logfile , path_prog ) ;
}
free ( path_prog ) ;
2013-04-21 22:36:59 +02:00
}
2020-08-07 23:15:40 +02:00
if ( daemonize )
{
if ( daemon ( 0 , 0 ) = = - 1 )
{
perror ( " daemon " ) ;
exit ( EXIT_FAILURE ) ;
}
}
2014-04-11 19:46:09 +02:00
}
2013-06-12 22:38:52 +02:00
2014-04-11 19:46:09 +02:00
void start_monitoring ( int ifd )
{
2013-04-21 22:36:59 +02:00
while ( 1 )
{
2014-04-11 19:46:09 +02:00
int len ;
int offset = 0 ;
char buf [ BUF_SIZE ] ;
len = read ( ifd , buf , BUF_SIZE ) ;
if ( len = = - 1 )
{
if ( errno = = EINTR )
continue ;
perror ( " read " ) ;
exit ( EXIT_FAILURE ) ;
}
2013-06-12 22:38:52 +02:00
2014-04-11 19:46:09 +02:00
while ( offset < len )
{
2013-04-21 22:36:59 +02:00
2014-04-11 19:46:09 +02:00
struct inotify_event * event = ( struct inotify_event * ) & buf [ offset ] ;
handle_event ( event ) ;
offset + = sizeof ( struct inotify_event ) + event - > len ;
}
2020-08-20 11:58:18 +02:00
}
2013-04-21 22:36:59 +02:00
}
2014-12-20 20:11:51 +01:00
void child_handler ( int signum , siginfo_t * info , void * context )
{
2015-10-12 22:40:36 +02:00
if ( signum ! = SIGCHLD )
{
2014-12-20 20:11:51 +01:00
return ;
2015-10-12 22:40:36 +02:00
}
2014-12-20 20:11:51 +01:00
int status ;
pid_t p = waitpid ( - 1 , & status , WNOHANG ) ;
if ( p = = - 1 )
{
logerror ( " waitpid failed when handling child exit \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2020-08-20 13:20:20 +02:00
int adhocify_exit_code = 0 ;
if ( WIFEXITED ( status ) )
2014-12-20 20:11:51 +01:00
{
2020-08-20 13:20:20 +02:00
adhocify_exit_code = WEXITSTATUS ( status ) ;
if ( adhocify_exit_code = = 127 )
2014-12-20 20:11:51 +01:00
{
2020-08-20 13:20:20 +02:00
logwrite ( " command not found, exiting \n " ) ;
exit ( adhocify_exit_code ) ;
2019-04-27 23:00:12 +02:00
}
2020-08-20 13:20:20 +02:00
if ( exit_with_child & & awaited_child_exit_code > - 1 )
2019-04-27 23:00:12 +02:00
{
2020-08-20 13:20:20 +02:00
bool must_exit = adhocify_exit_code = = awaited_child_exit_code ;
if ( negate_child_exit_code )
{
must_exit = ! must_exit ;
}
if ( must_exit )
{
2020-08-20 13:33:33 +02:00
logwrite ( " command exited with specified exit code, exiting too \n " ) ;
2020-08-20 13:20:20 +02:00
exit ( adhocify_exit_code ) ;
}
2014-12-20 20:11:51 +01:00
}
2020-08-20 13:20:20 +02:00
}
if ( exit_with_child & & WIFSIGNALED ( status ) )
{
adhocify_exit_code = 128 + WTERMSIG ( status ) ; //copy bash's behaviour
2019-04-27 23:00:12 +02:00
exit ( adhocify_exit_code ) ;
2014-12-20 20:11:51 +01:00
}
}
2020-08-20 13:20:20 +02:00
2014-12-20 20:11:51 +01:00
void set_signals ( )
{
struct sigaction action ;
action . sa_flags = SA_NOCLDSTOP | SA_SIGINFO ;
action . sa_sigaction = & child_handler ;
if ( sigaction ( SIGCHLD , & action , NULL ) = = - 1 )
{
logerror ( " Error when setting up the signal handler \n " ) ;
exit ( EXIT_FAILURE ) ;
}
}
2014-04-11 19:46:09 +02:00
int main ( int argc , char * * argv )
{
if ( argc < 2 )
{
print_usage ( ) ;
exit ( EXIT_FAILURE ) ;
}
2014-12-20 20:11:51 +01:00
//signal(SIGCHLD, SIG_IGN);
set_signals ( ) ;
2014-04-11 19:46:09 +02:00
parse_options ( argc , argv ) ;
process_options ( ) ;
int ifd = inotify_init ( ) ;
if ( ifd = = - 1 )
{
perror ( " inotify_init " ) ;
exit ( EXIT_FAILURE ) ;
}
create_watches ( ifd , mask ) ;
start_monitoring ( ifd ) ;
}