Watching folders for various changes in OS X can be exceptionally useful for automating tasks to handle any files added to the folder. This can be especially useful as a security measure for determining unwanted changes to key system folders. One robust solution for this is to use Apple’s Folder Actions feature for monitoring folders, and while still one I prefer and recommend, another approach is to use the system launcher.
The launcher is a background process called “launchd” that is the main umbrella process for all user processes in OS X, and is launched only second to the OS X Kernel at bootup. All other processes run underneath it, including a second instance of it that runs as an umbrella process for your user account. In this way, the environment and conditions for your user account can be separate from that of other user accounts, giving more control for resource management.
Since it is a process that manages others, launchd has a number of options for scheduling tasks and running them conditionally, which developers often use for their purposes, and which you can tap into for your own custom needs.
The basic setup of a launcher script is a text document in Apple’s “key and value” Property List XML file format, that has two requirementsL a Label key, and a Program or Program Argument key. Such a script will look like the following:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>MyAgent</string> <key>ProgramArguments</key> <array> <string>/path/to/script/or/program</string> </array> </dict> </plist>
In this case, the label of the property list is “MyAgent,” and the program that is launched is pointed to along the path of its string value. With these two label and program properties, the script can open the program and register it with the system under the given label. You can then start and stop it with the Terminal’s “launchctl” utility by running the following commands in the Terminal:
launchctl start LABEL launchctl stop LABEL
To automate this starting and stopping, you need to provide additional conditions to the script for when the specified program will be run. The options include setting time intervals, calendar dates and times, whether or not a drive is attached, among other specific conditions that the program is to run. As with setting the label and specifying the program, you do this using keys and corresponding values for the various options.
For the purposes of this property list, we are going to set a WatchPaths condition, that will set up the script to monitor the specified filesystem paths and then invoke the program whenever the watched folders are changed.
1. Create a script such as the following to issue an alert whenever run, and save it with the name “notice.sh” in your Documents folder (this one is just a simple AppleScript-based notification, but you can use any custom script you would like):
#/bin/bash osascript -e "tell Application \"System Events\" to display alert\ \"Watched Folder contents changed\""
2. Run the following Terminal command to make the script executable:
chmod +x ~/Documents/notice.sh
3. Create a plain text file and copy the following template plist structure into it, then modify the Label string to something like “WatchFolders,” then the Program string to the full path of the script in your Documents folder (ie, something like /Users/username/Documents/notice.sh). Then change the strings for the WatchPaths values to the full paths of any folder you would like to monitor for additions.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>MyAgent</string> <key>ProgramArguments</key> <array> <string>/path/to/script/or/program</string> </array> <key>WatchPaths</key> <array> <string>/path/to/first/watched/folder/</string> <string>/path/to/second/watched/folder/</string> <string>/path/to/third/watched/folder/</string> </array> </dict> </plist>
When done, save the plist with a name like “watchpaths.plist” and place it into your username/Library/LaunchAgents/ folder (to get here, hold the Option key down and choose Library from the Go menu in the Finder. When done, you can run the following command to load the launch agent script:
launchctl load ~/Library/LaunchAgents/watchpaths.plist
This will have the launchd process for your user account keep track of the various folders you have it configured to watch, and when you add or remove an item, then the “notice” script you created will run and display the alert.
This approach does have some limitations over the default use of the AppleScripts provided with Folder Actions. For example, the Folder Actions scripts will by default give you an option to reveal the new items added to the folders, and the system launcher script will not pass to the script which of its watched folders were modified; however, with some fancy shell scripting or by using advanced AppleScripts, you can program a way to have the system determine the changed files and present them to you.
This type of programming can be situation specific, so I will not cover them here; however, they can all be managed using the system launcher in the way I mentioned above.
To undo this launchd addition, go to the username > Library > LaunchAgents folder and remove the plist file you created there, followed by logging out and back into your account.