1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import os
17 import sys
18 import bz2
19 import gzip
20 import pwd
21 import grp
22 import shutil
23 import subprocess
24 import select
25 import stat
26 import tempfile
27 from spacewalk.common.checksum import getFileChecksum
28 from spacewalk.common.rhnLib import isSUSE
29 from spacewalk.common.usix import ListType, TupleType, MaxInt
32 """ take ~taw/../some/path/$MOUNT_POINT/blah and make it sensible.
33
34 Path returned is absolute.
35 NOTE: python 2.2 fixes a number of bugs with this and eliminates
36 the need for os.path.expanduser
37 """
38
39 if path is None:
40 return None
41 return os.path.abspath(
42 os.path.expanduser(
43 os.path.expandvars(path)))
44
47 """ take ~taw/../some/path/$MOUNT_POINT/blah and make it sensible.
48
49 Returned path may be relative.
50 NOTE: python 2.2 fixes a number of bugs with this and eliminates
51 the need for os.path.expanduser
52 """
53 if path is None:
54 return None
55 path = os.path.normpath(
56 os.path.expanduser(
57 os.path.expandvars(path)))
58 if dotYN and not (path and path[0] == '/'):
59 dirs = path.split('/')
60 if dirs[:1] not in (['.'], ['..']):
61 dirs = ['.'] + dirs
62 path = '/'.join(dirs)
63 return path
64
65
66 -def rotateFile(filepath, depth=5, suffix='.', verbosity=0):
67 """ backup/rotate a file
68 depth (-1==no limit) refers to num. of backups (rotations) to keep.
69
70 Behavior:
71 (1)
72 x.txt (current)
73 x.txt.1 (old)
74 x.txt.2 (older)
75 x.txt.3 (oldest)
76 (2)
77 all file stats preserved. Doesn't blow away original file.
78 (3)
79 if x.txt and x.txt.1 are identical (size or checksum), None is
80 returned
81 """
82
83
84 if not filepath or not isinstance(filepath, type('')):
85 raise ValueError("filepath '%s' is not a valid arguement" % filepath)
86 if not isinstance(depth, type(0)) or depth < -1 \
87 or depth > MaxInt - 1 or depth == 0:
88 raise ValueError("depth must fall within range "
89 "[-1, 1...%s]" % (MaxInt - 1))
90
91
92 verbosity = verbosity or 0
93 if not isinstance(verbosity, type(0)) or verbosity < -1 \
94 or verbosity > MaxInt - 1:
95 raise ValueError('invalid verbosity value: %s' % (verbosity))
96
97 filepath = cleanupAbsPath(filepath)
98 if not os.path.isfile(filepath):
99 raise ValueError("filepath '%s' does not lead to a file" % filepath)
100
101 pathNSuffix = filepath + suffix
102 pathNSuffix1 = pathNSuffix + '1'
103
104 if verbosity > 1:
105 sys.stderr.write("Working dir: %s\n"
106 % os.path.dirname(pathNSuffix))
107
108
109 checksum_type = 'sha1'
110 if os.path.exists(pathNSuffix1) and os.path.isfile(pathNSuffix1) \
111 and os.stat(filepath)[6] == os.stat(pathNSuffix1)[6] \
112 and getFileChecksum(checksum_type, filepath) == \
113 getFileChecksum(checksum_type, pathNSuffix1):
114
115 if verbosity:
116 sys.stderr.write("File '%s' is identical to its rotation. "
117 "Nothing to do.\n" % os.path.basename(filepath))
118 return None
119
120
121 last = 0
122 while os.path.exists('%s%d' % (pathNSuffix, last + 1)):
123 last = last + 1
124
125
126 for i in range(last, 0, -1):
127 os.rename('%s%d' % (pathNSuffix, i), '%s%d' % (pathNSuffix, i + 1))
128 if verbosity > 1:
129 filename = os.path.basename(pathNSuffix)
130 sys.stderr.write("Moving file: %s%d --> %s%d\n" % (filename, i,
131 filename, i + 1))
132
133
134 if depth != -1:
135 last = last + 1
136 for i in range(depth + 1, last + 1):
137 path = '%s%d' % (pathNSuffix, i)
138 os.unlink(path)
139 if verbosity:
140 sys.stderr.write("Rotated out: '%s'\n" % (
141 os.path.basename(path)))
142
143
144 shutil.copy2(filepath, pathNSuffix1)
145 if os.path.exists(pathNSuffix1) and verbosity:
146 sys.stderr.write("Backup made: '%s' --> '%s'\n"
147 % (os.path.basename(filepath),
148 os.path.basename(pathNSuffix1)))
149
150
151 return pathNSuffix1
152
153
154 -def rhn_popen(cmd, progressCallback=None, bufferSize=16384, outputLog=None):
155 """ popen-like function, that accepts execvp-style arguments too (i.e. an
156 array of params, thus making shell escaping unnecessary)
157
158 cmd can be either a string (like "ls -l /dev"), or an array of
159 arguments ["ls", "-l", "/dev"]
160
161 Returns the command's error code, a stream with stdout's contents
162 and a stream with stderr's contents
163
164 progressCallback --> progress bar twiddler
165 outputLog --> optional log file file object write method
166 """
167
168 cmd_is_list = isinstance(cmd, (ListType, TupleType))
169 if cmd_is_list:
170 cmd = list(map(str, cmd))
171
172 c = subprocess.Popen(cmd, bufsize=0, stdin=subprocess.PIPE,
173 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
174 close_fds=True, shell=(not cmd_is_list))
175
176
177 c.stdin.close()
178
179
180 child_out = tempfile.TemporaryFile(prefix='/tmp/my-popen-', mode='r+b')
181 child_err = tempfile.TemporaryFile(prefix='/tmp/my-popen-', mode='r+b')
182
183
184 fd_mappings = [(c.stdout, child_out), (c.stderr, child_err)]
185 exitcode = None
186 count = 1
187
188 while 1:
189
190 status = c.poll()
191 if status is not None:
192 if status >= 0:
193
194 exitcode = status
195 else:
196
197 if outputLog is not None:
198 outputLog("rhn_popen: Signal %s received\n" % (-status))
199 exitcode = status
200 break
201
202 fd_set = [x[0] for x in fd_mappings]
203 readfds = select.select(fd_set, [], [])[0]
204
205 for in_fd, out_fd in fd_mappings:
206 if in_fd in readfds:
207
208 output = os.read(in_fd.fileno(), bufferSize)
209 if output:
210
211 if progressCallback:
212 count = count + len(output)
213 progressCallback(count)
214
215 if outputLog is not None:
216 outputLog(output)
217
218
219 out_fd.write(output)
220 out_fd.flush()
221
222 if exitcode is not None:
223
224 break
225
226 for f_in, f_out in fd_mappings:
227 f_in.close()
228 f_out.seek(0, 0)
229
230 return exitcode, child_out, child_err
231
232
233 -def makedirs(path, mode=int('0755', 8), user=None, group=None):
234 "makedirs function that also changes the owners"
235
236 dirs_to_create = []
237 dirname = path
238
239 uid, gid = getUidGid(user, group)
240
241 while 1:
242 if os.path.isdir(dirname):
243
244 break
245
246 dirs_to_create.append(dirname)
247 dirname, last = os.path.split(dirname)
248 if not last:
249
250 break
251
252
253 while dirs_to_create:
254 dirname = dirs_to_create.pop()
255 try:
256 os.mkdir(dirname, mode)
257 except OSError:
258 e = sys.exc_info()[1]
259 if e.errno != 17:
260 raise
261
262 try:
263 os.chown(dirname, uid, gid)
264 except OSError:
265
266 sys.stderr.write("Changing owner for %s failed\n" % dirname)
267
268 -def createPath(path, user=None, group=None, chmod=int('0755', 8)):
269 """advanced makedirs
270
271 Will create the path if necessary.
272 Will chmod, and chown that path properly.
273 Defaults for user/group to the apache user
274
275 Uses the above makedirs() function.
276 """
277 if isSUSE():
278 if user is None:
279 user = 'wwwrun'
280 if group is None:
281 group = 'www'
282 else:
283 if user is None:
284 user = 'apache'
285 if group is None:
286 group = 'apache'
287
288 path = cleanupAbsPath(path)
289 if not os.path.exists(path):
290 makedirs(path, mode=chmod, user=user, group=group)
291 elif not os.path.isdir(path):
292 raise ValueError("ERROR: createPath('%s'): path doesn't lead to a directory" % str(path))
293 else:
294 os.chmod(path, chmod)
295 uid, gid = getUidGid(user, group)
296 try:
297 os.chown(path, uid, gid)
298 except OSError:
299
300 sys.stderr.write("Changing owner for %s failed\n" % path)
301
304 """chown user.group and set permissions to chmod"""
305 if isSUSE() and user is None:
306 user = 'wwwrun'
307 elif user is None:
308 user = 'apache'
309
310 if not os.path.exists(path):
311 raise OSError("*** ERROR: Path doesn't exist (can't set permissions): %s" % path)
312
313
314 if os.getuid() != 0:
315 return
316
317 gc = GecosCache()
318 uid = gc.getuid(user)
319 if uid is None:
320 raise OSError("*** ERROR: user '%s' doesn't exist. Cannot set permissions properly." % user)
321
322 gid = gc.getgid(group)
323 if gid is None:
324 raise OSError("*** ERROR: group '%s' doesn't exist. Cannot set permissions properly." % group)
325
326 uid_, gid_ = os.stat(path)[4:6]
327 if uid_ != uid or gid_ != gid:
328 os.chown(path, uid, gid)
329 os.chmod(path, chmod)
330
333
334 "Cache getpwnam() and getgrnam() calls"
335 __shared_data = {}
336
343
345 "Return the UID of the user by name"
346 if name in self._users:
347 return self._users[name]
348 try:
349 uid = pwd.getpwnam(name)[2]
350 except KeyError:
351
352 sys.stderr.write("XXX: User %s does not exist\n" % name)
353 return None
354 self._users[name] = uid
355 return uid
356
358 "Return the GID of the group by name"
359 if name in self._groups:
360 return self._groups[name]
361 try:
362 gid = grp.getgrnam(name)[2]
363 except KeyError:
364
365 sys.stderr.write("XXX: Group %s does not exist\n" % name)
366 return None
367 self._groups[name] = gid
368 return gid
369
373
376 "return uid, gid given user and group"
377
378 gc = GecosCache()
379 uid = os.getuid()
380 if uid != 0:
381
382
383 user = None
384 else:
385 uid = gc.getuid(user)
386
387 if group:
388 gid = gc.getgid(group)
389 else:
390 gid = None
391
392 if gid is None:
393 gid = os.getgid()
394 return uid, gid
395
396
397
398 FILETYPE2CHAR = {
399 'file': '-',
400 'directory': 'd',
401 'symlink': 'l',
402 'chardev': 'c',
403 'blockdev': 'b',
404 }
405
406
407
408
409
410 -def _ifelse(cond, thenval, elseval):
411 if cond:
412 return thenval
413 else:
414 return elseval
415
421 """ Convert filemode in octets (like '644') to string like "ls -l" ("-rwxrw-rw-")
422 ftype is one of: file, directory, symlink, chardev, blockdev.
423 """
424 mode = int(str(octstr), 8)
425
426 symstr = FILETYPE2CHAR.get(ftype, '?')
427
428 symstr += _ifelse(mode & stat.S_IRUSR, 'r', '-')
429 symstr += _ifelse(mode & stat.S_IWUSR, 'w', '-')
430 symstr += _ifelse(mode & stat.S_IXUSR,
431 _ifelse(mode & stat.S_ISUID, 's', 'x'),
432 _ifelse(mode & stat.S_ISUID, 'S', '-'))
433 symstr += _ifelse(mode & stat.S_IRGRP, 'r', '-')
434 symstr += _ifelse(mode & stat.S_IWGRP, 'w', '-')
435 symstr += _ifelse(mode & stat.S_IXGRP,
436 _ifelse(mode & stat.S_ISGID, 's', 'x'),
437 _ifelse(mode & stat.S_ISGID, 'S', '-'))
438 symstr += _ifelse(mode & stat.S_IROTH, 'r', '-')
439 symstr += _ifelse(mode & stat.S_IWOTH, 'w', '-')
440 symstr += _ifelse(mode & stat.S_IXOTH,
441 _ifelse(mode & stat.S_ISVTX, 't', 'x'),
442 _ifelse(mode & stat.S_ISVTX, 'T', '-'))
443 return symstr
444
445
446
447
448
449 -def f_date(dbiDate):
450 return "%04d-%02d-%02d %02d:%02d:%02d" % (dbiDate.year, dbiDate.month,
451 dbiDate.day, dbiDate.hour, dbiDate.minute, dbiDate.second)
452
455
456 """ this class implements simple file like object usable for reading payload
457 from rpm, mpm, etc.
458 it skips first 'skip' bytes of header
459 """
460
462 self.fileobj = open(filename, 'r')
463 self.skip = skip
464 self.seek(0)
465
466 - def seek(self, offset, whence=0):
467 if whence == 0:
468 offset += self.skip
469 return self.fileobj.seek(offset, whence)
470
472 return self.fileobj.tell() - self.skip
473
474 @staticmethod
476
477 raise AttributeError("'Payload' object do not implement this method")
478
479 @staticmethod
481 raise AttributeError("'Payload' object do not implement this method")
482
483 @staticmethod
485 raise AttributeError("'Payload' object do not implement this method")
486
488 return getattr(self.fileobj, x)
489
492 file_obj = None
493 if filename.endswith('.gz'):
494 file_obj = gzip.open(filename, mode)
495 elif filename.endswith('.bz2'):
496 file_obj = bz2.BZ2File(filename, mode)
497 elif filename.endswith('.xz'):
498 try:
499
500 import lzma
501 file_obj = lzma.LZMAFile(filename, mode)
502 except ImportError:
503
504
505 subprocess.call(['xz', '-d', '-k', filename])
506 uncompressed_path = filename.rsplit('.', 1)[0]
507 file_obj = open(uncompressed_path, mode)
508 else:
509 file_obj = open(filename, mode)
510 return file_obj
511