Solaris Tip: Have Your Files Changed Since Installation?
To get started on the task of determining whether files on your system look the way they did when they were first installed, you should take a look at the /var/sadm/install/contents
file on your Solaris system. This file was initially created when your Solaris system was installed and is updated any time you install a new package or remove an old one. As a result, this file contains details about most of the commands and configuration files on your system -- at least all those that arrived as part of the initial OS installation or a package add operation. For files that shouldn’t be changing, such as system system executables, you can check file sizes and contents as well as permissions and ownership.
Depending on the type of file you’re looking at, different information will be stored in the contents file, but most files will look like what I’ve shown below. In these examples, I’ve inserted spaces in the system output to align the file information for the two files -- /usr/bin/date and /etc/inet/hosts -- with the field descriptors to make it a little easier to identify what is what:
+-- file type | path f class mode owner group size cksum modtime package /usr/bin/date f none 0555 root bin 11056 63512 1106444884 SUNWcsu path e class mode owner group size cksum modtime package /etc/inet/hosts e hosts 0444 root sys 61 4625 1204210814 SUNWcsr
These lines shown above indicate that the two files are of different file "classes" as far as the system is concerned. A file in the class "f" is a standard executable. Class "e" files are editable files, expected to change size after installation.
If we now compare the current state of the two files with the data from the /var/sadm/contents file, we can see that the date command still matches its original sum while the hosts file has grown.
Both files have the same permissions and owners/groups that they had when installed.
# ls -l /usr/bin/date -r-xr-xr-x 1 root bin 11056 Jan 22 2005 /usr/bin/date # grep /usr/bin/date /var/sadm/install/contents /usr/bin/date f none 0555 root bin 11056 63512 1106444884 SUNWcsu # ls -l /etc/inet/hosts -r--r--r-- 1 root sys 302 Dec 30 2008 /etc/inet/hosts # grep /etc/inet/hosts /var/sadm/install/contents /etc/inet/hosts e hosts 0444 root sys 61 4625 1204210814 SUNWcsr
The pkgchk command with the -l and -p options can be used to check information on individual files.
# pkgchk -l -p /usr/bin/date Pathname: /usr/bin/date Type: regular file Expected mode: 0555 Expected owner: root Expected group: bin Expected file size (bytes): 11056 Expected sum(1) of contents: 63512 Expected last modification: Jan 22 20:48:04 2005 Referenced by the following packages: SUNWcsu Current status: installed
Using a simple grep command, you can pull the same information from the contents file in a more terse (non-tagged) format. Notice that the time stamp associated with the file is displayed in the internal (Unix time) format in the contents file record shown below.
# grep /usr/bin/date /var/sadm/install/contents /usr/bin/date f none 0555 root bin 11056 63512 1106444884 SUNWcsu
This grep command, for example, displays details on the /usr/bin/date file as it was installed. For files that are expected to change in size, the information displayed by pkgchk omits some of this information. Here, for example, is what it shows for the hosts file. Notice
that no date or sum values are displayed:
# pkgchk -l -p /etc/inet/hosts Pathname: /etc/inet/hosts Type: editted file Expected mode: 0444 Expected owner: root Expected group: sys Referenced by the following packages: SUNWcsr Current status: installed
We can use the sum command to determine whether the sum associated with a file matches that recorded when the file was first installed. You can also verify the file’s permissions (or "mode"), the owner and group.
If you want to automate checking individual files, you can use a script like that shown below. Let me explain some of the possibly non-obvious commands.
- The while statement following the first if command will keep looping until a full pathname is typed and exit if the specified file doesn’t exist. The pkgchk command needs a full path whether or not the command in question is on the user’s search path.
- Following the while loop, we grab the first five attributes of the target file (mode, number of links, etc.) and stuff them into obvious variable names.
Everything beyond the file size goes into $misc. - A little further down, we check the file type as stored in the contents file. This determined what kind of information is reported by pkgchk. For example, pkgchk won’t report on file sizes for files whose sizes are expected to change.
- We then run through a series of comparisons between the original and current settings. For checking permissions, we grab the string (e.g., 0444) from the pkgchk output and use them as an argument to chmod on a temporary file only so we can easily compare -r--r--r-- type strings.
- If you prefer no output when the file parameters match, you can simply omit the else portions of the if commands following the "Now, check current settings against the original settings" comment.
#!/bin/bash if [ $# == 0 ]; then echo -n "file to check> " read FILE else FILE=$1 fi # Make sure we have the full path while [ ! -f $FILE ] do if [ `echo $FILE | cut -c1` != "/" ]; then echo -n "Please enter file name with full path> " read FILE else echo "No such file: $FILE" exit 1 fi done # Get file stats ls -l $FILE > /tmp/stats$$ read mode links owner group size misc < /tmp/stats$$ # Get pkgchk output pkgchk -l -p $FILE > /tmp/check$$ # Get file type Type=`grep -w $FILE /var/sadm/install/contents | awk '{print $2}'` if [ "$Type" = "" ]; then echo "$FILE not found in the contents file" exit 2 fi # Get current file stats ls -l $FILE > /tmp/stats$$ read mode links owner group etc < /tmp/stats$$ # Now, check current settings against the original settings # compare owner/group OrigOwner=`grep owner /tmp/check$$ | awk '{print $NF}'` OrigGroup=`grep group /tmp/check$$ | awk '{print $NF}'` if [ $owner != $OrigOwner ]; then echo "File owners do not match" else echo owner ok fi if [ $group != $OrigGroup ]; then echo "File groups do not match" else echo group ok fi # Compare size if [ $Type == "f" ]; then CurrSize=`ls -l $FILE | awk '{print $5}'` if [ $CurrSize != $size ]; then echo "File size does not match" else echo File size ok fi fi # Compare permissions touch /tmp/junk$$ OrigMode=`grep mode: /tmp/check$$ | awk '{print $NF}'` chmod $OrigMode /tmp/junk$$ omode=`ls -l /tmp/junk$$ | cut -c1-10` if [ $mode != $omode ]; then echo "File permissions do not match" else echo "File permissions ok" fi # Compare sums if [ $Type == "f" ]; then CurrSum=`sum $FILE | awk '{print $1}'` OrigSum=`grep sum /tmp/check$$ | awk '{print $NF}'` if [ $CurrSum != $OrigSum ]; then echo "File sums do not match" else echo "File sum ok" fi fi # clean up rm /tmp/check$$ rm /tmp/stats$$ rm /tmp/junk$$
Author: Sandra Henry-Stocker