diff options
Diffstat (limited to 'src/inotifywait.c')
-rw-r--r-- | src/inotifywait.c | 288 |
1 files changed, 216 insertions, 72 deletions
diff --git a/src/inotifywait.c b/src/inotifywait.c index 9c031bc..98aadd4 100644 --- a/src/inotifywait.c +++ b/src/inotifywait.c @@ -1,33 +1,31 @@ -// kate: replace-tabs off; space-indent off; - #include "../config.h" #include "common.h" -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> #include <sys/types.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/time.h> -#include <fcntl.h> -#include <errno.h> + #include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> #include <regex.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <unistd.h> #include <inotifytools/inotifytools.h> #include <inotifytools/inotify.h> -#include <getopt.h> - extern char *optarg; extern int optind, opterr, optopt; -#define EXIT_OK 0 -#define EXIT_ERROR 1 -#define EXIT_TIMEOUT 2 - #define MAX_STRLEN 4096 #define EXCLUDE_CHUNK 1024 @@ -43,9 +41,12 @@ bool parse_opts( unsigned long int * timeout, int * recursive, bool * csv, + bool * daemon, + bool * syslog, char ** format, char ** timefmt, char ** fromfile, + char ** outfile, char ** regex, char ** iregex ); @@ -53,7 +54,6 @@ bool parse_opts( void print_help(); - char * csv_escape( char * string ) { static char csv[MAX_STRLEN+1]; static unsigned int i, ind; @@ -89,14 +89,13 @@ char * csv_escape( char * string ) { } - void validate_format( char * fmt ) { // Make a fake event struct inotify_event * event = (struct inotify_event *)malloc(sizeof(struct inotify_event) + 4); if ( !event ) { fprintf( stderr, "Seem to be out of memory... yikes!\n" ); - exit(EXIT_ERROR); + exit(EXIT_FAILURE); } event->wd = 0; event->mask = IN_ALL_EVENTS; @@ -110,7 +109,7 @@ void validate_format( char * fmt ) { } if ( -1 == inotifytools_fprintf( devnull, event, fmt ) ) { fprintf( stderr, "Something is wrong with your format string.\n" ); - exit(EXIT_ERROR); + exit(EXIT_FAILURE); } free( event ); fclose(devnull); @@ -118,7 +117,10 @@ void validate_format( char * fmt ) { void output_event_csv( struct inotify_event * event ) { - printf("%s,", csv_escape( inotifytools_filename_from_wd( event->wd ))); + char *filename = csv_escape(inotifytools_filename_from_wd(event->wd)); + if (filename != NULL) + printf("%s,", csv_escape(filename)); + printf("%s,", csv_escape( inotifytools_event_to_str( event->mask ) ) ); if ( event->len > 0 ) printf("%s", csv_escape( event->name ) ); @@ -126,25 +128,43 @@ void output_event_csv( struct inotify_event * event ) { } +void output_error( bool syslog, char* fmt, ... ) { + va_list va; + va_start(va, fmt); + if ( syslog ) { + vsyslog(LOG_INFO, fmt, va); + } else { + vfprintf(stderr, fmt, va); + } + va_end(va); +} + + int main(int argc, char ** argv) { int events = 0; + int orig_events; bool monitor = false; int quiet = 0; unsigned long int timeout = 0; int recursive = 0; bool csv = false; + bool daemon = false; + bool syslog = false; char * format = NULL; char * timefmt = NULL; char * fromfile = NULL; + char * outfile = NULL; char * regex = NULL; char * iregex = NULL; + pid_t pid; + int fd; // Parse commandline options, aborting if something goes wrong if ( !parse_opts(&argc, &argv, &events, &monitor, &quiet, &timeout, - &recursive, &csv, &format, &timefmt, &fromfile, - ®ex, &iregex) ) { - return EXIT_ERROR; + &recursive, &csv, &daemon, &syslog, &format, &timefmt, + &fromfile, &outfile, ®ex, &iregex) ) { + return EXIT_FAILURE; } if ( !inotifytools_initialize() ) { @@ -155,7 +175,7 @@ int main(int argc, char ** argv) "something mysterious has gone wrong. Please e-mail " PACKAGE_BUGREPORT "\n" " and mention that you saw this message.\n"); - return EXIT_ERROR; + return EXIT_FAILURE; } if ( timefmt ) inotifytools_set_printf_timefmt( timefmt ); @@ -165,7 +185,7 @@ int main(int argc, char ** argv) REG_ICASE)) ) { fprintf(stderr, "Error in `exclude' regular expression.\n"); - return EXIT_ERROR; + return EXIT_FAILURE; } @@ -175,21 +195,96 @@ int main(int argc, char ** argv) // If events is still 0, make it all events. if (events == 0) events = IN_ALL_EVENTS; + orig_events = events; + if ( monitor && recursive ) { + events = events | IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM; + } FileList list = construct_path_list( argc, argv, fromfile ); if (0 == list.watch_files[0]) { fprintf(stderr, "No files specified to watch!\n"); - return EXIT_ERROR; + return EXIT_FAILURE; } + + // Daemonize - BSD double-fork approach + if ( daemon ) { + + pid = fork(); + if (pid < 0) { + fprintf(stderr, "Failed to fork1 whilst daemonizing!\n"); + return EXIT_FAILURE; + } + if (pid > 0) { + _exit(0); + } + if (setsid() < 0) { + fprintf(stderr, "Failed to setsid whilst daemonizing!\n"); + return EXIT_FAILURE; + } + signal(SIGHUP,SIG_IGN); + pid = fork(); + if (pid < 0) { + fprintf(stderr, "Failed to fork2 whilst daemonizing!\n"); + return EXIT_FAILURE; + } + if (pid > 0) { + _exit(0); + } + if (chdir("/") < 0) { + fprintf(stderr, "Failed to chdir whilst daemonizing!\n"); + return EXIT_FAILURE; + } + + // Redirect stdin from /dev/null + fd = open("/dev/null", O_RDONLY); + if (fd != fileno(stdin)) { + dup2(fd, fileno(stdin)); + close(fd); + } + + // Redirect stdout to a file + fd = open(outfile, O_WRONLY | O_CREAT | O_APPEND, 0600); + if (fd < 0) { + fprintf( stderr, "Failed to open output file %s\n", outfile ); + return EXIT_FAILURE; + } + if (fd != fileno(stdout)) { + dup2(fd, fileno(stdout)); + close(fd); + } + + // Redirect stderr to /dev/null + fd = open("/dev/null", O_WRONLY); + if (fd != fileno(stderr)) { + dup2(fd, fileno(stderr)); + close(fd); + } + + } else if (outfile != NULL) { // Redirect stdout to a file if specified + fd = open(outfile, O_WRONLY | O_CREAT | O_APPEND, 0600); + if (fd < 0) { + fprintf( stderr, "Failed to open output file %s\n", outfile ); + return EXIT_FAILURE; + } + if (fd != fileno(stdout)) { + dup2(fd, fileno(stdout)); + close(fd); + } + } + + if ( syslog ) { + openlog ("inotifywait", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); + } + if ( !quiet ) { - fprintf( stderr, "Setting up watches. " ); if ( recursive ) { - fprintf( stderr, "Beware: since -r was given, this may take a " - "while!" ); + output_error( syslog, "Setting up watches. Beware: since -r " + "was given, this may take a while!\n" ); + } else { + output_error( syslog, "Setting up watches.\n" ); } - fprintf( stderr, "\n" ); } // now watch files @@ -201,22 +296,22 @@ int main(int argc, char ** argv) list.exclude_files )) || (!recursive && !inotifytools_watch_file( this_file, events )) ){ if ( inotifytools_error() == ENOSPC ) { - fprintf(stderr, "Failed to watch %s; upper limit on inotify " + output_error( syslog, "Failed to watch %s; upper limit on inotify " "watches reached!\n", this_file ); - fprintf(stderr, "Please increase the amount of inotify watches " + output_error( syslog, "Please increase the amount of inotify watches " "allowed per user via `/proc/sys/fs/inotify/" "max_user_watches'.\n"); } else { - fprintf(stderr, "Couldn't watch %s: %s\n", this_file, + output_error( syslog, "Couldn't watch %s: %s\n", this_file, strerror( inotifytools_error() ) ); } - return EXIT_ERROR; + return EXIT_FAILURE; } } if ( !quiet ) { - fprintf( stderr, "Watches established.\n" ); + output_error( syslog, "Watches established.\n" ); } // Now wait till we get event @@ -230,12 +325,12 @@ int main(int argc, char ** argv) return EXIT_TIMEOUT; } else { - fprintf(stderr, "%s\n", strerror( inotifytools_error() ) ); - return EXIT_ERROR; + output_error( syslog, "%s\n", strerror( inotifytools_error() ) ); + return EXIT_FAILURE; } } - if ( quiet < 2 ) { + if ( quiet < 2 && (event->mask & orig_events) ) { if ( csv ) { output_event_csv( event ); } @@ -251,7 +346,7 @@ int main(int argc, char ** argv) // moved_from file must have been moved outside of tree - so unwatch it. if ( moved_from && !(event->mask & IN_MOVED_TO) ) { if ( !inotifytools_remove_watch_by_filename( moved_from ) ) { - fprintf( stderr, "Error removing watch on %s: %s\n", + output_error( syslog, "Error removing watch on %s: %s\n", moved_from, strerror(inotifytools_error()) ); } free( moved_from ); @@ -270,7 +365,7 @@ int main(int argc, char ** argv) if ( isdir(new_file) && !inotifytools_watch_recursively( new_file, events ) ) { - fprintf( stderr, "Couldn't watch new directory %s: %s\n", + output_error( syslog, "Couldn't watch new directory %s: %s\n", new_file, strerror( inotifytools_error() ) ); } free( new_file ); @@ -305,15 +400,13 @@ int main(int argc, char ** argv) // If we weren't trying to listen for this event... if ( (events & event->mask) == 0 ) { // ...then most likely something bad happened, like IGNORE etc. - return EXIT_ERROR; + return EXIT_FAILURE; } - return EXIT_OK; + return EXIT_SUCCESS; } - - bool parse_opts( int * argc, char *** argv, @@ -323,21 +416,25 @@ bool parse_opts( unsigned long int * timeout, int * recursive, bool * csv, + bool * daemon, + bool * syslog, char ** format, char ** timefmt, char ** fromfile, + char ** outfile, char ** regex, char ** iregex ) { assert( argc ); assert( argv ); assert( events ); assert( monitor ); - assert( quiet ); assert( timeout ); assert( csv ); assert( format ); - assert( timefmt ); assert( fromfile ); assert( regex ); assert( iregex ); + assert( quiet ); assert( timeout ); assert( csv ); assert( daemon ); + assert( syslog ); assert( format ); assert( timefmt ); assert( fromfile ); + assert( outfile ); assert( regex ); assert( iregex ); // Short options - char * opt_string = "mrhcqt:fe:"; + char * opt_string = "mrhcdsqt:fo:e:"; // Construct array - struct option long_opts[14]; + struct option long_opts[17]; // --help long_opts[0].name = "help"; @@ -381,38 +478,53 @@ bool parse_opts( long_opts[7].has_arg = 0; long_opts[7].flag = NULL; long_opts[7].val = (int)'c'; - // --format - long_opts[8].name = "format"; - long_opts[8].has_arg = 1; + // --daemon + long_opts[8].name = "daemon"; + long_opts[8].has_arg = 0; long_opts[8].flag = NULL; - long_opts[8].val = (int)'n'; - // format with trailing newline - static char * newlineformat; - // --timefmt - long_opts[9].name = "timefmt"; - long_opts[9].has_arg = 1; + long_opts[8].val = (int)'d'; + // --syslog + long_opts[9].name = "syslog"; + long_opts[9].has_arg = 0; long_opts[9].flag = NULL; - long_opts[9].val = (int)'i'; - // --fromfile - long_opts[10].name = "fromfile"; + long_opts[9].val = (int)'s'; + // --format + long_opts[10].name = "format"; long_opts[10].has_arg = 1; long_opts[10].flag = NULL; - long_opts[10].val = (int)'z'; - // --exclude - long_opts[11].name = "exclude"; + long_opts[10].val = (int)'n'; + // format with trailing newline + static char * newlineformat; + // --timefmt + long_opts[11].name = "timefmt"; long_opts[11].has_arg = 1; long_opts[11].flag = NULL; - long_opts[11].val = (int)'a'; - // --excludei - long_opts[12].name = "excludei"; + long_opts[11].val = (int)'i'; + // --fromfile + long_opts[12].name = "fromfile"; long_opts[12].has_arg = 1; long_opts[12].flag = NULL; - long_opts[12].val = (int)'b'; + long_opts[12].val = (int)'z'; + // --outfile + long_opts[13].name = "outfile"; + long_opts[13].has_arg = 1; + long_opts[13].flag = NULL; + long_opts[13].val = (int)'o'; + // --exclude + long_opts[14].name = "exclude"; + long_opts[14].has_arg = 1; + long_opts[14].flag = NULL; + long_opts[14].val = (int)'a'; + // --excludei + long_opts[15].name = "excludei"; + long_opts[15].has_arg = 1; + long_opts[15].flag = NULL; + long_opts[15].val = (int)'b'; // Empty last element - long_opts[13].name = 0; - long_opts[13].has_arg = 0; - long_opts[13].flag = 0; - long_opts[13].val = 0; + long_opts[16].name = 0; + long_opts[16].has_arg = 0; + long_opts[16].flag = 0; + long_opts[16].val = 0; // Get first option char curr_opt = getopt_long(*argc, *argv, opt_string, long_opts, NULL); @@ -449,6 +561,18 @@ bool parse_opts( (*csv) = true; break; + // --daemon or -d + case 'd': + (*daemon) = true; + (*monitor) = true; + (*syslog) = true; + break; + + // --syslog or -s + case 's': + (*syslog) = true; + break; + // --filename or -f case 'f': fprintf(stderr, "The '--filename' option no longer exists. " @@ -489,6 +613,15 @@ bool parse_opts( (*fromfile) = optarg; break; + // --outfile + case 'o': + if (*outfile) { + fprintf(stderr, "Multiple --outfile options given.\n"); + return false; + } + (*outfile) = optarg; + break; + // --timeout or -t case 't': *timeout = strtoul(optarg, &timeout_end, 10); @@ -554,6 +687,11 @@ bool parse_opts( return false; } + if ( *daemon && *outfile == NULL ) { + fprintf(stderr, "-o must be specified with -d.\n"); + return false; + } + (*argc) -= optind; *argv = &(*argv)[optind]; @@ -580,10 +718,16 @@ void print_help() printf("\t-m|--monitor \tKeep listening for events forever. Without\n" "\t \tthis option, inotifywait will exit after one\n" "\t \tevent is received.\n"); + printf("\t-d|--daemon \tSame as --monitor, except run in the background\n" + "\t \tlogging events to a file specified by --outfile.\n" + "\t \tImplies --syslog.\n"); printf("\t-r|--recursive\tWatch directories recursively.\n"); printf("\t--fromfile <file>\n" "\t \tRead files to watch from <file> or `-' for " "stdin.\n"); + printf("\t-o|--outfile <file>\n" + "\t \tPrint events to <file> rather than stdout.\n"); + printf("\t-s|--syslog \tSend errors to syslog rather than stderr.\n"); printf("\t-q|--quiet \tPrint less (only print events).\n"); printf("\t-qq \tPrint nothing (not even events).\n"); printf("\t--format <fmt>\tPrint using a specified printf-like format\n" @@ -602,9 +746,9 @@ void print_help() "\t\tlistened for.\n\n"); printf("Exit status:\n"); printf("\t%d - An event you asked to watch for was received.\n", - EXIT_OK ); + EXIT_SUCCESS ); printf("\t%d - An event you did not ask to watch for was received\n", - EXIT_ERROR); + EXIT_FAILURE); printf("\t (usually delete_self or unmount), or some error " "occurred.\n"); printf("\t%d - The --timeout option was given and no events occurred\n", |