The previous Learning Perl Challenge asked you to find duplicate files. This challenge needs some of which you did there, but for a different purpose.
Write a program that monitors a directory to find any file changes. Programs such as tripwire do this by recording meta information about a file on its first run then checking that the same information matches later. For instance, the size and SHA1 digest should stay the same. You could also just store the original content, but that’s not very convenient.
Since you’re at the Learning Perl level, we can’t ask too much here or judge you too harshly. A lot of the problem is storing the data and reading it later. Here’s a hint: create a flat file to store the “good” data on the first run, then read this file on the second run:
#name:size:SHA1 file.txt:1023:53a0935982ae11a4784d51aa696733c947c0614f
How are you going to handle the security on this file after you create it? As an example, you might look at CPAN::Checksums, which handles the same task for the modules on CPAN.
There are many ways that you can employ use this. You can run it periodically from cron, for instance, but you might also make a daemon that runs continually and is always checking. Once you find a change, you can report it in many ways, but we’ll only ask you to print a line to the terminal, that might look something like:
file.txt changed. Was: Size: 1023 bytes SHA1: 53a0935982ae11a4784d51aa696733c947c0614f Is now: Size: 2001 bytes SHA1: 730c6983bb9f942ef5cf6c174d76ad0c1594c1a7
You can see a list of all Challenges and my summaries as well as the programs that I created and put in the Learning Perl Challenges GitHub repository.
Rather than write a script that would be run as a cron job, I decided to expand my horizons a bit and write my first daemon. I used Any::Daemon and added a few command line options, including a check frequency (ie the number of seconds to sleep between checks), and max_iterations (the number of check iterations to run before exiting–a value of 0 means that the daemon won’t exit on its own).
I’ve come up with a solution which can be run periodocally from cron. I’ve implemented a daemon in ruby, but I’m not sure yet how to do it properly in Perl.
My little script requires exactly one directory to monitor and takes an optional location for the flat file to store the “good” data. It will in any case check if it can actually create the file. The required directory is traversed recursively and the sizes and digests of all plain files found are stored next to the names. If it is the first run the “good” data flat file is created. If it’s not the first run the new sizes and digests are compared, output is generated accordingly and a new “good” data file is saved.
My next step would be to add a daemonize option and maybe use SQLite instead of a flat file for storing the “good” data.
Thanks for the suggestions, Dan. I’ve updtaed the function using a regular expression to determine the file extension and in_array() instead of looping. I also added an argument to make the comparison case-sensitive or not. Otherwise, the function works exactly the way it used to.
I wrote a Perl example here – https://plus.google.com/u/0/102874059713383300948/posts . (g+ doesn’t do code formatting…)
I like the inotify approach, but it just works with Linux. Is there actually something similar that works with FreeBSD or Mac OS X?
Below is my version of tripwire. I didn’t deal with creating or deleting a file.
BTW, what’s the intention of guiding us to read CPAN::Checksums?
Hello Eric,
You might want to have a look at the source for CPAN::Checksums and consider any line containing a variable that begins with $SIGNING_.
Rather than write a script that would be run as a cron job, I decided to expand my horizons a bit and write my first daemon. I used Any::Daemon and added a few command line options, including a check frequency (ie the number of seconds to sleep between checks), and max_iterations (the number of check iterations to run before exiting–a value of 0 means that the daemon won’t exit on its own).
You can also find the code here.