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
-<font color=green>r-xr-xr-x</font>   1 root     bin        <font color=red>11056</font> Jan 22  2005 /usr/bin/date
# grep /usr/bin/date /var/sadm/install/contents
/usr/bin/date f none <font color=green>0555</font> root bin <font color=red>11056</font> 63512 1106444884 SUNWcsu

# ls -l /etc/inet/hosts
-<font color=green>r--r--r--</font>   1 root     sys          <font color=red>302</font> Dec 30  2008 /etc/inet/hosts
# grep /etc/inet/hosts /var/sadm/install/contents
/etc/inet/hosts e hosts <font color=green>0444</font> root sys <font color=red>61</font> 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:
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 <font color=red>1106444884</font> 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:
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.

1) 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.

2) 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.

3) 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.

4) 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.

5) 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.


if [ $# == 0 ]; then
    echo -n "file to check> "
    read FILE

# Make sure we have the full path
while [ ! -f $FILE ]
    if [ `echo $FILE | cut -c1` != "/" ]; then
        echo -n "Please enter file name with full path> "
        read FILE
        echo "No such file: $FILE"
        exit 1

# 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

# 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"
    echo owner ok
if [ $group != $OrigGroup ]; then
    echo "File groups do not match"
    echo group ok

# Compare size
if [ $Type == "f" ]; then
    CurrSize=`ls -l $FILE | awk '{print $5}'`
    if [ $CurrSize != $size ]; then
        echo "File size does not match"
        echo File size ok

# 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"
    echo "File permissions ok"

# 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"
        echo "File sum ok"

# clean up
rm /tmp/check$$
rm /tmp/stats$$
rm /tmp/junk$$

This article is published as part of the IDG Contributor Network. Want to Join?

Computerworld's IT Salary Survey 2017 results
Shop Tech Products at Amazon