Compare commits

...

14 Commits
v1.0 ... master

Author SHA1 Message Date
Eric Curtin 0a32cc92e8
Update README.md with new inotify-tools link 2020-12-06 16:40:41 +00:00
Albert S. 75b7db7d36 reformat with clangformat - no functional change 2020-11-15 20:38:15 +01:00
Albert S. cf14367aa8 Update README: Fix misplaced comment 2020-09-09 16:50:30 +02:00
Albert S. 9925f12675 examples/move_downloads.sh: Ignore size 0 files 2020-08-21 11:07:31 +02:00
Albert S. 19e34c1904 Remove TODO file. It's replaced by the issue tracker.
Issue tracker: https://gitea.quitesimple.org/crtxcr/adhocify/issues/
2020-08-21 10:18:24 +02:00
Albert S. b8943c0c79 Update README.md to reflect recent changes 2020-08-20 23:00:01 +02:00
Albert S. 1202296495 Add placeholder %eventmaskstr% for command args containing event string
This helps scripts to determine which events occured on the file
without having to parse the environment variable ADHOCIFYEVENT.

Add placeholder %eventfilepath% as an alternative for {}
2020-08-20 22:49:38 +02:00
Albert S. d62d2956cd updated copyright... 2020-08-20 13:41:42 +02:00
Albert S. da1f55e340 messages: replace "child" with "command" for clarity 2020-08-20 13:33:33 +02:00
Albert S. 875c176cc1 exit adhocify if command is not found irrespective of -e 2020-08-20 13:20:20 +02:00
Albert S. 2e77170115 Replace execv with execvp as it has the more natural behaviour 2020-08-20 11:58:18 +02:00
Albert S. 200b6f6c33 README.md: Add Alpine install instructions 2020-08-20 10:56:28 +02:00
Albert S. bcc561d578 Examples: Add download mover 2020-08-20 10:46:12 +02:00
Albert S. 898e114eef Fixed error message for unsupported events 2020-08-20 00:20:52 +02:00
4 changed files with 352 additions and 234 deletions

View File

@ -58,7 +58,7 @@ adhocify -w /tmp/ -w /var/run /home/user/myscript.sh {}
Passes the full path of the file an event occured on to the specified command. It can be retreived from argv[1] in the called command.
```
adhocify -w /tmp/ /bin/echo the file {} was written to
adhocify -w /tmp/ echo the file {} was written to
```
Running echo "Test" > /tmp/test will print in the shell adhocify was launched in: "the file /tmp/test was written to"
@ -68,10 +68,34 @@ adhocify -m IN_CREATE -m IN_CLOSE_WRITE -w /path -- /bin/env
adhocify passes the inotify event to the command as an environment variable. The variable is called ```ADHOCIFYEVENT``` and contains the value of inotify_event->mask as set by inotify.
#### Receiving events as strings
You can also get a string of the inotify events. This is particularly useful if you have a shellscript and don't want to interpret the ```ADHOCIFYEVENT``` variable yourself
```
echo "test" > /tmp/test
adhocify -m IN_ALL_EVENTS -w /tmp/test echo File: "%eventfilepath%" Event: "%eventmaskstr%"
Starting execution of command echo
File: /tmp/test Event: IN_ATTRIB
Starting execution of command echo
Starting execution of command echo
Starting execution of command echo
File: /tmp/test Event: IN_OPEN
File: /tmp/test Event: IN_CLOSE,IN_CLOSE_WRITE
File: /tmp/test Event: IN_MODIFY
```
A second shell ran
```
chmod 600 /tmp/test
echo "test" >> /tmp/test
```
Passing ```-q``` would also keep adhocify silent, surpressing those "Starting execution..." messages.
Other tools
===========
If adhocify does not suit your needs, take a look at:
* inotify-tools: https://github.com/rvoicilas/inotify-tools/wiki
* inotify-tools: https://github.com/inotify-tools/inotify-tools/wiki
Install
=======
@ -85,6 +109,14 @@ sudo apt-get update
sudo apt-get install adhocify
```
## Alpine
```
wget https://repo.quitesimple.org/repo%40quitesimple.org-5f3d101.rsa.pub -O /etc/apk/repo@quitesimple.org-5f3d101.rsa.pub
echo "https://repo.quitesimple.org/alpine/quitesimple/" >> /etc/apk/repositories
apk update
apk add adhocify
```
## Other
To install from source, run
```

4
TODO
View File

@ -1,4 +0,0 @@
-e: if multiple children are running and we exit, they become orphaned.
Deal with IN_Q_OVERFLOW etc.?
General rename handling, e. g. refresh mappings after renames etc.
Deal with IN_IGNORE

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014-2017 Albert S. <adhocify@quitesimple.org>
* Copyright (c) 2014-2020 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
@ -13,7 +13,7 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _GNU_SOURCE
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
@ -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,13 +62,12 @@ 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;
bool fromstdin = false;
bool forkbombcheck = true;
bool daemonize = false;
bool daemonize = false;
bool exit_with_child = false;
int awaited_child_exit_code = -1;
bool negate_child_exit_code = false;
@ -74,15 +75,15 @@ 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;
void *xmalloc(size_t size)
{
void *m = malloc(size);
if(m == NULL)
if(m == NULL)
{
perror("malloc");
perror("malloc");
exit(EXIT_FAILURE);
}
return m;
@ -111,12 +112,11 @@ char *xrealpath(const char *path, char *resolved_path)
return tmp;
}
char *ndirname(const char *path)
{
if(path == NULL)
{
return xstrdup(".");
return xstrdup(".");
}
char *c = strdupa(path);
return xstrdup(dirname(c));
@ -125,8 +125,8 @@ char *ndirname(const char *path)
char *find_ifd_path(int ifd)
{
for(struct watchlistentry *lkp = watchlist_head; lkp != NULL; lkp = lkp->next)
{
if(lkp->ifd == ifd)
{
if(lkp->ifd == ifd)
{
return lkp->path;
}
@ -160,20 +160,17 @@ bool path_is_directory(const char *path)
static inline bool file_exists(const char *path)
{
return access(path, F_OK) == 0;
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 = &(*ignorelist_current)->next;
ignorelist_current = &(*ignorelist_current)->next;
}
void logwrite(const char *format, ...)
{
if(silent)
@ -191,14 +188,13 @@ void logerror(const char *format, ...)
{
va_list args;
va_start(args, format);
char *prefix = "Error: ";
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);
}
void watchqueue_add_path(const char *pathname)
@ -210,24 +206,22 @@ 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)
{
int ret = inotify_add_watch(fd, lkp->path, mask);
if(ret == -1)
if(ret == -1)
{
perror("inotify_add_watch");
exit(EXIT_FAILURE);
perror("inotify_add_watch");
exit(EXIT_FAILURE);
}
lkp->ifd = ret;
}
}
}
bool redirect_stdout(const char *outfile)
@ -248,52 +242,127 @@ bool redirect_stdout(const char *outfile)
return true;
}
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);
}
bool run_prog(const char *eventfile, uint32_t eventmask)
for(int i = n; i >= 0; --i)
{
if(ret[i] == ',')
{
ret[i] = 0;
break;
}
}
ret[1023] = 0;
return ret;
}
bool run_prog(const char *eventfile, uint32_t eventmask)
{
pid_t pid = fork();
if(pid == 0)
{
if(path_logfile)
{
if(! redirect_stdout(path_logfile))
if(!redirect_stdout(path_logfile))
{
return false;
return false;
}
}
if(! noenv)
if(!noenv)
{
char envvar[30];
snprintf(envvar, sizeof(envvar), "ADHOCIFYEVENT=%"PRIu32, eventmask);
snprintf(envvar, sizeof(envvar), "ADHOCIFYEVENT=%" PRIu32, eventmask);
putenv(envvar);
}
//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))
script_arguments[i] = eventfile;
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);
}
}
}
execv(prog, script_arguments);
perror("execv");
return false;
execvp(prog, script_arguments);
logerror("Exec of %s failed: %s\n", prog, strerror(errno));
int exitcode = (errno == ENOENT) ? 127 : EXIT_FAILURE;
exit(exitcode);
}
if(pid == -1)
{
perror("fork");
return false;
}
}
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;
else if(STREQ(name, "IN_OPEN"))
@ -324,24 +393,23 @@ uint32_t nameToMask(const char *name)
return IN_MOVE;
else
return 0;
}
void check_forkbomb(const char *path_logfile, const char *path_prog)
{
char *dir_log = ndirname(path_logfile);
char *dir_prog = ndirname(path_prog);
char *dir_prog = ndirname(path_prog);
struct watchlistentry *lkp = watchlist_head;
while(lkp)
{
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);
}
}
@ -356,12 +424,12 @@ void check_forkbomb(const char *path_logfile, const char *path_prog)
void queue_watches_from_stdin()
{
char *line = NULL;
size_t n = 0;
size_t n = 0;
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);
}
}
@ -373,9 +441,9 @@ char *get_eventfile_abspath(struct inotify_event *event)
{
return NULL;
}
char *result = NULL;
if((event->len) > 0 )
if((event->len) > 0)
{
if(asprintf(&result, "%s/%s", wdpath, event->name) == -1)
{
@ -389,171 +457,174 @@ char *get_eventfile_abspath(struct inotify_event *event)
return result;
}
void handle_event(struct inotify_event *event)
{
if(event->mask & mask)
{
if(event->mask & mask)
{
char *eventfile_abspath = get_eventfile_abspath(event);
if(eventfile_abspath == NULL)
{
logerror("Could not get absolute path for event. Watch descriptor %i\n", event->wd);
exit(EXIT_FAILURE);
}
if(is_ignored(eventfile_abspath))
if(is_ignored(eventfile_abspath))
{
free(eventfile_abspath);
return;
}
logwrite("Starting execution of child %s\n", prog);
logwrite("Starting execution of command %s\n", prog);
bool r = run_prog(eventfile_abspath, event->mask);
if(!r)
if(!r)
{
logerror("Execution of child %s failed\n", prog);
logerror("Execution of command %s failed\n", prog);
exit(EXIT_FAILURE);
}
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()
{
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("--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 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");
}
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'}};
//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
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;
}
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;
}
void parse_options(int argc, char **argv)
{
char *watchpath = NULL;
int option;
int option_index;
uint32_t optmask = 0;
int option_index;
uint32_t optmask = 0;
while((option = getopt_long(argc, argv, "absdo:w:m:l:i:e::", long_options, &option_index)) != -1)
{
switch(option)
{
case 'd':
daemonize = true;
break;
case 'o':
path_logfile = optarg;
break;
case 'm':
optmask = nameToMask(optarg);
if(optmask == 0) {
logerror("Not supported inotify event: %s\n", optmask);
exit(EXIT_FAILURE);
}
mask |= optmask;
break;
case 'w':
watchpath = optarg;
watchqueue_add_path(watchpath);
break;
case 'a':
noenv=true;
break;
case 's':
fromstdin=true;
break;
case 'b':
forkbombcheck=false;
break;
case 'i':
add_to_ignore_list(optarg);
break;
case 'q':
silent=true;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case 'e':
exit_with_child = true;
if(optarg)
case 'd':
daemonize = true;
break;
case 'o':
path_logfile = optarg;
break;
case 'm':
optmask = name_to_mask(optarg);
if(optmask == 0)
{
logerror("Not supported inotify event: %s\n", optarg);
exit(EXIT_FAILURE);
}
mask |= optmask;
break;
case 'w':
watchpath = optarg;
watchqueue_add_path(watchpath);
break;
case 'a':
noenv = true;
break;
case 's':
fromstdin = true;
break;
case 'b':
forkbombcheck = false;
break;
case 'i':
add_to_ignore_list(optarg);
break;
case 'q':
silent = true;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case 'e':
exit_with_child = true;
if(optarg)
{
if(*optarg == '!')
{
if(*optarg == '!')
{
negate_child_exit_code = true;
++optarg;
}
if(*optarg == '\0')
{
logerror("Please specify the exit code\n");
exit(EXIT_FAILURE);
}
awaited_child_exit_code = atoi(optarg);
negate_child_exit_code = true;
++optarg;
}
break;
}
if(*optarg == '\0')
{
logerror("Please specify the exit code\n");
exit(EXIT_FAILURE);
}
awaited_child_exit_code = atoi(optarg);
}
break;
}
}
if(optind == argc)
{
if(optind == argc)
{
print_usage();
logerror("missing command path\n");
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
prog = argv[optind++];
if(optind <= argc)
{
fill_script_arguments(argc - optind, &argv[optind]);
@ -575,14 +646,8 @@ void process_options()
if(mask == 0)
{
mask |= IN_CLOSE_WRITE;
}
if(! file_exists(prog))
{
fprintf(stderr, "File %s does not exist\n", prog);
exit(EXIT_FAILURE);
}
if(path_logfile)
{
path_logfile = xrealpath(path_logfile, NULL);
@ -590,13 +655,17 @@ void process_options()
if(forkbombcheck)
{
char *path_prog = xrealpath(prog, NULL);
check_forkbomb(path_logfile, path_prog);
char *path_prog = realpath(prog, NULL);
if(path_prog != NULL)
{
check_forkbomb(path_logfile, path_prog);
}
free(path_prog);
}
if(daemonize)
{
if(daemon(0,0) == -1)
if(daemon(0, 0) == -1)
{
perror("daemon");
exit(EXIT_FAILURE);
@ -606,28 +675,28 @@ void process_options()
void start_monitoring(int ifd)
{
while(1)
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);
}
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)
{
while(offset < len)
{
struct inotify_event *event = (struct inotify_event *)&buf[offset];
handle_event(event);
offset+=sizeof(struct inotify_event) + event->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)
@ -635,8 +704,8 @@ void child_handler(int signum, siginfo_t *info, void *context)
if(signum != SIGCHLD)
{
return;
}
}
int status;
pid_t p = waitpid(-1, &status, WNOHANG);
if(p == -1)
@ -644,36 +713,37 @@ void child_handler(int signum, siginfo_t *info, void *context)
logerror("waitpid failed when handling child exit\n");
exit(EXIT_FAILURE);
}
if(exit_with_child)
int adhocify_exit_code = 0;
if(WIFEXITED(status))
{
int adhocify_exit_code = 0;
if(WIFEXITED(status))
adhocify_exit_code = WEXITSTATUS(status);
if(adhocify_exit_code == 127)
{
adhocify_exit_code = WEXITSTATUS(status);
if(awaited_child_exit_code > -1)
logwrite("command not found, exiting\n");
exit(adhocify_exit_code);
}
if(exit_with_child && awaited_child_exit_code > -1)
{
bool must_exit = adhocify_exit_code == awaited_child_exit_code;
if(negate_child_exit_code)
{
bool must_exit = adhocify_exit_code == awaited_child_exit_code;
if(negate_child_exit_code)
{
must_exit = !must_exit;
}
if(must_exit)
{
logwrite("child exited with specified exit code, exiting too\n");
exit(adhocify_exit_code);
}
return; //not the exit code we wanted, keep running
must_exit = !must_exit;
}
if(must_exit)
{
logwrite("command exited with specified exit code, exiting too\n");
exit(adhocify_exit_code);
}
}
if(WIFSIGNALED(status))
{
adhocify_exit_code = 128 + WTERMSIG(status); //copy bash's behaviour
}
//TODO: coredump?
}
if(exit_with_child && WIFSIGNALED(status))
{
adhocify_exit_code = 128 + WTERMSIG(status); // copy bash's behaviour
exit(adhocify_exit_code);
}
}
void set_signals()
{
struct sigaction action;
@ -686,22 +756,20 @@ void set_signals()
}
}
int main(int argc, char **argv)
{
if(argc < 2)
{
if(argc < 2)
{
print_usage();
exit(EXIT_FAILURE);
}
//signal(SIGCHLD, SIG_IGN);
// signal(SIGCHLD, SIG_IGN);
set_signals();
parse_options(argc, argv);
process_options();
int ifd = inotify_init();
if(ifd == -1)
{

22
examples/move_downloads.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
#moves all incoming files (e. g. downloads) to another directory.
#There, they will be put into subdirectories which are named after the current date (YYYYMMDD) to get some minimal automatic "organization".
#adhocify -d -m IN_CLOSE_WRITE -m IN_MOVED_TO -w /home/user/Downloads -w /home/user/other_dir /path/to/move_downloads.sh
INCOMING="$1"
stat -c"%s" "$INCOMING" | grep -q "^0$" && exit
#ignore partial downloads (.part in firefox, .crdownload in chrome)
echo "$INCOMING" | grep -q .part$ && exit
echo "$INCOMING" | grep -q .crdownload$ && exit
today=$(date +%Y%m%d)
TARGET_DIR="/target/dir/path"
TODAY_DIR="$TARGET_DIR"/$today
if [ ! -d "$TODAY_DIR" ] ; then
mkdir "$TODAY_DIR"
rm -f "$TARGET_DIR"/today
ln -s "$TODAY_DIR" "$TARGET_DIR"/today
fi
#You can also filter/grep the filename here and move certain patterns to other designated locations...
mv "$INCOMING" "$TARGET_DIR"/$today/