summaryrefslogtreecommitdiffstats
path: root/func/minion/modules/filetracker.py
blob: f5f9dbbd4d3cd41d7380e72f1407f02d7a8b0360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
## func
##
## filetracker
##  maintains a manifest of files of which to keep track
##  provides file meta-data (and optionally full data) to func-inventory
##
## (C) Vito Laurenza <vitolaurenza@gmail.com>
## + Michael DeHaan <mdehaan@redhat.com>
##
## This software may be freely redistributed under the terms of the GNU
## general public license.
##
## 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., 675 Mass Ave, Cambridge, MA 02139, USA.
##

# func modules
import func_module

# other modules
from stat import *
import glob
import os
import md5

# defaults
CONFIG_FILE='/etc/func/modules/filetracker.conf'

class FileTracker(func_module.FuncModule):

    version = "0.0.1"
    api_version = "0.0.1"
    description = "Maintains a manifest of files to keep track of."

    def __load(self):
        """
        Parse file and return data structure.
        """

        filehash = {}
        if os.path.exists(CONFIG_FILE):
            config = open(CONFIG_FILE, "r")
            data   = config.read()
            lines  = data.split("\n")
            for line in lines:
                tokens = line.split(None)
                if len(tokens) < 2:
                    continue
                scan_mode = tokens[0]
                path = " ".join(tokens[1:])
                if str(scan_mode).lower() == "0":
                    scan_mode = 0
                else:
                    scan_mode = 1
                filehash[path] = scan_mode
        return filehash

    #==========================================================

    def __save(self, filehash):
        """
        Write data structure to file.
        """ 

        config = open(CONFIG_FILE, "w+")
        for (path, scan_mode) in filehash.iteritems():
            config.write("%s     %s\n" % (scan_mode, path))
        config.close()

    #==========================================================
               
    def track(self, file_name, full_scan=0):
        """
        Adds files to keep track of.
        full_scan implies tracking the full contents of the file, defaults to off
        """

        filehash = self.__load()
        filehash[file_name] = full_scan
        self.__save(filehash)
        return 1

    #==========================================================

    def untrack(self, file_name):
        """
        Stop keeping track of a file.
        This routine is tolerant of most errors since we're forgetting about the file anyway.
        """

        filehash = self.__load()
        if file_name in filehash.keys():
            del filehash[file_name]
        self.__save(filehash)
        return 1

    #==========================================================

    def inventory(self, flatten=1, checksum_enabled=1):
        """
        Returns information on all tracked files
        By default, 'flatten' is passed in as True, which makes printouts very clean in diffs
        for use by func-inventory.  If you are writting another software application, using flatten=False will
        prevent the need to parse the returns.
        """
 
        # XMLRPC feeds us strings from the CLI when it shouldn't
        flatten = int(flatten)
        checksum_enabled = int(checksum_enabled)

        filehash = self.__load()

        # we'll either return a very flat string (for clean diffs)
        # or a data structure
        if flatten:
            results = ""
        else:
            results = []

        for (file_name, scan_type) in filehash.iteritems():

            if not os.path.exists(file_name):
                if flatten:
                    results = results + "%s: does not exist\n" % file_name
                else:
                    results.append("%s: does not exist\n" % file_name)
                continue

            this_result = []

            # ----- always process metadata
            filestat = os.stat(file_name)
            mode = filestat[ST_MODE]
            mtime = filestat[ST_MTIME]
            uid = filestat[ST_UID]
            gid = filestat[ST_GID]
            if not os.path.isdir(file_name) and checksum_enabled:
                sum_handle = open(file_name)
                hash = self.__sumfile(sum_handle)
                sum_handle.close()
            else:
                hash = "N/A"

            # ------ what we return depends on flatten
            if flatten:
                this_result = "%s:  mode=%s  mtime=%s  uid=%s  gid=%s  md5sum=%s\n" % (file_name,mode,mtime,uid,gid,hash) 
            else:
                this_result = [file_name,mode,mtime,uid,gid,hash]

            # ------ add on file data only if requested
            if scan_type != 0 and os.path.isfile(file_name):
                tracked_file = open(file_name)
                data = tracked_file.read()
                if flatten:
                    this_result = this_result + "*** DATA ***\n" + data + "\n*** END DATA ***\n\n"
                else:
                    this_result.append(data)
                tracked_file.close()
           
            if os.path.isdir(file_name):
                if not file_name.endswith("/"):
                    file_name = file_name + "/"
                files = glob.glob(file_name + "*") 
                if flatten:
                    this_result = this_result + "*** FILES ***\n" + "\n".join(files) + "\n*** END FILES ***\n\n"
                else:
                    this_result.append({"files" : files})

            if flatten:
                results = results + "\n" + this_result
            else:
                results.append(this_result)


        return results

    #==========================================================

    def __sumfile(self, fobj):
        """
        Returns an md5 hash for an object with read() method.
        credit: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266486
        """

        m = md5.new()
        while True:
            d = fobj.read(8096)
            if not d:
                break
            m.update(d)
        return m.hexdigest()