Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

macls

by snafu (Chaplain)
on Jun 06, 2001 at 22:00 UTC ( #86322=sourcecode: print w/ replies, xml ) Need Help??

Category: Utility scripts
Author/Contact Info Jim Conner
jconner@enterit.com
http://www.myspace.com/notjames
Description:

Rev ver 1.0:
This script is intended to get the mtime, atime, ctime information from a file or a list of files in a given directory. This is helpful to do basic file security auditing on your system. Read the comments in the script to get usage.

Updates:
Rev ver 2.1

  • Added locale tz tweak.
  • Added symbolic permissions modes to output. Many thanks to pwalker@pwccanada.com @ http://php.ca/manual/ru/function.fileperms.php. He doesn't know he helped but his code was invaluable to the symbolic mode code I added to this script.
  • Finally made a few changes that were suggested from Jeffa. Made other minor (beautification) changes.
  • General clean up.
  • Syntax changes that I felt made the script easier to read and more maintainable. Added GPL comments at top with (c) 2001. Subversion tags added for info only.
  • If there are tweaks performed on the script I would sincerely appreciate an email with a copy of the script with your changes.

    Tested successfully on: Linux, Solaris, FreeBSD

    Comments would be appreciated.

    #!/usr/bin/perl -w
    # Written by Jim Conner (Snafu)
    # 4/10/2001 (original creation)
    #
    # macls --
    # Nifty lil utility I wrote to be able to
    # get the mtime, ctime, and atime values from
    # any file of your choice.
    #
    #    Copyright (C) 2001  Jim Conner jconner|@at@|enterit|.dot.|com
    #
    #    This program is free software; you can redistribute it and/or mod
    +ify
    #    it under the terms of the GNU General Public License as published
    + by
    #    the Free Software Foundation; either version 2 of the License, or
    #    (at your option) any later version.
    #
    #    This program is distributed in the hope that it will be useful,
    #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #    GNU General Public License for more details.
    #
    #    You should have received a copy of the GNU General Public License
    + along
    #    with this program; if not, write to the Free Software Foundation,
    + Inc.,
    #    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    #
    ###################################################
    # HOW TO USE!!
    # It is quite simple.  All you do is supply this
    # script with the directory(ies)/file(s) you want to get
    # info on and voila! there's your info.
    #
    # Examples:
    #
    # macls /tmp
    #  This will give you a listing of all files in /tmp
    #
    # macls file1 file2 file3
    #  This will give you a listing of files 1 2 and 3.
    #
    # macls
    #  This will give you a listing of all files in the
    #  present working directory.
    #
    # TODO:
    #  Add command line args and options.  One of the
    #  most important of which would be -h (help)
    #
    #  Translate the mode to human readable (Unix)
    #  terms ie (drwxrwxr-x) ==> completed for rev 2.0 (done)
    #
    # $Revision: 437 $
    # $Author: jconner $
    # $Id: macls 437 2006-08-17 16:44:39Z jconner $
    #
    # Version: 1.1
    #
    # Audit Trail:
    #  4/9/2k1  - First conceptualized and started on authoring
    #  4/10/2k1 - Released version 1.0
    #  4/5/2k2  - Released version 2.0
    #
    # ********************************************************
    use strict;
    use File::Basename;
    
    ####################
    
    my $DEBUG = 0;     # default to no debugging msgs.
    
    ####################
    my $plProgName = basename($0);
    my $count      = 0;
    my $total      = 0;
    my $locale     = (localtime time() + 3600 *
                     (12 - (gmtime)[2]))[2] - 12;
    my @filename;
    ####################
    
    
    if  ( $#ARGV == -1 )
    {
        @filename = &readall();
    }
    elsif ( $#ARGV >= 0 )
    {
        if ( -d $ARGV[0] )
        {
            @filename = &readall($ARGV[0]);
        }
        else
        {
            @filename = @ARGV;
        }
    }
    
    for my $filename ( @filename )
    {
    
    # reference info:
    #  0 dev      device number of filesystem
    #  1 ino      inode number
    #  2 mode     file mode  (type and permissions)
    #  3 nlink    number of (hard) links to the file
    #  4 uid      numeric user ID of file's owner
    #  5 gid      numeric group ID of file's owner
    #  6 rdev     the device identifier (special files only)
    #  7 size     total size of file, in bytes
    #  8 atime    last access time since the epoch
    #  9 mtime    last modify time since the epoch
    # 10 ctime    inode change time (NOT creation time!) since the epoch  
    +<-- NNNOOOTTT GOOD!! :(
    # 11 blksize  preferred block size for file system I/O
    # 12 blocks   actual number of blocks allocated
    #
    #(The epoch was at 00:00 January 1, 1970 GMT.)
    ####
    # from mknod(2) (good info!)
    #        S_ISUID   04000   Set user ID on execution.
    #        S_ISGID   020#0   Set group ID on execution if # is 7, 5, 3, 
    +or 1.
    #                          Enable mandatory file/record locking if # i
    +s
    #                          6, 4, 2, or 0.
    #        S_ISVTX   01000   Save text image  after execution.
    #        S_IRWXU   00700   Read, write, execute by owner.
    #        S_IRUSR   00400   Read by owner.
    #        S_IWUSR   00200   Write by owner.
    #        S_IXUSR   00100   Execute (search if a directory) by owner.
    #        S_IRWXG   00070   Read, write, execute by group.
    #        S_IRGRP   00040   Read by group.
    #        S_IWGRP   00020   Write by group.
    #        S_IXGRP   00010   Execute by group.
    #        S_IRWXO   00007   Read, write, execute  (search) by others.
    #        S_IROTH   00004   Read by others.
    #        S_IWOTH   00002   Write by others
    #        S_IXOTH   00001   Execute by others.
    #        S_ISVTX           On directories, restricted deletion flag.
    
        ( $count >= 1 ) ?
            printf("\n%65s\n",'*' x 55)
            :
            print "\n";
    
        warn "$0: $filename: $!\n" and next if ( ! open (F,$filename) );
    
        my ($mode,  $uid,
            $gid,   $size,
            $atime, $mtime,
            $ctime, $blksize,
            $blocks)       = (stat(F))[2,4,5,7 .. 12];
        my $sym_mode       = &determine_file_type($mode);
        my $username       = getpwuid($uid);
        my $grpname        = getgrgid($gid);
        my @atime_readable = &fixgmt($atime,'atime');
        my @mtime_readable = &fixgmt($mtime,'mtime');
        my @ctime_readable = &fixgmt($ctime,'ctime');
        $mode &= 07777;
    
        print "MACtime for: \"$filename\"\n";
        printf("Mode(%s => %04o) %s(%i) %s(%i)\t%i bytes  %i blocks\n",
                     $sym_mode,
                           $mode,
                                 ($username || 'Unknown'),
                                    $uid,
                                        ($grpname || 'Unknown'),
                                           $gid,
                                                $size,
                                                          $blocks,);
    
        print "Modified time\t........ @mtime_readable\n";
        print "Access time  \t........ @atime_readable\n";
        print "Inode Change \t........ @ctime_readable\n";
    
        close(F);
    
        $count++;
        $total = $total + $size;
    }
    
    my $K  = $total / 1024;
    my $M  = ($total / 1024) / 1024;
    
    print "\nTotal files checked : $count\n";
    print "Total size in bytes : $total\n";
    printf("Total size in Kbytes: %iK\n", $K);
    printf("Total size in Mbytes: %iM\n\n", $M) if ( $M > 0.9 );
    
    sub fixgmt
    {
        my ($object, $type) = @_ ;
    
        print gmtime($object) ."\n" if $DEBUG;
        my($aday,$mon,$nday,$gmtime,$year)
                            = split(/\s+/,gmtime($object));
        my($hour,$min,$sec) = split(/:/,$gmtime);
    
        # Do math on gmt time to make it localtime.
        # Should get this from system LCTIME but I dunno
        # how to do that yet...sooo
    
        # Im gonna brute force the math for now.
        $hour  = abs($hour + $locale );
        $hour  = "0$hour"  if ( $hour < 10 );
        $nday  = "0$nday"  if ( $nday  < 10 );
    
        my $ltime = "$hour:$min:$sec";
    
        if ( $DEBUG )
        {
            print "Object type is: $type\n";
            print "Object value is: $object\n";
            print "Alpha day = $aday\nMonth = $mon\n".
                  "Number day = $nday\nGMTTime = $gmtime\nYear = $year\n";
            print "Hour = $hour\nMinute = $min\nSecond = $sec\n";
            print "LocalTime = $ltime\n\n";
        }
    
        return($aday,$mon,$nday,$ltime,$year);
    }
    
    sub readall
    {
        my $dir = shift();
    
        if ( $dir )
        {
            chdir($dir);
            opendir(ALL,".") or die("Unable to read: $!\n");
        }
        else
        {
            opendir(ALL,".") or die("Unable to read: $!\n");
        }
    
        my @files = grep(!/(^\.\.*?)/,readdir(ALL))
                        or die("Unable to get file list: $!\n");
    
        return(@files);
    }
    
    sub determine_file_type
    {
        my $mode = shift();
        my ($type, %owner, %group, %world);
    
    # borrowed from:
    # pwalker@pwccanada.com @ http://php.ca/manual/ru/function.fileperms.p
    +hp
    
        # // Unknown
        $type = '?';
        # // Unix domain socket
        (($mode & 0xC000) == 0xC000 ) and $type = 's';
        # // Directory
        (($mode & 0x4000) == 0x4000 ) and $type = 'd';
        # // Symbolic link
        (($mode & 0xA000) == 0xA000 ) and $type = 'l';
        # // Regular file
        (($mode & 0x8000) == 0x8000 ) and $type = '-';
        # // Block special file
        (($mode & 0x6000) == 0x6000 ) and $type = 'b';
        # // Character special file
        (($mode & 0x2000) == 0x2000 ) and $type = 'c';
        # // Named pipe
        (($mode & 0x1000) == 0x1000 ) and $type = 'p';
    
    #/* Determine permissions */
        $owner{'read'}    = ($mode & 00400) ? 'r' : '-';
        $owner{'write'}   = ($mode & 00200) ? 'w' : '-';
        $owner{'execute'} = ($mode & 00100) ? 'x' : '-';
        $group{'read'}    = ($mode & 00040) ? 'r' : '-';
        $group{'write'}   = ($mode & 00020) ? 'w' : '-';
        $group{'execute'} = ($mode & 00010) ? 'x' : '-';
        $world{'read'}    = ($mode & 00004) ? 'r' : '-';
        $world{'write'}   = ($mode & 00002) ? 'w' : '-';
        $world{'execute'} = ($mode & 00001) ? 'x' : '-';
    
    #/* Adjust for SUID, SGID and sticky bit */
    
        $owner{'execute'} = ($owner{execute} eq 'x') ? 's' : 'S'
            if ( $mode & 0x800 );
    
        $group{'execute'} = ($group{execute} eq 'x') ? 's' : 'S'
            if ( $mode & 0x400 );
    
        $world{'execute'} = ($world{execute} eq 'x') ? 't' : 'T'
            if ( $mode & 0x200 );
    
    # done borrowing :)
    
        $type .= $owner{'read'}.$owner{'write'}.$owner{'execute'}.
                 $group{'read'}.$group{'write'}.$group{'execute'}.
                 $world{'read'}.$world{'write'}.$world{'execute'};
    
        return($type);
    }
    
    

    Comment on macls
    Download Code
    Replies are listed 'Best First'.
    (jeffa) Re: macls.pl
    by jeffa (Bishop) on Jun 06, 2001 at 22:15 UTC
      Very small critisms that go a long way:
      1. Instead of using $DEBUG as a variable, why not 'declare' it as a constant:
        use constant DEBUG => 0;
      2. Testing if something is equal to "1" is overkill, all you need to know is if the variable is non-zero or not. Both of these are equal in consequence:
        print gmtime($object) ."\n" if $DEBUG == "1"; print gmtime($object) ."\n" if $DEBUG; # ahem, but since we'll be using the constant DEBUG print gmtime($object) ."\n" if DEBUG; # sorry i didn't mention this ea +rlier
      3. last issue, your first for loop:
        for ( @filename ) { my $filename = $_; ... }
        can be more consisely written as:
        for my $filename (@filename) { ... }
      Other than that, it works, and that's the most important thing, right? :)

      Jeff

      R-R-R--R-R-R--R-R-R--R-R-R--R-R-R--
      L-L--L-L--L-L--L-L--L-L--L-L--L-L--
      
        Hey Jeffa,
        FYI, the constantness of the variables, once set, seems to mean that I don't have to use a '$' to reference a token. Instead, I should reference a constant token all by itself e.g. DEBUG vs $DEBUG
        use constant DEBUG => 0; if ( DEBUG ) { ... } # RIGHT if ( $DEBUG ) { ... } # Wrong

        Once, I figured this out using perldoc constant all worked very nicely. Thanks again for your input.

        ----------
        - Jim

        Oh man!! That is great! I didn't know I could create a for loop like that (similar to shell). Thanks for that bit of info.

        As for the $DEBUG...well, I didn't know I could do that either! =P That would make this much cleaner. I will make the changes. Thanks for the information.

        Yup...it works. I really like it. It has been a very helpful tool to me in my work. Thanks for the info Jeff. Talk at ya later.

        ----------
        - Jim

    Back to Code Catacombs

    Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Node Status?
    node history
    Node Type: sourcecode [id://86322]
    help
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others surveying the Monastery: (19)
    As of 2015-07-28 15:26 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









      Results (257 votes), past polls