Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
b8943c0c79 | |||
1202296495 | |||
d62d2956cd | |||
da1f55e340 | |||
875c176cc1 | |||
2e77170115 | |||
200b6f6c33 | |||
bcc561d578 | |||
898e114eef |
35
README.md
35
README.md
@ -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.
|
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"
|
Running echo "Test" > /tmp/test will print in the shell adhocify was launched in: "the file /tmp/test was written to"
|
||||||
|
|
||||||
@ -68,6 +68,31 @@ 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.
|
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%"
|
||||||
|
#second shell commands
|
||||||
|
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
|
Other tools
|
||||||
===========
|
===========
|
||||||
If adhocify does not suit your needs, take a look at:
|
If adhocify does not suit your needs, take a look at:
|
||||||
@ -85,6 +110,14 @@ sudo apt-get update
|
|||||||
sudo apt-get install adhocify
|
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
|
## Other
|
||||||
To install from source, run
|
To install from source, run
|
||||||
```
|
```
|
||||||
|
140
adhocify.c
140
adhocify.c
@ -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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -37,7 +37,12 @@
|
|||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#define BUF_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1) * 1024
|
#define BUF_SIZE (sizeof(struct inotify_event) + NAME_MAX + 1) * 1024
|
||||||
#define STREQ(s1,s2) ( strcmp(s1,s2) == 0 )
|
#define STREQ(s1,s2) ( strcmp(s1,s2) == 0 )
|
||||||
#define SCRIPT_PLACE_SPECIFIER "{}" //TODO: think of a better name...
|
|
||||||
|
|
||||||
|
#define SCRIPT_PLACE_SPECIFIER "{}" //same as EVENTFILE_PLACEHOLDER for backwards compatibility
|
||||||
|
#define EVENTFILE_PLACEHOLDER "%eventfilepath%"
|
||||||
|
#define EVENTSTR_PLACEHOLDER "%eventmaskstr%"
|
||||||
|
|
||||||
struct watchlistentry
|
struct watchlistentry
|
||||||
{
|
{
|
||||||
int ifd;
|
int ifd;
|
||||||
@ -248,6 +253,74 @@ bool redirect_stdout(const char *outfile)
|
|||||||
return true;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
bool run_prog(const char *eventfile, uint32_t eventmask)
|
||||||
{
|
{
|
||||||
@ -269,18 +342,26 @@ bool run_prog(const char *eventfile, uint32_t eventmask)
|
|||||||
putenv(envvar);
|
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++)
|
for(unsigned int i = 0; i < n_script_arguments; i++)
|
||||||
{
|
{
|
||||||
char *argument = 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;
|
script_arguments[i] = eventfile;
|
||||||
}
|
}
|
||||||
|
if(STREQ(argument, EVENTSTR_PLACEHOLDER))
|
||||||
|
{
|
||||||
|
script_arguments[i] = mask_to_names(eventmask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
execv(prog, script_arguments);
|
execvp(prog, script_arguments);
|
||||||
perror("execv");
|
logerror("Exec of %s failed: %s\n", prog, strerror(errno));
|
||||||
return false;
|
int exitcode = (errno == ENOENT) ? 127 : EXIT_FAILURE;
|
||||||
|
exit(exitcode);
|
||||||
}
|
}
|
||||||
if(pid == -1)
|
if(pid == -1)
|
||||||
{
|
{
|
||||||
@ -292,7 +373,7 @@ bool run_prog(const char *eventfile, uint32_t eventmask)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t nameToMask(const char *name)
|
uint32_t name_to_mask(const char *name)
|
||||||
{
|
{
|
||||||
if(STREQ(name, "IN_CLOSE_WRITE"))
|
if(STREQ(name, "IN_CLOSE_WRITE"))
|
||||||
return IN_CLOSE_WRITE;
|
return IN_CLOSE_WRITE;
|
||||||
@ -324,7 +405,6 @@ uint32_t nameToMask(const char *name)
|
|||||||
return IN_MOVE;
|
return IN_MOVE;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_forkbomb(const char *path_logfile, const char *path_prog)
|
void check_forkbomb(const char *path_logfile, const char *path_prog)
|
||||||
@ -337,7 +417,6 @@ void check_forkbomb(const char *path_logfile, const char *path_prog)
|
|||||||
{
|
{
|
||||||
if(lkp->isdir)
|
if(lkp->isdir)
|
||||||
{
|
{
|
||||||
|
|
||||||
char *dir_lkpPath = lkp->path;
|
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) )
|
||||||
{
|
{
|
||||||
@ -406,11 +485,11 @@ void handle_event(struct inotify_event *event)
|
|||||||
free(eventfile_abspath);
|
free(eventfile_abspath);
|
||||||
return;
|
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);
|
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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,9 +575,9 @@ void parse_options(int argc, char **argv)
|
|||||||
path_logfile = optarg;
|
path_logfile = optarg;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
optmask = nameToMask(optarg);
|
optmask = name_to_mask(optarg);
|
||||||
if(optmask == 0) {
|
if(optmask == 0) {
|
||||||
logerror("Not supported inotify event: %s\n", optmask);
|
logerror("Not supported inotify event: %s\n", optarg);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
mask |= optmask;
|
mask |= optmask;
|
||||||
@ -577,12 +656,6 @@ void process_options()
|
|||||||
mask |= IN_CLOSE_WRITE;
|
mask |= IN_CLOSE_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(! file_exists(prog))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "File %s does not exist\n", prog);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(path_logfile)
|
if(path_logfile)
|
||||||
{
|
{
|
||||||
path_logfile = xrealpath(path_logfile, NULL);
|
path_logfile = xrealpath(path_logfile, NULL);
|
||||||
@ -590,9 +663,13 @@ void process_options()
|
|||||||
|
|
||||||
if(forkbombcheck)
|
if(forkbombcheck)
|
||||||
{
|
{
|
||||||
char *path_prog = xrealpath(prog, NULL);
|
char *path_prog = realpath(prog, NULL);
|
||||||
|
if(path_prog != NULL)
|
||||||
|
{
|
||||||
check_forkbomb(path_logfile, path_prog);
|
check_forkbomb(path_logfile, path_prog);
|
||||||
}
|
}
|
||||||
|
free(path_prog);
|
||||||
|
}
|
||||||
|
|
||||||
if(daemonize)
|
if(daemonize)
|
||||||
{
|
{
|
||||||
@ -644,13 +721,17 @@ void child_handler(int signum, siginfo_t *info, void *context)
|
|||||||
logerror("waitpid failed when handling child exit\n");
|
logerror("waitpid failed when handling child exit\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if(exit_with_child)
|
|
||||||
{
|
|
||||||
int adhocify_exit_code = 0;
|
int adhocify_exit_code = 0;
|
||||||
if(WIFEXITED(status))
|
if(WIFEXITED(status))
|
||||||
{
|
{
|
||||||
adhocify_exit_code = WEXITSTATUS(status);
|
adhocify_exit_code = WEXITSTATUS(status);
|
||||||
if(awaited_child_exit_code > -1)
|
if(adhocify_exit_code == 127)
|
||||||
|
{
|
||||||
|
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;
|
bool must_exit = adhocify_exit_code == awaited_child_exit_code;
|
||||||
if(negate_child_exit_code)
|
if(negate_child_exit_code)
|
||||||
@ -659,21 +740,18 @@ void child_handler(int signum, siginfo_t *info, void *context)
|
|||||||
}
|
}
|
||||||
if(must_exit)
|
if(must_exit)
|
||||||
{
|
{
|
||||||
logwrite("child exited with specified exit code, exiting too\n");
|
logwrite("command exited with specified exit code, exiting too\n");
|
||||||
exit(adhocify_exit_code);
|
exit(adhocify_exit_code);
|
||||||
}
|
}
|
||||||
return; //not the exit code we wanted, keep running
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(WIFSIGNALED(status))
|
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
|
||||||
}
|
|
||||||
//TODO: coredump?
|
|
||||||
exit(adhocify_exit_code);
|
exit(adhocify_exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_signals()
|
void set_signals()
|
||||||
{
|
{
|
||||||
struct sigaction action;
|
struct sigaction action;
|
||||||
|
21
examples/move_downloads.sh
Executable file
21
examples/move_downloads.sh
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/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"
|
||||||
|
#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/
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user