home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / python2.4 / site-packages / apport_utils.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-08-31  |  16.6 KB  |  466 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.4)
  3.  
  4. '''Various utility functions to handle apport problem reports.
  5.  
  6. Copyright (C) 2006 Canonical Ltd.
  7. Author: Martin Pitt <martin.pitt@ubuntu.com>
  8.  
  9. This program is free software; you can redistribute it and/or modify it
  10. under the terms of the GNU General Public License as published by the
  11. Free Software Foundation; either version 2 of the License, or (at your
  12. option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
  13. the full text of the license.
  14. '''
  15. import subprocess
  16. import os
  17. import os.path as os
  18. import glob
  19. import time
  20. import warnings
  21. warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)
  22. import apt
  23. from problem_report import ProblemReport
  24. report_dir = os.environ.get('APPORT_REPORT_DIR', '/var/crash')
  25.  
  26. def find_package_desktopfile(package):
  27.     '''If given package is installed and has a single .desktop file, return the
  28.     path to it, otherwise return None.'''
  29.     dpkg = subprocess.Popen([
  30.         'dpkg',
  31.         '-L',
  32.         package], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
  33.     out = dpkg.communicate(input)[0]
  34.     if dpkg.returncode != 0:
  35.         return None
  36.     
  37.     desktopfile = None
  38.     for line in out.splitlines():
  39.         if line.endswith('.desktop'):
  40.             if desktopfile:
  41.                 return None
  42.             else:
  43.                 desktopfile = line
  44.         desktopfile
  45.     
  46.     return desktopfile
  47.  
  48.  
  49. def find_file_package(file):
  50.     '''Return the package that ships the given file (or None if no package
  51.     ships it).'''
  52.     p = subprocess.Popen([
  53.         'fgrep',
  54.         '-lxm',
  55.         '1',
  56.         file] + glob.glob('/var/lib/dpkg/info/*.list'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, close_fds = True)
  57.     out = p.communicate()[0]
  58.     if p.returncode != 0:
  59.         return None
  60.     
  61.     return os.path.splitext(os.path.basename(out))[0]
  62.  
  63.  
  64. def seen_report(report):
  65.     '''Check whether the given report file has already been processed
  66.     earlier.'''
  67.     st = os.stat(report)
  68.     if not st.st_atime > st.st_mtime:
  69.         pass
  70.     return st.st_size == 0
  71.  
  72.  
  73. def mark_report_seen(report):
  74.     '''Mark given report file as seen.'''
  75.     timeout = 12
  76.     while timeout > 0:
  77.         f = open(report)
  78.         f.read(1)
  79.         f.close()
  80.         st = os.stat(report)
  81.         if st.st_atime > st.st_mtime:
  82.             break
  83.         
  84.         time.sleep(0.10000000000000001)
  85.         timeout -= 1
  86.     if timeout == 0:
  87.         raise OSError, 'could not modify atime of report file ' + report
  88.     
  89.  
  90.  
  91. def get_all_reports():
  92.     '''Return a list with all report files which are accessible to the calling
  93.     user.'''
  94.     return _[1]
  95.  
  96.  
  97. def get_new_reports():
  98.     '''Return a list with all report files which have not yet been processed
  99.     and are accessible to the calling user.'''
  100.     return _[1]
  101.  
  102.  
  103. def delete_report(report):
  104.     '''Delete the given report file.
  105.  
  106.     If unlinking the file fails due to a permission error (if report_dir is not
  107.     writable to normal users), the file will be truncated to 0 bytes instead.'''
  108.     
  109.     try:
  110.         os.unlink(report)
  111.     except OSError:
  112.         open(report, 'w').truncate(0)
  113.  
  114.  
  115.  
  116. def get_recent_crashes(report):
  117.     '''Return the number of recent crashes for the given report file.
  118.     
  119.     Return the number of recent crashes (currently, crashes which happened more
  120.     than 24 hours ago are discarded).'''
  121.     pr = ProblemReport()
  122.     pr.load(report, False)
  123.     
  124.     try:
  125.         count = int(pr['CrashCounter'])
  126.         report_time = time.mktime(time.strptime(pr['Date']))
  127.         cur_time = time.mktime(time.localtime())
  128.         if cur_time - report_time > 24 * 3600:
  129.             return 0
  130.         
  131.         return count
  132.     except (ValueError, KeyError):
  133.         return 0
  134.  
  135.  
  136.  
  137. def _transitive_dependencies(package, depends_set, cache):
  138.     '''Recursively add dependencies of package to depends_set, using the given
  139.     apt cache.'''
  140.     
  141.     try:
  142.         cur_ver = cache[package]._pkg.CurrentVer
  143.     except (AttributeError, KeyError):
  144.         return None
  145.  
  146.     if not cur_ver:
  147.         return None
  148.     
  149.     for p in cur_ver.DependsList.get('Depends', []) + cur_ver.DependsList.get('PreDepends', []):
  150.         name = p[0].TargetPkg.Name
  151.         if name not in depends_set:
  152.             depends_set.add(name)
  153.             _transitive_dependencies(name, depends_set, cache)
  154.             continue
  155.     
  156.  
  157.  
  158. def report_add_package_info(report, package):
  159.     '''Add packaging information to the given report.
  160.  
  161.     This adds:
  162.     - Package: package name and installed version
  163.     - SourcePackage: source package name
  164.     - Dependencies: package names and versions of all dependencies and
  165.       pre-dependencies'''
  166.     cache = apt.Cache()
  167.     report['Package'] = '%s %s' % (package, cache[package].installedVersion)
  168.     report['SourcePackage'] = cache[package].sourcePackageName
  169.     dependencies = set([])
  170.     _transitive_dependencies(package, dependencies, cache)
  171.     report['Dependencies'] = ''
  172.     for dep in dependencies:
  173.         
  174.         try:
  175.             cur_ver = cache[dep]._pkg.CurrentVer
  176.         except (KeyError, AttributeError):
  177.             continue
  178.  
  179.         if report['Dependencies']:
  180.             report['Dependencies'] += '\n'
  181.         
  182.         report['Dependencies'] += '%s %s' % (dep, cur_ver.VerStr)
  183.     
  184.  
  185.  
  186. def report_add_os_info(report):
  187.     '''Add operating system information to the given report.
  188.  
  189.     This adds:
  190.     - DistroRelease: lsb_release -sir output
  191.     - Uname: uname -a output'''
  192.     p = subprocess.Popen([
  193.         'lsb_release',
  194.         '-sir'], stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True)
  195.     report['DistroRelease'] = p.communicate()[0].strip().replace('\n', ' ')
  196.     p = subprocess.Popen([
  197.         'uname',
  198.         '-a'], stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True)
  199.     report['Uname'] = p.communicate()[0].strip()
  200.  
  201.  
  202. def _read_file(f):
  203.     '''Try to read given file and return its contents, or return a textual
  204.     error if it failed.'''
  205.     
  206.     try:
  207.         return open(f).read().strip()
  208.     except (OSError, IOError):
  209.         e = None
  210.         return 'Error: ' + str(e)
  211.  
  212.  
  213.  
  214. def report_add_proc_info(report, pid = None, extraenv = []):
  215.     """Add /proc/pid information to the given report.
  216.  
  217.     If pid is not given, it defaults to the process' current pid.
  218.     
  219.     This adds the following fields:
  220.     - ProcEnviron: A subset of the process' environment (only some standard
  221.       variables that do not disclose potentially sensitive information, plus
  222.       the ones mentioned in extraenv)
  223.     - ProcCmdline: /proc/pid/cmdline contents
  224.     - ProcStatus: /proc/pid/status contents
  225.     - ProcMaps: /proc/pid/maps contents"""
  226.     safe_vars = [
  227.         'SHELL',
  228.         'PATH',
  229.         'LANGUAGE',
  230.         'LANG',
  231.         'LC_CTYPE',
  232.         'LC_COLLATE',
  233.         'LC_TIME',
  234.         'LC_NUMERIC',
  235.         'LC_MONETARY',
  236.         'LC_MESSAGES',
  237.         'LC_PAPER',
  238.         'LC_NAME',
  239.         'LC_ADDRESS',
  240.         'LC_TELEPHONE',
  241.         'LC_MEASUREMENT',
  242.         'LC_IDENTIFICATION',
  243.         'LOCPATH'] + extraenv
  244.     if not pid:
  245.         pid = os.getpid()
  246.     
  247.     pid = str(pid)
  248.     report['ProcEnviron'] = ''
  249.     env = _read_file('/proc/' + pid + '/environ').replace('\n', '\\n')
  250.     if env.startswith('Error:'):
  251.         report['ProcEnviron'] = env
  252.     else:
  253.         for l in env.split('\x00'):
  254.             if l.split('=', 1)[0] in safe_vars:
  255.                 if report['ProcEnviron']:
  256.                     report['ProcEnviron'] += '\n'
  257.                 
  258.                 report['ProcEnviron'] += l
  259.                 continue
  260.         
  261.     report['ProcStatus'] = _read_file('/proc/' + pid + '/status')
  262.     report['ProcCmdline'] = _read_file('/proc/' + pid + '/cmdline').rstrip('\x00').replace('\\', '\\\\').replace(' ', '\\ ').replace('\x00', ' ')
  263.     report['ProcMaps'] = _read_file('/proc/' + pid + '/maps')
  264.  
  265.  
  266. def make_report_path(report, uid = None):
  267.     '''Construct a canonical pathname for the given report.
  268.     
  269.     If uid is not given, it defaults to the uid of the current process.'''
  270.     if report.has_key('ExecutablePath'):
  271.         subject = report['ExecutablePath'].replace('/', '_')
  272.     elif report.has_key('Package'):
  273.         subject = report['Package'].split(None, 1)[0]
  274.     else:
  275.         raise ValueError, 'report has neither ExecutablePath nor Package attribute'
  276.     if not uid:
  277.         uid = os.getuid()
  278.     
  279.     return os.path.join(report_dir, '%s.%i.crash' % (subject, uid))
  280.  
  281. import unittest
  282. import tempfile
  283. import os
  284. import shutil
  285. import sys
  286. import time
  287. import StringIO
  288.  
  289. class _ApportUtilsTest(unittest.TestCase):
  290.     
  291.     def setUp(self):
  292.         global report_dir
  293.         self.orig_report_dir = report_dir
  294.         report_dir = tempfile.mkdtemp()
  295.  
  296.     
  297.     def tearDown(self):
  298.         global report_dir
  299.         shutil.rmtree(report_dir)
  300.         report_dir = self.orig_report_dir
  301.         self.orig_report_dir = None
  302.  
  303.     
  304.     def _create_reports(self, create_inaccessible = False):
  305.         '''Create some test reports.'''
  306.         r1 = os.path.join(report_dir, 'rep1.crash')
  307.         r2 = os.path.join(report_dir, 'rep2.crash')
  308.         open(r1, 'w').write('report 1')
  309.         open(r2, 'w').write('report 2')
  310.         os.chmod(r1, 384)
  311.         os.chmod(r2, 384)
  312.         if create_inaccessible:
  313.             ri = os.path.join(report_dir, 'inaccessible.crash')
  314.             open(ri, 'w').write('inaccessible')
  315.             os.chmod(ri, 0)
  316.             return [
  317.                 r1,
  318.                 r2,
  319.                 ri]
  320.         else:
  321.             return [
  322.                 r1,
  323.                 r2]
  324.  
  325.     
  326.     def test_get_all_reports(self):
  327.         '''Test get_all_reports() behaviour.'''
  328.         self.assertEqual(get_all_reports(), [])
  329.         tr = _[1]
  330.         self.assertEqual(set(get_all_reports()), set(tr))
  331.         for r in tr:
  332.             mark_report_seen(r)
  333.         
  334.         self.assertEqual(set(get_all_reports()), set(tr))
  335.  
  336.     
  337.     def test_seen(self):
  338.         '''Test get_new_reports() and seen_report() behaviour.'''
  339.         self.assertEqual(get_new_reports(), [])
  340.         tr = _[1]
  341.         self.assertEqual(set(get_new_reports()), set(tr))
  342.         nr = set(tr)
  343.         for r in tr:
  344.             self.assertEqual(seen_report(r), False)
  345.             nr.remove(r)
  346.             mark_report_seen(r)
  347.             self.assertEqual(seen_report(r), True)
  348.             self.assertEqual(set(get_new_reports()), nr)
  349.         
  350.  
  351.     
  352.     def test_delete_report(self):
  353.         '''Test delete_report() behaviour.'''
  354.         tr = self._create_reports()
  355.         while tr:
  356.             self.assertEqual(set(get_all_reports()), set(tr))
  357.             delete_report(tr.pop())
  358.  
  359.     
  360.     def test_find_package_desktopfile(self):
  361.         '''Test find_package_desktopfile() behaviour.'''
  362.         sp = subprocess.Popen("grep -c '\\.desktop$' /var/lib/dpkg/info/*.list | grep -m 1 ':0$'", shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
  363.         nodesktop = os.path.splitext(os.path.basename(sp.communicate()[0]))[0]
  364.         sp = subprocess.Popen("grep -c '\\.desktop$' /var/lib/dpkg/info/*.list | grep -m 1 ':1$'", shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
  365.         onedesktop = os.path.splitext(os.path.basename(sp.communicate()[0]))[0]
  366.         sp = subprocess.Popen("grep -c '\\.desktop$' /var/lib/dpkg/info/*.list | grep -m 1 -v ':\\(0\\|1\\)$'", shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
  367.         multidesktop = os.path.splitext(os.path.basename(sp.communicate()[0]))[0]
  368.         self.assertEqual(find_package_desktopfile(nodesktop), None, 'no-desktop package %s' % nodesktop)
  369.         self.assertEqual(find_package_desktopfile(multidesktop), None, 'multi-desktop package %s' % multidesktop)
  370.         d = find_package_desktopfile(onedesktop)
  371.         self.assertNotEqual(d, None, 'one-desktop package %s' % onedesktop)
  372.         self.assert_(os.path.exists(d))
  373.         self.assert_(d.endswith('.desktop'))
  374.  
  375.     
  376.     def test_get_recent_crashes(self):
  377.         '''Test get_recent_crashes() behaviour.'''
  378.         r = StringIO.StringIO('ProblemType: Crash')
  379.         self.assertEqual(get_recent_crashes(r), 0)
  380.         r = StringIO.StringIO('ProblemType: Crash\nDate: Wed Aug 01 00:00:01 1990')
  381.         self.assertEqual(get_recent_crashes(r), 0)
  382.         r = StringIO.StringIO('ProblemType: Crash\nDate: Wed Aug 01 00:00:01 1990\nCrashCounter: 3')
  383.         self.assertEqual(get_recent_crashes(r), 0)
  384.         r = StringIO.StringIO('ProblemType: Crash\nDate: %s\nCrashCounter: 3' % time.ctime(time.mktime(time.localtime()) - 25 * 3600))
  385.         self.assertEqual(get_recent_crashes(r), 0)
  386.         r = StringIO.StringIO('ProblemType: Crash\nDate: %s\nCrashCounter: 3' % time.ctime(time.mktime(time.localtime()) - 3600))
  387.         self.assertEqual(get_recent_crashes(r), 3)
  388.  
  389.     
  390.     def test_find_file_package(self):
  391.         '''Test find_file_package() behaviour.'''
  392.         self.assertEqual(find_file_package('/bin/cat'), 'coreutils')
  393.         self.assertEqual(find_file_package('/nonexisting'), None)
  394.  
  395.     
  396.     def test_report_add_package_info(self):
  397.         '''Test report_add_package_info() behaviour.'''
  398.         p = subprocess.Popen('dpkg -s bash | grep ^Version: | cut -f2 -d\\ ', shell = True, stdout = subprocess.PIPE)
  399.         bashversion = p.communicate()[0]
  400.         p = subprocess.Popen('dpkg -s libc6 | grep ^Version: | cut -f2 -d\\ ', shell = True, stdout = subprocess.PIPE)
  401.         libcversion = p.communicate()[0]
  402.         pr = ProblemReport()
  403.         self.assertRaises(KeyError, report_add_package_info, pr, 'nonexistant_package')
  404.         report_add_package_info(pr, 'bash')
  405.         self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
  406.         self.assertEqual(pr['SourcePackage'], 'bash')
  407.         self.assert_(pr['Dependencies'].find('libc6 ' + libcversion) >= 0)
  408.  
  409.     
  410.     def test_report_add_os_info(self):
  411.         '''Test report_add_os_info() behaviour.'''
  412.         pr = ProblemReport()
  413.         report_add_os_info(pr)
  414.         self.assert_(pr['Uname'].startswith('Linux'))
  415.         self.assert_(type(pr['DistroRelease']) == type(''))
  416.  
  417.     
  418.     def test_report_add_proc_info(self):
  419.         '''Test report_add_proc_info() behaviour.'''
  420.         pr = ProblemReport()
  421.         report_add_proc_info(pr)
  422.         self.assert_(set([
  423.             'ProcEnviron',
  424.             'ProcMaps',
  425.             'ProcCmdline',
  426.             'ProcMaps']).issubset(set(pr.keys())), 'report has required fields')
  427.         self.assert_(pr['ProcEnviron'].find('LANG=' + os.environ['LANG']) >= 0)
  428.         self.assert_(pr['ProcEnviron'].find('USER') < 0)
  429.         self.assert_(pr['ProcEnviron'].find('PWD') < 0)
  430.         pr = ProblemReport()
  431.         report_add_proc_info(pr, extraenv = [
  432.             'PWD'])
  433.         self.assert_(pr['ProcEnviron'].find('USER') < 0)
  434.         self.assert_(pr['ProcEnviron'].find('PWD=' + os.environ['PWD']) >= 0)
  435.         pr = ProblemReport()
  436.         report_add_proc_info(pr, pid = 1)
  437.         self.assert_(pr['ProcStatus'].find('init') >= 0, pr['ProcStatus'])
  438.         self.assert_(pr['ProcEnviron'].startswith('Error:'), pr['ProcEnviron'])
  439.         p = subprocess.Popen([
  440.             'cat',
  441.             '/foo bar',
  442.             '\\h',
  443.             '\\ \\',
  444.             '-'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, close_fds = True)
  445.         while not open('/proc/%i/cmdline' % p.pid).read():
  446.             time.sleep(0.10000000000000001)
  447.         pr = ProblemReport()
  448.         report_add_proc_info(pr, pid = p.pid)
  449.         p.communicate('\n')
  450.         self.assertEqual(pr['ProcCmdline'], 'cat /foo\\ bar \\\\h \\\\\\ \\\\ -')
  451.  
  452.     
  453.     def test_make_report_path(self):
  454.         '''Test make_report_path() behaviour.'''
  455.         pr = ProblemReport()
  456.         self.assertRaises(ValueError, make_report_path, pr)
  457.         pr['Package'] = 'bash 1'
  458.         self.assert_(make_report_path(pr).startswith('%s/bash' % report_dir))
  459.         pr['ExecutablePath'] = '/bin/bash'
  460.         self.assert_(make_report_path(pr).startswith('%s/_bin_bash' % report_dir))
  461.  
  462.  
  463. if __name__ == '__main__':
  464.     unittest.main()
  465.  
  466.