1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import os
17 import sys
18 import stat
19 import time
20 import tempfile
21 import base64
22 import difflib
23 import pwd
24 import grp
25 try:
26 from selinux import lgetfilecon
27 except:
28
31
32 from config_common import utils
33 from config_common.local_config import get as get_config
34 from rhn.i18n import bstr, sstr
35
36 decodestring = base64.decodestring
37 if hasattr(base64, 'decodebytes'):
38 decodestring = base64.decodebytes
39
41 file_struct_fields = {
42 'file_contents' : None,
43 'delim_start' : None,
44 'delim_end' : None,
45 }
48
49 - def process(self, file_struct, directory=None, strict_ownership=1):
50
51
52
53 if file_struct.get('filetype') == 'directory':
54 if directory is None:
55 directory = ""
56 return None, utils.mkdir_p(directory + file_struct['path'])
57
58 if directory:
59 directory += os.path.split(file_struct['path'])[0]
60 if file_struct.get('filetype') == 'symlink':
61 if 'symlink' not in file_struct:
62 raise Exception("Missing key symlink")
63
64 (fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory)
65 os.close(fd)
66 os.unlink(fullpath)
67 os.symlink(file_struct['symlink'], fullpath)
68 return fullpath, dirs_created
69
70 for k in self.file_struct_fields.keys():
71 if k not in file_struct:
72
73 raise Exception("Missing key %s" % k)
74
75 encoding = ''
76
77 if 'encoding' in file_struct:
78 encoding = file_struct['encoding']
79
80 contents = file_struct['file_contents']
81
82 if contents and (encoding == 'base64'):
83 contents = decodestring(bstr(contents))
84
85 delim_start = file_struct['delim_start']
86 delim_end = file_struct['delim_end']
87
88 if ('checksum' in file_struct
89 and 'checksum_type' in file_struct
90 and 'verify_contents' in file_struct
91 and file_struct['verify_contents']):
92 if file_struct['checksum'] != utils.getContentChecksum(
93 file_struct['checksum_type'], contents):
94 raise Exception("Corrupt file received: Content checksums do not match!")
95 elif ('md5sum' in file_struct and 'verify_contents' in file_struct
96 and file_struct['verify_contents']):
97 if file_struct['md5sum'] != utils.getContentChecksum(
98 'md5', contents):
99 raise Exception("Corrupt file received: Content checksums do not match!")
100 elif ('verify_contents' in file_struct
101 and file_struct['verify_contents']):
102 raise Exception("Corrupt file received: missing checksum information!")
103
104
105 (fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory)
106
107 try:
108 os.write(fd, bstr(contents))
109 except Exception:
110 raise
111 finally:
112 os.close(fd)
113
114
115
116 if 'modified' in file_struct:
117 try:
118 modified = xmlrpc_time(file_struct['modified'].value)
119 epoch_time = time.mktime(modified)
120 os.utime(fullpath, (epoch_time, epoch_time))
121 except (ValueError, AttributeError):
122
123 pass
124
125 return fullpath, dirs_created
126
127
128 - def diff(self, file_struct):
129 self._validate_struct(file_struct)
130
131 temp_file, temp_dirs = self.process(file_struct)
132 path = file_struct['path']
133 sectx_result = ''
134 owner_result = ''
135 group_result = ''
136 perm_result = ''
137 result = ''
138
139 stat_err = 0
140
141 try:
142 cur_stat = os.lstat(path)
143 except:
144 stat_err = 1
145
146 if file_struct['filetype'] != 'symlink':
147 if not stat_err:
148
149 cur_uid = cur_stat[stat.ST_UID]
150 try:
151 cur_user = pwd.getpwuid(cur_uid)[0]
152 except KeyError:
153
154 cur_user = "unknown(UID %d)" % (cur_uid,)
155 else:
156 cur_user = "missing"
157
158 if cur_user == file_struct['username']:
159 owner_result = ""
160
161 else:
162 owner_result = "User name differ: actual: [%s], expected: [%s]\n" % (cur_user, file_struct['username'])
163
164 if not stat_err:
165
166 cur_gid = cur_stat[stat.ST_GID]
167 try:
168 cur_group = grp.getgrgid(cur_gid)[0]
169 except KeyError:
170
171 cur_group = "unknown(GID %d)" % (cur_gid,)
172 else:
173 cur_group = "missing"
174
175 if cur_group == file_struct['groupname']:
176 group_result = ""
177 else:
178 group_result = "Group name differ: actual: [%s], expected: [%s]\n" % (cur_group, file_struct['groupname'])
179
180
181 if not stat_err:
182 cur_perm = str(oct(stat.S_IMODE(cur_stat[stat.ST_MODE])))
183 else:
184 cur_perm = "missing"
185
186
187 if cur_perm[0] == '0':
188 cur_perm = cur_perm[2:] if cur_perm[1] == 'o' else cur_perm[1:]
189
190
191 if cur_perm == str(file_struct['filemode']):
192 perm_result = ""
193 else:
194 perm_result = "File mode differ: actual: [%s], expected: [%s]\n" % (cur_perm, file_struct['filemode'])
195
196 try:
197 cur_sectx = lgetfilecon(path)[1]
198 except OSError:
199 cur_sectx = None
200
201 if cur_sectx == None:
202 cur_sectx = ''
203
204 if 'selinux_ctx' in file_struct and file_struct['selinux_ctx']:
205 if cur_sectx != file_struct['selinux_ctx']:
206 sectx_result = "SELinux contexts differ: actual: [%s], expected: [%s]\n" % (cur_sectx, file_struct['selinux_ctx'])
207
208 if file_struct['filetype'] == 'directory':
209 if os.path.isdir(file_struct['path']):
210 result = ''
211 else:
212 result = "Deployed directory is no longer a directory!"
213 elif file_struct['filetype'] == 'symlink':
214 try:
215 curlink = os.readlink(path)
216 newlink = os.readlink(temp_file)
217 if curlink == newlink:
218 result = ''
219 else:
220 result = "Link targets differ for [%s]: actual: [%s], expected: [%s]\n" % (path, curlink, newlink)
221 except OSError:
222 e = sys.exc_info()[1]
223 if e.errno == 22:
224 result = "Deployed symlink is no longer a symlink!"
225 else:
226 raise e
227 else:
228 result = ''.join(diff(temp_file, path, display_diff=get_config('display_diff'),
229 is_binary=True if file_struct['is_binary'] == 'Y' else False))
230
231 if temp_file:
232 os.unlink(temp_file)
233 return owner_result + group_result + perm_result + sectx_result + result
234
240
241
242 -def diff(src, dst, srcname=None, dstname=None, display_diff=False, is_binary=False):
243 def f_content(path, name, is_binary):
244 statinfo = None
245 if os.access(path, os.R_OK):
246 f = open(path, ('r' if int(sys.version[0]) == 3 else 'U') + ('b' if is_binary else ''))
247 content = [sstr(i) for i in f.readlines()]
248 f.close()
249 statinfo = os.stat(path)
250 f_time = time.ctime(statinfo.st_mtime)
251 if not is_binary and content and content[-1] and content[-1][-1] != "\n":
252 content[-1] += "\n"
253 else:
254 content = []
255 f_time = time.ctime(0)
256 if not name:
257 name = path
258 return (content, name, f_time, statinfo)
259
260 (src_content, src_name, src_time, src_stat) = f_content(src, srcname, is_binary)
261 (dst_content, dst_name, dst_time, dst_stat) = f_content(dst, dstname, is_binary)
262
263 diff_u = difflib.unified_diff(src_content, dst_content,
264 src_name, dst_name,
265 src_time, dst_time)
266
267 ret_list = list(diff_u)
268
269
270 if (len(ret_list) > 0
271 and not display_diff
272 and (dst_stat == None
273 or (dst_stat.st_uid == 0
274 and not dst_stat.st_mode & stat.S_IROTH))):
275 ret_list = [
276 "Differences exist in a file %s that is not readable by all. " % dst,
277 "Re-deployment of configuration file is recommended.\n"]
278 return ret_list
279
280
281
282 -def maketemp(prefix=None, directory=None):
283 """Creates a temporary file (guaranteed to be new), using the
284 specified prefix.
285
286 Returns the filename and a file descriptor
287 """
288 if not directory:
289 directory = tempfile.gettempdir()
290
291 dirs_created = None
292 if not os.path.exists(directory):
293 dirs_created = utils.mkdir_p(directory)
294
295 if not prefix:
296
297 prefix = 'rhncfg-tempfile'
298
299 file_prefix = "%s-%s-" % (prefix, os.getpid())
300 (fd, filename) = tempfile.mkstemp(prefix=file_prefix, dir=directory)
301
302 return filename, dirs_created, fd
303
304
305
306
307 FILETYPE2CHAR = {
308 'file' : '-',
309 'directory' : 'd',
310 'symlink' : 'l',
311 'chardev' : 'c',
312 'blockdev' : 'b',
313 }
314
315
316
317 -def _ifelse(cond, thenval, elseval):
318 if cond:
319 return thenval
320 else:
321 return elseval
322
323
324
326 """ Convert filemode in octets (like '644') to string like "ls -l" ("-rwxrw-rw-")
327 ftype is one of: file, directory, symlink, chardev, blockdev.
328 """
329 mode = int(str(octstr), 8)
330
331 symstr = FILETYPE2CHAR.get(ftype, '?')
332
333 symstr += _ifelse(mode & stat.S_IRUSR, 'r', '-')
334 symstr += _ifelse(mode & stat.S_IWUSR, 'w', '-')
335 symstr += _ifelse(mode & stat.S_IXUSR,
336 _ifelse(mode & stat.S_ISUID, 's', 'x'),
337 _ifelse(mode & stat.S_ISUID, 'S', '-'))
338 symstr += _ifelse(mode & stat.S_IRGRP, 'r', '-')
339 symstr += _ifelse(mode & stat.S_IWGRP, 'w', '-')
340 symstr += _ifelse(mode & stat.S_IXGRP,
341 _ifelse(mode & stat.S_ISGID, 's', 'x'),
342 _ifelse(mode & stat.S_ISGID, 'S', '-'))
343 symstr += _ifelse(mode & stat.S_IROTH, 'r', '-')
344 symstr += _ifelse(mode & stat.S_IWOTH, 'w', '-')
345 symstr += _ifelse(mode & stat.S_IXOTH,
346 _ifelse(mode & stat.S_ISVTX, 't', 'x'),
347 _ifelse(mode & stat.S_ISVTX, 'T', '-'))
348 return symstr
349
350
351
353 return "%04d-%02d-%02d %02d:%02d:%02d" % (dbiDate.year, dbiDate.month,
354 dbiDate.day, dbiDate.hour, dbiDate.minute, dbiDate.second)
355
357 if xtime[8] == 'T':
358
359 timefmt='%Y%m%dT%H:%M:%S'
360 else:
361
362 timefmt='%Y-%m-%d %H:%M:%S'
363 xtime = xtime[:19]
364
365 return time.strptime(xtime, timefmt)
366