This file is indexed.

/usr/bin/crash-digger is in apport-retrace 2.20.9-0ubuntu7.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#!/usr/bin/python3

# Copyright (C) 2007 - 2011 Canonical Ltd.
# Author: 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, optparse, subprocess, sys, zlib, errno, shutil

import apport
from apport.crashdb import get_crashdb


#
# classes
#

class CrashDigger:
    def __init__(self, config_dir, auth_file, cache_dir, sandbox_dir,
                 apport_retrace, verbose=False, dup_db=None, dupcheck_mode=False,
                 publish_dir=None, crash_db=None):
        '''Initialize pools.'''

        self.retrace_pool = set()
        self.dupcheck_pool = set()
        self.config_dir = config_dir
        self.cache_dir = cache_dir
        self.sandbox_dir = sandbox_dir
        self.verbose = verbose
        self.auth_file = auth_file
        self.dup_db = dup_db
        self.dupcheck_mode = dupcheck_mode
        try:
            self.crashdb = get_crashdb(auth_file, name=crash_db)
        except KeyError:
            apport.error('Crash database %s does not exist', crash_db)
            sys.exit(1)
        self.lp = False
        try:
            if self.crashdb.launchpad:
                self.lp = True
        except AttributeError:
            pass
        self.apport_retrace = apport_retrace
        self.publish_dir = publish_dir
        if config_dir:
            self.releases = os.listdir(config_dir)
            self.releases.sort()
            apport.log('Available releases: %s' % str(self.releases), True)
        else:
            self.releases = None

        if self.dup_db:
            self.crashdb.init_duplicate_db(self.dup_db)
            # this verified DB integrity; make a backup now
            shutil.copy2(self.dup_db, self.dup_db + '.backup')

    def fill_pool(self):
        '''Query crash db for new IDs to process.'''

        if self.dupcheck_mode:
            self.dupcheck_pool.update(self.crashdb.get_dup_unchecked())
            apport.log('fill_pool: dup check pool now: %s' % str(self.dupcheck_pool), True)
        else:
            self.retrace_pool.update(self.crashdb.get_unretraced())
            apport.log('fill_pool: retrace pool now: %s' % str(self.retrace_pool), True)

    def retrace_next(self):
        '''Grab an ID from the retrace pool and retrace it.'''

        id = self.retrace_pool.pop()
        apport.log('retracing %s#%i (left in pool: %i)' %
                   ("LP: " if self.lp else "", id, len(self.retrace_pool)), True)

        try:
            rel = self.crashdb.get_distro_release(id)
        except ValueError:
            apport.log('could not determine release -- no DistroRelease field?', True)
            self.crashdb.mark_retraced(id)
            return
        if rel not in self.releases:
            apport.log('crash is release %s which does not have a config available, skipping' % rel, True)
            return

        argv = [self.apport_retrace, '-S', self.config_dir, '--auth',
                self.auth_file, '--timestamps']
        if self.cache_dir:
            argv += ['--cache', self.cache_dir]
        if self.sandbox_dir:
            argv += ['--sandbox-dir', self.sandbox_dir]
        if self.dup_db:
            argv += ['--duplicate-db', self.dup_db]
        if self.verbose:
            argv.append('-v')
        argv.append(str(id))

        result = subprocess.call(argv, stdout=sys.stdout, stderr=subprocess.STDOUT)
        if result != 0:
            apport.log('retracing %s#%i failed with status: %i' %
                       ("LP: " if self.lp else "", id, result), True)
            if result == 99:
                self.retrace_pool = set()
                apport.log('transient error reported; halting', True)
                return

        self.crashdb.mark_retraced(id)

    def dupcheck_next(self):
        '''Grab an ID from the dupcheck pool and process it.'''

        id = self.dupcheck_pool.pop()
        apport.log('checking %s#%i for duplicate (left in pool: %i)' %
                   ("LP: " if self.lp else "", id, len(self.dupcheck_pool)), True)

        try:
            report = self.crashdb.download(id)
        except (MemoryError, TypeError, ValueError, IOError, AssertionError, zlib.error) as e:
            if str(e) == "bug description must contain standard apport format data":
                apport.log('Cannot download report: ' + str(e), True)
                apport.error('Cannot download report %i: %s', id, str(e))
                return
            apport.log('Cannot download report: ' + str(e), True)
            apport.error('Cannot download report %i: %s', id, str(e))
            return

        res = self.crashdb.check_duplicate(id, report)
        if res:
            if res[1] is None:
                apport.log('Report is a duplicate of #%i (not fixed yet)' % res[0], True)
            elif res[1] == '':
                apport.log('Report is a duplicate of #%i (fixed in latest version)' % res[0], True)
            else:
                apport.log('Report is a duplicate of #%i (fixed in version %s)' % res, True)
        else:
            apport.log('Duplicate check negative', True)

    def run(self):
        '''Process the work pools until they are empty.'''

        self.fill_pool()
        while self.dupcheck_pool:
            self.dupcheck_next()
        while self.retrace_pool:
            self.retrace_next()

        if self.publish_dir:
            self.crashdb.duplicate_db_publish(self.publish_dir)


#
# functions
#

def parse_options():
    '''Parse command line options and return (options, args) tuple.'''

    optparser = optparse.OptionParser('%prog [options]')
    optparser.add_option('-c', '--config-dir', metavar='DIR',
                         help='Packaging system configuration base directory.')
    optparser.add_option('--sandbox-dir', metavar='DIR',
                         help='Directory for unpacked packages. Future runs will assume that any already downloaded package is also extracted to this sandbox.')
    optparser.add_option('-C', '--cache', metavar='DIR',
                         help='Cache directory for packages downloaded in the sandbox')
    optparser.add_option('-a', '--auth', dest='auth_file',
                         help='Path to a file with the crash database authentication information.')
    optparser.add_option('-l', '--lock', dest='lockfile',
                         help='Lock file; will be created and removed on successful exit, and '
                              'program immediately aborts if it already exists')
    optparser.add_option('-d', '--duplicate-db', dest='dup_db', metavar='PATH',
                         help='Path to the duplicate sqlite database (default: disabled)')
    optparser.add_option('--crash-db', metavar='NAME',
                         help='Use a different crash database than the "default" in /etc/apport/crashdb.conf')
    optparser.add_option('-D', '--dupcheck', dest='dupcheck_mode', default=False, action='store_true',
                         help='Only check duplicates for architecture independent crashes (like Python exceptions)')
    optparser.add_option('-v', '--verbose', action='store_true', default=False,
                         help='Verbose operation (also passed to apport-retrace)')
    optparser.add_option('--apport-retrace', metavar='PATH',
                         help='Path to apport-retrace script (default: directory of crash-digger or $PATH)')
    optparser.add_option('--publish-db', metavar='DIR',
                         help='After processing all reports, publish duplicate database to given directory')

    (opts, args) = optparser.parse_args()

    if not opts.config_dir and not opts.dupcheck_mode:
        apport.fatal('Error: --config-dir or --dupcheck needs to be given')
    if not opts.auth_file:
        apport.fatal('Error: -a/--auth needs to be given')

    return (opts, args)


#
# main
#

opts, args = parse_options()


# support running from tree, then fall back to $PATH
if not opts.apport_retrace:
    opts.apport_retrace = os.path.join(os.path.dirname(sys.argv[0]), 'apport-retrace')
    if not os.access(opts.apport_retrace, os.X_OK):
        opts.apport_retrace = 'apport-retrace'

if opts.lockfile:
    try:
        f = os.open(opts.lockfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o666)
        os.write(f, ("%u\n" % os.getpid()).encode())
        os.close(f)
    except OSError as e:
        if e.errno == errno.EEXIST:
            sys.exit(0)
        else:
            raise

try:
    CrashDigger(opts.config_dir, opts.auth_file, opts.cache, opts.sandbox_dir,
                opts.apport_retrace, opts.verbose, opts.dup_db,
                opts.dupcheck_mode, opts.publish_db, opts.crash_db).run()
except SystemExit as exit:
    if exit.code == 99:
        pass  # fall through lock cleanup
    else:
        raise

if opts.lockfile:
    os.unlink(opts.lockfile)