|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2014-2020 Albert S. <adhocify@quitesimple.org>
|
|
|
|
|
* Copyright (c) 2014-2024 Albert S. <adhocify@quitesimple.org>
|
|
|
|
|
*
|
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
@ -36,8 +36,12 @@
|
|
|
|
|
#include <ftw.h>
|
|
|
|
|
#include <linux/limits.h>
|
|
|
|
|
#define BUF_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1) * 1024
|
|
|
|
|
#define STREQ(s1,s2) ( strcmp(s1,s2) == 0 )
|
|
|
|
|
#define SCRIPT_PLACE_SPECIFIER "{}" //TODO: think of a better name...
|
|
|
|
|
#define STREQ(s1, s2) (strcmp(s1, s2) == 0)
|
|
|
|
|
|
|
|
|
|
#define SCRIPT_PLACE_SPECIFIER "{}" // same as EVENTFILE_PLACEHOLDER for backwards compatibility
|
|
|
|
|
#define EVENTFILE_PLACEHOLDER "%eventfilepath%"
|
|
|
|
|
#define EVENTSTR_PLACEHOLDER "%eventmaskstr%"
|
|
|
|
|
|
|
|
|
|
struct watchlistentry
|
|
|
|
|
{
|
|
|
|
|
int ifd;
|
|
|
|
@ -49,8 +53,6 @@ struct watchlistentry
|
|
|
|
|
struct watchlistentry *watchlist_head = NULL;
|
|
|
|
|
struct watchlistentry **watchlist = &watchlist_head;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct ignorelist
|
|
|
|
|
{
|
|
|
|
|
char *ignore;
|
|
|
|
@ -60,7 +62,6 @@ struct ignorelist
|
|
|
|
|
struct ignorelist *ignorelist_head = NULL;
|
|
|
|
|
struct ignorelist **ignorelist_current = &ignorelist_head;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Write-once globals. Set from process_arguments*/
|
|
|
|
|
bool silent = false;
|
|
|
|
|
bool noenv = false;
|
|
|
|
@ -74,9 +75,11 @@ bool negate_child_exit_code = false;
|
|
|
|
|
uint32_t mask = 0;
|
|
|
|
|
char *prog = NULL;
|
|
|
|
|
char *path_logfile = NULL;
|
|
|
|
|
char **script_arguments = NULL; //options to be passed to script we are calling
|
|
|
|
|
char **script_arguments = NULL; // options to be passed to script we are calling
|
|
|
|
|
size_t n_script_arguments = 0;
|
|
|
|
|
|
|
|
|
|
volatile sig_atomic_t handle_child_exits = 0;
|
|
|
|
|
|
|
|
|
|
void *xmalloc(size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *m = malloc(size);
|
|
|
|
@ -111,7 +114,6 @@ char *xrealpath(const char *path, char *resolved_path)
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *ndirname(const char *path)
|
|
|
|
|
{
|
|
|
|
|
if(path == NULL)
|
|
|
|
@ -163,17 +165,14 @@ static inline bool file_exists(const char *path)
|
|
|
|
|
return access(path, F_OK) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void add_to_ignore_list(const char *str)
|
|
|
|
|
{
|
|
|
|
|
*ignorelist_current = xmalloc(sizeof(struct ignorelist));
|
|
|
|
|
(*ignorelist_current)->ignore = xstrdup(str);
|
|
|
|
|
|
|
|
|
|
(*ignorelist_current)->next = NULL;
|
|
|
|
|
ignorelist_current = &(*ignorelist_current)->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void logwrite(const char *format, ...)
|
|
|
|
|
{
|
|
|
|
|
if(silent)
|
|
|
|
@ -198,7 +197,6 @@ void logerror(const char *format, ...)
|
|
|
|
|
vfprintf(stderr, tmp, args);
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void watchqueue_add_path(const char *pathname)
|
|
|
|
@ -210,11 +208,9 @@ void watchqueue_add_path(const char *pathname)
|
|
|
|
|
e->path = path;
|
|
|
|
|
e->isdir = path_is_directory(pathname);
|
|
|
|
|
e->next = NULL;
|
|
|
|
|
watchlist= &e->next;
|
|
|
|
|
watchlist = &e->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create_watches(int fd, uint32_t mask)
|
|
|
|
|
{
|
|
|
|
|
for(struct watchlistentry *lkp = watchlist_head; lkp != NULL; lkp = lkp->next)
|
|
|
|
@ -248,6 +244,75 @@ bool redirect_stdout(const char *outfile)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *mask_to_names(int mask)
|
|
|
|
|
{
|
|
|
|
|
char ret[1024] = {0};
|
|
|
|
|
FILE *f = fmemopen(ret, sizeof(ret), "w");
|
|
|
|
|
if(f == NULL)
|
|
|
|
|
{
|
|
|
|
|
logerror("fmemopen() failed\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_ATTRIB)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_ATTRIB,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_OPEN)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_OPEN,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_CLOSE)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_CLOSE,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_CLOSE_NOWRITE)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_CLOSE_NOWRITE,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_CLOSE_WRITE)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_CLOSE_WRITE,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_CREATE)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_CREATE,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_DELETE)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_DELETE,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_DELETE_SELF)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_DELETE_SELF,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_MODIFY)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_MODIFY,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_MOVE)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_MOVE,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_MOVE_SELF)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_MOVE_SELF,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_MOVED_FROM)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_MOVED_FROM,", f);
|
|
|
|
|
}
|
|
|
|
|
if(mask & IN_MOVED_TO)
|
|
|
|
|
{
|
|
|
|
|
fputs("IN_MOVED_TO,", f);
|
|
|
|
|
}
|
|
|
|
|
long pos = ftell(f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
if(pos > 0)
|
|
|
|
|
{
|
|
|
|
|
ret[pos-1] = '\0';
|
|
|
|
|
}
|
|
|
|
|
return xstrdup(ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool run_prog(const char *eventfile, uint32_t eventmask)
|
|
|
|
|
{
|
|
|
|
@ -256,27 +321,45 @@ bool run_prog(const char *eventfile, uint32_t eventmask)
|
|
|
|
|
{
|
|
|
|
|
if(path_logfile)
|
|
|
|
|
{
|
|
|
|
|
if(! redirect_stdout(path_logfile))
|
|
|
|
|
if(!redirect_stdout(path_logfile))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! noenv)
|
|
|
|
|
if(!noenv)
|
|
|
|
|
{
|
|
|
|
|
char envvar[30];
|
|
|
|
|
snprintf(envvar, sizeof(envvar), "ADHOCIFYEVENT=%"PRIu32, eventmask);
|
|
|
|
|
putenv(envvar);
|
|
|
|
|
snprintf(envvar, sizeof(envvar), "%" PRIu32, eventmask);
|
|
|
|
|
int ret = setenv("ADHOCIFYEVENT", envvar, 1);
|
|
|
|
|
if(ret != 0)
|
|
|
|
|
{
|
|
|
|
|
perror("setenv");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//This can be thrown away, if in create_script_args we create pointer to the pointer pointing to the SCRIPT_PLACE_SPECIFIER string.
|
|
|
|
|
//Then all what we have to do here is to set the pointer to eventfile. However, we can have multiple of those, probably not worth it as we shouldn't have tons of arguments
|
|
|
|
|
for(unsigned int i = 0; i < n_script_arguments; i++)
|
|
|
|
|
{
|
|
|
|
|
char *argument = script_arguments[i];
|
|
|
|
|
if(argument && STREQ(argument, SCRIPT_PLACE_SPECIFIER))
|
|
|
|
|
if(argument != NULL)
|
|
|
|
|
{
|
|
|
|
|
if(STREQ(argument, SCRIPT_PLACE_SPECIFIER) || STREQ(argument, EVENTFILE_PLACEHOLDER))
|
|
|
|
|
{
|
|
|
|
|
script_arguments[i] = eventfile;
|
|
|
|
|
}
|
|
|
|
|
if(STREQ(argument, EVENTSTR_PLACEHOLDER))
|
|
|
|
|
{
|
|
|
|
|
const char *names = mask_to_names(eventmask);
|
|
|
|
|
if(names == NULL)
|
|
|
|
|
{
|
|
|
|
|
logerror("Failed to convert mask to strings");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
script_arguments[i] = names;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
execvp(prog, script_arguments);
|
|
|
|
|
logerror("Exec of %s failed: %s\n", prog, strerror(errno));
|
|
|
|
@ -290,10 +373,9 @@ bool run_prog(const char *eventfile, uint32_t eventmask)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t nameToMask(const char *name)
|
|
|
|
|
uint32_t name_to_mask(const char *name)
|
|
|
|
|
{
|
|
|
|
|
if(STREQ(name, "IN_CLOSE_WRITE"))
|
|
|
|
|
return IN_CLOSE_WRITE;
|
|
|
|
@ -325,7 +407,6 @@ uint32_t nameToMask(const char *name)
|
|
|
|
|
return IN_MOVE;
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void check_forkbomb(const char *path_logfile, const char *path_prog)
|
|
|
|
@ -339,9 +420,10 @@ void check_forkbomb(const char *path_logfile, const char *path_prog)
|
|
|
|
|
if(lkp->isdir)
|
|
|
|
|
{
|
|
|
|
|
char *dir_lkpPath = lkp->path;
|
|
|
|
|
if( STREQ(dir_lkpPath, dir_log) || STREQ(dir_lkpPath, dir_prog) )
|
|
|
|
|
if(STREQ(dir_lkpPath, dir_log) || STREQ(dir_lkpPath, dir_prog))
|
|
|
|
|
{
|
|
|
|
|
logerror("Don't place your logfiles or command in a directory you are watching for events. Pass -b to bypass this check.\n");
|
|
|
|
|
logerror("Don't place your logfiles or command in a directory you are watching for events. Pass -b to "
|
|
|
|
|
"bypass this check.\n");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -360,10 +442,11 @@ void queue_watches_from_stdin()
|
|
|
|
|
ssize_t r;
|
|
|
|
|
while((r = getline(&line, &n, stdin)) != -1)
|
|
|
|
|
{
|
|
|
|
|
if(line[r-1] == '\n')
|
|
|
|
|
line[r-1] = 0;
|
|
|
|
|
if(line[r - 1] == '\n')
|
|
|
|
|
line[r - 1] = 0;
|
|
|
|
|
watchqueue_add_path(line);
|
|
|
|
|
}
|
|
|
|
|
free(line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *get_eventfile_abspath(struct inotify_event *event)
|
|
|
|
@ -375,7 +458,7 @@ char *get_eventfile_abspath(struct inotify_event *event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *result = NULL;
|
|
|
|
|
if((event->len) > 0 )
|
|
|
|
|
if((event->len) > 0)
|
|
|
|
|
{
|
|
|
|
|
if(asprintf(&result, "%s/%s", wdpath, event->name) == -1)
|
|
|
|
|
{
|
|
|
|
@ -389,7 +472,6 @@ char *get_eventfile_abspath(struct inotify_event *event)
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void handle_event(struct inotify_event *event)
|
|
|
|
|
{
|
|
|
|
|
if(event->mask & mask)
|
|
|
|
@ -417,13 +499,13 @@ void handle_event(struct inotify_event *event)
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
|
|
free (eventfile_abspath);
|
|
|
|
|
free(eventfile_abspath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline char *get_cwd()
|
|
|
|
|
{
|
|
|
|
|
return getcwd(NULL,0);
|
|
|
|
|
return getcwd(NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void print_usage()
|
|
|
|
@ -432,60 +514,64 @@ void print_usage()
|
|
|
|
|
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("--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("--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("\nIf your command should know the file the event occured on, use the {} placeholder when you specify the arguments (like xargs)\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 with the command. You can also specify a return code and negations (e. g. -e'!0' to "
|
|
|
|
|
"exit only on errors)\n");
|
|
|
|
|
printf("\nIf your command should know the file the event occured on, use the {} placeholder when you specify the "
|
|
|
|
|
"arguments (like xargs)\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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' },
|
|
|
|
|
{ "help", no_argument, 0, 'h' },
|
|
|
|
|
{ "exit-with-child", optional_argument, 0, 'e' }
|
|
|
|
|
};
|
|
|
|
|
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'},
|
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
|
{"exit-with-child", optional_argument, 0, 'e'},
|
|
|
|
|
{0,0,0,0}};
|
|
|
|
|
|
|
|
|
|
//fills global n_script_arguments and script_arguments var
|
|
|
|
|
// 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
|
|
|
|
|
n_script_arguments = n_args + 2; // 2 = argv0 and terminating NULL
|
|
|
|
|
|
|
|
|
|
char **arguments = xmalloc( n_script_arguments * sizeof(char *) );
|
|
|
|
|
char **arguments = xmalloc(n_script_arguments * sizeof(char *));
|
|
|
|
|
|
|
|
|
|
const char *argv0 = memrchr(prog, '/', strlen(prog));
|
|
|
|
|
argv0 = ( argv0 == NULL ) ? prog : argv0+1;
|
|
|
|
|
arguments[0] = argv0;
|
|
|
|
|
argv0 = (argv0 == NULL) ? prog : argv0 + 1;
|
|
|
|
|
arguments[0] = xstrdup(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[i + begin_offset] = xstrdup(argument);
|
|
|
|
|
}
|
|
|
|
|
arguments[n_args+begin_offset] = NULL;
|
|
|
|
|
arguments[n_args + begin_offset] = NULL;
|
|
|
|
|
|
|
|
|
|
script_arguments = arguments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void parse_options(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
char *watchpath = NULL;
|
|
|
|
|
int option;
|
|
|
|
|
int option_index;
|
|
|
|
|
uint32_t optmask = 0;
|
|
|
|
|
while((option = getopt_long(argc, argv, "absdo:w:m:l:i:e::", long_options, &option_index)) != -1)
|
|
|
|
|
while((option = getopt_long(argc, argv, "absdo:w:m:i:e::", long_options, &option_index)) != -1)
|
|
|
|
|
{
|
|
|
|
|
switch(option)
|
|
|
|
|
{
|
|
|
|
@ -496,9 +582,10 @@ void parse_options(int argc, char **argv)
|
|
|
|
|
path_logfile = optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'm':
|
|
|
|
|
optmask = nameToMask(optarg);
|
|
|
|
|
if(optmask == 0) {
|
|
|
|
|
logerror("Not supported inotify event: %s\n", optarg);
|
|
|
|
|
optmask = name_to_mask(optarg);
|
|
|
|
|
if(optmask == 0)
|
|
|
|
|
{
|
|
|
|
|
logerror("Unsupported inotify event: %s\n", optarg);
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
mask |= optmask;
|
|
|
|
@ -508,19 +595,19 @@ void parse_options(int argc, char **argv)
|
|
|
|
|
watchqueue_add_path(watchpath);
|
|
|
|
|
break;
|
|
|
|
|
case 'a':
|
|
|
|
|
noenv=true;
|
|
|
|
|
noenv = true;
|
|
|
|
|
break;
|
|
|
|
|
case 's':
|
|
|
|
|
fromstdin=true;
|
|
|
|
|
fromstdin = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'b':
|
|
|
|
|
forkbombcheck=false;
|
|
|
|
|
forkbombcheck = false;
|
|
|
|
|
break;
|
|
|
|
|
case 'i':
|
|
|
|
|
add_to_ignore_list(optarg);
|
|
|
|
|
break;
|
|
|
|
|
case 'q':
|
|
|
|
|
silent=true;
|
|
|
|
|
silent = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'h':
|
|
|
|
|
print_usage();
|
|
|
|
@ -594,7 +681,7 @@ void process_options()
|
|
|
|
|
|
|
|
|
|
if(daemonize)
|
|
|
|
|
{
|
|
|
|
|
if(daemon(0,0) == -1)
|
|
|
|
|
if(daemon(0, 0) == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("daemon");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
@ -602,47 +689,22 @@ void process_options()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void start_monitoring(int ifd)
|
|
|
|
|
|
|
|
|
|
void wait_for_children()
|
|
|
|
|
{
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(offset < len)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
struct inotify_event *event = (struct inotify_event *)&buf[offset];
|
|
|
|
|
handle_event(event);
|
|
|
|
|
offset+=sizeof(struct inotify_event) + event->len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void child_handler(int signum, siginfo_t *info, void *context)
|
|
|
|
|
{
|
|
|
|
|
if(signum != SIGCHLD)
|
|
|
|
|
int status;
|
|
|
|
|
pid_t p = waitpid(-1, &status, WNOHANG);
|
|
|
|
|
if(p == 0 || (p == -1 && errno == ECHILD)) // No more children to reap
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int status;
|
|
|
|
|
pid_t p = waitpid(-1, &status, WNOHANG);
|
|
|
|
|
if(p == -1)
|
|
|
|
|
{
|
|
|
|
|
logerror("waitpid failed when handling child exit\n");
|
|
|
|
|
logerror("waitpid() failed when handling child exit\n");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int adhocify_exit_code = 0;
|
|
|
|
|
if(WIFEXITED(status))
|
|
|
|
|
{
|
|
|
|
@ -652,9 +714,9 @@ void child_handler(int signum, siginfo_t *info, void *context)
|
|
|
|
|
logwrite("command not found, exiting\n");
|
|
|
|
|
exit(adhocify_exit_code);
|
|
|
|
|
}
|
|
|
|
|
if(exit_with_child && awaited_child_exit_code > -1)
|
|
|
|
|
if(exit_with_child)
|
|
|
|
|
{
|
|
|
|
|
bool must_exit = adhocify_exit_code == awaited_child_exit_code;
|
|
|
|
|
bool must_exit = adhocify_exit_code == awaited_child_exit_code || awaited_child_exit_code == -1;
|
|
|
|
|
if(negate_child_exit_code)
|
|
|
|
|
{
|
|
|
|
|
must_exit = !must_exit;
|
|
|
|
@ -668,16 +730,57 @@ void child_handler(int signum, siginfo_t *info, void *context)
|
|
|
|
|
}
|
|
|
|
|
if(exit_with_child && WIFSIGNALED(status))
|
|
|
|
|
{
|
|
|
|
|
adhocify_exit_code = 128 + WTERMSIG(status); //copy bash's behaviour
|
|
|
|
|
adhocify_exit_code = 128 + WTERMSIG(status); // copy bash's behaviour
|
|
|
|
|
exit(adhocify_exit_code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void start_monitoring(int ifd)
|
|
|
|
|
{
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
if(handle_child_exits)
|
|
|
|
|
{
|
|
|
|
|
handle_child_exits = 0;
|
|
|
|
|
wait_for_children();
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
while(offset < len)
|
|
|
|
|
{
|
|
|
|
|
struct inotify_event *event = (struct inotify_event *)&buf[offset];
|
|
|
|
|
handle_event(event);
|
|
|
|
|
offset += sizeof(struct inotify_event) + event->len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void child_handler(int signum, siginfo_t *info, void *context)
|
|
|
|
|
{
|
|
|
|
|
if(signum != SIGCHLD)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
handle_child_exits = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_signals()
|
|
|
|
|
{
|
|
|
|
|
struct sigaction action;
|
|
|
|
|
struct sigaction action = {0};
|
|
|
|
|
action.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
|
|
|
|
action.sa_sigaction = &child_handler;
|
|
|
|
|
sigemptyset(&action.sa_mask);
|
|
|
|
|
if(sigaction(SIGCHLD, &action, NULL) == -1)
|
|
|
|
|
{
|
|
|
|
|
logerror("Error when setting up the signal handler\n");
|
|
|
|
@ -685,7 +788,6 @@ void set_signals()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
if(argc < 2)
|
|
|
|
@ -694,17 +796,16 @@ int main(int argc, char **argv)
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//signal(SIGCHLD, SIG_IGN);
|
|
|
|
|
// signal(SIGCHLD, SIG_IGN);
|
|
|
|
|
set_signals();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parse_options(argc, argv);
|
|
|
|
|
process_options();
|
|
|
|
|
|
|
|
|
|
int ifd = inotify_init();
|
|
|
|
|
int ifd = inotify_init1(O_CLOEXEC);
|
|
|
|
|
if(ifd == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("inotify_init");
|
|
|
|
|
perror("inotify_init1");
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
create_watches(ifd, mask);
|
|
|
|
|