16
16
from xml.parsers.expat import ExpatError
18
18
from problem_report import ProblemReport
20
from packaging_impl import impl as packaging
20
import apport.fileutils
21
from apport.packaging_impl import impl as packaging
22
23
_data_dir = os.environ.get('APPORT_DATA_DIR','/usr/share/apport')
23
24
_hook_dir = '%s/package-hooks/' % (_data_dir)
58
59
return open(path).read().strip()
59
except (OSError, IOError), e:
60
except (OSError, IOError) as e:
60
61
return 'Error: ' + str(e)
62
63
def _read_maps(pid):
68
69
maps = 'Error: unable to read /proc maps file'
70
maps = file('/proc/%d/maps' % pid).read().strip()
71
except (OSError,IOError), e:
71
maps = open('/proc/%d/maps' % pid).read().strip()
72
except (OSError,IOError) as e:
72
73
return 'Error: ' + str(e)
84
85
if sp.returncode == 0:
87
raise OSError, 'Error: command %s failed with exit code %i: %s' % (
88
str(command), sp.returncode, err)
88
raise OSError('Error: command %s failed with exit code %i: %s' % (
89
str(command), sp.returncode, err))
90
91
def _check_bug_pattern(report, pattern):
91
92
'''Check if given report matches the given bug pattern XML DOM node.
333
334
self['ProcMaps'] = _read_maps(int(pid))
335
336
self['ExecutablePath'] = os.readlink('/proc/' + pid + '/exe')
337
338
if e.errno == errno.ENOENT:
338
raise ValueError, 'invalid process'
339
raise ValueError('invalid process')
341
342
for p in ('rofs', 'rwfs', 'squashmnt', 'persistmnt'):
716
717
dom = xml.dom.minidom.parse(ifpath)
717
except ExpatError, e:
718
raise ValueError, '%s has invalid format: %s' % (_ignore_file, str(e))
718
except ExpatError as e:
719
raise ValueError('%s has invalid format: %s' % (_ignore_file, str(e)))
720
721
# remove whitespace so that writing back the XML does not accumulate
1093
1094
import unittest, shutil, signal, time
1094
from cStringIO import StringIO
1096
from cStringIO import StringIO
1098
from io import StringIO
1096
1100
class _T(unittest.TestCase):
1097
1101
def test_add_package_info(self):
1106
1110
pr.add_package_info('bash')
1107
1111
self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
1108
1112
self.assertEqual(pr['SourcePackage'], 'bash')
1109
self.assert_('libc' in pr['Dependencies'])
1113
self.assertTrue('libc' in pr['Dependencies'])
1111
1115
# test without specifying a package, but with ExecutablePath
1115
1119
pr.add_package_info()
1116
1120
self.assertEqual(pr['Package'], 'bash ' + bashversion.strip())
1117
1121
self.assertEqual(pr['SourcePackage'], 'bash')
1118
self.assert_('libc' in pr['Dependencies'])
1122
self.assertTrue('libc' in pr['Dependencies'])
1119
1123
# check for stray empty lines
1120
self.assert_('\n\n' not in pr['Dependencies'])
1121
self.assert_(pr.has_key('PackageArchitecture'))
1124
self.assertTrue('\n\n' not in pr['Dependencies'])
1125
self.assertTrue(pr.has_key('PackageArchitecture'))
1124
1128
pr['ExecutablePath'] = '/nonexisting'
1125
1129
pr.add_package_info()
1126
self.assert_(not pr.has_key('Package'))
1130
self.assertTrue(not pr.has_key('Package'))
1128
1132
def test_add_os_info(self):
1129
1133
'''add_os_info().'''
1132
1136
pr.add_os_info()
1133
self.assert_(pr['Uname'].startswith('Linux'))
1134
self.assert_(type(pr['DistroRelease']) == type(''))
1135
self.assert_(pr['Architecture'])
1137
self.assertTrue(pr['Uname'].startswith('Linux'))
1138
self.assertTrue(type(pr['DistroRelease']) == type(''))
1139
self.assertTrue(pr['Architecture'])
1137
1141
def test_add_user_info(self):
1138
1142
'''add_user_info().'''
1141
1145
pr.add_user_info()
1142
self.assert_(pr.has_key('UserGroups'))
1146
self.assertTrue(pr.has_key('UserGroups'))
1144
1148
# double-check that user group names are removed
1145
1149
for g in pr['UserGroups'].split():
1146
self.assert_(grp.getgrnam(g).gr_gid < 1000)
1147
self.assert_(grp.getgrgid(os.getgid()).gr_name not in pr['UserGroups'])
1150
self.assertTrue(grp.getgrnam(g).gr_gid < 1000)
1151
self.assertTrue(grp.getgrgid(os.getgid()).gr_name not in pr['UserGroups'])
1149
1153
def test_add_proc_info(self):
1150
1154
'''add_proc_info().'''
1159
1163
self.assertEqual(pr.pid, None)
1160
1164
pr.add_proc_info()
1161
1165
self.assertEqual(pr.pid, os.getpid())
1162
self.assert_(set(['ProcEnviron', 'ProcMaps', 'ProcCmdline',
1166
self.assertTrue(set(['ProcEnviron', 'ProcMaps', 'ProcCmdline',
1163
1167
'ProcMaps']).issubset(set(pr.keys())), 'report has required fields')
1164
self.assert_('LANG='+os.environ['LANG'] in pr['ProcEnviron'])
1165
self.assert_('USER' not in pr['ProcEnviron'])
1166
self.assert_('PWD' not in pr['ProcEnviron'])
1168
self.assertTrue('LANG='+os.environ['LANG'] in pr['ProcEnviron'])
1169
self.assertTrue('USER' not in pr['ProcEnviron'])
1170
self.assertTrue('PWD' not in pr['ProcEnviron'])
1168
1172
# check with one additional safe environment variable
1170
1174
pr.add_proc_info(extraenv=['PWD'])
1171
self.assert_('USER' not in pr['ProcEnviron'])
1172
self.assert_('PWD='+os.environ['PWD'] in pr['ProcEnviron'])
1175
self.assertTrue('USER' not in pr['ProcEnviron'])
1176
self.assertTrue('PWD='+os.environ['PWD'] in pr['ProcEnviron'])
1174
1178
# check process from other user
1175
1179
assert os.getuid() != 0, 'please do not run this test as root for this check.'
1177
1181
self.assertRaises(OSError, pr.add_proc_info, 1) # EPERM for init process
1178
1182
self.assertEqual(pr.pid, 1)
1179
self.assert_('init' in pr['ProcStatus'], pr['ProcStatus'])
1180
self.assert_(pr['ProcEnviron'].startswith('Error:'), pr['ProcEnviron'])
1181
self.assert_(not pr.has_key('InterpreterPath'))
1183
self.assertTrue('init' in pr['ProcStatus'], pr['ProcStatus'])
1184
self.assertTrue(pr['ProcEnviron'].startswith('Error:'), pr['ProcEnviron'])
1185
self.assertTrue(not pr.has_key('InterpreterPath'))
1183
1187
# check escaping of ProcCmdline
1184
1188
p = subprocess.Popen(['cat', '/foo bar', '\\h', '\\ \\', '-'],
1194
1198
p.communicate('\n')
1195
1199
self.assertEqual(pr['ProcCmdline'], 'cat /foo\ bar \\\\h \\\\\\ \\\\ -')
1196
1200
self.assertEqual(pr['ExecutablePath'], '/bin/cat')
1197
self.assert_(not pr.has_key('InterpreterPath'))
1201
self.assertTrue(not pr.has_key('InterpreterPath'))
1198
1202
self.assertTrue('/bin/cat' in pr['ProcMaps'])
1199
1203
self.assertTrue('[stack]' in pr['ProcMaps'])
1224
1228
pr.add_proc_info(pid=p.pid)
1225
1229
p.communicate('\n')
1226
self.assert_(pr['ExecutablePath'].endswith('bin/zgrep'))
1230
self.assertTrue(pr['ExecutablePath'].endswith('bin/zgrep'))
1227
1231
self.assertEqual(pr['InterpreterPath'],
1228
1232
os.path.realpath(open(pr['ExecutablePath']).readline().strip()[2:]))
1229
1233
self.assertTrue('[stack]' in pr['ProcMaps'])
1235
1239
sys.stdin.readline()
1238
os.chmod(testscript, 0755)
1242
os.chmod(testscript, 0o755)
1239
1243
p = subprocess.Popen([testscript], stdin=subprocess.PIPE,
1240
1244
stderr=subprocess.PIPE, close_fds=True)
1247
1251
p.communicate('\n')
1248
1252
os.unlink(testscript)
1249
1253
self.assertEqual(pr['ExecutablePath'], testscript)
1250
self.assert_('python' in pr['InterpreterPath'])
1254
self.assertTrue('python' in pr['InterpreterPath'])
1251
1255
self.assertTrue('python' in pr['ProcMaps'])
1252
1256
self.assertTrue('[stack]' in pr['ProcMaps'])
1275
1279
r.add_proc_environ(pid=p.pid)
1276
1280
p.communicate('')
1277
self.assert_('PATH=(custom, no user)' in r['ProcEnviron'],
1281
self.assertTrue('PATH=(custom, no user)' in r['ProcEnviron'],
1278
1282
'PATH is customized without user paths')
1285
1289
r.add_proc_environ(pid=p.pid)
1286
1290
p.communicate('')
1287
self.assert_('PATH=(custom, user)' in r['ProcEnviron'],
1291
self.assertTrue('PATH=(custom, user)' in r['ProcEnviron'],
1288
1292
'PATH is customized with user paths')
1290
1294
def test_check_interpreted(self):
1461
1465
def _validate_gdb_fields(self,pr):
1462
self.assert_(pr.has_key('Stacktrace'))
1463
self.assert_(pr.has_key('ThreadStacktrace'))
1464
self.assert_(pr.has_key('StacktraceTop'))
1465
self.assert_(pr.has_key('Registers'))
1466
self.assert_(pr.has_key('Disassembly'))
1467
self.assert_('(no debugging symbols found)' not in pr['Stacktrace'])
1468
self.assert_('Core was generated by' not in pr['Stacktrace'], pr['Stacktrace'])
1469
self.assert_(not re.match(r'(?s)(^|.*\n)#0 [^\n]+\n#0 ',
1466
self.assertTrue(pr.has_key('Stacktrace'))
1467
self.assertTrue(pr.has_key('ThreadStacktrace'))
1468
self.assertTrue(pr.has_key('StacktraceTop'))
1469
self.assertTrue(pr.has_key('Registers'))
1470
self.assertTrue(pr.has_key('Disassembly'))
1471
self.assertTrue('(no debugging symbols found)' not in pr['Stacktrace'])
1472
self.assertTrue('Core was generated by' not in pr['Stacktrace'], pr['Stacktrace'])
1473
self.assertTrue(not re.match(r'(?s)(^|.*\n)#0 [^\n]+\n#0 ',
1470
1474
pr['Stacktrace']))
1471
self.assert_('#0 0x' in pr['Stacktrace'])
1472
self.assert_('#1 0x' in pr['Stacktrace'])
1473
self.assert_('#0 0x' in pr['ThreadStacktrace'])
1474
self.assert_('#1 0x' in pr['ThreadStacktrace'])
1475
self.assert_('Thread 1 (' in pr['ThreadStacktrace'])
1476
self.assert_(len(pr['StacktraceTop'].splitlines()) <= 5)
1475
self.assertTrue('#0 0x' in pr['Stacktrace'])
1476
self.assertTrue('#1 0x' in pr['Stacktrace'])
1477
self.assertTrue('#0 0x' in pr['ThreadStacktrace'])
1478
self.assertTrue('#1 0x' in pr['ThreadStacktrace'])
1479
self.assertTrue('Thread 1 (' in pr['ThreadStacktrace'])
1480
self.assertTrue(len(pr['StacktraceTop'].splitlines()) <= 5)
1478
1482
def test_add_gdb_info(self):
1479
1483
'''add_gdb_info() with core dump file reference.'''
1499
1503
self._validate_gdb_fields(pr)
1500
self.assert_('Cannot access memory at address 0x0' in pr['Disassembly'], pr['Disassembly'])
1504
self.assertTrue('Cannot access memory at address 0x0' in pr['Disassembly'], pr['Disassembly'])
1501
1505
self.failIf ('AssertionMessage' in pr)
1503
1507
def test_add_gdb_info_load(self):
1523
1527
pr.load(open(rep.name))
1524
1528
pr['Signal'] = '1'
1525
1529
pr.add_hooks_info('fake_ui')
1526
self.assert_('SegvAnalysis' not in pr.keys())
1530
self.assertTrue('SegvAnalysis' not in pr.keys())
1529
1533
pr.load(open(rep.name))
1530
1534
pr.add_hooks_info('fake_ui')
1531
self.assert_('Skipped: missing required field "Architecture"' in pr['SegvAnalysis'],
1535
self.assertTrue('Skipped: missing required field "Architecture"' in pr['SegvAnalysis'],
1532
1536
pr['SegvAnalysis'])
1534
1538
pr.add_os_info()
1535
1539
pr.add_hooks_info('fake_ui')
1536
self.assert_('Skipped: missing required field "ProcMaps"' in pr['SegvAnalysis'],
1540
self.assertTrue('Skipped: missing required field "ProcMaps"' in pr['SegvAnalysis'],
1537
1541
pr['SegvAnalysis'])
1539
1543
pr.add_proc_info()
1540
1544
pr.add_hooks_info('fake_ui')
1541
self.assert_('not located in a known VMA region' in pr['SegvAnalysis'],
1545
self.assertTrue('not located in a known VMA region' in pr['SegvAnalysis'],
1542
1546
pr['SegvAnalysis'])
1544
1548
def test_add_gdb_info_script(self):
1573
1577
os.unlink(script)
1575
1579
self._validate_gdb_fields(pr)
1576
self.assert_('libc.so' in pr['Stacktrace'] or 'in execute_command' in pr['Stacktrace'])
1580
self.assertTrue('libc.so' in pr['Stacktrace'] or 'in execute_command' in pr['Stacktrace'])
1578
1582
def test_add_gdb_info_abort(self):
1579
1583
'''add_gdb_info() with SIGABRT/assert()
1613
1617
os.unlink('core')
1615
1619
self._validate_gdb_fields(pr)
1616
self.assert_("<stdin>:2: main: Assertion `1 < 0' failed." in
1620
self.assertTrue("<stdin>:2: main: Assertion `1 < 0' failed." in
1617
1621
pr['AssertionMessage'], pr['AssertionMessage'])
1618
1622
self.failIf(pr['AssertionMessage'].startswith('$'), pr['AssertionMessage'])
1619
1623
self.failIf('= 0x' in pr['AssertionMessage'], pr['AssertionMessage'])
1655
1659
os.unlink('core')
1657
1661
self._validate_gdb_fields(pr)
1658
self.assert_("** buffer overflow detected ***: %s.bin terminated" % (script) in
1662
self.assertTrue("** buffer overflow detected ***: %s.bin terminated" % (script) in
1659
1663
pr['AssertionMessage'], pr['AssertionMessage'])
1660
1664
self.failIf(pr['AssertionMessage'].startswith('$'), pr['AssertionMessage'])
1661
1665
self.failIf('= 0x' in pr['AssertionMessage'], pr['AssertionMessage'])
2020
2024
self.failIf(r.has_useful_stacktrace())
2022
2026
r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
2023
self.assert_(r.has_useful_stacktrace())
2027
self.assertTrue(r.has_useful_stacktrace())
2025
2029
r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()'
2026
self.assert_(r.has_useful_stacktrace())
2030
self.assertTrue(r.has_useful_stacktrace())
2028
2032
r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()\n?? ()'
2029
self.assert_(r.has_useful_stacktrace())
2033
self.assertTrue(r.has_useful_stacktrace())
2031
2035
r['StacktraceTop'] = 'read () from /lib/libc.6.so\n?? ()\nfoo (i=1) from /usr/lib/libfoo.so\n?? ()\n?? ()'
2032
2036
self.failIf(r.has_useful_stacktrace())
2121
2125
Restarting AWN usually solves this issue'''
2123
2127
t = report.standard_title()
2124
self.assert_(t.startswith('apport-gtk crashed with'))
2125
self.assert_(t.endswith('setup_chooser()'))
2128
self.assertTrue(t.startswith('apport-gtk crashed with'))
2129
self.assertTrue(t.endswith('setup_chooser()'))
2127
2131
# Python crash at top level in module
2128
2132
report = Report()
2366
2370
del r['Signal']
2367
2371
r['Traceback'] = '''Traceback (most recent call last):
2368
2372
File "test.py", line 7, in <module>
2370
2374
File "test.py", line 5, in _f
2371
2375
return g_foo00(x+1)
2372
2376
File "test.py", line 2, in g_foo00