~jconti/apport/expand_user_in_retrace

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
'''Python sys.excepthook hook to generate apport crash dumps.'''

# Copyright (c) 2006 - 2009 Canonical Ltd.
# Authors: Robert Collins <robert@ubuntu.com>
#          Martin Pitt <martin.pitt@ubuntu.com>
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
# the full text of the license.

import os
import sys

CONFIG = '/etc/default/apport'

# This doesn't use apport.packaging.enabled() because it is too heavyweight
# See LP: #528355
def enabled():
    '''Return whether Apport should generate crash reports.'''

    import re
    try:
        conf = open(CONFIG).read()
        return re.search('^\s*enabled\s*=\s*0\s*$', conf, re.M) is None
    except IOError:
        # if the file does not exist, assume it's enabled
        return True

def apport_excepthook(exc_type, exc_obj, exc_tb):
    '''Catch an uncaught exception and make a traceback.'''

    # create and save a problem report. Note that exceptions in this code
    # are bad, and we probably need a per-thread reentrancy guard to
    # prevent that happening. However, on Ubuntu there should never be
    # a reason for an exception here, other than [say] a read only var
    # or some such. So what we do is use a try - finally to ensure that
    # the original excepthook is invoked, and until we get bug reports
    # ignore the other issues.

    # import locally here so that there is no routine overhead on python
    # startup time - only when a traceback occurs will this trigger.
    try:
        # ignore 'safe' exit types.
        if exc_type in (KeyboardInterrupt, ):
            return

        # do not do anything if apport was disabled
        if not enabled():
            return

        try:
            from cStringIO import StringIO
        except ImportError:
            from io import StringIO

        import re, tempfile, traceback
        from apport.fileutils import likely_packaged, get_recent_crashes

        # apport will look up the package from the executable path.
        try:
            binary = os.path.realpath(os.path.join(os.getcwdu(), sys.argv[0]))
        except (TypeError, AttributeError, IndexError):
            # the module has mutated sys.argv, plan B
            try:
                binary = os.readlink('/proc/%i/exe' % os.getpid())
            except OSError:
                return

        # for interactive python sessions, sys.argv[0] == ''; catch that and
        # other irregularities
        if not os.access(binary, os.X_OK) or not os.path.isfile(binary):
            return

        # filter out binaries in user accessible paths
        if not likely_packaged(binary):
            return

        import apport.report

        pr = apport.report.Report()
        # append a basic traceback. In future we may want to include
        # additional data such as the local variables, loaded modules etc.
        tb_file = StringIO()
        traceback.print_exception(exc_type, exc_obj, exc_tb, file=tb_file)
        pr['Traceback'] = tb_file.getvalue().strip()
        pr.add_proc_info()
        pr.add_user_info()
        # override the ExecutablePath with the script that was actually running.
        pr['ExecutablePath'] = binary
        try:
            pr['PythonArgs'] = '%r' % sys.argv
        except AttributeError:
            pass
        if pr.check_ignored():
            return
        mangled_program = re.sub('/', '_', binary)
        # get the uid for now, user name later
        user = os.getuid()
        pr_filename = '%s/%s.%i.crash' % (os.environ.get('APPORT_REPORT_DIR',
            '/var/crash'), mangled_program, user)
        crash_counter = 0
        if os.path.exists(pr_filename):
            if apport.fileutils.seen_report(pr_filename):
                # flood protection
                crash_counter = get_recent_crashes(open(pr_filename)) + 1
                if crash_counter > 1:
                    return

                # remove the old file, so that we can create the new one with
                # os.O_CREAT|os.O_EXCL
                os.unlink(pr_filename)
            else:
                # don't clobber existing report
                return

        if crash_counter:
            pr['CrashCounter'] = str(crash_counter)
        report_file = os.fdopen(os.open(pr_filename,
            os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0o600), 'w')
        try:
            pr.write(report_file)
        finally:
            report_file.close()

    finally:
        # resume original processing to get the default behaviour,
        # but do not trigger an AttributeError on interpreter shutdown.
        if sys:
            sys.__excepthook__(exc_type, exc_obj, exc_tb)


def install():
    '''Install the python apport hook.'''

    sys.excepthook = apport_excepthook