There is often the need in a deployment scenario to repackage an installer into some other format. This might be a [radmind] transcript, a simple payload only installer package or perhaps just a manifest of what exactly changed on the system.
Apple installer style packages contain simple payloads, but often contain pre or postflight scripts which can make additional and important changes to the filesystem.
There are a couple methods that have been used to try to deduce what exactly was installed:
The snapshot method:
This involves taking a snapshot of the filesystem before and after the install and diffing for changes - this is a common method of building radmind transcripts. One disadvantage of this is that it requires two complete traverses of the entire filesystem to determine changes. Not so horrible, but if you have 10 minor updates, it can add up.
The packagemaker watch:
From the packagemaker man page:
>If the --watch ﬂag is speciﬁed, packagemaker will monitor ﬁlesystem changes until it receives the >SIGUSR1 signal. It will then construct a package of all ﬁles that were created/modiﬁed while it was watching.
The problem with these first two methods is that the filesystem is a noisy place, with all sort of changes being made by the various processes running. These are captured with the above methods and then need to be sorted through by hand. There can arise a lot of confusion over whether a given change was made by the installer or by some other process as all changes are captured without any note of the source of the change.
Install on non-boot volume:
Installing on a non-boot volume with the snapshot method resolves the noise issue, as generally most of the FS noise is happening on the boot volume. The problem with this method is that a number of installers either expect or require, sometimes quietly and implicitly that they be run on the boot volume.
Shooting for the best of both worlds
watchedinstall is a tool composed of a FSEvents logging tool and a Python script that tries to determine exactly what gets installed by an installer.
The tool takes an apple installer package, or the process id (pid) of a 3rd party installer and then logs two sets of information while the installer runs:
- All spawned processes and their parent
- All changes to the filesystem
When the installation is done, watched install builds a list of all the processes involved in the installation. Even for something simple like the sound effects installer from iLife involves nearly a dozen descendent processes of the initial installer process.
This list of installer related pids is used to filter the list of FS changes to only those made by the installation process.
The filtered list of changes is then output in a number of formats, currently the choices are:
- A radmind transcript
- A simple payload installer package
- A text file manifest of the changes
- Copy the fsewatcher tool somewhere on your path, for example /usr/local/bin and make sure its executable (chmod +x)
- The watchedinstall.py python script can be run from anywhere.
You must run watchedinstall.py as root - it uses the dtrace facility and the companion fsewatcher tool, both of which require root privileges to run.
Commonly you will specify a process ID to watch or an installer package to install (although you can also specify a path to an installer which watchedinstall will launch directly). watchedinstall.py will either wait for the process to exit or will install the package. Afterwards it will determine changes made to the filesystem by the process or the package and will generate a transcript where each line is a path, preceded by '+' or '-' or '!' to indicate the path was created, deleted or modified respectively (like diff format).
You can specify the transcript should be printed to a file, be printed to a file using Radmind transcript format or be used to create an Apple installer package. You can specify more than one of these output formats. The default is to print the transcript to standard out in diff format.
Install a package and print a diff-style transcript of filesystem changes to standard out:
sudo watchedinstall.py AppleIntermediateCodec.pkg
Wait for an installer process with PID 1234 to finish and create a package containing the filesystem additions as Custom.pkg:
sudo watchedinstall.py 1234 --pkg Custom.pkg
Install a package and print a Radmind-style transcript of filesystem changes to standard out:
sudo watchedinstall.py AppleIntermediateCodec.pkg --radmind -
Wait for an installer process with PID 1234 to finish and create a Radmind-style transcript as custom.T using the Radmind command file customized.K:
sudo watchedinstall.py 1234 --radmind custom.T -K customized.K
Wait for a process and print a transcript to standard out, ignoring any paths created in /Users directory:
sudo watchedinstall.py 1234 --grep '/Users/'
Launch and wait for an application and print a transcript to standard out.
sudo watchedinstall.py /Volumes/Example/Installer.app/Contents/MacOS/Installer