Merge lp:~james-page/ubuntu/raring/apport/cloud-archive into lp:ubuntu/raring/apport

Proposed by James Page
Status: Superseded
Proposed branch: lp:~james-page/ubuntu/raring/apport/cloud-archive
Merge into: lp:ubuntu/raring/apport
Diff against target: 140367 lines (+139168/-0) (has conflicts)
226 files modified
.bzr-builddeb/default.conf (+2/-0)
.bzrignore (+9/-0)
AUTHORS (+34/-0)
COPYING (+339/-0)
NEWS (+1824/-0)
README (+87/-0)
TODO (+21/-0)
apport/REThread.py (+65/-0)
apport/__init__.py (+73/-0)
apport/com.ubuntu.apport.policy.in (+34/-0)
apport/crashdb.py (+856/-0)
apport/crashdb_impl/debian.py (+113/-0)
apport/crashdb_impl/launchpad.py (+1993/-0)
apport/crashdb_impl/memory.py (+300/-0)
apport/fileutils.py (+376/-0)
apport/hookutils.py (+900/-0)
apport/packaging.py (+237/-0)
apport/report.py (+1530/-0)
apport/sandboxutils.py (+212/-0)
apport/ui.py (+1560/-0)
apport_python_hook.py (+197/-0)
backends/packaging-apt-dpkg.py (+927/-0)
bin/apport-bug (+93/-0)
bin/apport-cli (+368/-0)
bin/apport-retrace (+412/-0)
bin/apport-unpack (+66/-0)
bin/apport-valgrind (+174/-0)
bin/crash-digger (+217/-0)
bin/dupdb-admin (+96/-0)
data/apport (+461/-0)
data/apport-checkreports (+40/-0)
data/apportcheckresume (+91/-0)
data/dump_acpi_tables.py (+54/-0)
data/gcc_ice_hook (+35/-0)
data/general-hooks/automatix.py (+24/-0)
data/general-hooks/cloud_archive.py (+21/-0)
data/general-hooks/generic.py (+94/-0)
data/general-hooks/parse_segv.py (+376/-0)
data/general-hooks/ubuntu-gnome.py (+31/-0)
data/general-hooks/ubuntu.py (+477/-0)
data/icons/scalable/apps/apport.svg (+444/-0)
data/is-enabled (+19/-0)
data/java_uncaught_exception (+92/-0)
data/kernel_crashdump (+61/-0)
data/kernel_oops (+39/-0)
data/package-hooks/source_apport.py (+20/-0)
data/package-hooks/source_debian-installer.py (+59/-0)
data/package-hooks/source_linux-nexus7.py (+25/-0)
data/package-hooks/source_linux.py (+113/-0)
data/package-hooks/source_ubiquity.py (+138/-0)
data/package_hook (+65/-0)
data/recoverable_problem (+47/-0)
data/root_info_wrapper (+3/-0)
data/unkillable_shutdown (+108/-0)
debhelper/apport.pm (+10/-0)
debhelper/dh_apport (+83/-0)
debian/apport-gtk.install (+2/-0)
debian/apport-kde.install (+9/-0)
debian/apport-retrace.install (+5/-0)
debian/apport-valgrind.install (+2/-0)
debian/apport.install (+33/-0)
debian/apport.links (+4/-0)
debian/apport.logrotate (+9/-0)
debian/apport.maintscript (+1/-0)
debian/apport.postinst (+10/-0)
debian/apport.upstart (+50/-0)
debian/changelog (+7471/-0)
debian/clean (+2/-0)
debian/compat (+1/-0)
debian/control (+223/-0)
debian/copyright (+15/-0)
debian/dh-apport.install (+2/-0)
debian/dh-apport.manpages (+1/-0)
debian/python-apport.install (+2/-0)
debian/python-problem-report.install (+1/-0)
debian/python3-apport.install (+2/-0)
debian/python3-problem-report.install (+1/-0)
debian/rules (+39/-0)
debian/source/format (+1/-0)
debian/tests/control (+3/-0)
debian/tests/upstream-system (+16/-0)
debian/watch (+2/-0)
doc/crashdb-conf.txt (+149/-0)
doc/data-format.tex (+301/-0)
doc/package-hooks.txt (+158/-0)
doc/symptoms.txt (+73/-0)
etc/apport/blacklist.d/README.blacklist (+4/-0)
etc/apport/blacklist.d/apport (+5/-0)
etc/apport/crashdb.conf (+43/-0)
etc/bash_completion.d/apport_completion (+268/-0)
etc/cron.daily/apport (+5/-0)
etc/default/apport (+4/-0)
etc/init.d/apport (+110/-0)
gtk/apport-gtk (+590/-0)
gtk/apport-gtk-mime.desktop.in (+12/-0)
gtk/apport-gtk.desktop.in (+12/-0)
gtk/apport-gtk.ui (+597/-0)
java/README (+13/-0)
java/com/ubuntu/apport/ApportUncaughtExceptionHandler.java (+108/-0)
java/crash.java (+8/-0)
kde/apport-kde (+522/-0)
kde/apport-kde-mime.desktop.in (+12/-0)
kde/apport-kde-mimelnk.desktop.in (+9/-0)
kde/apport-kde.desktop.in (+11/-0)
kde/bugreport.ui (+177/-0)
kde/choices.ui (+95/-0)
kde/error.ui (+136/-0)
kde/progress.ui (+115/-0)
kde/userpass.ui (+127/-0)
man/apport-bug.1 (+129/-0)
man/apport-cli.1 (+150/-0)
man/apport-retrace.1 (+190/-0)
man/apport-unpack.1 (+32/-0)
man/apport-valgrind.1 (+125/-0)
man/dupdb-admin.1 (+68/-0)
pm-utils/sleep.d/000record-status (+16/-0)
po/ace.po (+1028/-0)
po/af.po (+1028/-0)
po/am.po (+1028/-0)
po/an.po (+1028/-0)
po/apport.pot (+843/-0)
po/ar.po (+1181/-0)
po/ast.po (+1235/-0)
po/be.po (+1259/-0)
po/bg.po (+1180/-0)
po/bn.po (+1046/-0)
po/br.po (+1028/-0)
po/bs.po (+1061/-0)
po/ca.po (+1299/-0)
po/ca@valencia.po (+1088/-0)
po/cs.po (+1241/-0)
po/cv.po (+1028/-0)
po/da.po (+1230/-0)
po/de.po (+1349/-0)
po/el.po (+1304/-0)
po/en_AU.po (+1292/-0)
po/en_CA.po (+1269/-0)
po/en_GB.po (+1292/-0)
po/eo.po (+1241/-0)
po/es.po (+1324/-0)
po/et.po (+1215/-0)
po/eu.po (+1213/-0)
po/fa.po (+1028/-0)
po/fi.po (+1274/-0)
po/fr.po (+1279/-0)
po/ga.po (+1028/-0)
po/gd.po (+1028/-0)
po/gl.po (+1321/-0)
po/gu.po (+1072/-0)
po/he.po (+1190/-0)
po/hi.po (+1069/-0)
po/hr.po (+1185/-0)
po/hu.po (+1298/-0)
po/hy.po (+1028/-0)
po/id.po (+1217/-0)
po/is.po (+1163/-0)
po/it.po (+1292/-0)
po/ja.po (+1170/-0)
po/kab.po (+1028/-0)
po/kk.po (+1028/-0)
po/km.po (+1146/-0)
po/kn.po (+1096/-0)
po/ko.po (+1150/-0)
po/ku.po (+1116/-0)
po/lo.po (+1028/-0)
po/lt.po (+1283/-0)
po/lv.po (+1047/-0)
po/mk.po (+1028/-0)
po/ml.po (+1028/-0)
po/ms.po (+1182/-0)
po/my.po (+1043/-0)
po/nb.po (+1207/-0)
po/nds.po (+1066/-0)
po/ne.po (+1040/-0)
po/nl.po (+1243/-0)
po/oc.po (+1312/-0)
po/pl.po (+1297/-0)
po/pt.po (+1218/-0)
po/pt_BR.po (+1316/-0)
po/ro.po (+1243/-0)
po/ru.po (+1264/-0)
po/sc.po (+1028/-0)
po/se.po (+1028/-0)
po/shn.po (+1028/-0)
po/si.po (+1032/-0)
po/sk.po (+1196/-0)
po/sl.po (+1266/-0)
po/sq.po (+1233/-0)
po/sr.po (+1310/-0)
po/sv.po (+1292/-0)
po/ta.po (+1062/-0)
po/te.po (+1038/-0)
po/th.po (+1067/-0)
po/tr.po (+1269/-0)
po/ug.po (+1179/-0)
po/uk.po (+1207/-0)
po/uz.po (+1028/-0)
po/vi.po (+1196/-0)
po/zh_CN.po (+1153/-0)
po/zh_HK.po (+1032/-0)
po/zh_TW.po (+1167/-0)
problem_report.py (+634/-0)
setup.py (+129/-0)
test/run (+128/-0)
test/test_apport_unpack.py (+116/-0)
test/test_apport_valgrind.py (+154/-0)
test/test_backend_apt_dpkg.py (+781/-0)
test/test_crash_digger.py (+237/-0)
test/test_crashdb.py (+694/-0)
test/test_fileutils.py (+342/-0)
test/test_hooks.py (+327/-0)
test/test_hookutils.py (+453/-0)
test/test_java_crashes.py (+92/-0)
test/test_packaging.py (+16/-0)
test/test_parse_segv.py (+544/-0)
test/test_problem_report.py (+988/-0)
test/test_python_crashes.py (+398/-0)
test/test_recoverable_problem.py (+76/-0)
test/test_report.py (+2120/-0)
test/test_rethread.py (+88/-0)
test/test_signal_crashes.py (+639/-0)
test/test_ui.py (+2106/-0)
test/test_ui_gtk.py (+863/-0)
test/test_ui_kde.py (+607/-0)
use-local (+7/-0)
xdg-mime/apport.xml (+11/-0)
Conflict adding file AUTHORS.  Moved existing file to AUTHORS.moved.
Conflict adding file COPYING.  Moved existing file to COPYING.moved.
Conflict adding file NEWS.  Moved existing file to NEWS.moved.
Conflict adding file README.  Moved existing file to README.moved.
Conflict adding file TODO.  Moved existing file to TODO.moved.
Conflict adding file apport.  Moved existing file to apport.moved.
Conflict adding file apport_python_hook.py.  Moved existing file to apport_python_hook.py.moved.
Conflict adding file backends.  Moved existing file to backends.moved.
Conflict adding file bin.  Moved existing file to bin.moved.
Conflict adding file data.  Moved existing file to data.moved.
Conflict adding file debhelper.  Moved existing file to debhelper.moved.
Conflict adding file debian.  Moved existing file to debian.moved.
Conflict adding file doc.  Moved existing file to doc.moved.
Conflict adding file etc.  Moved existing file to etc.moved.
Conflict adding file gtk.  Moved existing file to gtk.moved.
Conflict adding file java.  Moved existing file to java.moved.
Conflict adding file kde.  Moved existing file to kde.moved.
Conflict adding file man.  Moved existing file to man.moved.
Conflict adding file pm-utils.  Moved existing file to pm-utils.moved.
Conflict adding file po.  Moved existing file to po.moved.
Conflict adding file problem_report.py.  Moved existing file to problem_report.py.moved.
Conflict adding file setup.py.  Moved existing file to setup.py.moved.
Conflict adding file test.  Moved existing file to test.moved.
Conflict adding file use-local.  Moved existing file to use-local.moved.
Conflict adding file xdg-mime.  Moved existing file to xdg-mime.moved.
To merge this branch: bzr merge lp:~james-page/ubuntu/raring/apport/cloud-archive
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+158375@code.launchpad.net

This proposal has been superseded by a proposal from 2013-04-11.

To post a comment you must log in.
2182. By James Page

Move crashdb configuration for cloud-archive into cloud_archive hook

2183. By James Page

Use apport.packaging to determine version and origin of cloud_archive packages

2184. By James Page

Update changelog to reflect changes

Unmerged revisions

2184. By James Page

Update changelog to reflect changes

2183. By James Page

Use apport.packaging to determine version and origin of cloud_archive packages

2182. By James Page

Move crashdb configuration for cloud-archive into cloud_archive hook

2181. By James Page

etc/apport/crashdb.conf,data/general-hooks/cloud_archive.py: Send
reports for packages from the Ubuntu Cloud Archive to the cloud-archive
project on Launchpad.

2180. By Martin Pitt

releasing version 2.9.2-0ubuntu6

2179. By Martin Pitt

etc/apport/crashdb.conf: Disable Launchpad crash/kernel reports for the
raring release. Only report to http://errors.ubuntu.com from now on.

2178. By Martin Pitt

releasing version 2.9.2-0ubuntu5

2177. By Martin Pitt

* Merge from trunk:
  - data/gcc_ice_hook: Fix crash with source files that have non-UTF8 data.
    (LP: #1045283)

2176. By Martin Pitt

data/package-hooks/source_ubiquity.py: Fix crash on non-UTF-8 data in log
files.

2175. By Martin Pitt

releasing version 2.9.2-0ubuntu4

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory '.bzr-builddeb'
=== added file '.bzr-builddeb/default.conf'
--- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
+++ .bzr-builddeb/default.conf 2013-04-11 13:56:24 +0000
@@ -0,0 +1,2 @@
1[BUILDDEB]
2merge = True
03
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2013-04-11 13:56:24 +0000
@@ -0,0 +1,9 @@
1apport/packaging_impl.py
2debhelper/dh_apport.1
3doc/*.aux
4doc/*.log
5doc/*.pdf
6doc/*.toc
7build
8dist
9MANIFEST
010
=== added file 'AUTHORS'
--- AUTHORS 1970-01-01 00:00:00 +0000
+++ AUTHORS 2013-04-11 13:56:24 +0000
@@ -0,0 +1,34 @@
1Copyright:
2---------
3General:
4 Copyright (C) 2006 - 2011 Canonical Ltd.
5
6backends/packaging_rpm.py:
7 Copyright (C) 2007 Red Hat Inc.
8
9Authors and Contributors:
10-------------------------
11Martin Pitt <martin.pitt@ubuntu.com>:
12 Lead developer, design, backend, GTK frontend development,
13 maintenance of other frontends
14
15Michael Hofmann <mh21@piware.de>:
16 Creation of Qt4 and CLI frontends
17
18Richard A. Johnson <nixternal@ubuntu.com>:
19 Changed Qt4 frontend to KDE frontend
20
21Robert Collins <robert@ubuntu.com>:
22 Python crash hook
23
24Will Woods <wwoods@redhat.com>:
25 RPM packaging backend
26
27Matt Zimmerman <mdz@canonical.com>:
28 Convenience function library for hooks (apport/hookutils.py)
29
30Troy James Sobotka <troy.sobotka@gmail.com>:
31 Apport icon (apport/apport.svg)
32
33Kees Cook <kees.cook@canonical.com>:
34 Various fixes, additional GDB output, SEGV parser.
035
=== renamed file 'AUTHORS' => 'AUTHORS.moved'
=== added file 'COPYING'
--- COPYING 1970-01-01 00:00:00 +0000
+++ COPYING 2013-04-11 13:56:24 +0000
@@ -0,0 +1,339 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12freedom to share and change it. By contrast, the GNU General Public
13License is intended to guarantee your freedom to share and change free
14software--to make sure the software is free for all its users. This
15General Public License applies to most of the Free Software
16Foundation's software and to any other program whose authors commit to
17using it. (Some other Free Software Foundation software is covered by
18the GNU Lesser General Public License instead.) You can apply it to
19your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it
25if you want it, that you can change the software or use pieces of it
26in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29anyone to deny you these rights or to ask you to surrender the rights.
30These restrictions translate to certain responsibilities for you if you
31distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that
35you have. You must make sure that they, too, receive or can get the
36source code. And you must show them these terms so they know their
37rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40(2) offer you this license which gives you legal permission to copy,
41distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44that everyone understands that there is no warranty for this free
45software. If the software is modified by someone else and passed on, we
46want its recipients to know that what they have is not the original, so
47that any problems introduced by others will not reflect on the original
48authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51patents. We wish to avoid the danger that redistributors of a free
52program will individually obtain patent licenses, in effect making the
53program proprietary. To prevent this, we have made it clear that any
54patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63a notice placed by the copyright holder saying it may be distributed
64under the terms of this General Public License. The "Program", below,
65refers to any such program or work, and a "work based on the Program"
66means either the Program or any derivative work under copyright law:
67that is to say, a work containing the Program or a portion of it,
68either verbatim or with modifications and/or translated into another
69language. (Hereinafter, translation is included without limitation in
70the term "modification".) Each licensee is addressed as "you".
71
72Activities other than copying, distribution and modification are not
73covered by this License; they are outside its scope. The act of
74running the Program is not restricted, and the output from the Program
75is covered only if its contents constitute a work based on the
76Program (independent of having been made by running the Program).
77Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80source code as you receive it, in any medium, provided that you
81conspicuously and appropriately publish on each copy an appropriate
82copyright notice and disclaimer of warranty; keep intact all the
83notices that refer to this License and to the absence of any warranty;
84and give any other recipients of the Program a copy of this License
85along with the Program.
86
87You may charge a fee for the physical act of transferring a copy, and
88you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91of it, thus forming a work based on the Program, and copy and
92distribute such modifications or work under the terms of Section 1
93above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114These requirements apply to the modified work as a whole. If
115identifiable sections of that work are not derived from the Program,
116and can be reasonably considered independent and separate works in
117themselves, then this License, and its terms, do not apply to those
118sections when you distribute them as separate works. But when you
119distribute the same sections as part of a whole which is a work based
120on the Program, the distribution of the whole must be on the terms of
121this License, whose permissions for other licensees extend to the
122entire whole, and thus to each and every part regardless of who wrote it.
123
124Thus, it is not the intent of this section to claim rights or contest
125your rights to work written entirely by you; rather, the intent is to
126exercise the right to control the distribution of derivative or
127collective works based on the Program.
128
129In addition, mere aggregation of another work not based on the Program
130with the Program (or with a work based on the Program) on a volume of
131a storage or distribution medium does not bring the other work under
132the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135under Section 2) in object code or executable form under the terms of
136Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155The source code for a work means the preferred form of the work for
156making modifications to it. For an executable work, complete source
157code means all the source code for all modules it contains, plus any
158associated interface definition files, plus the scripts used to
159control compilation and installation of the executable. However, as a
160special exception, the source code distributed need not include
161anything that is normally distributed (in either source or binary
162form) with the major components (compiler, kernel, and so on) of the
163operating system on which the executable runs, unless that component
164itself accompanies the executable.
165
166If distribution of executable or object code is made by offering
167access to copy from a designated place, then offering equivalent
168access to copy the source code from the same place counts as
169distribution of the source code, even though third parties are not
170compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173except as expressly provided under this License. Any attempt
174otherwise to copy, modify, sublicense or distribute the Program is
175void, and will automatically terminate your rights under this License.
176However, parties who have received copies, or rights, from you under
177this License will not have their licenses terminated so long as such
178parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181signed it. However, nothing else grants you permission to modify or
182distribute the Program or its derivative works. These actions are
183prohibited by law if you do not accept this License. Therefore, by
184modifying or distributing the Program (or any work based on the
185Program), you indicate your acceptance of this License to do so, and
186all its terms and conditions for copying, distributing or modifying
187the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190Program), the recipient automatically receives a license from the
191original licensor to copy, distribute or modify the Program subject to
192these terms and conditions. You may not impose any further
193restrictions on the recipients' exercise of the rights granted herein.
194You are not responsible for enforcing compliance by third parties to
195this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198infringement or for any other reason (not limited to patent issues),
199conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot
202distribute so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you
204may not distribute the Program at all. For example, if a patent
205license would not permit royalty-free redistribution of the Program by
206all those who receive copies directly or indirectly through you, then
207the only way you could satisfy both it and this License would be to
208refrain entirely from distribution of the Program.
209
210If any portion of this section is held invalid or unenforceable under
211any particular circumstance, the balance of the section is intended to
212apply and the section as a whole is intended to apply in other
213circumstances.
214
215It is not the purpose of this section to induce you to infringe any
216patents or other property right claims or to contest validity of any
217such claims; this section has the sole purpose of protecting the
218integrity of the free software distribution system, which is
219implemented by public license practices. Many people have made
220generous contributions to the wide range of software distributed
221through that system in reliance on consistent application of that
222system; it is up to the author/donor to decide if he or she is willing
223to distribute software through any other system and a licensee cannot
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns.
241
242Each version is given a distinguishing version number. If the Program
243specifies a version number of this License which applies to it and "any
244later version", you have the option of following the terms and conditions
245either of that version or of any later version published by the Free
246Software Foundation. If the Program does not specify a version number of
247this License, you may choose any version ever published by the Free Software
248Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251programs whose distribution conditions are different, write to the author
252to ask for permission. For software which is copyrighted by the Free
253Software Foundation, write to the Free Software Foundation; we sometimes
254make exceptions for this. Our decision will be guided by the two goals
255of preserving the free status of all derivatives of our free software and
256of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it
286free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) <year> <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License along
307 with this program; if not, write to the Free Software Foundation, Inc.,
308 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
310Also add information on how to contact you by electronic and paper mail.
311
312If the program is interactive, make it output a short notice like this
313when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320The hypothetical commands `show w' and `show c' should show the appropriate
321parts of the General Public License. Of course, the commands you use may
322be called something other than `show w' and `show c'; they could even be
323mouse-clicks or menu items--whatever suits your program.
324
325You should also get your employer (if you work as a programmer) or your
326school, if any, to sign a "copyright disclaimer" for the program, if
327necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335This General Public License does not permit incorporating your program into
336proprietary programs. If your program is a subroutine library, you may
337consider it more useful to permit linking proprietary applications with the
338library. If this is what you want to do, use the GNU Lesser General
339Public License instead of this License.
0340
=== renamed file 'COPYING' => 'COPYING.moved'
=== added file 'NEWS'
--- NEWS 1970-01-01 00:00:00 +0000
+++ NEWS 2013-04-11 13:56:24 +0000
@@ -0,0 +1,1824 @@
1This file summarizes the major and interesting changes for each release. For a
2detailled list of changes, please see ChangeLog.
3
42.9.3 (UNRELEASED):
5-------------------
6Bug fixes:
7 * hookutils.attach_conffiles(): Fix IOError crash on inaccessible conffiles;
8 mark them as '[inaccessible: <reason>]' instead. (LP: #1154536)
9 * hookutils.in_session_of_problem(): Fix crash when the current locale is
10 invalid. (LP: #1154896)
11 * data/gcc_ice_hook: Fix crash with source files that have non-UTF8 data.
12 (LP: #1045283)
13
142.9.2 (2013-03-19):
15-------------------
16Improvements:
17 * report.py, add_package_info(): Add "[origin: unknown]" tag to
18 Package/Dependencies fields for non-distro package whose origin cannot be
19 determined. (LP: #1148116)
20 * Adjust kernel_crashdump to the format kdump-tools produces. Thanks Louis
21 Bouchard.
22
23Bug fixes:
24 * Write core dumps on SIGQUIT if ulimit allows. Thanks Graeme Hewson.
25 (LP: #1153662)
26
272.9.1 (2013-03-07):
28-------------------
29Improvements:
30 * launchpad.py: Add support for filing bugs as private. Document this in
31 doc/crashdb-conf.txt. (LP: #1144647)
32
33Bug fixes:
34 * test_signal_crashes.py: Fix test_crash_apport() when being run under
35 LD_PRELOAD.
36 * Fix crash in error() and warning() if there is no sys.stderr. (LP: #1012445)
37 * Fix Turkish translation to add missing keyboard accelerator. (LP: #648750)
38 * fileutils.py, find_package_desktopfile(): Restrict to autostart and
39 application .desktop files. (LP: #1147528)
40 * apt/dpkg get_modified_files(): Fix crash when encountering non-ASCII file
41 names in an ASCII locale. (LP: #1044014)
42
432.9 (2013-03-01):
44-----------------
45Improvements:
46 * fileutils.py, shared_libraries(): Return a "name → path" dict instead of
47 just a set of names. Thanks Kyle Nitzsche.
48 * sandboxutils.py: Support unpackaged executables, i. e. reports which do not
49 have "Package" and "Dependencies" fields. For those, get required libraries
50 from "ProcMaps". Thanks Kyle Nitzsche.
51
52Bug fixes:
53 * Add "com.ubuntu.apport.apport-gtk-root" polkit action for running apport-gtk
54 through pkexec to access system crash reports. Thanks Brian Murray.
55 * ui.py: Check $PKEXEC_UID in addition to $SUDO_UID for opening a browser.
56 * apport/report.py: report if LD_PRELOAD and LD_LIBRARY_PATH are set. Thanks
57 James Hunt.
58 * apport-valgrind: Cleanly exit on keyboard interrupts. Thanks Kyle Nitzsche.
59 * debian.py: Fix "string payload expected" crash when building the report.
60 Thanks Dmitry Shachnev. (Debian #698010)
61 * Move shared_libraries() and links_with_shared_library() from hookutils into
62 fileutils, so that we can use it from apport-valgrind. Thanks to Kyle
63 Nitzsche for the initial patch.
64 * fileutils.shared_libraries(): Filter out virtual "linux-vdso" from result.
65 Thanks Kyle Nitzsche.
66 * apport-valgrind: Fix path to debug symbols in the sandbox.
67 * ui.py, get_desktop_entry(): Fix for Python 2.
68
692.8 (2013-01-08):
70-----------------
71Improvements:
72 * Factor out sandbox management functions from apport-retrace into
73 apport/sandboxutils.py, so that other programs can re-use the API easily.
74 Thanks to Kyle Nitzsche for the initial work on this.
75 * Generate a crash signature for kernel OOPSes.
76 * Add "apport-valgrind" tool to produce valgrind reports in a temporary
77 sandbox with debug symbols (similar to apport-retrace). Thanks Alex Chiang
78 and Kyle Nitzsche!
79
80Bug fixes:
81 * Fix StacktraceAddressSignature generation on ARM. (LP: #1089778)
82 * debian.py: Fix TypeError crash in payload generation. Thanks Ritesh Raj
83 Sarraf.
84 * apport_python_hook.py: Update "ExecutableTimestamp" field when mangling
85 "ExecutablePath". (LP: #1077253)
86
872.7 (2012-12-10):
88-----------------
89Improvements:
90 * packaging.py, get_file_package(): Add optional "release" and "arch"
91 arguments for getting a file's package for a foreign release or
92 architecture. Implement this for apt/dpkg backend.
93 * packaging.py, install_packages(): Add optional "architecture" argument for
94 creating a sandbox for a foreign architecture. Implement this for apt/dpkg
95 backend.
96 * When a report's architecture does not match the system architecture, try to
97 use gdb-multiarch (if available, as packaged on Debian/Ubuntu), and set
98 architecture and gnutarget accordingly in gdb. This supports x86, x86_64,
99 and ARM for now, so that reports from all these architectures can be
100 retraced on an x86_84 machine. (LP: #1044437)
101 * launchpad.py: Add "architecture" option to process reports for a foreign
102 architecture.
103 * Add exceptions from package hooks to new HookError_<filename> report field,
104 to make them more visible. Until now they were only written to stderr.
105 (LP: #1086309)
106
107Bug fixes:
108 * Fix test_find_package_desktopfile test to not consider packages with only
109 one "NoDisplay=true" .desktop file for the "has one desktop file" test.
110 * report.py, mark_ignore(): Use home directory of actual effective user, not
111 of $HOME. Fixes ignore file when using through sudo.
112 * apport-cli: Fix showing of prompt. Thanks Dmitry Shachnev!
113 * fileutils.py, mark_report_upload(): Do not try to remove the .uploaded file,
114 as this is not owned by the user.
115 * backends/packaging-apt-dpkg.py, install_packages(): Set mirror to the one in
116 the sandbox config.
117 * apportcheckresume: Fix crash if state file does not exist.
118
1192.6.3 (2012-11-30):
120-------------------
121 * test_signal_crashes.py: Fix incompatibility with Python 3.3.
122 * test_signal_crashes.py: Allow XDG_RUNTIME_DIR environment variable, as it
123 only shows whether or not it is set. (Test regression from 2.6)
124 * debian.py: Only reject reports with useless stack traces if the report
125 actually has a stack trace at all.
126 * debian.py: Fix UTF-8 string handling. Thanks Ritesh Raj Sarraf.
127 * debian.py: Fix crash on broken "Package" fields, as generated by current
128 Debian/Ubuntu dkms package.
129 * data/apport: Call fsync when writing upstart crash reports.
130 * report.py, add_gdb_info(): Handle libnih's assertion messages.
131 (LP: #997359)
132 * apport-gtk, apport-kde: Don't provide an option to restart a crashed
133 application when the crash occurred in a thread (LP: #1033902).
134 * apport-retrace: Disallow option -C without option -S. Thanks Kyle Nitzsche.
135 * fileutils.py, mark_report_upload(): Refresh the .upload stamps if a previous
136 version of the report was already uploaded, but another instance of the
137 problem happened since then. Thanks Brian Murray. (LP: #1084296)
138 * Ignore implausibly low addresses when computing StacktraceAddressSignature.
139 These are usually artifacts from gdb when not having debug symbols, and
140 having too many of them prevents proper client-side duplicate detection and
141 proper bucketing in daisy. (LP: #1084996)
142 * fileutils.py: Ignore .desktop files with NoDisplay=true. (LP: #1048524)
143
1442.6.2 (2012-11-06):
145-------------------
146 * problem_report.py: Fix UnicodeDecodeError crash under Python 2 when the
147 report has an unicode field with an unprintable ASCII character < 20.
148 * debian.py: Fix calling of parent accepts() method and return value. Thanks
149 Ritesh Raj Sarraf.
150 * bin/apport-retrace: Fix crash when not using --sandbox mode.
151 * report.py, add_proc_info(): Throw correct exception if the executable path
152 does not exist, to provide a more appropriate error message. (LP: #1065129)
153 * report.py, add_gdb_info(): Check __glib_assert_msg for assertion messages,
154 too.
155 * REThread.py: Fix for Python 3.3.
156
1572.6.1 (2012-10-01):
158-------------------
159 * setup.py: Specify "-source 1.5" javac option as well, to avoid build failure
160 with OpenJDK 7.
161
1622.6 (2012-10-01):
163-----------------
164 * setup.py: Build java module with "-target 1.5" option, so that you can run
165 it with OpenJDK 6 even if you build with OpenJDK 7.
166 * report.py, add_proc_info(): Show if $XDG_RUNTIME_DIR is set.
167 * Add apport/crashdb_impl/debian.py: Initial crash database implementation for
168 the Debian BTS. Add configuration for it to etc/apport/crashdb.conf. Thanks
169 Ritesh Raj Sarraf!
170 * test_python_crashes.py: Robustify "$PYTHONPATH in ProcEnviron" check.
171
1722.5.3 (2012-09-28):
173-------------------
174 * data/apportcheckresume: Open report file in binary mode. (LP: #1040353)
175 * packaging-apt-dpkg.py: When throwing ValueErrors, show the non-existing
176 package name. This makes it easier to debug such crashes.
177 * launchpad.py: Replace characters from tags which are not allowed by
178 Launchpad with '.' (LP: #1029479)
179 * launchpad.py: Temporarily disable filing private bugs in the test suite, to
180 work around the SSLHandshakeError error when downloading private attachments
181 from staging.
182 * hookutils.py, attach_root_command_outputs(): Ignore IOError crash about
183 nonexisting files, which can happen if the user dismisses authorization.
184 (LP: #1051222)
185 * report.py, search_bug_patterns(): Fix bug patterns containing non-ASCII
186 characters. Thanks Brian Murray.
187 * apport_python_hook.py: Capture $PYTHONPATH and $PYTHONHOME environment
188 variables for Python crashes. Thanks Brian Murray.
189
1902.5.2 (2012-09-17):
191-------------------
192 * test/run: Ignore root_info_wrapper with pyflakes.
193 * packaging-apt-dpkg.py: Add recommended packages to "Dependencies:" field.
194 (LP: #1014428)
195 * test_hookutils.py, test_in_session_of_problem(): Use year 2038 for a future
196 date instead of 2211, as current Python 3.2 now crashes with an
197 OverflowError on 32 bit machines with later years.
198 * Fix crash on broken .desktop files. (LP: #1039889)
199 * apport-gtk: For console program crashes, say "stopped" instead of "closed".
200 Add a subtitle label with a hint about hanging programs. Thanks Matt Price
201 and Matthew Paul Thomas!
202 * report.py: Fix crash on determination of Python module path when examining a
203 crash of "python -m ...".
204 * apport-kde: Fix crash with undefined QString under Python 3. Thanks Jonathan
205 Riddell! (LP: #1028984)
206 * launchpad.py: Add missing "Pre-release Freeze" status. Thanks Brian Murray!
207 * report.py, _check_bug_pattern(): Fix bug pattern matching against binary
208 values. Thanks Brian Murray for the original patch. (LP: #1016380)
209
2102.5.1 (2012-08-22):
211-------------------
212 * data/root_info_wrapper: Turn into a real file, a symlink can cause some
213 packaging problems.
214
2152.5 (2012-08-22):
216-------------------
217Bug fixes:
218 * test_recoverable_problem.py: Fix test for calling test runner with absolute
219 path.
220 * packaging-apt-dpkg.py: Fix crash on writing virtual_mapping.db when running
221 with --sandbox-dir and -S system or giving no --cache.
222 * REThread.py: Fix re-raising of exceptions in Python 3. Thanks Martin
223 Packman! (LP: #1024836)
224 * apport-retrace: Keep compressed CoreDump from .crash files instead of
225 uncompressing them into memory. This dramatically reduces memory usage.
226 (LP: #981155)
227
228Improvements:
229 * Add an apport.memdbg() function which prints out current memory usage if
230 APPORT_MEMDEBUG is set. Annotate apport-retrace with it.
231 * hookutils.py: Allow specifying a list of profile names when using
232 attach_mac_events(). Thanks Marc Deslauriers.
233 * hookutils.py, attach_root_command_outputs() and root_command_output(): Drop
234 usage of sudo, kdesudo, and gksu, and replace with pkexec. For
235 attach_root_command_outputs(), use a wrapper and proper .policy file which
236 explains the action and works under every environment; thus
237 attach_root_command_outputs() is preferred over root_command_output() now,
238 as it provides a better user experience.
239 * Package hooks which want to send the report to a different crash database
240 than "default" can now also give the database specification itself in the
241 "CrashDB" field, not just the DB name. With this, packages do not need to
242 ship a separate /etc/apport/crashdb.conf.d/ file. Please see
243 doc/package-hooks.txt for details. (LP: #551330)
244 * report.py, add_hooks_info(): If reporting against a package/program in /opt,
245 also search for package hooks in the corresponding /opt directory. This
246 allows such hooks to define a custom crash database and thus report bugs
247 against their own project instead of against the distribution.
248 (LP: #1020503)
249
2502.4 (2012-07-18):
251-----------------
252Improvements:
253 * apport_python_hook.py: For org.freedesktop.DBus.Error.ServiceUnknown
254 exceptions, add a 'DbusErrorAnalysis' field to the report which points out
255 whether any .service file provides the service it tried to talk to, and
256 whether the processes for those are running. This helps to determine the
257 root cause for such errors (missing dependencies, broken .service files,
258 talking to the wrong bus, etc.) (LP: #1020572)
259 * hookutils.py, attach_alsa(): Use alsa-info.sh when available. Thanks David
260 Henningson.
261 * Add new "RecoverableProblem" report type for problems which the application
262 can handle, but still wishes to notify the user and send a problem report
263 about. As an example, the application may wish to notify the user because
264 handling the error resulted in degraded functionality. The user interface
265 may fail to load items, or the action just performed may not return any
266 data. Applications call /usr/share/apport/recoverable_problem with a
267 string of arbitrary NUL-separated key/value pairs that are added to the
268 report. Thanks Evan Dandrea!
269
270Bug fixes:
271 * ui tests, test_wait_for_pid(): Fix eternal hang when running as root.
272 * testsuite: Fix ResourceWarnings when running with Python 3.
273 * test_python_crashes.py: Fix race condition in timeout test.
274 * launchpad.py: Fix setting of 'Medium' importance on duplicate checking.
275 * apport-retrace: Fix StacktraceSource generation for relative --cache paths.
276 * crashdb.py, check_duplicate(): Do not try to mark a bug as duplicate of
277 itself. This can happen when re-processing a previously retraced bug.
278 * apport-retrace: Fix UnicodeDecodeError when encountering a non-ASCII source
279 code file and running under a non-UTF-8 locale.
280
2812.3 (2012-07-09):
282-----------------
283Improvements:
284 * launchpad.py: Rework test suite to not use Launchpad's +storeblob facility
285 at all any more. It almost never works on staging and is horribly slow. Fake
286 the bug creation from a blob by manually creating the comment and
287 attachments ourselves, and just assume that storeblob works on production.
288 Also change the structure to allow running every test individually.
289 * crash-digger: Add --crash-db option to specify a non-default crash databae
290 name. (LP: #1003506)
291 * apport-gtk: Add --hanging option to specify the process ID of a hanging
292 application. If the user chooses to report this error, apport will terminate
293 the pid with SIGABRT, otherwise it will send SIGKILL. The normal core pipe
294 handler will be used to process the resulting report file, with a .hanging
295 file in /var/crash to separate these from regular crashes.
296
297Bug fixes:
298 * apport: Also treat a binary as modified if the /proc/pid/exe symlink does
299 not point to an existing file any more. (LP: #984944)
300 * Fix PEP-8 violations picked up by latest pep8 checker.
301 * ui.py: Do not ignore certain exceptions during upload which are not likely
302 to be a network error.
303 * launchpad.py: Recongize Launchpad projects for bug query and marking
304 operations. (LP: #1003506)
305 * packaging-apt-dpkg.py: Fix get_source_tree() to work with apt sandboxes.
306 * apport-retrace: Turn StacktraceSource generation back on, now that it works
307 with the current sandboxing.
308 * launchpad.py: Ensure that upload chunk size does not underrun. (LP: #1013334)
309 * apport_python_hook: Fix UnicodeEncodeError crash with Python 2 for
310 exceptions with non-ASCII characters. (LP: #972436)
311 * test_ui_kde.py: Fix occasional test failure in test_1_crash_details if the
312 application ends before the "is progress bar visible" check is done.
313
3142.2.5 (2012-06-21):
315-------------------
316 * launchpad.py: Fix str vs. bytes crash for already known bugs, take 2. (LP: #1015788)
317 * apport/ui.py, get_desktop_entry(): Disable interpolation, to correctly read
318 desktop files with % signs. (LP: #1014341)
319 * apport/ui.py: Fix rare crash if a report is already being updated in the
320 background when the UI tries to update a previous version. (LP: #949196)
321 * GTK and KDE UI tests: Avoid eternal hangs due to "this is not a distro
322 package" error messages.
323
3242.2.4 (2012-06-21):
325--------------------
326Bug fixes:
327 * test_apport_unpack.py: Fix test_unpack_python() test when running the
328 system-installed tests.
329 * data/java_uncaught_exception: Fix for Python 3.
330 * test_signal_crashes.py: Show crash reports in /var/crash/.
331 * test_crash_digger.py: Do not write crash reports of crash-digger into system
332 /var/crash, use a temporary directory.
333 * test/run: Wait for a previous xvfb server to finish before trying to start
334 one. This fixes a race condition in the KDE UI tests which often failed to
335 start up xvfb.
336 * apport-cli: Unbreak "keep" option. (LP: #1007826)
337 * launchpad.py: Fix str vs. bytes crash for already known bugs. (LP: #1015788)
338
3392.2.3 (2012-06-15):
340-------------------
341Bug fixes:
342 * test/run: Do not run pep8 and pyflakes when running against the sytem
343 installed Apport.
344 * test_backend_apt_dpkg.py: For the "are we online" check, verify that we can
345 download from http://ddebs.ubuntu.com/, not just whether we have a default
346 route. The latter is not sufficient for e. g. buildd environments which are
347 online, but are restricted by proxies or firewalls.
348 * test_report.py: Call "sync" after test script write core dumps, to ensure
349 that subsequent operations have a complete one.
350 * test_signal_crashes.py: Drop the broken and obsolete test_local_python()
351 test. Instead, add two tests which check proper logging.
352 * launchpad.py: Fix urlopen() for Python3. Thanks Steve Langasek.
353 * test/run: Run the tests under LC_MESSAGES=C, to avoid failing tests on
354 translated strings.
355
3562.2.2 (2012-06-13):
357-------------------
358Improvements:
359 * testsuite: Run with Python 3 by default. To test with Python 2, run
360 "PYTHON=python2 test/run".
361
362Bug fixes:
363 * apport: Redefine sys.std{out,err} when redirecting output, as they are None
364 in Python 3 when being called from the kernel.
365 * test/test_signal_crashes.py: Clean up unexpected core dumps on failed test
366 cases.
367 * apport-gtk: Fix crash when closing the crash dialog while the information is
368 being collected.
369 * hookutils.py, xsession_errors(): Fix crash when running under a non-UTF8 locale.
370 * data/apport: Do not use sys.stdin.fileno(), it is invalid when being called
371 from the kernel with Python 3.
372 * data/apport: When core dumps are enabled, read them from the written report
373 instead of directly from stdin (and then reading the written core file into
374 the .crash report). If the core file size is limited, we otherwise stop
375 reading the core dump from the kernel in the middle and have no (or a
376 broken) core dump to be put into the report.
377 * data/apport: Properly close the written crash report before changing its
378 permissions to be readable. This prevents having crash reporting UI from
379 looking at incomplete .crash files.
380
3812.2.1 (2012-06-11)
382------------------
383Bug fixes:
384 * apport-cli: Port to work with Python 3.
385 * setup.py: When fixing hashbang lines of installed scripts, only include the
386 major Python version.
387 * hookutils.py, read_file, attach_file(), attach_file_if_exists(): Convert
388 file contents to unicode if the contents is UTF-8, or the newly added
389 force_unicode argument is True.
390 * hooktuils, command_output(): Convert output to unicode by default, and add
391 a "decode_utf8" parameter to disable this.
392 * hookutils.py, recent_logfile(): Fix fd leak.
393 * data/apport: Do not assume that sys.stdout and sys.stderr always have a
394 name; they can be None in Python 3.
395 * data/dump_acpi_tables.py: Fix for Python 3.
396
3972.2 (2012-06-11)
398----------------
399Improvements:
400 * Clean up module imports.
401 * test/run: Run pyflakes, if available.
402 * package_hook: Add --tags option. Thanks to Brian Murray.
403 * launchpad.py: Drop the external multipartpost_handler.py (which is not
404 portable to Python 3) and replace it with using the standard email module.
405 * launchpad.py: Also work with Python 3. Deal gracefully with a missing
406 "launchpadlib" module; this is not yet available for Python 3, but not
407 required for client-side reporting.
408 * apport-kde: Port to work with Python 3.
409
410Bug fixes:
411 * apport-retrace: Fix crash when using the --procmaps option.
412 * setup.py: Update hashbang lines of installed scripts in data directory to
413 the python executable setup.py was run with, similar to what already happens
414 to scripts installed to ../bin/.
415
4162.1.1 (2012-05-30)
417------------------
418Improvements:
419 * launchpad.py: When closing a bug as a duplicate, copy some well-known tags
420 to the master bug. Thanks Brian Murray.
421 * launchpad.py: Set importance of Python crash reports to "Medium" by default,
422 similar to signal crashes. Thanks Brian Murray.
423 * hookutils.py: Add attach_default_grub() convenience function from the grub2
424 package hook so it can be used by other packages. Thanks Brian Murray.
425 * launchpad.py: Make Launchpad bug subscription user/team configurable: The
426 initial subscriber after filing a bug can be set with the
427 "initial_subscriber" crashdb option, and the team which gets subscribed
428 after retracing with "triaging_team". (LP: #980726)
429
430Bug fixes:
431 * report.py: Do not change the SourcePackage: field if the binary package is
432 not installed and does not exist. This fixes source package hooks to
433 actually work in some cases where source and binary package names overlap.
434 (part of LP: #993810)
435 * apport-gtk, apport-kde: Avoid collecting information twice in "bug update"
436 mode. This caused a crash in cases where the source package in a bug report
437 does not correspond to an installed binary package. (LP: #993810)
438
4392.1 (2012-05-18)
440----------------
441Improvements:
442 * packaging.py, install_packages(): Add permanent_rootdir flag and if set,
443 only unpack newly downloaded packages. Implement it for the apt/dpkg
444 backend. Thanks Evan Dandrea.
445 * apport-retrace: Add --sandbox-dir option for keeping a permanent sandbox
446 (unpacked packages). This provides a considerable speedup. Thanks Evan
447 Dandrea.
448 * crash-digger: Add --sandbox-dir option and pass it to apport-retrace.
449 * Fix the whole code to be PEP-8 compatible, and enforce this in test/run by
450 running the "pep8" tool.
451 * GTK UI tests: Ensure that there are no GLib/GTK warnings or criticals.
452 * Support Python 3. Everything except the launchpad crashdb backend now works
453 with both Python 2 and 3. An important change is that the load(),
454 write(), and write_mime() methods of a ProblemReport and apport.Report
455 object now require the file stream to be opened in binary mode.
456 * data/apport: Ignore a crash if the executable was modified after the process
457 started. This often happens if the package is upgraded and a long-running
458 process is not stopped before. (LP: #984944)
459 * Add test cases for apport-unpack.
460 * apport-retrace: Add information about outdated packages to the
461 "RetraceOutdatedPackages" field.
462 * ui.py: Drop python-xdg dependency, use ConfigParser to read the .desktop
463 files.
464
465Bug fixes:
466 * apport-gtk: Work around GTK crash when trying to set pixmap on an already
467 destroyed parent window. (LP: #938090)
468 * data/dump_acpi_tables.py: Fix crash on undefined variable with non-standard
469 tables. (LP: #982267)
470 * backends/packaging-apt-dpkg.py: Fix crash if a package is installed, but has
471 no candidates in apt. (LP: #980094)
472 * data/general-hooks/generic.py: Bump minimum free space requirement from 10
473 to 50 MB. 10 is not nearly enough particularly for /tmp. (LP: #979928)
474 * hookutils.py, recent_logfile(): Use a default limit of 10000 lines and call
475 "tail" instead of reading the whole file. This protects against using up all
476 memory when there are massive repeated log messages. (LP: #984256)
477 * apport-gtk: Do not assume that an icon requested for size 42 actually
478 delivers size 42; some themes do not have this available and deliver a
479 smaller one instead, causing overflows. Also, copy the image as
480 gtk_icon_theme_load_icon() returns a readonly result which we must not
481 modify. (LP: #937249)
482 * ui.py: Don't show the duplicate warning when the crash database does not
483 accept the problem type, and they are just being sent to whoopsie. Thanks
484 Evan Dandrea. (LP: #989779)
485 * report.py: Correctly escape the file path passed to gdb.
486 * apport-gtk, apport-kde: Do not show the information collection progress
487 dialog if the crash database does not accept this kind of report. In that
488 case whoopsie will upload it in the background and the dialog is not
489 necessary. (LP: #989698)
490
4912.0.1 (2012-04-10)
492------------------
493Bug fixes:
494 * test_ui_gtk.py: Disable package hooks for the tests, as they might ask for
495 sudo passwords and other interactive bits, and thus make the tests hang.
496 * test_backend_apt_dpkg.py: Fix checks for the installation of -dbgsym
497 packages. This should always happen, as the sandboxes have a ddeb apt
498 source. Only make it conditional on the system apt sources in the "use
499 system config" test.
500 * test_report.py: Sleep a bit after calling our test crash script, to ensure
501 the kernel has time to finish writing the core file.
502 * generic package hook: Also check /tmp for enough space. Thanks Brian Murray.
503 (LP: #972933)
504 * problem_report.py, write_mime(): Fix regression from version 1.95: Add a
505 value as attachment if it is bigger than 1000 bytes, not if it is bigger
506 than 100. (LP: #977882)
507
508Improvements:
509 * packaging-apt-dpkg.py: Avoid constructing and updating the apt.Cache()
510 objects multiple times, to speed up retracing. Thanks Evan Dandrea.
511 (LP: #973494)
512
5132.0 (2012-03-30)
514----------------
515This is the final 2.0 release, featuring the overhauled and simplified GUI,
516support for whoopsie-daemon, and client-side duplicate checking.
517
518Bug fixes:
519 - report.py, anonymize(): Only replace whole words, not substrings.
520 (LP: #966562)
521 - apport_python_hook.py: Fix filtering of org.freedesktop.DBus.Error.NoReply
522 exceptions. (LP: #958575)
523 - crashdb.py: When publishing the crash database, cut hash file names after
524 quoting, to avoid that the quoting causes them to become too long.
525 (LP: #968070) This also uncovered that known() did not actually find any
526 signature which contained an URL-quoted character, therefore breaking
527 client-side duplicate checking in a lot of cases. Double-quote the file name
528 now, as urlopen() unquotes it.
529 - Add a new crash database option "problem_types" and a CrashDatabase method
530 "accepts(report)". This can be used to stop uploading particular problem
531 report types to that database. E. g. a distribution might decide to not get
532 "Crash" reports any more after release. Document the new option in
533 doc/crashdb-conf.txt.
534 - ui.py: Do not upload a report if the crash database does not accept the
535 report's type. This behaviour is not really correct, but necessary as long
536 as we only support a single crashdb and have whoopsie hardcoded. Once we
537 have multiple crash dbs, we need to not even present the data if none of the
538 DBs wants the report. See LP #957177 for details. (LP: #968121)
539 - ui.py: Do not short-circuit information collection if report already has a
540 "DistroRelease" field, as the GUIs add that in some cases. Check for
541 "Dependencies" instead. This fixes information collection for kernel
542 problems (which now has a full GTK GUI test case). (LP: #968488)
543
5441.95 (2012-03-22)
545-----------------
546Bug fixes:
547 - ui.py: Ensure that the report file is readable by the crash reporting daemon
548 after running through collect_info(). Thanks Evan Dandrea.
549 - apport-gtk, apport-kde: Set the window title to the distribution name, as
550 per http://wiki.ubuntu.com/ErrorTracker#error . Thanks Evan Dandrea.
551 (LP: #948015)
552 - test/run: Ignore obsolete packages on the system, to avoid breaking the GUI
553 tests due to them.
554 - apport-gtk, apport-kde: When reporting a "system crash", don't say "... of
555 this program version", but "...of this type", as we don't show a program
556 version in the initial dialog (https://wiki.ubuntu.com/ErrorTracker#error)
557 (LP: #961065)
558 - problem_report.py, write_mime(): Do not put a key inline if it is bigger
559 than 1 kB, to guard against very long lines. (LP: #957326)
560 - etc/cron.daily/apport: Do not remove whoopsie's *.upload* stamps every day,
561 only if they are older than a week. whoopsie comes with its own cron job
562 which deals with them. Thanks Steve Langasek. (LP: #957102)
563 - report.py, mark_ignore(): Fix crash if executable went away underneath us.
564 (LP: #961410)
565 - apport-gtk: Do not compare current continue button label against a
566 translated string. Instead just remember whether or not we can restart the
567 application. (LP: #960439)
568 - hookutils.py, command_output(): Add option to keep the locale instead of
569 disabling it.
570 - hookutils.py, command_output(): Actually make the "input" parameter work,
571 instead of causing an eternal hang. Add tests for all possible modes of
572 operation.
573 - hooktuils.py: Change root_command_output() and attach_root_command_outputs()
574 to disable translated messages (LC_MESSAGES=C) only as part of the command
575 to be run, not already for the root prefix command. This will keep the
576 latter (gksu, kdesudo, etc.) translated. (LP: #961659)
577 - apport-gtk: Cut off text values after 4000 characters, as Gtk's TreeView
578 does not get along well with huge values. KDE's copes fine, so continue to
579 display the complete value there. (LP: #957062)
580 - apport-gtk: Make details window resizable in bug reporting mode.
581 - crashdb.py, known(): Check the address signature duplicate database if the
582 symbolic signature exists, but did not find any result. (LP: #103083)
583 - ui.py: Run anonymization after checking for duplicates, to prevent host or
584 user names which look like hex numbers to corrupt the stack trace.
585 (LP: #953104)
586 - apport-gtk: Require an application to both have TERM and SHELL in its
587 environment to consider it a command line application that was started by
588 the user. (LP: #962130)
589 - backends/packaging-apt-dpkg.py, _check_files_md5(): Fix double encoding,
590 which caused UnicodeDecodeErrors on non-ASCII characters in an md5sum file.
591 (LP: #953682)
592 - apport-kde, apport-gtk: Only show "Relaunch" if the report has a
593 ProcCmdline, otherwise we cannot restart it. (LP: #956173)
594
595Improvements:
596 - hookutils.py, attach_alsa(): Add the full "pacmd list" output instead of
597 just sinks and sources. Thanks David Henningsson.
598 - apport-gtk, apport-kde: Show the ExecutablePath while we're collecting data
599 for the crash report. Thanks Evan Dandrea. (LP: #938707).
600
6011.94.1 (2012-03-07)
602-------------------
603Bug fixes:
604 - test_ui_kde.py: Re-enable inadvertently disabled "bug report for uninstalled
605 package" test.
606 - ui.py, collect_info(): Do not assume that reports have a "ProblemType"
607 field. This is not the case when updating a bug. (LP: #947519)
608 - apport-cli: Consistently handle unicode vs. byte arrays. (LP: #946207)
609 - report.py, anonymize(): Fix crash when the hostname or user name contain
610 non-ASCII characters. (LP: #945230)
611 - packaging-apt-dpkg.py: Fix UnicodeDecodeError on unexpected md5sum output.
612 (LP: #921037)
613 - apport-gtk: Fix handling of non-ASCII strings in message dialogs.
614 (LP: #865394)
615
6161.94 (2012-03-02)
617-----------------
618Bug fixes:
619 - apport: Set the group of written reports to "whoopsie" if that group exists.
620 - Fix tests to run properly against the system-installed modules and binaries.
621 - test/run: Run under LC_MESSAGES=C to avoid test failures due to translated
622 strings.
623 - general-hooks/generic.py: Also attach xsession-errors for programs that link
624 to libgtk-3.
625 - launchpad.py: Properly handle "Expired" status, to avoid marking new bugs as
626 duplicates of expired ones. (LP: #941854)
627 - apport: Fix crash if the "whoopsie" group does not exist. (LP: #942326)
628 - report.py, crash_signature(): Do not put "<module>" frames into Python crash
629 signatures that happen outside of function/method calls. Fall back to the
630 file/line number as a frame description instead. This will do a much better
631 job at disambiguating e. g. different ImportError crashes. (LP: #920403)
632 - Make "binary changed since the time of the crash" error message more
633 comprehensible, thanks Paolo Rotolo. (LP: #942830)
634 - crashdb.py, check_duplicate(): It can happen that a bug gets identified as
635 being a duplicate of bug S by symbolic signatures and a duplicate of bug A
636 by address signatures. Empirical evidence shows that this is due to the
637 unavoidable jitter in stack traces (A and S not being identified as
638 duplicates as their signatures differ slightly) and not a logic error. So
639 instead of erroring out, duplicate all three bugs and keep the lowest number
640 as the master ID. (LP: #943117)
641 - Revert the usage of multiple nested threads during data collection, and
642 switch back to only using one UI thread. The UI implementations can, and now
643 do, decide between showing a spinner and showing a progress dialog in the
644 ui_*_info_collection_progress() methods. This fixes libX11 crashes when
645 multiple UI threads do changes concurrently (LP: #901675), and also avoids
646 multi-thread induced crashes in Pango (LP: #943661). The removal of the
647 collect() method also fixes the new crashes in it. (LP: #942098, #939803)
648 - ui.py, get_desktop_entry(): Fix crash on uninstalled package. (LP: #940984)
649 - data/unkillable_shutdown: Fix crash on race condition when PID goes away
650 while the report is created. (LP: #546369)
651 - apport/hookutils.py, pci_devices(): Fix crash on unexpected lines from
652 lspci. (LP: #904489)
653 - Drop hardcoded "Ubuntu" words again which crept in with the whoopsie support
654 merge. Use the DistroRelease: field.
655 - apport-kde: Fix Home page URL in KApplication metadata.
656 - apport-gtk: Fix resizability and size after hiding details. (LP: #405418)
657
658Improvements:
659 - test/run: Drop "local" argument. This now tests against the source tree when
660 run in the source tree root, and against the system libraries/programs when
661 run from anywhere else.
662 - test/run: Consider command line arguments as test names and only run those
663 when given. Also support just running a single test.
664 - testsuite: Force the skipping of online tests when $SKIP_ONLINE_TESTS is
665 set.
666 - hookutils.py, xsession_errors(): Add a reasonable default pattern which
667 matches glib-style warnings, errors, criticals etc. and X window errors.
668 In data/general-hooks/generic.py, call it with that default instead of the
669 rather incomplete custom pattern. (LP: #932660)
670 - packaging.py: Add get_package_origin() method, and implement it for
671 apt-dpkg.
672 - report.py, add_package_info(): Add "[origin: ...]" tag to "Package" and
673 "Dependencies" fields for any package which is not native to the
674 distribution. If any such package is present, tag the report with
675 "third-party-packages" in data/general-hooks/generic.py. (LP: #927912)
676 - apport/packaging.py: Add get_uninstalled_package() method as a helper method
677 for the test suite. Use it instead of a hardcoded Debian/Ubuntu specific
678 name in test/test_hooks.py.
679 - test/test_ui_{gtk,kde}.py: Add test cases for complete UI workflow runs for
680 reporting a bug against an installed/uninstalled package, and reporting a
681 crash with and without showing details. This reproduces the recent crashes
682 like LP #901675 or LP #943661.
683 - test_ui.py: Add a test case for reporting a complete report on uninstalled
684 package. This happens when reporting a problem from a different machine
685 through copying a .crash file.
686 - test/run: Add a test that there are no hardcoded "Ubuntu" words in the
687 source. The code should use the DistroRelease: field or lsb_release.
688
6891.93 (2012-02-23):
690------------------
691Bug fixes:
692 - apport-gtk: Fix crash on nonexisting icon. Thanks Evan Dandrea.
693 (LP: #937354)
694 - ui.py, open_url(): Revert back to calling sudo instead of dropping
695 privileges ourselves; with the latter, calling firefox as the sudo'ing user
696 fails. (LP: #916810, #938128)
697 - ui.py: Fix aborting with "AssertionError" if the report is already known,
698 but without an URL. (LP: #938778)
699 - launchpad.py: If a bug is already known, but the report is private, do not
700 send the report. There is little sense piling up lots of duplicates.
701 (LP: #938700)
702 - test/crash: Fix regression of test_crash_apport(), consider $TERM a
703 non-sensitive variable.
704 - ui.py: Fix test failures for data collection progress, they are not expected
705 to happen for "ProblemType: Crash" any more (happens in the background
706 during sending, or if user clicks on "Show Details").
707 - test/hooks: Use a package from Debian/Ubuntu main, so that this works better
708 during package builds on build servers.
709 - test/python: Do not assume that /var/crash/ exists. Use /var/tmp/ for the
710 fake binaries instead.
711 - data/general-hooks/parse_segv.py: Fix test case name.
712 - ui.py: Fix crash on invalid core dumps. (LP: #937215)
713 - launchpad.py: Fix crash on unicode report titles. (LP: #896626)
714
715Improvements:
716 - apport-gtk: Show the most interesting fields first in the details view.
717 - do-release: Call pyflakes and abort on errors other than unused imports.
718 - Move all test suites out of the code modules into test/test_<module>.py.
719 This avoids having to load it every time the program runs, and also allows
720 running the tests against the installed version of Apport.
721 - Clean up the other executable test script in test/* and change them to the
722 same structure as the module tests.
723
7241.92 (2012-02-20):
725------------------
726Bug fixes:
727 - ui.py: Fix wrong creation of "~" folder instead of expanding it to home
728 directory when using "Examine locally". Thanks Jason Conti! (LP: #909149)
729 - Replace file() calls with open() for Python 3 compatibility. Thanks Colin
730 Watson!
731 - launchpad.py: Avoid sending tag names with upper case. (LP: #924181)
732 - report.py, crash_signature_addresses(): Fix crash if report does not have
733 "Signal".
734 - apport-gtk: Fix resize handling of expander in details window. Thanks Thomas
735 Bechtold! (LP: #930562)
736 - Clean up unnecessary imports. Thanks Evan Dandrea!
737
738Improvements:
739 - man/apport-bug.1: Mention where crash files are stored. Thanks David
740 Kastrup.
741 - hookutils.py, attach_hardware(): Sort ProcModules, thanks Brian Murray.
742 - launchpad.py: Keep "Dependencies" attachment in duplicates. Thanks Brian
743 Murray.
744 - Reorganize the GNOME and KDE user interface to do the crash notifications
745 and detail browser in a single dialog. Add test/gtk and test/kde tests to
746 check expected dialog layout for different cases. Thanks Evan Dandrea!
747 - Add support for the whoopsie-daisy crash reporting daemon by creating
748 zero-byte .upload file stamps for crash reports. Thanks Evan Dandrea!
749
7501.91 (2012-01-18):
751------------------
752Bug fixes:
753 - crashdb.py, check_duplicate(): If a crash has a signature but no existing
754 duplicate in the DB, also check for an existing address signature duplicate
755 in the DB.
756 - apport-retrace: Use DistroRelease specific subdirectory of the cache dir for
757 mapping a file to a package, as these maps are release specific.
758 - packaging-apt-dpkg.py: Refresh Contents.gz cache if it is older than one
759 day.
760 - crashdb.py: Ensure that address_signature duplicate db table does not have
761 multiple identical signatures by making it a primary key. Bump the db format
762 to "3". Existing databases need to be migrated manually as SQLite does not
763 allow adding a "PRIMARY KEY" constraint to existing tables.
764 - crashdb.py: Do not add a new address signature entry if one already exists.
765 - apport-cli: Fix UnicodeDecodeError on unicode report values. (LP: #275972)
766 - launchpad.py: Only set bug task importance if it is undecided.
767 - apport-retrace: Fix "an useful" typo. (LP: #911437)
768 - report.py: Filter out frames which are internal kernel/glibc implementation
769 details and not stable across duplicates. In particular, filter out
770 __kernel-syscall() and the SSE stubs.
771 - crashdb.py: Remove debugging leftover which completely disabled bug pattern
772 checking.
773 - report.py: Update reading AssertionMessage. Current (e)glibc turned
774 __abort_msg from a simple static string into a struct.
775
776Improvements:
777 - Change permissions of .crash files from 0600 to 0640, so that /var/crash can
778 be made g+s and crash handling daemons can access those.
779 - Python exceptions: Blacklist DBus.Error.NoReply. It does not help to get
780 these traces from the client-side application, you need the actual exception
781 in the D-Bus server backend instead. (LP: #914220)
782 - Support /etc/apport/whitelist.d/ similarly to /etc/apport/blacklist.d/, for
783 cases like installer environments where only crashes of a few selected
784 programs should be reported.
785
7861.90 (2011-11-24):
787------------------
788First beta release of 2.0 which introduces client-side duplicate checking.
789
790Bug fixes:
791 - backends/packaging-apt-dpkg.py: Fix another test case failure when ddeb
792 repository is not enabled.
793 - backends/packaging-apt-dpkg.py: Fix handling of explicit cache directory
794 name when it is a relative path.
795 - launchpad.py: Only query for bugs after 2011-08-01, to avoid timeouts.
796 - ui.py: Also anonymize standard bug title. (LP: #893863)
797 - launchpad.py: Current Launchpad cannot have private bugs which affect
798 multiple projects. Fix test suite accordingly.
799
800Improvements:
801 - report.py: Break out new method stacktrace_top_function() from
802 standard_title(), so that other parts of the code can use this as well.
803 - launchpad.net: When sending retraced results back to the bug report, update
804 the topmost function in the bug title. (LP: #869970)
805 - report.py, add_gdb_info(): Add a new field "StacktraceAddressSignature"
806 which is a heuristic signature for signal crashes. This should be used if
807 crash_signature() fails, i. e. the Stacktrace field does not have enough
808 symbols. This can be used to check for duplicates on the client side,
809 provided that the crash database server supports querying for these.
810 Do not expose this field when uploading to crash databases though, as it can
811 be recomputed from the already existing information (ProcMaps and
812 Stacktrace) and thus would just clutter the reports.
813 - crashdb.py: Add a table "version" with the database format version. Add
814 automatic upgrading to the most current format.
815 - crashdb.py: Put address signatures from reports checked with
816 check_duplicate() into the duplicate database, so that implementations of
817 known() can check for these.
818 - dupdb-admin: Add "publish" dupdb-admin command which exports the
819 duplicate database into a set of text files suitable for WWW publishing.
820 - crashdb.py: Add new method "known(report)" which can be implemented to check
821 if the crash db already knows about the crash signature. If so, the report
822 will not be uploaded, and instead the user will be directed to the existing
823 report URL (if available), similar to bug patterns. The default
824 implementation checks this format, if the crash database is initialized with
825 a "dupdb_url" option pointing to the exported database.
826 - launchpad.py: Override known() to check if the master bug is actually
827 accessible by the reporter, and is not tagged with "apport-failed-retrace"
828 or "apport-request-retrace"; otherwise file it anyway.
829 - crash-digger: Add --publish-db option to conveniently integrate duplicate DB
830 publication (similar to dupdb-admin publish) into retracer setups.
831 - launchpad.py: Attach updated stack traces from a duplicate to the master bug
832 if it failed retracing previously or has an "apport-request-retrace" tag.
833 (LP: #869982)
834 - apport-kde, apport-gtk: Support the "Annotation" field for custom dialog
835 titles for "Crash" and "Package" problem types as well, not just for
836 "Kernel". (LP: #664378)
837
8381.26 (2011-11-11):
839------------------
840Bug fixes:
841 - backends/packaging-apt-dpkg.py: Port to current python-apt API.
842 - hookutils.py: Fix path_to_key() to also work with unicode arguments.
843 - test/crash: Exit successfully if apport is not enabled in the system. This
844 allows packages to run the test suite during build.
845 - report.py, add_proc_info(): Correctly handle "python -m <modulename>"
846 programs as being interpreted and determine the appropriate module path.
847 - Fix some import statements to also work for the system-installed test suite.
848 - test/run: Fix testing data/general-hooks/parse_segv.py when called in
849 system-installed mode.
850 - apport/ui.py: Clean up test .crash file after test cases.
851 - Fix tests when running as root.
852 - setup.py: Fix crash when "javac -version" fails.
853 - README: Update command for one-time enablement.
854 - backends/packaging-apt-dpkg.py: Fix interleaving usage of install_packages()
855 with other operations such as get_version(), by resetting the apt status
856 after building and using the sandbox.
857 - report.py test suite: Remove requirement that $USER is set, which makes it
858 easier to run this from package build environments.
859 - apport/ui.py, test/crash: Use "yes" as test process instead of "cat". The
860 former is less likely to run already, and does not depend on having a stdin,
861 so it runs better in test environments like autopkgtest.
862 - backends/packaging-apt-dpkg.py: Fix tests if system does not have a dbgsym
863 apt source.
864
865Improvements:
866 - Ignore a crash if gnome-session is running and says that the session is
867 being shut down. These often die because X.org or other services are going
868 away, are usually harmless, and just cause a lot of clutter in bug trackers.
869 (LP: #460932)
870 - test/crash: Rewrite using Python's unittest, to be in line with other tests,
871 and be easier to maintain and extend.
872
8731.25 (2011-11-02):
874------------------
875Improvements:
876 - Add new response "Examine locally" to presenting the report details, which
877 runs apport-retrace in the chosen mode in a terminal. This should be made
878 available for crash reports if apport-retrace and a Terminal application are
879 installed; add an abstrace UI method for this. (LP: #75901)
880 - apport-gtk: Add "Examine locally..." button, and implement
881 ui_run_terminal().
882 - apport-cli: Add "Examine locally..." responses, and implement
883 ui_run_terminal().
884 - apport-cli: Greatly speed up displaying large reports. This also changes the
885 format to avoid indenting each line with a space, and visually set apart the
886 keys in a better way.
887 - apport_python_hook.py: Move tests out of this file into test/python, to
888 avoid having to parse the unit tests at each Python startup.
889 - test/python: Also make tests work if Python hook is not installed in
890 system's sitecustomize.py.
891 - packaging.py: Add get_modified_conffiles() API, and implement it in
892 packaging-apt-dpkg.py.
893 - hookutils.py: Add attach_conffiles().
894 - hookutils.py: Add attach_upstart_overrides().
895
896Bug fixes:
897 - launchpad.py: Remove "Ubuntu" in bug response, replace with "this software".
898 (LP: #883234)
899 - apport-kde: Rearrange order of imports to get intended error message if
900 PyKDE is not installed.
901 - packaging-apt-dpkg.py: Ignore hardening-wrapper diversions, to make
902 gcc_ice_hook work if hardening-wrapper is installed.
903 - apport_python_hook: Respect $APPORT_REPORT_DIR.
904 - apport_python_hook: Limit successive crashes per program and user to 3 per
905 day, just like signal crashes. (LP: #603503)
906 - packaging-apt-dpkg.py: Skip online tests when there is no default route.
907 - ui.py: Fix test suite to not fail if system has some obsolete or non-distro
908 packages.
909
9101.24 (2011-10-19):
911------------------
912Bug fixes:
913 - backends/packaging-apt-dpkg.py, install_packages(): Also copy
914 apt/sources.list.d/ into sandbox.
915 - backends/packaging-apt-dpkg.py, install_packages(): Install apt keyrings
916 from config dir or from system into sandbox. (LP: #856216)
917 - packaging.py, backends/packaging-apt-dpkg.py: Define that install_packages()
918 should return a SystemError for broken configs/unreachable servers etc., and
919 fix the apt/dpkg implementation accordingly.
920 - apport-retrace: Don't crash, just give a proper error message if servers are
921 unreachable, or configuration files are broken. (LP: #859248)
922 - backends/packaging-apt-dpkg.py: Fix crash when /etc/apport/native-origins.d
923 contains any files. (LP: #865199)
924 - hookutils, recent_logfile(): Fix invalid return value if log file is not
925 readable. (LP: #819357)
926 - test/crash: Fix race condition in the "second instance terminates
927 immediately" check.
928 - hookutils.py: Replace attach_gconf() with a no-op stub. It used static
929 python modules like "gconf" which broke the PyGI GTK user interface, and
930 gconf is rather obsolete these days.
931 - ui.py, open_url(): Greatly simply and robustify by just using xdg-open. This
932 already does the right thing wrt. reading the default browser from GNOME,
933 KDE, XCE, and other desktops. (LP: #198449)
934 - data/general-hooks/generic.py: Only attach ~/.xsession_errors if the bug is
935 reported in the same XDG session as the crash happened. (LP: #869974)
936 - Ignore crashes for programs which got updated in between the crash and
937 reporting. (LP: #132904)
938 - Special-case crashes of 'twistd': Try to determine the client program and
939 assign the report to that, or fail with an UnreportableReason. (LP: #755025)
940 - apport-gtk: In bug update mode, make details dialog resizable and fix
941 default size. (LP: #865754)
942 - apport-gtk: Fix crash if report does not have ProcCmdline. (LP: #854452)
943 - hookutils.py, attach_wifi(): Anonymize ESSID and AP MAC from "iwconfig"
944 output. (LP: #746900)
945 - test/crash: Fix test failure if user is not in any system groups.
946 - test/crash: Change to /tmp/ for test crash process, to fix failure if the
947 user that runs the test suite cannot write into the current directory.
948 (LP: #868695)
949 - ui.py: Improve error message if package is not a genuine distro package.
950 Thanks to Ronan Jouchet. (LP: #559345)
951
952Improvements:
953 - apport-retrace: Add --timestamp option to prepend a timestamp to log
954 messages. This is useful for batch operations.
955 - crash-digger: Call apport-retrace with --timestamps, to get consistent
956 timestamps in log output.
957 - hookutils.py: Add two new functions attach_gsettings_package() and
958 attach_gsettings_schema() for adding user-modified gsettings keys to a
959 report. (LP: #836489)
960 - hookutils.py: Add new function in_session_of_problem() which returns whether
961 the given report happened in the currently running XDG session. This can be
962 used to determine if e. g. ~/.xsession-errors is relevant and should be
963 attached.
964
9651.23.1 (2011-09-29)
966-------------------
967Bug fixes:
968 - apport/crashdb.py: Ensure that duplicate table only has one entry per report
969 ID.
970 - apport-retrace: Pass correct executable path to gdb in --gdb with --sandbox
971 mode.
972 - apport-retrace: Do not leave behind temporary directories on errors.
973 - apport-retrace: Drop assertion failure for existance of "Stacktrace". This
974 isn't present in the case of gdb crashing, and there is not much we can do
975 about it. This should not break the retracer.
976 - apport/report.py: Unwind XError() from stack traces for the "StacktraceTop"
977 field, as they take a significant part of the trace. This causes bugs to be
978 duplicated which really have different causes.
979
9801.23 (2011-09-14)
981-----------------
982Improvements:
983 - crashdb.py, crash-digger, dupdb-admin: Drop the concept of "duplicate DB
984 consolidation". Such massive queries cause timeouts with e. g. Launchpad.
985 Instead, update the status of potential master bugs in the crash DB whenever
986 check_duplicate() is called.
987
988Bug fixes:
989 - launchpad.py: Fix crash in close_duplicate() if master bug was already
990 marked as a duplicate of the examined bug.
991 - problem_report.py, load(): Fix missing last character if the last line in a
992 multi-line field is not terminated with a newline.
993 - launchpad.py: Fix test_marking_python_task_mangle() check to work with
994 current Launchpad.
995 - apport-retrace: If the user did not specify a --cache directory, create a
996 shared one instead of letting the two install_packages() calls create their
997 own. This ensures that the apt and dpkg status is up to date, and avoids
998 downloading the package indexes multiple times. (LP: #847951)
999 - apport-retrace: Give proper error mesage instead of AssertionError crash if
1000 a report does not contain standard Apport format data. (LP: #843221)
1001 - fileutils.py, get_new_reports(): Fix crash if report file disappears in the
1002 middle of the operation. (LP: #640216)
1003 - apport/ui.py, load_report(): Intercept another case of broken report files.
1004 (LP: #445142)
1005 - apport/report.py, standard_title(): Escape regular expression control
1006 characters in custom exception names. (LP: #762998)
1007
10081.22.1 (2011-09-06)
1009-------------------
1010Improvements:
1011 - dupdb-admin: Add "removeid" command.
1012
1013Bug fixes:
1014 - dupdb-admin: Use the in-memory CrashDB implementation for simple operations
1015 like dump or changeid, which do not require an actual backend. This makes
1016 the command work in checkouts without a /etc/apport/crashdb.conf.
1017 - dupdb-admin: Fix UnicodeEncodeError crash.
1018 - launchpad.py: Fix crash if a crash report does not have a DistroRelease.
1019 - Set the default "Apport" title for choice dialogs instead of the default
1020 apport-gtk title. Thanks Robert Roth. (LP: #608222)
1021 - apport-gtk: Update markup_escape_text() call to current glib. (LP: #829635)
1022
10231.22 (2011-08-25)
1024-----------------
1025Improvements:
1026 - Completely rework apport-retrace to use gdb's "debug-file-directory" and
1027 "solib-absolute-prefix" settings and only unpack the necessary packages in a
1028 temporary directory. This makes it possible to use it in a running system
1029 without actually touching installed packages, does not need any root
1030 privileges, and stops the requirement of using chroots with fakechroot and
1031 fakeroot. This is a lot easier to maintain and use, and a lot faster, too.
1032 As a consequence, drop the chroot module, and update crash-digger
1033 accordingly. See "man apport-retrace" for the new usage.
1034 It is now also easier to port to other packaging backends, as a lot of the
1035 common logic moved out of the packaging API;
1036 packaging.install_retracing_packages() got dropped in favor of the simpler
1037 packaging.install_packages().
1038 - crash-digger: Show how many bugs are left in the pool with each new retrace.
1039
1040Bug fixes:
1041 - apport-gtk: Fix crash in GLib.markup_escape_text() call, regression from
1042 1.21.3. (LP: #828010)
1043 - launchpad.py: When searchTasks() times out, exit with 99 as this is a
1044 transient error.
1045 - crash-digger: Intercept OverflowError from downloaded compressed
1046 attachments.
1047
10481.21.3 (2011-08-17)
1049-------------------
1050Bug fixes:
1051 - gtk/apport-gtk.desktop.in: Also show in Unity. (LP: #803519)
1052 - apport-unpack: Fix crash on file errors.
1053 - Add apport.packaging.get_library_paths() interface and implement it for
1054 backends/packaging-apt-dpkg.py using dpkg multiarch directories. Use it in
1055 chroot.py.
1056 - hookutils.py: Don't attach empty values. Thanks Bryce Harrington.
1057 (LP: #813798)
1058 - apport-gtk: Correctly pass message dialog type.
1059 - apport-gtk: Fix GLib and GObject imports to be compatible with the future
1060 pygobject 3.0.
1061
1062Improvements:
1063 - hookutils.py: Add attach_mac_events() for reporting logs of MAC systems.
1064 Looks for AppArmor messages for now. Thanks Marc Deslauriers!
1065 - hookutils.py, attach_alsa(): Get a list of outputs/inputs that PulseAudio
1066 knows about, which also shows the currently selected output/input, as well
1067 as volumes. This should help with "no sound" bug troubleshooting. Thanks
1068 Luke Yelavich.
1069
10701.21.2 (2011-07-01)
1071-------------------
1072Improvements:
1073 - test/run: Check $PYTHON for using a different Python interpreter (such as
1074 "python3") for the tests.
1075 - generic hook: Don't report package installation failures due to segfaulting
1076 maintainer scripts. We want the actual crash report only. Thanks Brian
1077 Murray.
1078 - hookutils.py, attach_wifi(): Also include wpasupplicant logs. Thanks Mathieu
1079 Trudel-Lapierre!
1080
1081Bug fixes:
1082 - backends/packaging-apt-dpkg.py: Fix crash introduced in 1.21.1's multiarch
1083 fixes.
1084 - report.py: Fix bug patterns to correctly match against compressed report
1085 fields.
1086
10871.21.1 (2011-06-20)
1088-------------------
1089Improvements:
1090 - data/general-hooks/generic.py: Also check for low space on /var. Thanks
1091 Brian Murray.
1092 - hookutils.py, attach_file() and attach_file_if_exists(): Add a new
1093 "overwrite" flag option. If not given, now default to overwriting an
1094 existing key, as this is usually what you need when attaching files
1095 (instead of attaching it several times with '_' appended to the keys). You
1096 can get the old behaviour by setting overwrite=False.
1097
1098Bug fixes:
1099 - When showing the size of the full report, take the compressed size of binary
1100 values instead of their uncompressed size, as the crash db upload will use
1101 the compressed values.
1102 - backends/packaging-apt-dpkg.py: Fix for current dpkg with multiarch support.
1103 - test/run: Fix the test suite to run against the system installed libraries
1104 with current Python versions (2.6, 2.7) where __file__ does not work any
1105 more with imports.
1106
11071.21 (2011-06-08)
1108-----------------
1109Improvements:
1110 - Supply --desktop option to kdesudo to improve the description which program
1111 is requesting administrative privileges.
1112 - apport-checkreports: Exit with status 2 if there are new reports, but apport
1113 is disabled. This helps crash notification GUIs to not display new crash
1114 reports in that case. Thanks to Michael Vogt for the original patch.
1115 - Add data/is-enabled: Shell script to check if apport is enabled. Non-Python
1116 programs (which can't use apport.packaging.enabled() ) can call this instead
1117 of having to parse /etc/default/apport themselves, and just check the exit
1118 code. Inspired by original patch from Michael Vogt, thanks!
1119
1120Bug fixes:
1121 - apport-gtk: HTML-escape text for dialogs with URLs. (LP: #750870)
1122 - dump_acpi_tables.py: Check to see if acpi/tables dir is mounted first.
1123 Thanks Brian Murray. (LP: #729622)
1124 - man/apport-cli.1: Document recently added -w/--window option. Thanks Abhinav
1125 Upadhyay! (LP: #765600)
1126 - Use kde-open instead of kfmclient to open URLs under KDE. Thanks Philip
1127 Muškovac. (LP: #765808)
1128
11291.20.1 (2011-03-31)
1130-------------------
1131Bug fixes:
1132 - Add bash completion support for new -w/--window option that was introduced
1133 in 1.20. Thanks Philip Muškovac.
1134 - apport-unpack: Fix crash if target directory already exists.
1135 - Fix crash if UnreportableReason is a non-ASCII string. (LP: #738632)
1136 - Fix crash if application from desktop name is a non-ASCII string.
1137 (LP: #737799)
1138 - unkillable_shutdown: Fix rare crash if ExecutablePath does not exist (any
1139 more). (LP: #537904)
1140 - kernel_crashdump: Fix crash if the vmcore file disappeared underneath us.
1141 (LP: #450295)
1142 - unkillable_shutdown: Fix crash if the checked process terminated underneath
1143 us. (LP: #540436)
1144 - ui.py: Properly raise exceptions from the upload thread that happen at its
1145 very end. (LP: #469943)
1146
11471.20 (2011-03-17)
1148-----------------
1149Improvements:
1150 - Add support for -w/--window option which will enable user to select a
1151 window as a target for filing a problem report. Thanks Abhinav Upadhyay for
1152 the patch! (LP: #357847)
1153 - Disable the filtering on SIGABRT without assertion messages. Turns out that
1154 developers want these crash reports after all. (LP: #729223)
1155 - Add support for a "DuplicateSignature" report fields. This allows package
1156 hooks to implement custom duplicate problem handling which doesn't need to
1157 be hardcoded in Apport itself. Update the launchpad backend to tag such bugs
1158 as "need-duplicate-check".
1159
1160Bug fixes:
1161 - report.py, add_hooks_info(): Properly report TypeErrors from hooks.
1162 - apport-retrace: Intercept SystemErrors from ill-formed gzip attachments as
1163 well.
1164 - Fix crash if crash database configuration does not specify a
1165 bug_pattern_url. Just assume None. (LP: #731526)
1166 - If a custom crash database does not specify a bug_pattern_url, fall back to
1167 using the default database's. (LP: #731526)
1168 - hookutils.py Update WifiSyslog regex to correctly catch application log
1169 messages in syslog. Thanks Mathieu Trudel-Lapierre. (LP: #732917)
1170 - hookutils.py, attach_hardware(): Avoid error message if machine does not
1171 have a PCI bus. Thanks Marcin Juszkiewicz! (LP: #608449)
1172 - backends/packaging-apt-dpkg.py: Replace deprecated getChanges() call with
1173 get_changes().
1174 - apport-gtk: Fix broken dialog heading if the name of the crashed program
1175 contains an & or other markup specific characters.
1176 - apport-gtk: Don't crash if GTK cannot be initialized. This usually happens
1177 without a $DISPLAY or when the session is being shut down. Just print an
1178 error message. If there are pending crashes, they will be shown again the
1179 next time a session starts. (LP: #730569)
1180
11811.19 (2011-02-28)
1182-----------------
1183Bug fixes:
1184 - Update stack unwind patterns for current glib (slightly changed function
1185 names), and also ignore a preceding '*'. (LP: #716251)
1186 - Fix crash_signature() to fail if there is an empty or too short
1187 StacktraceTop.
1188 - apt backend: Do not generate a warning if the opportunistically added -dbg
1189 package does not exist.
1190 - apt backend: Only add -dbg in --no-pkg mode, as there will be conflicts in
1191 normal package mode.
1192 - apt backend: Call tar with target cwd instead of using -C; the latter causes
1193 an extra openat() call which breaks with current fakechroot.
1194 - launchpad.py: Fix retracer crash if DistroRelease field does not exist.
1195 - Convert deprecated failIf()/assert_() TestCase method calls to
1196 assertFalse()/assertTrue().
1197
1198Improvements:
1199 - In apport-bug, if the user specifies a PID referring to a kernel thread,
1200 do the right thing and file the bug against the kernel
1201 - In hookutils.attach_dmesg, skip over an initial truncated message if one
1202 is present (this happens when the ring buffer overflows)
1203 - Change bug patterns to just use one central file instead of per-package
1204 files. This allows bug patterns to be written which are not package
1205 specific, and is easier to maintain as well. IMPORTANT: This changed the
1206 format of crashdb.conf: bug_pattern_base is now obsolete, and the new
1207 attribute bug_pattern_url now points to the full URL/path of the patterns
1208 file. Thanks to Matt Zimmerman!
1209
12101.18 (2011-02-16)
1211-----------------
1212Bug fixes:
1213 - Ensure that symptom scripts define a run() function, and don't show them if
1214 not.
1215 - Do not show symptom scripts which start with an underscore. These can be
1216 used for private libraries for the actual symptom scripts.
1217 - Update bash completion. Thanks Philip Muškovac.
1218 - etc/default/apport: Remove obsolete "maxsize" setting. (LP: #719564)
1219
1220Improvements:
1221 - Remove explicit handling of KDE *.ui files in setup.py, as
1222 python-distutils-extra 2.24 fixes this. Bump version check.
1223 - hookutils.py: Add attach_root_command_outputs() to run several commands
1224 at once. This avoids asking for the password several times. (LP: #716595)
1225
12261.17.2 (2011-02-04)
1227-------------------
1228Improvements:
1229 - Be more Python 3 compatible (not fully working with Python 3 yet, though).
1230 - apt/dpkg backend: Drop support for pre-0.7.9 python-apt API.
1231 - Add --tag option to add extra tags to reports. (LP: #572504)
1232
1233Bug fixes:
1234 - hookutils.py, attach_dmesg(): Do not overwrite already existing dmesg.
1235 - hookutils.py: Be more robust against file permission errors. (LP: #444678)
1236 - ui.py: Do not show all the options in --help when invoked as *-bug.
1237 (LP: #665953)
1238 - launchpad.py: Adapt test cases to current standard_title() behaviour.
1239
12401.17.1 (2011-01-10)
1241-------------------
1242Bug fixes:
1243 - Make the GTK frontend work with GTK 2.0 as well, and drop "3.0" requirement.
1244
12451.17 (2010-12-31)
1246-----------------
1247Improvements:
1248 - Better standard bug titles for Python crashes. Thanks Matt Zimmerman!
1249 (LP: #681574)
1250 - Add handler for uncaught Java exceptions. There is no integration for
1251 automatically intercepting all Java crashes yet, see java/README.
1252 Thanks Matt Zimmerman! (LP: #548877)
1253
1254Bug fixes:
1255 - GTK frontend: Require GTK 3.0.
1256 - launchpad.py: Default to "production" instance, not "edge", since edge is
1257 obsolete now.
1258 - hookutils.py, attach_alsa(): Fix crash if /proc/asound/cards does not exist.
1259 (LP: #626215)
1260 - ui.py, format_filesize(): Fix to work with stricter locale.format() in
1261 Python 2.7. (LP: #688535). While we are at it, also change it to use base-10
1262 units.
1263 - hookutils.py, package_versions(): Always include all requested package names
1264 even if they're unknown to us. Thanks Matt Zimmerman! (LP: #695188)
1265 - launchpad.py: When updating a bug, also add new tags. Thanks Brian Murray!
1266
12671.16 (2010-11-19)
1268-----------------
1269New features:
1270 - Port GTK frontend from pygtk2 to GTK+3.0 and gobject-introspection.
1271
1272Bug fixes:
1273 - Fix symptoms again. Version 1.15 broke the default symptom directory.
1274 - Fix memory test case to work with current Python versions, where the SQLite
1275 integrity check throws a different exception.
1276
12771.15 (2010-11-11)
1278-----------------
1279New features:
1280 - Add dump_acpi_tables.py script. This can be called by package hooks which
1281 need ACPI table information (in particular, kernel bug reports). Thanks to
1282 Brad Figg for the script!
1283 - Order symptom descriptions alphabetically. Thanks to Javier Collado.
1284 - Check $APPORT_SYMPTOMS_DIR environment variable for overriding the system
1285 default path. Thanks to Javier Collado.
1286
1287Bug fixes:
1288 - testsuite: Check that crashdb.conf can have dynamic code to determine DB
1289 names and options.
1290 - ui.py test suite: Rewrite _gen_test_crash() to have the test process core
1291 dump itself, instead of using gdb to do it. The latter fails in ptrace
1292 restricted environments, such as Ubuntu 10.10.
1293 - packaging-apt-dpkg.py: Fix handling of /etc/apport/native-origins.d to
1294 actually work. Thanks Steve Langasek. (LP: #627777)
1295 - apport-kde: Load correct translation catalogue. Thanks Jonathan Riddell.
1296 (LP: #633483)
1297 - launchpad.py: Use launchpadlib to file a bug instead of screen scraping.
1298 The latter was completely broken with current Launchpad, so this makes the
1299 test suite actually work again. Thanks to Diogo Matsubara!
1300 - launchpad.py: Change $APPORT_STAGING to $APPORT_LAUNCHPAD_INSTANCE, so that
1301 you can now specify "staging", "edge", or "dev" (for a local
1302 http://launchpad.dev installation). Thanks to Diogo Matsubara!
1303 - backends/packaging-apt-dpkg.py: Fix crash on empty lines in ProcMaps
1304 attachment.
1305 - doc/symptoms.txt: Fix typo, thanks Philip Muskovac. (LP: #590521)
1306 - apport/hookutils.py: rename ProcCmdLine to ProcKernelCmdLine to not wipe
1307 wipe out /proc/$pid/cmdline information. (LP: #657091)
1308 - apport/hookutils.py: attach_file() will not overwrite existing report
1309 keys, instead appending "_" until the key is unique.
1310 - Fix --save option to recognise ~, thanks Philip Muškovac. (LP: #657278)
1311 - Remove escalation_subscription from Ubuntu bug DB definition, turned out to
1312 not be useful; thanks Brian Murray.
1313 - launchpad.py: Fix APPORT_LAUNCHPAD_INSTANCE values with a https:// prefix.
1314 - apt backend: Opportunistically try to install a -dbg package in addition to
1315 -dbgsym, to increase the chance that at least one of it exists. Thanks
1316 Daniel J Blueman!
1317
13181.14.1 (2010-06-24)
1319-------------------
1320Bug fixes:
1321 - hookutils.py, attach_drm_info(): Sanitize connector names. Thanks Chris
1322 Halse Rogers! (LP: #597558)
1323 - bash completion: Complete all path names, apport-bug can be invoked with a
1324 path to a program. Thanks Philip Muskovac.
1325
13261.14 (2010-06-16)
1327-----------------
1328New features:
1329 - hookutils.py: Add new method attach_drm_info() to read and format
1330 /sys/class/drm/*.
1331
1332Bug fixes:
1333 - packaging-apt-dpkg.py: Fix deprecated python-apt variables, thanks David
1334 Stansby. (LP: #591695)
1335 - launchpad.py: Fix crash on attachments which are named *.gz, but
1336 uncompressed. (LP: #574360)
1337 - hookutils.py, attach_gconf(): Fix defaults parsing for boolean keys.
1338 (LP: #583109)
1339
13401.13.4 (2010-05-04)
1341-------------------
1342 - bash completion: Fix error message if /usr/share/apport/symptoms does not
1343 exist. Thanks Philip Muškovac! (LP: #562118)
1344 - general-hooks/parse_segv.py: Report stack exhaustion more clearly and
1345 correctly handle register dereferencing calls.
1346 - Save/restore environment when calling hooks, in case they change the locale,
1347 etc. (LP: #564422)
1348 - hookutils.py, command_output(): Do not set $LC_MESSAGES for the calling
1349 process/hook, just for the command to be called.
1350 - ui.py: When displaying strings from system exceptions, decode them into an
1351 unicode string, to avoid crashing the KDE UI. (LP: #567253)
1352 - apport-retrace: Fix crash for retracing kernel vmcores, which do not have an
1353 ExecutablePath.
1354 - apport-bug manpage: Clarify when apport-collect may be used. Thanks Brian
1355 Murray! (LP: #537273)
1356 - generic hook: Check ProcMaps for unpackaged libraries, and ask the user if
1357 he really wants to continue. If he does, tag the report as "local-libs" and
1358 add a "LocalLibraries" field to the report with a list of them.
1359 (LP: #545227)
1360
13611.13.3 (2010-04-14)
1362-------------------
1363 - data/general-hooks/parse_segv.py: suggest segv-in-kernel possibility.
1364 - ui.py: When running as root, only show system crash reports, to avoid
1365 restarting user programs as root. (LP: #445017)
1366
13671.13.2 (2010-03-31)
1368-------------------
1369 - problem_report.py, write_mime(): Add new optional argument "priority_fields"
1370 for ordering report keys. Patch by Brian Murray, thanks!
1371 - launchpad.py: Put some interesting fields first in the report, with the new
1372 priority_fields argument. Patch by Brian Murray, thanks!
1373 - packaging-apt-dpkg.py, _install_debug_kernel(): Do not crash on an outdated
1374 kernel, just return that it is outdated. (LP: #532923)
1375 - launchpad.py test suite: Add "Referer" HTTP header, now required by
1376 launchpad.
1377 - launchpad.py: Fix crash if configuration does not have an "escalated_tag"
1378 option.
1379 - launchpad.py: Port to launchpadlib 1.0 API, thanks Michael Bienia for the
1380 initial patch! (LP: #545009)
1381 - gtk/apport-gtk-mime.desktop.in, kde/apport-kde-mime.desktop.in: Change
1382 categories so that these do not ever appear in menu editors. (LP: #449215)
1383 - launchpad.py: Some LP bugs have broken attachments (this is a bug in
1384 Launchpad itself). Ignore those instead of crashing.
1385 - apport-gtk: Turn http:// and https:// links into clickable hyperlinks in
1386 information and error dialogs. (LP: #516323)
1387 - apport-retrace: Fix crash when trying to rebuild package info for reports
1388 without an ExecutablePath. (LP: #436157)
1389 - ui.py: Fix crash when package information cannot be determined due to broken
1390 apt status. (LP: #362743)
1391 - ui.py: Fix crash when /etc/apport/crashdb.conf is damaged; print an
1392 appropriate error message instead. (LP: #528327)
1393 - data/kernel_crashdump: Fix crash if log file disappeared underneath us.
1394 (LP: #510327)
1395 - data/apport: Fix IOError when apport is called with invalid number of
1396 arguments, and stderr is not a valid fd. (LP: #467363)
1397 - hookutils.py: Factor out the DMI collection code from attach_hardware()
1398 into attach_dmi(), and call that in attach_alsa() as well. Thanks to Brad
1399 Figg for the patch! (LP: #552091)
1400 - apport/ui.py: Fix the help output if Apport is invoked under an alternative
1401 name (like apport-collect). (LP: #539427)
1402
14031.13.1 (2010-03-20)
1404-------------------
1405Bug fixes:
1406 - Update parse-segv to handle gdb 7.1 output.
1407 - Enhance test suite to work with gdb 7.1 as well, and catch future outputs.
1408 - UI: Add exception string to the "network error" dialog, to better tell what
1409 the problem is.
1410 - UI: Add back -p option to apport-collect/apport-update-bug (regression from
1411 1.13). (LP: #538944)
1412 - launchpad.py: Add yet another workaround for LP#336866. (LP: #516381)
1413 - launchpad.py, download(): Ignore attachments with invalid key names.
1414 - Fix regression from 1.10 which made it impossible for a package hook to set
1415 a third-party crash database for non-native packages. (LP: #517272)
1416 - apport-cli: Create the 'details' string only if user wants to view details,
1417 and do not show files larger than 1MB. Thanks Scott Moser! (LP: #486122)
1418 - packaging-apt-dpkg.py: Silence apt.Cache() spewage to stdout with newer
1419 python-apt versions. (LP: #531518)
1420
1421Improvements:
1422 - unkillable_shutdown: Add list of running processes and blacklisted pids to
1423 report. (LP: #537262)
1424 - Sort the report by key in the details view. (LP: #519416)
1425
14261.13 (2010-03-10)
1427-----------------
1428New features:
1429 - Add "unkillable_shutdown" script to collect information about processes
1430 which are still running after sending SIGTERM to them. This can be hooked
1431 into e. g. /etc/init.d/sendsigs on Debian/Ubuntu systems.
1432
1433Improvements:
1434 - apport_python_hook.py: Directly check /etc/default/apport instead of
1435 querying packaging.enabled(), to avoid importing lots of modules for
1436 non-packaged scripts. Thanks Stuart Colville! (LP: #528355)
1437
1438Bug fixes:
1439 - Fix SegV parser to notice walking off the stack during "call" or "ret"
1440 (LP: #531672).
1441 - Fix --help output for bug updating mode (invocation as apport-collect or
1442 apport-update-bug). (LP: #504116)
1443 - Fix bug escalation tagging, thanks to Brian Murray.
1444 - Fix option processing when being invoked as apport-bug. Thanks to Daniel
1445 Hahler for the patch! (LP: #532944)
1446
14471.12.1 (2010-02-22)
1448-------------------
1449Bug fixes:
1450 - launchpad.py: Do not keep escalating bugs, just escalate at the 10th
1451 duplicate.
1452 - Improve error message if a symptom script did not determine a package name.
1453 (LP: #503834)
1454 - general-hooks/generic.py: Fix crash on libGL check with empty StacktraceTop.
1455 - Review and clean up usage of chmod(). This fixes a small race condition in the
1456 Python exception hook where a local attacker could read the information from
1457 another user's crash report. (LP: #516029)
1458 - hookutils, package_versions(): Ignore "None" packages, for more robust
1459 package hooks. (LP: #518295)
1460
14611.12 (2010-01-20)
1462-----------------
1463Improvements:
1464 - launchpad.py: Add options 'escalation_subscription' and 'escalation_tag' for
1465 handling bugs with more than 10 duplicates.
1466 - crashdb.conf: For Ubuntu, escalate bugs with >= 10 duplicates to
1467 "ubuntu-bugcontrol" and tag them with "bugpattern-needed". (LP: #487900)
1468 - general-hooks/generic.py: Filter out crashes on missing GLX (LP: #327673)
1469 - Add bash completion script. Thanks to Philip Muškovac. (LP: #218933)
1470
1471Bug fixes:
1472 - launchpad.py: Drop APPORT_FILES whitelist for download() and instead just
1473 filter out file extensions that we know about (*.txt and *.gz).
1474 (LP: #444975)
1475 - launchpad.py: Do not put the Tags: field into the bug description, since
1476 they are already proper tags. In download(), convert the real tags back to
1477 the Tags: field. (LP: #505671)
1478 - test/crash: Update expected core dump flags for changed rlimit behaviour in
1479 Linux 2.6.32.
1480 - launchpad.py: Fix marking of 'checked for duplicate' for bugs with upstream
1481 tasks.
1482 - launchpad.py, get_fixed_version(): Do not consider a bug as invalid just
1483 because it has any invalid distro package task.
1484
14851.11 (2009-12-23)
1486-----------------
1487Improvements:
1488 - Add "--save" UI option to store the collected information into an .apport
1489 file instead of sending it right away. The file can then later be sent
1490 through apport-bug. Update manpages accordingly.
1491 - Update all copyright and description headers and consistently format them.
1492 - Rename all TestCase classes to "_T", which makes it much easier to run
1493 individual tests from the command line.
1494 - Testsuite: Verify that report details are/are not shown. This uncovered that
1495 details about package installation failures were not shown before sending
1496 them, which is fixed now.
1497
1498Bug fixes:
1499 - test/hooks: Do not try to add hook information to kernel_crashdump test
1500 case, since we do not have an UI here. This test case broke when the system
1501 had an interactive package hook for the kernel.
1502 - When reporting a bug from a saved .apport file, let the user review/confirm
1503 the content before sending.
1504
15051.10.1 (2009-12-23)
1506-------------------
1507Improvements:
1508 - Install apport-collect symlink.
1509 - Update translations from Launchpad.
1510
1511Bug fixes:
1512 - Move all remaining option/argument parsing from apport-bug into ui.py. This
1513 allows the user to add options to apport-bug/apport-collect, and also avoids
1514 unwieldy dissection of options/arguments in shell.
1515
15161.10 (2009-12-19)
1517-----------------
1518New features:
1519 - Add a mode for updating an existing problem report to ui.py (-u/--update).
1520 This is similar to the Ubuntu specific "apport-collect" tool, but
1521 implemented the right way now: In particular, this has access to the UI and
1522 thus can use interactive hooks (LP: #385811) and show you what is being sent
1523 for confirmation/cancelling (LP: #371827)
1524 - apport-bug: If invoked as "apport-collect" or "apport-update-bug" (i. e.
1525 through a symlink), run apport in update mode (-u <number>). This provides a
1526 convenient no-options command line program. Please note that setup.py does
1527 not currently install such a symlink. Update the apport-bug manpage
1528 accordingly.
1529
1530Improvements:
1531 - launchpad.py: Use new login_with() to clean up code, and specify allowed
1532 access levels (WRITE_PRIVATE is the only sensible one anyway). (LP: #410205)
1533 - New hookutils functions:
1534 xsession_errors (match lines from ~/.xsession-errors)
1535 shared_libraries (determine which libraries a binary links with)
1536 links_with_shared_library (test if a binary links with a particular library)
1537 - New CrashDatabase API: get_affected_packages(), can_update(), is_reporter()
1538 - Rename CrashDatabase.update() to update_traces().
1539 - Add CrashDatabase.update() for adding all new fields of a report. This is
1540 primarily useful for collecting local standard and package hook data for an
1541 already existing bug report which was not filed through Apport. This checks
1542 can_update()/is_reporter() if the user is eligible for updating that
1543 particular bug. (LP: #485880)
1544
1545Bug fixes:
1546 - Ignore SIGXCPU and SIGXFSZ; thanks to Kees Cook. (LP: #498074)
1547 - launchpad.py: Do not mark non-Ubuntu bugs as needs-retrace, since there is
1548 no retracer right now. (LP: #489794)
1549 - packaging-apt-dpkg.py, install_retracing_packages(): Do not crash on
1550 malformed Dependencies.txt lines. (LP: #441709)
1551 - use-local: Fix for new source tree location of "apport" binary.
1552
15531.9.6 (2009-12-01)
1554------------------
1555Improvements:
1556 - Add pm-utils hook to record current operation, so that apportcheckresume can
1557 check it. Before this was kept in Ubuntu's pm-utils package.
1558 - general-hooks/generic.py: Check if using ecryptfs, and which directory.
1559 (LP: #444656)
1560
1561Bug fixes:
1562 - launchpad.py: Ensure that text attachments on initial bug filing are valid
1563 UTF-8. (LP: #453203)
1564 - man/apport-retrace.1: Document -R option.
1565
15661.9.5 (2009-11-20)
1567------------------
1568Bug fixes:
1569 - apport-retrace: Fix crash if InterpreterPath/ExecutablePath do not exist.
1570 - hookutils.py, attach_alsa(): Attach /proc/cpuinfo too, for CPU flags.
1571 - Fix crash if InterpreterPath does not exist any more at the time of
1572 reporting. (LP: #428289)
1573 - apport-gtk: Connect signals properly, to repair cancel/window close buttons.
1574 (LP: #427814)
1575 - Update German translations and fix "konnre" typo. (LP: #484119)
1576
15771.9.4 (2009-11-06)
1578------------------
1579Bug fixes:
1580 - Fix crash when ExecutablePath isn't part of a package. (LP: #424965)
1581 - hookutils.py, attach_hardware(): Anonymize disk labels. Thanks to Marco
1582 Rodrigues. (LP: #394411)
1583 - hookutils.py, attach_wifi(): Anonymize encryption key (which appeared in hex
1584 when being called as root). Thanks to Marco Rodrigues. (LP: #446299)
1585 - launchpad.py: If unset, set bug task source package also for interpreter
1586 crashes.
1587 - apport-gtk: Give details window a minimize/maximize button, which were
1588 missing in some window managers. Thanks to Marien Zwart. (LP: #447749)
1589 - apport-kde: Properly terminate program after closing the last dialog.
1590 (LP: #458662)
1591 - hookutils.py, attach_alsa(): Attach /proc/asound/version. (LP: #467233)
1592 - general-hooks/generic.py: Only collect ~/.xsession-errors bits when we have
1593 an ExecutablePath linked to libgtk.
1594
15951.9.3 (2009-10-14)
1596------------------
1597Changes:
1598 - Drop handling of the APPORT_REPORT_THIRDPARTY environment variable and
1599 "thirdparty" configuration file option. This has never been documented, and
1600 conceptually does not work. There is a proper mechanism for this in place
1601 now, e. g. launchpad.py's "project" option.
1602
1603Bug fixes:
1604 - hookutils.py: Fix error codes from "comm", thanks to Brian Murray.
1605 - general-hooks/generic.py: Catch xkbcomp error messages. (LP: #431807)
1606 - launchpad.py: Assert that we have exactly one of "distro" or "project"
1607 option.
1608 - doc/crashdb-conf.txt: Improve documentation of crash database options.
1609 - apport-gtk: Make Cancel/Send buttons focusable. Thanks to Marco Rodrigues.
1610 (LP: #447780)
1611
16121.9.2 (2009-10-02)
1613------------------
1614Improvements:
1615 - apport-cli: Print the URL and ask whether to open a browser. In many
1616 situations (such as usage on a server through ssh), it's preferable to not
1617 open the browser on the reporting computer. Thanks to Matt Zimmerman for the
1618 initial patch! (LP: #286415)
1619 - general-hooks/generic.py: Collect important glib errors/assertions (which
1620 should not have private data) from ~/.xsession-errors (LP: #431807)
1621 - launchpad.py: Link hardware data submission key if it exists. (LP: #424382)
1622
1623Bug fixes:
1624 - apport-cli: Fix crash with non-ASCII characters in prompts.
1625 - Fix "apport-bug symptomname" to actually work.
1626 - launchpad.py: Fix crash on invalid credentials file. Thanks to Marco
1627 Rodrigues for the initial patch! (LP: #414055)
1628
16291.9.1 (2009-09-22)
1630------------------
1631Bug fixes:
1632 - hookutils.py, attach_hardware(): Do not attach empty Pccardctl*.
1633 - apport/report.py, add_gdb_info(): Do not throw away stderr from gdb.
1634 - data/general-hooks/parse_segv.py:
1635 + Handle arithmetic wrapping correctly.
1636 + Handle empty base, scale, or index registers in disassembly.
1637 + Handle in/out ioport faults.
1638 - Various improvements to user-visible strings, thanks to Marco Rodrigues!
1639 (LP: #178507)
1640 - Various apport-retrace robustifications.
1641 - setup.py: Fix DistUtilsExtra version check. (LP: #428337)
1642 - hookutils.py, attach_gconf(): Do not overwrite previous values from other
1643 packages, thanks LoĂŻc Minier!
1644 - hookutils.py, attach_gconf(): Fix crash with nonexisting <applyto> tags.
1645
16461.9 (2009-09-08)
1647----------------
1648New features:
1649 - Add "do what I mean" mode to command line argument parsing (applies to all
1650 interfaces: -cli, -gtk, -kde). When giving a single argument and no options,
1651 determine the most likely mode, like reporting a bug against a symptom,
1652 package, executable name, or PID.
1653 - Add program "apport-bug" which determines the most appropriate user
1654 interface (GTK, KDE, CLI) and files a bug through it, using the single
1655 argument "do what I mean" mode. This is an improved version of Ubuntu's
1656 "ubuntu-bug" script.
1657
1658Bug fixes:
1659 - Update apport-cli manpage to current set of options and behaviour. Also
1660 point out that apport-gtk and apport-kde share the same CLI.
1661 - setup.py now installs apport-{gtk,kde} into $prefix/share/apport/, they are
1662 not supposed to be called directly. This also reflects the path which the
1663 .desktop files expect.
1664 - setup.py now installs the internal helper scripts like "kernel_crashdump",
1665 "apport", or "apportcheckresume" into $prefix/share/apport instead of
1666 $prefix/bin.
1667 - Update usage of gettext to work around Python bug of gettext() not returning
1668 unicodes, but str. Fixes UnicodeDecodeErrors on translated --help output.
1669
16701.8.2 (2009-09-05)
1671------------------
1672Bug fixes.
1673
16741.8.1 (2009-09-03)
1675------------------
1676Lots of bug fixes.
1677
16781.8 (2009-08-26)
1679----------------
1680New features:
1681 - Do not generally ignore SIGABRT any more. Try to extract the assertion
1682 message from the core dump, and add it as "AssertionMessage" field. Mark
1683 reports as unreportable if they do not have an assertion message and crashed
1684 with SIGABRT. This requires your glibc to have this patch:
1685 http://sourceware.org/git/?p=glibc.git;a=commitdiff;h=48dcd0ba
1686 - report.py, add_hooks_info(): Add optional package/srcpackage argument. Hooks
1687 can use that to change the affected package or call hooks from different
1688 packages.
1689 - KDE frontend implementation of ui_question_userpass(), for crash databases
1690 which need to ask for credentials.
1691 - hookutils.py: New funtion attach_wifi() to add wireless network related
1692 information to reports.
1693
1694Important bug fixes:
1695 - Fix the test suite on current kernels; test/crash previously often failed
1696 with python segfaults, since it killed the test processes too early.
1697
16981.7 (2009-08-05):
1699-----------------
1700New features:
1701 - Support for "symptom" scripts, which figure out the package for a bug report
1702 based on interactive questions.
1703
17041.6 (2009-07-15)
1705----------------
1706New features:
1707 - Integrate analysis and retracing of kernel vmcore crashes with the "crash"
1708 tool. Courtesy of Michael Vogt.
1709
1710Various little bug fixes.
1711
17121.5 (2009-06-29)
1713----------------
1714New features:
1715 - Drop all Makefiles, po/POTFILES.in, and most code from setup.py, and use
1716 DistUtilsExtras.auto which "just does the right thing" for most build system
1717 tasks. This requires python-distutils-extra >= 2.2, see
1718 https://launchpad.net/python-distutils-extra
1719
1720Cleanup:
1721 - Move all test scripts into test/, to unclutter source tree.
1722 - setup.py now auto-detects the required packaging backend if
1723 apport/packaging_impl.py is not manually installed.
1724
17251.4 (2009-06-26)
1726----------------
1727New features:
1728 - Replaced Qt4 frontend with a KDE frontend for better KDE integration.
1729
1730Major bug fixes:
1731 - packaging-apt-dpkg.py: Add backwards compatibility code for python-apt <
1732 0.7.9 to not break backportability.
1733
17341.3 (2009-06-10)
1735----------------
1736New features:
1737- Interactive package hooks:
1738 * Add apport.ui.HookUI class which provides GUI functionality such as yes/no
1739 questions or file dialogs to hooks.
1740 * add_info() in package hooks now can (optionally) take a second argument which
1741 is the HookUI instance.
1742 * See doc/package-hooks.txt for details.
1743- New function apport.hookutils.root_command_output() to run a command as root,
1744 through gksu/kdesudo/sudo, depending on the desktop environment.
1745- Add general hook for analyzing reason of a segfault.
1746
1747Bug fixes:
1748- Drop "UnsupportableReason" field, it is too similar to UnreportableReason and
1749 just confusing.
1750- Report key names can now contain dashes ('-') and underscores ('_').
1751 (LP #380811)
1752
17531.2.1 (2009-05-15)
1754------------------
1755Bug fixes:
1756- Fix setup.py and PO file merging for recent .glade -> .ui renaming.
1757
1758Translations:
1759- Update German translations.
1760
17611.2.0 (2009-05-15)
1762------------------
1763Moving away from deprecated APIs:
1764- packaging-apt-dpkg.py: Use python-apt >= 0.7.9 official API and drop usage of
1765 internal symbols.
1766- hookutils.py: Drop hal related functions and queries, replace with udev
1767 database, udev log file, and DMI information from sysfs.
1768- gtk UI: Convert from libglade to gtk.Builder.
1769
1770Bug fixes:
1771- hookutils.py: Drop /proc/version_signature collection, it is Ubuntu specific.
1772- apportcheckresume: Fix log collection from pm-utils.
1773- Fix various crashes and report properties for reporting against uninstalled
1774 packages.
1775
17761.1.1 (2009-04-30)
1777------------------
1778Security fix:
1779- etc/cron.daily/apport: Only attempt to remove files and symlinks, do not
1780 descend into subdirectories of /var/crash/. Doing so might be exploited by a
1781 race condition between find traversing a huge directory tree, changing an
1782 existing subdir into a symlink to e. g. /etc/, and finally getting that piped
1783 to rm. This also changes the find command to not use GNU extensions. Thanks
1784 to Stephane Chazelas for discovering this! (LP #357024, CVE-2009-1295)
1785
1786Bug fixes:
1787- launchpad.py: Send and read Date: field again, reverting r1128; it is useful
1788 after all. (LP #349139)
1789- Only add ProcAttrCurrent to reports if it is not "unconfined", to remove some
1790 noise from reports.
1791- Detect invalid PIDs in the UI (such as for kernel processes) and give a
1792 friendly error message instead of silently doing nothing. (LP #360608)
1793- Always run common hooks, and run source package hooks if we do not have a
1794 binary package name. (LP #350131)
1795- launchpad.py: Consider socket errors when connecting as transient, so
1796 that crash-digger doesn't stop completely on them.
1797
17981.1 (2009-04-20)
1799----------------
1800New features:
1801- Add hookutils methods for attaching relevant packages, greatly improve
1802 attach_alsa() for sound problem debugging.
1803- Move launchpad crash database implementation from ever-breaking
1804 python-launchpad-bugs (screenscraping) to launchpadlib (official and stable
1805 Launchpad API).
1806
1807Bug fixes:
1808- Drop some remaining distro specific pieces of code.
1809- Add new field Report.pid which gets set on add_proc_info() and can be used by
1810 hooks.
1811- setup.py: Properly clean up all generated files, install missing
1812 mimetypes/text-x-apport.svg icon symlink.
1813- Add README file.
1814- Add translations from Launchpad.
1815- Remove preloadlib/*; it's undermaintained, and not really useful any more
1816 these days.
1817- Various bug fixes; most visible being the misnamed etc/default/apport.default
1818 file (which should just be etc/default/apport).
1819
18201.0 (2009-04-06)
1821----------------
1822First upstream release, based on Ubuntu packaging branch; that had been the
1823de-facto trunk for many years, but this becomes unpractical with several
1824distributions using it now.
01825
=== renamed file 'NEWS' => 'NEWS.moved'
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2013-04-11 13:56:24 +0000
@@ -0,0 +1,87 @@
1Apport crash detection/reporting
2================================
3
4Apport intercepts Program crashes, collects debugging information about the
5crash and the operating system environment, and sends it to bug trackers in a
6standardized form. It also offers the user to report a bug about a package,
7with again collecting as much information about it as possible.
8
9It currently supports
10
11 - Crashes from standard signals (SIGSEGV, SIGILL, etc.) through the kernel
12 coredump handler (in piping mode)
13 - Unhandled Python exceptions
14 - GTK, KDE, and command line user interfaces
15 - Packages can ship hooks for collecting specific data (such as
16 /var/log/Xorg.0.log for X.org, or modified gconf settings for GNOME
17 programs)
18 - apt/dpkg and rpm backend (in production use in Ubuntu and OpenSUSE)
19 - Reprocessing a core dump and debug symbols for post-mortem (and preferably
20 server-side) generation of fully symbolic stack traces (apport-retrace)
21 - Reporting bugs to Launchpad (more backends can be easily added)
22
23Please see https://wiki.ubuntu.com/Apport for more details and further links.
24The files in doc/ document particular details such as package hooks, crash
25database configuration, or the internal data format.
26
27Temporarily enabling apport
28===========================
29
30The automatic crash interception component of apport is disabled by default in
31stable releases for a number of reasons [1]. To enable it just for the current
32session, do
33
34 sudo service apport start force_start=1
35
36Then you can simply trigger the crash again, and Apport's dialog will show up
37with instructions to report a bug with traces. Apport will be automatically
38disabled on next start.
39
40If you are triaging bugs, this is the best way to get traces from bug reporters
41that didn't use Apport in the first place.
42
43To enable it permanently, do:
44
45 sudo nano /etc/default/apport
46
47and change enabled from "0" to "1".
48
49[1] https://wiki.ubuntu.com/Apport#How%20to%20enable%20apport
50
51Crash notification on servers
52=============================
53
54You can add
55
56if [ -x /usr/bin/apport-cli ]; then
57 if groups | grep -qw admin && /usr/share/apport/apport-checkreports -s; then
58 cat <<-EOF
59 You have new problem reports waiting in /var/crash.
60 To take a look at them, run "sudo apport-cli".
61
62 EOF
63 elif /usr/share/apport/apport-checkreports; then
64 cat <<-EOF
65 You have new problem reports waiting in /var/crash.
66 To take a look at them, run "apport-cli".
67
68 EOF
69 fi
70fi
71
72to your ~/.bashrc to get automatic notification of problem reports.
73
74Contributing
75============
76
77Please visit Apport's Launchpad homepage for links to the source code revision
78control, the bug tracker, translations, downloads, etc.:
79
80 https://launchpad.net/apport
81
82The preferred mode of operation for Linux distribution packagers is to create
83their own branch from 'trunk' and add the distro specific packaging and patches
84to it. Please send patches which are applicable to trunk as merge requests or
85bug reports, so that (1) other distributions can benefit from them as well, and
86(2) you reduce the code delta to upstream.
87
088
=== renamed file 'README' => 'README.moved'
=== added file 'TODO'
--- TODO 1970-01-01 00:00:00 +0000
+++ TODO 2013-04-11 13:56:24 +0000
@@ -0,0 +1,21 @@
1apport:
2 - check crashes of root processes with dropped privs in test suite
3
4dup detection:
5 - add merging of two databases -> needs time stamp of last change
6
7GUI:
8 - point out bug privacy and to leave it private by default
9
10hooks:
11 - add hooks which run during program crash, to collect more runtime data
12
13hookutils:
14- run hooks for related packages in attach_related_packages
15
16apt-dpkg backend:
17- use python-apt's Version.get_source() instead of apt-get source
18
19apport-retrace:
20- add "gdb" mode: apport-gdb /usr/bin/myprog -> imply -S system, throw you into
21 gdb session with debug symbols
022
=== renamed file 'TODO' => 'TODO.moved'
=== added directory 'apport'
=== renamed directory 'apport' => 'apport.moved'
=== added file 'apport/REThread.py'
--- apport/REThread.py 1970-01-01 00:00:00 +0000
+++ apport/REThread.py 2013-04-11 13:56:24 +0000
@@ -0,0 +1,65 @@
1'''Enhanced Thread with support for return values and exception propagation.'''
2
3# Copyright (C) 2007 Canonical Ltd.
4# Author: Martin Pitt <martin.pitt@ubuntu.com>
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the
8# Free Software Foundation; either version 2 of the License, or (at your
9# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
10# the full text of the license.
11
12import threading, sys
13
14
15class REThread(threading.Thread):
16 '''Thread with return values and exception propagation.'''
17
18 def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
19 '''Initialize Thread, identical to threading.Thread.__init__().'''
20
21 threading.Thread.__init__(self, group, target, name, args, kwargs)
22 self.__target = target
23 self.__args = args
24 self.__kwargs = kwargs
25 self._retval = None
26 self._exception = None
27
28 def run(self):
29 '''Run target function, identical to threading.Thread.run().'''
30
31 if self.__target:
32 try:
33 self._retval = self.__target(*self.__args, **self.__kwargs)
34 except:
35 if sys:
36 self._exception = sys.exc_info()
37
38 def return_value(self):
39 '''Return value from target function.
40
41 This can only be called after the thread has finished, i. e. when
42 isAlive() is False and did not terminate with an exception.
43 '''
44 assert not self.isAlive()
45 assert not self._exception
46 return self._retval
47
48 def exc_info(self):
49 '''Return (type, value, traceback) of the exception caught in run().'''
50
51 return self._exception
52
53 def exc_raise(self):
54 '''Raise the exception caught in the thread.
55
56 Do nothing if no exception was caught.
57 '''
58 if self._exception:
59 # there is no syntax which both Python 2 and 3 parse, so we need a
60 # hack using exec() here
61 # Python 3:
62 if sys.version > '3':
63 raise self._exception[1].with_traceback(self._exception[2])
64 else:
65 exec('raise self._exception[0], self._exception[1], self._exception[2]')
066
=== added file 'apport/__init__.py'
--- apport/__init__.py 1970-01-01 00:00:00 +0000
+++ apport/__init__.py 2013-04-11 13:56:24 +0000
@@ -0,0 +1,73 @@
1import sys
2import os
3import time
4
5from apport.report import Report
6
7from apport.packaging_impl import impl as packaging
8
9Report # pyflakes
10packaging # pyflakes
11
12# fix gettext to output proper unicode strings
13import gettext
14
15
16def unicode_gettext(str):
17 trans = gettext.gettext(str)
18 if type(trans) == type(b''):
19 return trans.decode('UTF-8')
20 else:
21 return trans
22
23
24def log(message, timestamp=False):
25 '''Log the given string to stdout. Prepend timestamp if requested'''
26
27 if timestamp:
28 sys.stdout.write('%s: ' % time.strftime('%x %X'))
29 print(message)
30
31
32def fatal(msg, *args):
33 '''Print out an error message and exit the program.'''
34
35 error(msg, *args)
36 sys.exit(1)
37
38
39def error(msg, *args):
40 '''Print out an error message.'''
41
42 if sys.stderr:
43 sys.stderr.write('ERROR: ')
44 sys.stderr.write(msg % args)
45 sys.stderr.write('\n')
46
47
48def warning(msg, *args):
49 '''Print out an warning message.'''
50
51 if sys.stderr:
52 sys.stderr.write('WARNING: ')
53 sys.stderr.write(msg % args)
54 sys.stderr.write('\n')
55
56
57def memdbg(checkpoint):
58 '''Print current memory usage.
59
60 This is only done if $APPORT_MEMDEBUG is set.
61 '''
62 if not 'APPORT_MEMDEBUG' in os.environ or not sys.stderr:
63 return
64
65 memstat = {}
66 with open('/proc/self/status') as f:
67 for l in f:
68 if l.startswith('Vm'):
69 (field, size, unit) = l.split()
70 memstat[field[:-1]] = int(size) / 1024.
71
72 sys.stderr.write('Size: %.1f MB, RSS: %.1f MB, Stk: %.1f MB @ %s\n' %
73 (memstat['VmSize'], memstat['VmRSS'], memstat['VmStk'], checkpoint))
074
=== added file 'apport/com.ubuntu.apport.policy.in'
--- apport/com.ubuntu.apport.policy.in 1970-01-01 00:00:00 +0000
+++ apport/com.ubuntu.apport.policy.in 2013-04-11 13:56:24 +0000
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE policyconfig PUBLIC
3 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
4 "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
5<policyconfig>
6 <vendor>Apport</vendor>
7 <vendor_url>https://wiki.ubuntu.com/Apport</vendor_url>
8 <icon_name>apport</icon_name>
9
10 <action id="com.ubuntu.apport.root-info">
11 <_description>Collect system information</_description>
12 <_message>Authentication is required to collect system information for this problem report</_message>
13 <annotate key="org.freedesktop.policykit.exec.path">/usr/share/apport/root_info_wrapper</annotate>
14 <!-- <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate> -->
15 <defaults>
16 <allow_any>auth_admin</allow_any>
17 <allow_inactive>auth_admin</allow_inactive>
18 <allow_active>auth_admin</allow_active>
19 </defaults>
20 </action>
21
22 <action id="com.ubuntu.apport.apport-gtk-root">
23 <_description>System problem reports</_description>
24 <_message>Please enter your password to access problem reports of system programs</_message>
25 <annotate key="org.freedesktop.policykit.exec.path">/usr/share/apport/apport-gtk</annotate>
26 <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
27 <defaults>
28 <allow_any>auth_admin</allow_any>
29 <allow_inactive>auth_admin</allow_inactive>
30 <allow_active>auth_admin</allow_active>
31 </defaults>
32 </action>
33
34</policyconfig>
035
=== added file 'apport/crashdb.py'
--- apport/crashdb.py 1970-01-01 00:00:00 +0000
+++ apport/crashdb.py 2013-04-11 13:56:24 +0000
@@ -0,0 +1,856 @@
1'''Abstract crash database interface.'''
2
3# Copyright (C) 2007 - 2009 Canonical Ltd.
4# Author: Martin Pitt <martin.pitt@ubuntu.com>
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the
8# Free Software Foundation; either version 2 of the License, or (at your
9# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
10# the full text of the license.
11
12import os, os.path, sys, shutil
13
14try:
15 from exceptions import Exception
16 from urllib import quote_plus, urlopen
17 URLError = IOError
18 (quote_plus, urlopen) # pyflakes
19except ImportError:
20 # python 3
21 from functools import cmp_to_key
22 from urllib.parse import quote_plus
23 from urllib.request import urlopen
24 from urllib.error import URLError
25
26import apport
27
28
29def _u(str):
30 '''Convert str to an unicode if it isn't already.'''
31
32 if type(str) == type(b''):
33 return str.decode('UTF-8', 'ignore')
34 return str
35
36
37class CrashDatabase:
38 def __init__(self, auth_file, options):
39 '''Initialize crash database connection.
40
41 You need to specify an implementation specific file with the
42 authentication credentials for retracing access for download() and
43 update(). For upload() and get_comment_url() you can use None.
44
45 options is a dictionary with additional settings from crashdb.conf; see
46 get_crashdb() for details.
47 '''
48 self.auth_file = auth_file
49 self.options = options
50 self.duplicate_db = None
51
52 def get_bugpattern_baseurl(self):
53 '''Return the base URL for bug patterns.
54
55 See apport.report.Report.search_bug_patterns() for details. If this
56 function returns None, bug patterns are disabled.
57 '''
58 return self.options.get('bug_pattern_url')
59
60 def accepts(self, report):
61 '''Check if this report can be uploaded to this database.
62
63 Crash databases might limit the types of reports they get with e. g.
64 the "problem_types" option.
65 '''
66 if 'problem_types' in self.options:
67 return report.get('ProblemType') in self.options['problem_types']
68
69 return True
70
71 #
72 # API for duplicate detection
73 #
74 # Tests are in apport/crashdb_impl/memory.py.
75
76 def init_duplicate_db(self, path):
77 '''Initialize duplicate database.
78
79 path specifies an SQLite database. It will be created if it does not
80 exist yet.
81 '''
82 import sqlite3 as dbapi2
83
84 assert dbapi2.paramstyle == 'qmark', \
85 'this module assumes qmark dbapi parameter style'
86
87 self.format_version = 3
88
89 init = not os.path.exists(path) or path == ':memory:' or \
90 os.path.getsize(path) == 0
91 self.duplicate_db = dbapi2.connect(path, timeout=7200)
92
93 if init:
94 cur = self.duplicate_db.cursor()
95 cur.execute('CREATE TABLE version (format INTEGER NOT NULL)')
96 cur.execute('INSERT INTO version VALUES (?)', [self.format_version])
97
98 cur.execute('''CREATE TABLE crashes (
99 signature VARCHAR(255) NOT NULL,
100 crash_id INTEGER NOT NULL,
101 fixed_version VARCHAR(50),
102 last_change TIMESTAMP,
103 CONSTRAINT crashes_pk PRIMARY KEY (crash_id))''')
104
105 cur.execute('''CREATE TABLE address_signatures (
106 signature VARCHAR(1000) NOT NULL,
107 crash_id INTEGER NOT NULL,
108 CONSTRAINT address_signatures_pk PRIMARY KEY (signature))''')
109
110 self.duplicate_db.commit()
111
112 # verify integrity
113 cur = self.duplicate_db.cursor()
114 cur.execute('PRAGMA integrity_check')
115 result = cur.fetchall()
116 if result != [('ok',)]:
117 raise SystemError('Corrupt duplicate db:' + str(result))
118
119 try:
120 cur.execute('SELECT format FROM version')
121 result = cur.fetchone()
122 except self.duplicate_db.OperationalError as e:
123 if 'no such table' in str(e):
124 # first db format did not have version table yet
125 result = [0]
126 if result[0] > self.format_version:
127 raise SystemError('duplicate DB has unknown format %i' % result[0])
128 if result[0] < self.format_version:
129 print('duplicate db has format %i, upgrading to %i' %
130 (result[0], self.format_version))
131 self._duplicate_db_upgrade(result[0])
132
133 def check_duplicate(self, id, report=None):
134 '''Check whether a crash is already known.
135
136 If the crash is new, it will be added to the duplicate database and the
137 function returns None. If the crash is already known, the function
138 returns a pair (crash_id, fixed_version), where fixed_version might be
139 None if the crash is not fixed in the latest version yet. Depending on
140 whether the version in report is smaller than/equal to the fixed
141 version or larger, this calls close_duplicate() or mark_regression().
142
143 If the report does not have a valid crash signature, this function does
144 nothing and just returns None.
145
146 By default, the report gets download()ed, but for performance reasons
147 it can be explicitly passed to this function if it is already available.
148 '''
149 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
150
151 if not report:
152 report = self.download(id)
153
154 self._mark_dup_checked(id, report)
155
156 if 'DuplicateSignature' in report:
157 sig = report['DuplicateSignature']
158 else:
159 sig = report.crash_signature()
160 existing = []
161 if sig:
162 # use real duplicate signature
163 existing = self._duplicate_search_signature(sig, id)
164
165 if existing:
166 # update status of existing master bugs
167 for (ex_id, _) in existing:
168 self._duplicate_db_sync_status(ex_id)
169 existing = self._duplicate_search_signature(sig, id)
170
171 try:
172 report_package_version = report['Package'].split()[1]
173 except (KeyError, IndexError):
174 report_package_version = None
175
176 # check the existing IDs whether there is one that is unfixed or not
177 # older than the report's package version; if so, we have a duplicate.
178 master_id = None
179 master_ver = None
180 for (ex_id, ex_ver) in existing:
181 if not ex_ver or not report_package_version or apport.packaging.compare_versions(report_package_version, ex_ver) < 0:
182 master_id = ex_id
183 master_ver = ex_ver
184 break
185 else:
186 # if we did not find a new enough open master report,
187 # we have a regression of the latest fix. Mark it so, and create a
188 # new unfixed ID for it later on
189 if existing:
190 self.mark_regression(id, existing[-1][0])
191
192 # now query address signatures, they might turn up another duplicate
193 # (not necessarily the same, due to Stacktraces sometimes being
194 # slightly different)
195 addr_sig = report.crash_signature_addresses()
196 if addr_sig:
197 addr_match = self._duplicate_search_address_signature(addr_sig)
198 if addr_match and addr_match != master_id:
199 if master_id is None:
200 # we have a duplicate only identified by address sig, close it
201 master_id = addr_match
202 else:
203 # our bug is a dupe of two different masters, one from
204 # symbolic, the other from addr matching (see LP#943117);
205 # make them all duplicates of each other, using the lower
206 # number as master
207 if master_id < addr_match:
208 self.close_duplicate(report, addr_match, master_id)
209 self._duplicate_db_merge_id(addr_match, master_id)
210 else:
211 self.close_duplicate(report, master_id, addr_match)
212 self._duplicate_db_merge_id(master_id, addr_match)
213 master_id = addr_match
214 master_ver = None # no version tracking for address signatures yet
215
216 if master_id is not None and master_id != id:
217 if addr_sig:
218 self._duplicate_db_add_address_signature(addr_sig, master_id)
219 self.close_duplicate(report, id, master_id)
220 return (master_id, master_ver)
221
222 # no duplicate detected; create a new record for the ID if we don't have one already
223 if sig:
224 cur = self.duplicate_db.cursor()
225 cur.execute('SELECT count(*) FROM crashes WHERE crash_id == ?', [id])
226 count_id = cur.fetchone()[0]
227 if count_id == 0:
228 cur.execute('INSERT INTO crashes VALUES (?, ?, ?, CURRENT_TIMESTAMP)', (_u(sig), id, None))
229 self.duplicate_db.commit()
230 if addr_sig:
231 self._duplicate_db_add_address_signature(addr_sig, id)
232
233 return None
234
235 def known(self, report):
236 '''Check if the crash db already knows about the crash signature.
237
238 Check if the report has a DuplicateSignature, crash_signature(), or
239 StacktraceAddressSignature, and ask the database whether the problem is
240 already known. If so, return an URL where the user can check the status
241 or subscribe (if available), or just return True if the report is known
242 but there is no public URL. In that case the report will not be
243 uploaded (i. e. upload() will not be called).
244
245 Return None if the report does not have any signature or the crash
246 database does not support checking for duplicates on the client side.
247
248 The default implementation uses a text file format generated by
249 duplicate_db_publish() at an URL specified by the "dupdb_url" option.
250 Subclasses are free to override this with a custom implementation, such
251 as a real database lookup.
252 '''
253 if not self.options.get('dupdb_url'):
254 return None
255
256 for kind in ('sig', 'address'):
257 # get signature
258 if kind == 'sig':
259 if 'DuplicateSignature' in report:
260 sig = report['DuplicateSignature']
261 else:
262 sig = report.crash_signature()
263 else:
264 sig = report.crash_signature_addresses()
265
266 if not sig:
267 continue
268
269 # build URL where the data should be
270 h = self.duplicate_sig_hash(sig)
271 if not h:
272 return None
273
274 # the hash is already quoted, but we really want to open the quoted
275 # file names; as urlopen() unquotes, we need to double-quote here
276 # again so that urlopen() sees the single-quoted file names
277 url = os.path.join(self.options['dupdb_url'], kind, quote_plus(h))
278
279 # read data file
280 try:
281 f = urlopen(url)
282 contents = f.read().decode('UTF-8')
283 f.close()
284 if '<title>404 Not Found' in contents:
285 continue
286 except (IOError, URLError):
287 # does not exist, failed to load, etc.
288 continue
289
290 # now check if we find our signature
291 for line in contents.splitlines():
292 try:
293 id, s = line.split(None, 1)
294 id = int(id)
295 except ValueError:
296 continue
297 if s == sig:
298 result = self.get_id_url(report, id)
299 if not result:
300 # if we can't have an URL, just report as "known"
301 result = '1'
302 return result
303
304 return None
305
306 def duplicate_db_fixed(self, id, version):
307 '''Mark given crash ID as fixed in the duplicate database.
308
309 version specifies the package version the crash was fixed in (None for
310 'still unfixed').
311 '''
312 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
313
314 cur = self.duplicate_db.cursor()
315 n = cur.execute('UPDATE crashes SET fixed_version = ?, last_change = CURRENT_TIMESTAMP WHERE crash_id = ?',
316 (version, id))
317 assert n.rowcount == 1
318 self.duplicate_db.commit()
319
320 def duplicate_db_remove(self, id):
321 '''Remove crash from the duplicate database.
322
323 This happens when a report got rejected or manually duplicated.
324 '''
325 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
326
327 cur = self.duplicate_db.cursor()
328 cur.execute('DELETE FROM crashes WHERE crash_id = ?', [id])
329 cur.execute('DELETE FROM address_signatures WHERE crash_id = ?', [id])
330 self.duplicate_db.commit()
331
332 def duplicate_db_change_master_id(self, old_id, new_id):
333 '''Change a crash ID.'''
334
335 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
336
337 cur = self.duplicate_db.cursor()
338 cur.execute('UPDATE crashes SET crash_id = ?, last_change = CURRENT_TIMESTAMP WHERE crash_id = ?',
339 [new_id, old_id])
340 cur.execute('UPDATE address_signatures SET crash_id = ? WHERE crash_id = ?',
341 [new_id, old_id])
342 self.duplicate_db.commit()
343
344 def duplicate_db_publish(self, dir):
345 '''Create text files suitable for www publishing.
346
347 Create a number of text files in the given directory which Apport
348 clients can use to determine whether a problem is already reported to
349 the database, through the known() method. This directory is suitable
350 for publishing to the web.
351
352 The database is indexed by the first two fields of the duplicate or
353 crash signature, to avoid having to download the entire database every
354 time.
355
356 If the directory already exists, it will be updated. The new content is
357 built in a new directory which is the given one with ".new" appended,
358 then moved to the given name in an almost atomic way.
359 '''
360 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
361
362 # first create the temporary new dir; if that fails, nothing has been
363 # changed and we fail early
364 out = dir + '.new'
365 os.mkdir(out)
366
367 # crash addresses
368 addr_base = os.path.join(out, 'address')
369 os.mkdir(addr_base)
370 cur_hash = None
371 cur_file = None
372
373 cur = self.duplicate_db.cursor()
374
375 cur.execute('SELECT * from address_signatures ORDER BY signature')
376 for (sig, id) in cur.fetchall():
377 h = self.duplicate_sig_hash(sig)
378 if h is None:
379 # some entries can't be represented in a single line
380 continue
381 if h != cur_hash:
382 cur_hash = h
383 if cur_file:
384 cur_file.close()
385 cur_file = open(os.path.join(addr_base, cur_hash), 'w')
386
387 cur_file.write('%i %s\n' % (id, sig))
388
389 if cur_file:
390 cur_file.close()
391
392 # duplicate signatures
393 sig_base = os.path.join(out, 'sig')
394 os.mkdir(sig_base)
395 cur_hash = None
396 cur_file = None
397
398 cur.execute('SELECT signature, crash_id from crashes ORDER BY signature')
399 for (sig, id) in cur.fetchall():
400 h = self.duplicate_sig_hash(sig)
401 if h is None:
402 # some entries can't be represented in a single line
403 continue
404 if h != cur_hash:
405 cur_hash = h
406 if cur_file:
407 cur_file.close()
408 cur_file = open(os.path.join(sig_base, cur_hash), 'wb')
409
410 cur_file.write(('%i %s\n' % (id, sig)).encode('UTF-8'))
411
412 if cur_file:
413 cur_file.close()
414
415 # switch over tree; this is as atomic as we can be with directories
416 if os.path.exists(dir):
417 os.rename(dir, dir + '.old')
418 os.rename(out, dir)
419 if os.path.exists(dir + '.old'):
420 shutil.rmtree(dir + '.old')
421
422 def _duplicate_db_upgrade(self, cur_format):
423 '''Upgrade database to current format'''
424
425 # Format 3 added a primary key which can't be done as an upgrade in
426 # SQLite
427 if cur_format < 3:
428 raise SystemError('Cannot upgrade database from format earlier than 3')
429
430 cur = self.duplicate_db.cursor()
431
432 cur.execute('UPDATE version SET format = ?', (cur_format,))
433 self.duplicate_db.commit()
434
435 assert cur_format == self.format_version
436
437 def _duplicate_search_signature(self, sig, id):
438 '''Look up signature in the duplicate db.
439
440 Return [(id, fixed_version)] tuple list.
441
442 There might be several matches if a crash has been reintroduced in a
443 later version. The results are sorted so that the highest fixed version
444 comes first, and "unfixed" being the last result.
445
446 id is the bug we are looking to find a duplicate for. The result will
447 never contain id, to avoid marking a bug as a duplicate of itself if a
448 bug is reprocessed more than once.
449 '''
450 cur = self.duplicate_db.cursor()
451 cur.execute('SELECT crash_id, fixed_version FROM crashes WHERE signature = ? AND crash_id <> ?', [_u(sig), id])
452 existing = cur.fetchall()
453
454 def cmp(x, y):
455 x = x[1]
456 y = y[1]
457 if x == y:
458 return 0
459 if x == '':
460 if y is None:
461 return -1
462 else:
463 return 1
464 if y == '':
465 if x is None:
466 return 1
467 else:
468 return -1
469 if x is None:
470 return 1
471 if y is None:
472 return -1
473 return apport.packaging.compare_versions(x, y)
474
475 if sys.version[0] >= '3':
476 existing.sort(key=cmp_to_key(cmp))
477 else:
478 existing.sort(cmp=cmp)
479
480 return existing
481
482 def _duplicate_search_address_signature(self, sig):
483 '''Return ID for crash address signature.
484
485 Return None if signature is unknown.
486 '''
487 if not sig:
488 return None
489
490 cur = self.duplicate_db.cursor()
491
492 cur.execute('SELECT crash_id FROM address_signatures WHERE signature == ?', [sig])
493 existing_ids = cur.fetchall()
494 assert len(existing_ids) <= 1
495 if existing_ids:
496 return existing_ids[0][0]
497 else:
498 return None
499
500 def _duplicate_db_dump(self, with_timestamps=False):
501 '''Return the entire duplicate database as a dictionary.
502
503 The returned dictionary maps "signature" to (crash_id, fixed_version)
504 pairs.
505
506 If with_timestamps is True, then the map will contain triples
507 (crash_id, fixed_version, last_change) instead.
508
509 This is mainly useful for debugging and test suites.
510 '''
511 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
512
513 dump = {}
514 cur = self.duplicate_db.cursor()
515 cur.execute('SELECT * FROM crashes')
516 for (sig, id, ver, last_change) in cur:
517 if with_timestamps:
518 dump[sig] = (id, ver, last_change)
519 else:
520 dump[sig] = (id, ver)
521 return dump
522
523 def _duplicate_db_sync_status(self, id):
524 '''Update the duplicate db to the reality of the report in the crash db.
525
526 This uses get_fixed_version() to get the status of the given crash.
527 An invalid ID gets removed from the duplicate db, and a crash which got
528 fixed is marked as such in the database.
529 '''
530 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
531
532 cur = self.duplicate_db.cursor()
533 cur.execute('SELECT fixed_version FROM crashes WHERE crash_id = ?', [id])
534 db_fixed_version = cur.fetchone()
535 if not db_fixed_version:
536 return
537 db_fixed_version = db_fixed_version[0]
538
539 real_fixed_version = self.get_fixed_version(id)
540
541 # crash got rejected
542 if real_fixed_version == 'invalid':
543 print('DEBUG: bug %i was invalidated, removing from database' % id)
544 self.duplicate_db_remove(id)
545 return
546
547 # crash got fixed
548 if not db_fixed_version and real_fixed_version:
549 print('DEBUG: bug %i got fixed in version %s, updating database' % (id, real_fixed_version))
550 self.duplicate_db_fixed(id, real_fixed_version)
551 return
552
553 # crash got reopened
554 if db_fixed_version and not real_fixed_version:
555 print('DEBUG: bug %i got reopened, dropping fixed version %s from database' % (id, db_fixed_version))
556 self.duplicate_db_fixed(id, real_fixed_version)
557 return
558
559 def _duplicate_db_add_address_signature(self, sig, id):
560 # sanity check
561 existing = self._duplicate_search_address_signature(sig)
562 if existing:
563 if existing != id:
564 raise SystemError('ID %i has signature %s, but database already has that signature for ID %i' % (
565 id, sig, existing))
566 else:
567 cur = self.duplicate_db.cursor()
568 cur.execute('INSERT INTO address_signatures VALUES (?, ?)', (_u(sig), id))
569 self.duplicate_db.commit()
570
571 def _duplicate_db_merge_id(self, dup, master):
572 '''Merge two crash IDs.
573
574 This is necessary when having to mark a bug as a duplicate if it
575 already is in the duplicate DB.
576 '''
577 assert self.duplicate_db, 'init_duplicate_db() needs to be called before'
578
579 cur = self.duplicate_db.cursor()
580 cur.execute('DELETE FROM crashes WHERE crash_id = ?', [dup])
581 cur.execute('UPDATE address_signatures SET crash_id = ? WHERE crash_id = ?',
582 [master, dup])
583 self.duplicate_db.commit()
584
585 @classmethod
586 def duplicate_sig_hash(klass, sig):
587 '''Create a www/URL proof hash for a duplicate signature'''
588
589 # cannot hash multi-line custom duplicate signatures
590 if '\n' in sig:
591 return None
592
593 # custom DuplicateSignatures have a free format, split off first word
594 i = sig.split(' ', 1)[0]
595 # standard crash/address signatures use ':' as field separator, usually
596 # for ExecutableName:Signal
597 i = '_'.join(i.split(':', 2)[:2])
598 # we manually quote '/' to make them nicer to read
599 i = i.replace('/', '_')
600 i = quote_plus(i.encode('UTF-8'))
601 # avoid too long file names
602 i = i[:200]
603 return i
604
605 #
606 # Abstract functions that need to be implemented by subclasses
607 #
608
609 def upload(self, report, progress_callback=None):
610 '''Upload given problem report return a handle for it.
611
612 This should happen noninteractively.
613
614 If the implementation supports it, and a function progress_callback is
615 passed, that is called repeatedly with two arguments: the number of
616 bytes already sent, and the total number of bytes to send. This can be
617 used to provide a proper upload progress indication on frontends.
618
619 Implementations ought to "assert self.accepts(report)". The UI logic
620 already prevents uploading a report to a database which does not accept
621 it, but for third-party users of the API this should still be checked.
622
623 This method can raise a NeedsCredentials exception in case of failure.
624 '''
625 raise NotImplementedError('this method must be implemented by a concrete subclass')
626
627 def get_comment_url(self, report, handle):
628 '''Return an URL that should be opened after report has been uploaded
629 and upload() returned handle.
630
631 Should return None if no URL should be opened (anonymous filing without
632 user comments); in that case this function should do whichever
633 interactive steps it wants to perform.
634 '''
635 raise NotImplementedError('this method must be implemented by a concrete subclass')
636
637 def get_id_url(self, report, id):
638 '''Return URL for a given report ID.
639
640 The report is passed in case building the URL needs additional
641 information from it, such as the SourcePackage name.
642
643 Return None if URL is not available or cannot be determined.
644 '''
645 raise NotImplementedError('this method must be implemented by a concrete subclass')
646
647 def download(self, id):
648 '''Download the problem report from given ID and return a Report.'''
649
650 raise NotImplementedError('this method must be implemented by a concrete subclass')
651
652 def update(self, id, report, comment, change_description=False,
653 attachment_comment=None, key_filter=None):
654 '''Update the given report ID with all data from report.
655
656 This creates a text comment with the "short" data (see
657 ProblemReport.write_mime()), and creates attachments for all the
658 bulk/binary data.
659
660 If change_description is True, and the crash db implementation supports
661 it, the short data will be put into the description instead (like in a
662 new bug).
663
664 comment will be added to the "short" data. If attachment_comment is
665 given, it will be added to the attachment uploads.
666
667 If key_filter is a list or set, then only those keys will be added.
668 '''
669 raise NotImplementedError('this method must be implemented by a concrete subclass')
670
671 def update_traces(self, id, report, comment=''):
672 '''Update the given report ID for retracing results.
673
674 This updates Stacktrace, ThreadStacktrace, StacktraceTop,
675 and StacktraceSource. You can also supply an additional comment.
676 '''
677 self.update(id, report, comment, key_filter=[
678 'Stacktrace', 'ThreadStacktrace', 'StacktraceSource', 'StacktraceTop'])
679
680 def set_credentials(self, username, password):
681 '''Set username and password.'''
682
683 raise NotImplementedError('this method must be implemented by a concrete subclass')
684
685 def get_distro_release(self, id):
686 '''Get 'DistroRelease: <release>' from the report ID.'''
687
688 raise NotImplementedError('this method must be implemented by a concrete subclass')
689
690 def get_unretraced(self):
691 '''Return set of crash IDs which have not been retraced yet.
692
693 This should only include crashes which match the current host
694 architecture.
695 '''
696 raise NotImplementedError('this method must be implemented by a concrete subclass')
697
698 def get_dup_unchecked(self):
699 '''Return set of crash IDs which need duplicate checking.
700
701 This is mainly useful for crashes of scripting languages such as
702 Python, since they do not need to be retraced. It should not return
703 bugs that are covered by get_unretraced().
704 '''
705 raise NotImplementedError('this method must be implemented by a concrete subclass')
706
707 def get_unfixed(self):
708 '''Return an ID set of all crashes which are not yet fixed.
709
710 The list must not contain bugs which were rejected or duplicate.
711
712 This function should make sure that the returned list is correct. If
713 there are any errors with connecting to the crash database, it should
714 raise an exception (preferably IOError).
715 '''
716 raise NotImplementedError('this method must be implemented by a concrete subclass')
717
718 def get_fixed_version(self, id):
719 '''Return the package version that fixes a given crash.
720
721 Return None if the crash is not yet fixed, or an empty string if the
722 crash is fixed, but it cannot be determined by which version. Return
723 'invalid' if the crash report got invalidated, such as closed a
724 duplicate or rejected.
725
726 This function should make sure that the returned result is correct. If
727 there are any errors with connecting to the crash database, it should
728 raise an exception (preferably IOError).
729 '''
730 raise NotImplementedError('this method must be implemented by a concrete subclass')
731
732 def get_affected_packages(self, id):
733 '''Return list of affected source packages for given ID.'''
734
735 raise NotImplementedError('this method must be implemented by a concrete subclass')
736
737 def is_reporter(self, id):
738 '''Check whether the user is the reporter of given ID.'''
739
740 raise NotImplementedError('this method must be implemented by a concrete subclass')
741
742 def can_update(self, id):
743 '''Check whether the user is eligible to update a report.
744
745 A user should add additional information to an existing ID if (s)he is
746 the reporter or subscribed, the bug is open, not a duplicate, etc. The
747 exact policy and checks should be done according to the particular
748 implementation.
749 '''
750 raise NotImplementedError('this method must be implemented by a concrete subclass')
751
752 def duplicate_of(self, id):
753 '''Return master ID for a duplicate bug.
754
755 If the bug is not a duplicate, return None.
756 '''
757 raise NotImplementedError('this method must be implemented by a concrete subclass')
758
759 def close_duplicate(self, report, id, master):
760 '''Mark a crash id as duplicate of given master ID.
761
762 If master is None, id gets un-duplicated.
763 '''
764 raise NotImplementedError('this method must be implemented by a concrete subclass')
765
766 def mark_regression(self, id, master):
767 '''Mark a crash id as reintroducing an earlier crash which is
768 already marked as fixed (having ID 'master').'''
769
770 raise NotImplementedError('this method must be implemented by a concrete subclass')
771
772 def mark_retraced(self, id):
773 '''Mark crash id as retraced.'''
774
775 raise NotImplementedError('this method must be implemented by a concrete subclass')
776
777 def mark_retrace_failed(self, id, invalid_msg=None):
778 '''Mark crash id as 'failed to retrace'.
779
780 If invalid_msg is given, the bug should be closed as invalid with given
781 message, otherwise just marked as a failed retrace.
782
783 This can be a no-op if you are not interested in this.
784 '''
785 raise NotImplementedError('this method must be implemented by a concrete subclass')
786
787 def _mark_dup_checked(self, id, report):
788 '''Mark crash id as checked for being a duplicate
789
790 This is an internal method that should not be called from outside.
791 '''
792 raise NotImplementedError('this method must be implemented by a concrete subclass')
793
794#
795# factory
796#
797
798
799def get_crashdb(auth_file, name=None, conf=None):
800 '''Return a CrashDatabase object for the given crash db name.
801
802 This reads the configuration file 'conf'.
803
804 If name is None, it defaults to the 'default' value in conf.
805
806 If conf is None, it defaults to the environment variable
807 APPORT_CRASHDB_CONF; if that does not exist, the hardcoded default is
808 /etc/apport/crashdb.conf. This Python syntax file needs to specify:
809
810 - A string variable 'default', giving a default value for 'name' if that is
811 None.
812
813 - A dictionary 'databases' which maps names to crash db configuration
814 dictionaries. These need to have at least the key 'impl' (Python module
815 in apport.crashdb_impl which contains a concrete 'CrashDatabase' class
816 implementation for that crash db type). Other generally known options are
817 'bug_pattern_url', 'dupdb_url', and 'problem_types'.
818 '''
819 if not conf:
820 conf = os.environ.get('APPORT_CRASHDB_CONF', '/etc/apport/crashdb.conf')
821 settings = {}
822 with open(conf) as f:
823 exec(compile(f.read(), conf, 'exec'), settings)
824
825 # Load third parties crashdb.conf
826 confdDir = conf + '.d'
827 if os.path.isdir(confdDir):
828 for cf in os.listdir(confdDir):
829 cfpath = os.path.join(confdDir, cf)
830 if os.path.isfile(cfpath) and cf.endswith('.conf'):
831 try:
832 with open(cfpath) as f:
833 exec(compile(f.read(), cfpath, 'exec'), settings['databases'])
834 except Exception as e:
835 # ignore broken files
836 sys.stderr.write('Invalid file %s: %s\n' % (cfpath, str(e)))
837 pass
838
839 if not name:
840 name = settings['default']
841
842 return load_crashdb(auth_file, settings['databases'][name])
843
844
845def load_crashdb(auth_file, spec):
846 '''Return a CrashDatabase object for a given DB specification.
847
848 spec is a crash db configuration dictionary as described in get_crashdb().
849 '''
850 m = __import__('apport.crashdb_impl.' + spec['impl'], globals(), locals(), ['CrashDatabase'])
851 return m.CrashDatabase(auth_file, spec)
852
853
854class NeedsCredentials(Exception):
855 '''This may be raised when unable to log in to the crashdb.'''
856 pass
0857
=== added directory 'apport/crashdb_impl'
=== added file 'apport/crashdb_impl/__init__.py'
=== added file 'apport/crashdb_impl/debian.py'
--- apport/crashdb_impl/debian.py 1970-01-01 00:00:00 +0000
+++ apport/crashdb_impl/debian.py 2013-04-11 13:56:24 +0000
@@ -0,0 +1,113 @@
1'''Debian crash database interface.'''
2
3# Debian adaptation Copyright (C) 2012 Ritesh Raj Sarraf <rrs@debian.org>
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the
7# Free Software Foundation; either version 2 of the License, or (at your
8# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
9# the full text of the license.
10
11
12import smtplib, tempfile
13from email.mime.text import MIMEText
14
15import apport
16import apport.crashdb
17
18
19class CrashDatabase(apport.crashdb.CrashDatabase):
20 '''
21 Debian crash database
22 This is a Apport CrashDB implementation for interacting with Debian BTS
23 '''
24 def __init__(self, auth_file, options):
25 '''
26 Initialize crash database connection.
27
28 Debian implementation is pretty basic as most of its bug management
29 processes revolve around the email interface
30 '''
31 apport.crashdb.CrashDatabase.__init__(self, auth_file, options)
32 self.options = options
33
34 if not self.options.get('smtphost'):
35 self.options['smtphost'] = 'reportbug.debian.org'
36
37 if not self.options.get('recipient'):
38 self.options['recipient'] = 'submit@bugs.debian.org'
39
40 def accepts(self, report):
41 '''
42 Check if this report can be uploaded to this database.
43 Checks for the proper settings of apport.
44 '''
45 if not self.options.get('sender') and 'UnreportableReason' not in report:
46 report['UnreportableReason'] = 'Please configure sender settings in /etc/apport/crashdb.conf'
47
48 # At this time, we are not ready to take CrashDumps
49 if 'Stacktrace' in report and not report.has_useful_stacktrace():
50 report['UnreportableReason'] = 'Incomplete backtrace. Please install the debug symbol packages'
51
52 return apport.crashdb.CrashDatabase.accepts(self, report)
53
54 def upload(self, report, progress_callback=None):
55 '''Upload given problem report return a handle for it.
56
57 In Debian, we use BTS, which is heavily email oriented
58 This method crafts the bug into an email report understood by Debian BTS
59 '''
60 # first and foremost, let's check if the apport bug filing settings are set correct
61 assert self.accepts(report)
62
63 # Frame the report in the format the BTS understands
64 try:
65 (buggyPackage, buggyVersion) = report['Package'].split(' ')
66 except (KeyError, ValueError):
67 return False
68
69 temp = tempfile.NamedTemporaryFile()
70
71 temp.file.write(('Package: ' + buggyPackage + '\n').encode('UTF-8'))
72 temp.file.write(('Version: ' + buggyVersion + '\n\n\n').encode('UTF-8'))
73 temp.file.write(('=============================\n\n').encode('UTF-8'))
74
75 # Let's remove the CoreDump first
76
77 # Even if we have a valid backtrace, we already are reporting it as text
78 # We don't want to send very large emails to the BTS.
79 # OTOH, if the backtrace is invalid, has_useful_backtrace() will already
80 # deny reporting of the bug report.
81 try:
82 del report['CoreDump']
83 except KeyError:
84 pass
85
86 # Now write the apport bug report
87 report.write(temp)
88
89 temp.file.seek(0)
90
91 msg = MIMEText(temp.file.read().decode('UTF-8'))
92 msg['Subject'] = report['Title']
93 msg['From'] = self.options['sender']
94 msg['To'] = self.options['recipient']
95
96 # Subscribe the submitted to the bug report
97 msg.add_header('X-Debbugs-CC', self.options['sender'])
98 msg.add_header('Usertag', 'apport-%s' % report['ProblemType'].lower())
99
100 s = smtplib.SMTP(self.options['smtphost'])
101 s.sendmail(self.options['sender'], self.options['recipient'], msg.as_string().encode('UTF-8'))
102 s.quit()
103
104 def get_comment_url(self, report, handle):
105 '''
106 Return an URL that should be opened after report has been uploaded
107 and upload() returned handle.
108
109 Should return None if no URL should be opened (anonymous filing without
110 user comments); in that case this function should do whichever
111 interactive steps it wants to perform.
112 '''
113 return None
0114
=== added file 'apport/crashdb_impl/launchpad.py'
--- apport/crashdb_impl/launchpad.py 1970-01-01 00:00:00 +0000
+++ apport/crashdb_impl/launchpad.py 2013-04-11 13:56:24 +0000
@@ -0,0 +1,1993 @@
1# vim: set fileencoding=UTF-8 :
2'''Crash database implementation for Launchpad.'''
3
4# Copyright (C) 2007 - 2009 Canonical Ltd.
5# Authors: Martin Pitt <martin.pitt@ubuntu.com> and Markus Korn <thekorn@gmx.de>
6#
7# This program is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License as published by the
9# Free Software Foundation; either version 2 of the License, or (at your
10# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
11# the full text of the license.
12
13import tempfile, os.path, re, gzip, sys, email, time
14
15from io import BytesIO
16
17if sys.version_info.major == 2:
18 from urllib2 import HTTPSHandler, Request, build_opener
19 from httplib import HTTPSConnection
20 from urllib import urlencode, urlopen
21 (HTTPSHandler, Request, build_opener, HTTPSConnection, urlencode, urlopen) # pyflakes
22 _python2 = True
23else:
24 from urllib.request import HTTPSHandler, Request, build_opener, urlopen
25 from urllib.parse import urlencode
26 from http.client import HTTPSConnection
27 _python2 = False
28
29try:
30 from launchpadlib.errors import HTTPError
31 from launchpadlib.launchpad import Launchpad
32 Launchpad # pyflakes
33except ImportError:
34 # if launchpadlib is not available, only client-side reporting will work
35 Launchpad = None
36
37import apport.crashdb
38import apport
39
40default_credentials_path = os.path.expanduser('~/.cache/apport/launchpad.credentials')
41
42
43def filter_filename(attachments):
44 for attachment in attachments:
45 try:
46 f = attachment.data.open()
47 except HTTPError:
48 apport.error('Broken attachment on bug, ignoring')
49 continue
50 name = f.filename
51 if name.endswith('.txt') or name.endswith('.gz'):
52 yield f
53
54
55def id_set(tasks):
56 # same as set(int(i.bug.id) for i in tasks) but faster
57 return set(int(i.self_link.split('/').pop()) for i in tasks)
58
59
60class CrashDatabase(apport.crashdb.CrashDatabase):
61 '''Launchpad implementation of crash database interface.'''
62
63 def __init__(self, auth, options):
64 '''Initialize Launchpad crash database.
65
66 You need to specify a launchpadlib-style credentials file to
67 access launchpad. If you supply None, it will use
68 default_credentials_path (~/.cache/apport/launchpad.credentials).
69
70 Recognized options are:
71 - distro: Name of the distribution in Launchpad
72 - project: Name of the project in Launchpad
73 (Note that exactly one of "distro" or "project" must be given.)
74 - launchpad_instance: If set, this uses the given launchpad instance
75 instead of production (optional). This can be overriden or set by
76 $APPORT_LAUNCHPAD_INSTANCE environment.
77 - cache_dir: Path to a permanent cache directory; by default it uses a
78 temporary one. (optional). This can be overridden or set by
79 $APPORT_LAUNCHPAD_CACHE environment.
80 - escalation_subscription: This subscribes the given person or team to
81 a bug once it gets the 10th duplicate.
82 - escalation_tag: This adds the given tag to a bug once it gets more
83 than 10 duplicates.
84 - initial_subscriber: The Launchpad user which gets subscribed to newly
85 filed bugs (default: "apport"). It should be a bot user which the
86 crash-digger instance runs as, as this will get to see all bug
87 details immediately.
88 - triaging_team: The Launchpad user/team which gets subscribed after
89 updating a crash report bug by the retracer (default:
90 "ubuntu-crashes-universe")
91 - architecture: If set, this sets and watches out for needs-*-retrace
92 tags of this architecture. This is useful when being used with
93 apport-retrace and crash-digger to process crash reports of foreign
94 architectures. Defaults to system architecture.
95 '''
96 if os.getenv('APPORT_LAUNCHPAD_INSTANCE'):
97 options['launchpad_instance'] = os.getenv(
98 'APPORT_LAUNCHPAD_INSTANCE')
99 if not auth:
100 lp_instance = options.get('launchpad_instance')
101 if lp_instance:
102 auth = default_credentials_path + '.' + lp_instance.split('://', 1)[-1]
103 else:
104 auth = default_credentials_path
105 apport.crashdb.CrashDatabase.__init__(self, auth, options)
106
107 self.distro = options.get('distro')
108 if self.distro:
109 assert 'project' not in options, 'Must not set both "project" and "distro" option'
110 else:
111 assert 'project' in options, 'Need to have either "project" or "distro" option'
112
113 if 'architecture' in options:
114 self.arch_tag = 'need-%s-retrace' % options['architecture']
115 else:
116 self.arch_tag = 'need-%s-retrace' % apport.packaging.get_system_architecture()
117 self.options = options
118 self.auth = auth
119 assert self.auth
120
121 self.__launchpad = None
122 self.__lp_distro = None
123 self.__lpcache = os.getenv('APPORT_LAUNCHPAD_CACHE', options.get('cache_dir'))
124
125 @property
126 def launchpad(self):
127 '''Return Launchpad instance.'''
128
129 if self.__launchpad:
130 return self.__launchpad
131
132 if Launchpad is None:
133 sys.stderr.write('ERROR: The launchpadlib Python module is not installed. This functionality is not available.\n')
134 sys.exit(1)
135
136 if self.options.get('launchpad_instance'):
137 launchpad_instance = self.options.get('launchpad_instance')
138 else:
139 launchpad_instance = 'production'
140
141 auth_dir = os.path.dirname(self.auth)
142 if auth_dir and not os.path.isdir(auth_dir):
143 os.makedirs(auth_dir)
144
145 try:
146 self.__launchpad = Launchpad.login_with('apport-collect',
147 launchpad_instance,
148 launchpadlib_dir=self.__lpcache,
149 allow_access_levels=['WRITE_PRIVATE'],
150 credentials_file=self.auth,
151 version='1.0')
152 except Exception as e:
153 if hasattr(e, 'content'):
154 msg = e.content
155 else:
156 msg = str(e)
157 apport.error('connecting to Launchpad failed: %s\nYou can reset the credentials by removing the file "%s"', msg, self.auth)
158 sys.exit(99) # transient error
159
160 return self.__launchpad
161
162 def _get_distro_tasks(self, tasks):
163 if not self.distro:
164 raise StopIteration
165
166 for t in tasks:
167 if t.bug_target_name.lower() == self.distro or \
168 re.match('^.+\(%s.*\)$' % self.distro, t.bug_target_name.lower()):
169 yield t
170
171 @property
172 def lp_distro(self):
173 if self.__lp_distro is None:
174 if self.distro:
175 self.__lp_distro = self.launchpad.distributions[self.distro]
176 elif 'project' in self.options:
177 self.__lp_distro = self.launchpad.projects[self.options['project']]
178 else:
179 raise SystemError('distro or project needs to be specified in crashdb options')
180
181 return self.__lp_distro
182
183 def upload(self, report, progress_callback=None):
184 '''Upload given problem report return a handle for it.
185
186 This should happen noninteractively.
187
188 If the implementation supports it, and a function progress_callback is
189 passed, that is called repeatedly with two arguments: the number of
190 bytes already sent, and the total number of bytes to send. This can be
191 used to provide a proper upload progress indication on frontends.
192 '''
193 assert self.accepts(report)
194
195 blob_file = self._generate_upload_blob(report)
196 ticket = upload_blob(blob_file, progress_callback, hostname=self.get_hostname())
197 blob_file.close()
198 assert ticket
199 return ticket
200
201 def get_hostname(self):
202 '''Return the hostname for the Launchpad instance.'''
203
204 launchpad_instance = self.options.get('launchpad_instance')
205 if launchpad_instance:
206 if launchpad_instance == 'staging':
207 hostname = 'staging.launchpad.net'
208 else:
209 hostname = 'launchpad.dev'
210 else:
211 hostname = 'launchpad.net'
212 return hostname
213
214 def get_comment_url(self, report, handle):
215 '''Return an URL that should be opened after report has been uploaded
216 and upload() returned handle.
217
218 Should return None if no URL should be opened (anonymous filing without
219 user comments); in that case this function should do whichever
220 interactive steps it wants to perform.'''
221
222 args = {}
223 title = report.get('Title', report.standard_title())
224 if title:
225 # always use UTF-8 encoding, urlencode() blows up otherwise in
226 # python 2.7
227 if type(title) != type(b''):
228 title = title.encode('UTF-8')
229 args['field.title'] = title
230
231 hostname = self.get_hostname()
232
233 project = self.options.get('project')
234
235 if not project:
236 if 'SourcePackage' in report:
237 return 'https://bugs.%s/%s/+source/%s/+filebug/%s?%s' % (
238 hostname, self.distro, report['SourcePackage'], handle, urlencode(args))
239 else:
240 return 'https://bugs.%s/%s/+filebug/%s?%s' % (
241 hostname, self.distro, handle, urlencode(args))
242 else:
243 return 'https://bugs.%s/%s/+filebug/%s?%s' % (
244 hostname, project, handle, urlencode(args))
245
246 def get_id_url(self, report, id):
247 '''Return URL for a given report ID.
248
249 The report is passed in case building the URL needs additional
250 information from it, such as the SourcePackage name.
251
252 Return None if URL is not available or cannot be determined.
253 '''
254 return 'https://bugs.launchpad.net/bugs/' + str(id)
255
256 def download(self, id):
257 '''Download the problem report from given ID and return a Report.'''
258
259 report = apport.Report()
260 b = self.launchpad.bugs[id]
261
262 # parse out fields from summary
263 m = re.search(r'(ProblemType:.*)$', b.description, re.S)
264 if not m:
265 m = re.search(r'^--- \r?$[\r\n]*(.*)', b.description, re.M | re.S)
266 assert m, 'bug description must contain standard apport format data'
267
268 description = m.group(1).encode('UTF-8').replace('\xc2\xa0', ' ').replace('\r\n', '\n')
269
270 if '\n\n' in description:
271 # this often happens, remove all empty lines between top and
272 # 'Uname'
273 if 'Uname:' in description:
274 # this will take care of bugs like LP #315728 where stuff
275 # is added after the apport data
276 (part1, part2) = description.split('Uname:', 1)
277 description = part1.replace('\n\n', '\n') + 'Uname:' \
278 + part2.split('\n\n', 1)[0]
279 else:
280 # just parse out the Apport block; e. g. LP #269539
281 description = description.split('\n\n', 1)[0]
282
283 report.load(BytesIO(description))
284
285 if 'Date' not in report:
286 # We had not submitted this field for a while, claiming it
287 # redundant. But it is indeed required for up-to-the-minute
288 # comparison with log files, etc. For backwards compatibility with
289 # those reported bugs, read the creation date
290 try:
291 report['Date'] = b.date_created.ctime()
292 except AttributeError:
293 # support older wadllib API which returned strings
294 report['Date'] = b.date_created
295 if 'ProblemType' not in report:
296 if 'apport-bug' in b.tags:
297 report['ProblemType'] = 'Bug'
298 elif 'apport-crash' in b.tags:
299 report['ProblemType'] = 'Crash'
300 elif 'apport-kernelcrash' in b.tags:
301 report['ProblemType'] = 'KernelCrash'
302 elif 'apport-package' in b.tags:
303 report['ProblemType'] = 'Package'
304 else:
305 raise ValueError('cannot determine ProblemType from tags: ' + str(b.tags))
306
307 report['Tags'] = ' '.join(b.tags)
308
309 if 'Title' in report:
310 report['OriginalTitle'] = report['Title']
311
312 report['Title'] = b.title
313
314 for attachment in filter_filename(b.attachments):
315 key, ext = os.path.splitext(attachment.filename)
316 # ignore attachments with invalid keys
317 try:
318 report[key] = ''
319 except:
320 continue
321 if ext == '.txt':
322 report[key] = attachment.read()
323 elif ext == '.gz':
324 try:
325 report[key] = gzip.GzipFile(fileobj=attachment).read()
326 except IOError as e:
327 # some attachments are only called .gz, but are
328 # uncompressed (LP #574360)
329 if 'Not a gzip' not in str(e):
330 raise
331 attachment.seek(0)
332 report[key] = attachment.read()
333 else:
334 raise Exception('Unknown attachment type: ' + attachment.filename)
335 return report
336
337 def update(self, id, report, comment, change_description=False,
338 attachment_comment=None, key_filter=None):
339 '''Update the given report ID with all data from report.
340
341 This creates a text comment with the "short" data (see
342 ProblemReport.write_mime()), and creates attachments for all the
343 bulk/binary data.
344
345 If change_description is True, and the crash db implementation supports
346 it, the short data will be put into the description instead (like in a
347 new bug).
348
349 comment will be added to the "short" data. If attachment_comment is
350 given, it will be added to the attachment uploads.
351
352 If key_filter is a list or set, then only those keys will be added.
353 '''
354 bug = self.launchpad.bugs[id]
355
356 if key_filter:
357 skip_keys = set(report.keys()) - set(key_filter)
358 else:
359 skip_keys = None
360
361 # we want to reuse the knowledge of write_mime() with all its different input
362 # types and output formatting; however, we have to dissect the mime ourselves,
363 # since we can't just upload it as a blob
364 mime = tempfile.TemporaryFile()
365 report.write_mime(mime, skip_keys=skip_keys)
366 mime.flush()
367 mime.seek(0)
368 msg = email.message_from_file(mime)
369 msg_iter = msg.walk()
370
371 # first part is the multipart container
372 part = msg_iter.next()
373 assert part.is_multipart()
374
375 # second part should be an inline text/plain attachments with all short
376 # fields
377 part = msg_iter.next()
378 assert not part.is_multipart()
379 assert part.get_content_type() == 'text/plain'
380
381 if not key_filter:
382 # when we update a complete report, we are updating an existing bug
383 # with apport-collect
384 x = bug.tags[:] # LP#254901 workaround
385 x.append('apport-collected')
386 # add any tags (like the release) to the bug
387 if 'Tags' in report:
388 x += self._filter_tag_names(report['Tags']).split()
389 bug.tags = x
390 bug.lp_save()
391 bug = self.launchpad.bugs[id] # fresh bug object, LP#336866 workaround
392
393 # short text data
394 if change_description:
395 bug.description = bug.description + '\n--- \n' + part.get_payload(decode=True).decode('UTF-8', 'replace')
396 bug.lp_save()
397 else:
398 bug.newMessage(content=part.get_payload(decode=True), subject=comment)
399
400 # other parts are the attachments:
401 for part in msg_iter:
402 # print ' attachment: %s...' % part.get_filename()
403 bug.addAttachment(comment=attachment_comment or '',
404 description=part.get_filename(),
405 content_type=None,
406 data=part.get_payload(decode=True),
407 filename=part.get_filename(), is_patch=False)
408
409 def update_traces(self, id, report, comment=''):
410 '''Update the given report ID for retracing results.
411
412 This updates Stacktrace, ThreadStacktrace, StacktraceTop,
413 and StacktraceSource. You can also supply an additional comment.
414 '''
415 apport.crashdb.CrashDatabase.update_traces(self, id, report, comment)
416
417 bug = self.launchpad.bugs[id]
418 # ensure it's assigned to a package
419 if 'SourcePackage' in report:
420 for task in bug.bug_tasks:
421 if task.target.resource_type_link.endswith('#distribution'):
422 task.target = self.lp_distro.getSourcePackage(name=report['SourcePackage'])
423 task.lp_save()
424 bug = self.launchpad.bugs[id]
425 break
426
427 # remove core dump if stack trace is usable
428 if report.has_useful_stacktrace():
429 for a in bug.attachments:
430 if a.title == 'CoreDump.gz':
431 try:
432 a.removeFromBug()
433 except HTTPError:
434 pass # LP#249950 workaround
435 try:
436 task = self._get_distro_tasks(bug.bug_tasks).next()
437 if task.importance == 'Undecided':
438 task.importance = 'Medium'
439 task.lp_save()
440 except StopIteration:
441 pass # no distro tasks
442
443 # update bug title with retraced function name
444 fn = report.stacktrace_top_function()
445 if fn:
446 m = re.match('^(.*crashed with SIG.* in )([^( ]+)(\(\).*$)', bug.title)
447 if m and m.group(2) != fn:
448 bug.title = m.group(1) + fn + m.group(3)
449 try:
450 bug.lp_save()
451 except HTTPError:
452 pass # LP#336866 workaround
453 bug = self.launchpad.bugs[id]
454
455 self._subscribe_triaging_team(bug, report)
456
457 def get_distro_release(self, id):
458 '''Get 'DistroRelease: <release>' from the given report ID and return
459 it.'''
460 bug = self.launchpad.bugs[id]
461 m = re.search('DistroRelease: ([-a-zA-Z0-9.+/ ]+)', bug.description)
462 if m:
463 return m.group(1)
464 raise ValueError('URL does not contain DistroRelease: field')
465
466 def get_affected_packages(self, id):
467 '''Return list of affected source packages for given ID.'''
468
469 bug_target_re = re.compile(
470 r'/%s/(?:(?P<suite>[^/]+)/)?\+source/(?P<source>[^/]+)$' % self.distro)
471
472 bug = self.launchpad.bugs[id]
473 result = []
474
475 for task in bug.bug_tasks:
476 match = bug_target_re.search(task.target.self_link)
477 if not match:
478 continue
479 if task.status in ('Invalid', "Won't Fix", 'Fix Released'):
480 continue
481 result.append(match.group('source'))
482 return result
483
484 def is_reporter(self, id):
485 '''Check whether the user is the reporter of given ID.'''
486
487 bug = self.launchpad.bugs[id]
488 return bug.owner.name == self.launchpad.me.name
489
490 def can_update(self, id):
491 '''Check whether the user is eligible to update a report.
492
493 A user should add additional information to an existing ID if (s)he is
494 the reporter or subscribed, the bug is open, not a duplicate, etc. The
495 exact policy and checks should be done according to the particular
496 implementation.
497 '''
498 bug = self.launchpad.bugs[id]
499 if bug.duplicate_of:
500 return False
501
502 if bug.owner.name == self.launchpad.me.name:
503 return True
504
505 # check subscription
506 me = self.launchpad.me.self_link
507 for sub in bug.subscriptions.entries:
508 if sub['person_link'] == me:
509 return True
510
511 return False
512
513 def get_unretraced(self):
514 '''Return an ID set of all crashes which have not been retraced yet and
515 which happened on the current host architecture.'''
516 try:
517 bugs = self.lp_distro.searchTasks(tags=self.arch_tag, created_since='2011-08-01')
518 return id_set(bugs)
519 except Exception as e:
520 apport.error('connecting to Launchpad failed: %s', str(e))
521 sys.exit(99) # transient error
522
523 def get_dup_unchecked(self):
524 '''Return an ID set of all crashes which have not been checked for
525 being a duplicate.
526
527 This is mainly useful for crashes of scripting languages such as
528 Python, since they do not need to be retraced. It should not return
529 bugs that are covered by get_unretraced().'''
530
531 try:
532 bugs = self.lp_distro.searchTasks(tags='need-duplicate-check', created_since='2011-08-01')
533 return id_set(bugs)
534 except Exception as e:
535 apport.error('connecting to Launchpad failed: %s', str(e))
536 sys.exit(99) # transient error
537
538 def get_unfixed(self):
539 '''Return an ID set of all crashes which are not yet fixed.
540
541 The list must not contain bugs which were rejected or duplicate.
542
543 This function should make sure that the returned list is correct. If
544 there are any errors with connecting to the crash database, it should
545 raise an exception (preferably IOError).'''
546
547 bugs = self.lp_distro.searchTasks(tags='apport-crash')
548 return id_set(bugs)
549
550 def _get_source_version(self, package):
551 '''Return the version of given source package in the latest release of
552 given distribution.
553
554 If 'distro' is None, we will look for a launchpad project .
555 '''
556 sources = self.lp_distro.main_archive.getPublishedSources(
557 exact_match=True,
558 source_name=package,
559 distro_series=self.lp_distro.current_series
560 )
561 # first element is the latest one
562 return sources[0].source_package_version
563
564 def get_fixed_version(self, id):
565 '''Return the package version that fixes a given crash.
566
567 Return None if the crash is not yet fixed, or an empty string if the
568 crash is fixed, but it cannot be determined by which version. Return
569 'invalid' if the crash report got invalidated, such as closed a
570 duplicate or rejected.
571
572 This function should make sure that the returned result is correct. If
573 there are any errors with connecting to the crash database, it should
574 raise an exception (preferably IOError).
575 '''
576 # do not do version tracking yet; for that, we need to get the current
577 # distrorelease and the current package version in that distrorelease
578 # (or, of course, proper version tracking in Launchpad itself)
579
580 try:
581 b = self.launchpad.bugs[id]
582 except KeyError:
583 return 'invalid'
584
585 if b.duplicate_of:
586 return 'invalid'
587
588 tasks = list(b.bug_tasks) # just fetch it once
589
590 if self.distro:
591 distro_identifier = '(%s)' % self.distro.lower()
592 fixed_tasks = filter(lambda task: task.status == 'Fix Released' and
593 distro_identifier in task.bug_target_display_name.lower(), tasks)
594
595 if not fixed_tasks:
596 fixed_distro = filter(lambda task: task.status == 'Fix Released' and
597 task.bug_target_name.lower() == self.distro.lower(), tasks)
598 if fixed_distro:
599 # fixed in distro inself (without source package)
600 return ''
601
602 if len(fixed_tasks) > 1:
603 apport.warning('There is more than one task fixed in %s %s, using first one to determine fixed version', self.distro, id)
604 return ''
605
606 if fixed_tasks:
607 task = fixed_tasks.pop()
608 try:
609 return self._get_source_version(task.bug_target_display_name.split()[0])
610 except IndexError:
611 # source does not exist any more
612 return 'invalid'
613 else:
614 # check if there only invalid ones
615 invalid_tasks = filter(lambda task: task.status in ('Invalid', "Won't Fix", 'Expired') and
616 distro_identifier in task.bug_target_display_name.lower(), tasks)
617 if invalid_tasks:
618 non_invalid_tasks = filter(
619 lambda task: task.status not in ('Invalid', "Won't Fix", 'Expired') and
620 distro_identifier in task.bug_target_display_name.lower(), tasks)
621 if not non_invalid_tasks:
622 return 'invalid'
623 else:
624 fixed_tasks = filter(lambda task: task.status == 'Fix Released', tasks)
625 if fixed_tasks:
626 # TODO: look for current series
627 return ''
628 # check if there any invalid ones
629 if filter(lambda task: task.status == 'Invalid', tasks):
630 return 'invalid'
631
632 return None
633
634 def duplicate_of(self, id):
635 '''Return master ID for a duplicate bug.
636
637 If the bug is not a duplicate, return None.
638 '''
639 b = self.launchpad.bugs[id].duplicate_of
640 if b:
641 return b.id
642 else:
643 return None
644
645 def close_duplicate(self, report, id, master_id):
646 '''Mark a crash id as duplicate of given master ID.
647
648 If master is None, id gets un-duplicated.
649 '''
650 bug = self.launchpad.bugs[id]
651
652 if master_id:
653 assert id != master_id, 'cannot mark bug %s as a duplicate of itself' % str(id)
654
655 # check whether the master itself is a dup
656 master = self.launchpad.bugs[master_id]
657 if master.duplicate_of:
658 master = master.duplicate_of
659 master_id = master.id
660 if master.id == id:
661 # this happens if the bug was manually duped to a newer one
662 apport.warning('Bug %i was manually marked as a dupe of newer bug %i, not closing as duplicate',
663 id, master_id)
664 return
665
666 for a in bug.attachments:
667 if a.title in ('CoreDump.gz', 'Stacktrace.txt',
668 'ThreadStacktrace.txt', 'ProcMaps.txt',
669 'ProcStatus.txt', 'Registers.txt',
670 'Disassembly.txt'):
671 try:
672 a.removeFromBug()
673 except HTTPError:
674 pass # LP#249950 workaround
675
676 bug = self.launchpad.bugs[id] # fresh bug object, LP#336866 workaround
677 bug.newMessage(content='Thank you for taking the time to report this crash and helping \
678to make this software better. This particular crash has already been reported and \
679is a duplicate of bug #%i, so is being marked as such. Please look at the \
680other bug report to see if there is any missing information that you can \
681provide, or to see if there is a workaround for the bug. Additionally, any \
682further discussion regarding the bug should occur in the other report. \
683Please continue to report any other bugs you may find.' % master_id,
684 subject='This bug is a duplicate')
685
686 bug = self.launchpad.bugs[id] # refresh, LP#336866 workaround
687 if bug.private:
688 bug.private = False
689
690 # set duplicate last, since we cannot modify already dup'ed bugs
691 if not bug.duplicate_of:
692 bug.duplicate_of = master
693
694 # cache tags of master bug report instead of performing multiple
695 # queries
696 master_tags = master.tags
697
698 if len(master.duplicates) == 10:
699 if 'escalation_tag' in self.options and self.options['escalation_tag'] not in master_tags and self.options.get('escalated_tag', ' invalid ') not in master_tags:
700 master.tags = master_tags + [self.options['escalation_tag']] # LP#254901 workaround
701 master.lp_save()
702
703 if 'escalation_subscription' in self.options and self.options.get('escalated_tag', ' invalid ') not in master_tags:
704 p = self.launchpad.people[self.options['escalation_subscription']]
705 master.subscribe(person=p)
706
707 # requesting updated stack trace?
708 if report.has_useful_stacktrace() and ('apport-request-retrace' in master_tags
709 or 'apport-failed-retrace' in master_tags):
710 self.update(master_id, report, 'Updated stack trace from duplicate bug %i' % id,
711 key_filter=['Stacktrace', 'ThreadStacktrace',
712 'Package', 'Dependencies', 'ProcMaps', 'ProcCmdline'])
713
714 master = self.launchpad.bugs[master_id]
715 x = master.tags[:] # LP#254901 workaround
716 try:
717 x.remove('apport-failed-retrace')
718 except ValueError:
719 pass
720 try:
721 x.remove('apport-request-retrace')
722 except ValueError:
723 pass
724 master.tags = x
725 try:
726 master.lp_save()
727 except HTTPError:
728 pass # LP#336866 workaround
729
730 # white list of tags to copy from duplicates bugs to the master
731 tags_to_copy = ['bugpattern-needed']
732 for series in self.lp_distro.series:
733 if series.status not in ['Active Development',
734 'Current Stable Release',
735 'Supported', 'Pre-release Freeze']:
736 continue
737 tags_to_copy.append(series.name)
738 # copy tags over from the duplicate bug to the master bug
739 dupe_tags = set(bug.tags)
740 # reload master tags as they may have changed
741 master_tags = master.tags
742 missing_tags = dupe_tags.difference(master_tags)
743
744 for tag in missing_tags:
745 if tag in tags_to_copy:
746 master_tags.append(tag)
747
748 master.tags = master_tags
749 master.lp_save()
750
751 else:
752 if bug.duplicate_of:
753 bug.duplicate_of = None
754
755 if bug._dirty_attributes: # LP#336866 workaround
756 bug.lp_save()
757
758 def mark_regression(self, id, master):
759 '''Mark a crash id as reintroducing an earlier crash which is
760 already marked as fixed (having ID 'master').'''
761
762 bug = self.launchpad.bugs[id]
763 bug.newMessage(content='This crash has the same stack trace characteristics as bug #%i. \
764However, the latter was already fixed in an earlier package version than the \
765one in this report. This might be a regression or because the problem is \
766in a dependent package.' % master,
767 subject='Possible regression detected')
768 bug = self.launchpad.bugs[id] # fresh bug object, LP#336866 workaround
769 bug.tags = bug.tags + ['regression-retracer'] # LP#254901 workaround
770 bug.lp_save()
771
772 def mark_retraced(self, id):
773 '''Mark crash id as retraced.'''
774
775 bug = self.launchpad.bugs[id]
776 if self.arch_tag in bug.tags:
777 x = bug.tags[:] # LP#254901 workaround
778 x.remove(self.arch_tag)
779 bug.tags = x
780 try:
781 bug.lp_save()
782 except HTTPError:
783 pass # LP#336866 workaround
784
785 def mark_retrace_failed(self, id, invalid_msg=None):
786 '''Mark crash id as 'failed to retrace'.'''
787
788 bug = self.launchpad.bugs[id]
789 if invalid_msg:
790 try:
791 task = self._get_distro_tasks(bug.bug_tasks).next()
792 except StopIteration:
793 # no distro task, just use the first one
794 task = bug.bug_tasks[0]
795 task.status = 'Invalid'
796 task.lp_save()
797 bug.newMessage(content=invalid_msg,
798 subject='Crash report cannot be processed')
799
800 for a in bug.attachments:
801 if a.title == 'CoreDump.gz':
802 try:
803 a.removeFromBug()
804 except HTTPError:
805 pass # LP#249950 workaround
806 else:
807 if 'apport-failed-retrace' not in bug.tags:
808 bug.tags = bug.tags + ['apport-failed-retrace'] # LP#254901 workaround
809 bug.lp_save()
810
811 def _mark_dup_checked(self, id, report):
812 '''Mark crash id as checked for being a duplicate.'''
813
814 bug = self.launchpad.bugs[id]
815
816 # if we have a distro task without a package, fix it
817 if 'SourcePackage' in report:
818 for task in bug.bug_tasks:
819 if task.target.resource_type_link.endswith('#distribution'):
820 task.target = self.lp_distro.getSourcePackage(
821 name=report['SourcePackage'])
822 task.lp_save()
823 bug = self.launchpad.bugs[id]
824 break
825
826 if 'need-duplicate-check' in bug.tags:
827 x = bug.tags[:] # LP#254901 workaround
828 x.remove('need-duplicate-check')
829 bug.tags = x
830 bug.lp_save()
831 if 'Traceback' in report:
832 for task in bug.bug_tasks:
833 if '#distribution' in task.target.resource_type_link:
834 if task.importance == 'Undecided':
835 task.importance = 'Medium'
836 task.lp_save()
837 self._subscribe_triaging_team(bug, report)
838
839 def known(self, report):
840 '''Check if the crash db already knows about the crash signature.
841
842 Check if the report has a DuplicateSignature, crash_signature(), or
843 StacktraceAddressSignature, and ask the database whether the problem is
844 already known. If so, return an URL where the user can check the status
845 or subscribe (if available), or just return True if the report is known
846 but there is no public URL. In that case the report will not be
847 uploaded (i. e. upload() will not be called).
848
849 Return None if the report does not have any signature or the crash
850 database does not support checking for duplicates on the client side.
851
852 The default implementation uses a text file format generated by
853 duplicate_db_publish() at an URL specified by the "dupdb_url" option.
854 Subclasses are free to override this with a custom implementation, such
855 as a real database lookup.
856 '''
857 # we override the method here to check if the user actually has access
858 # to the bug, and if the bug requests more retraces; in either case we
859 # should file it.
860 url = apport.crashdb.CrashDatabase.known(self, report)
861
862 if not url:
863 return url
864
865 # record the fact that it is a duplicate, for triagers
866 report['DuplicateOf'] = url
867
868 try:
869 f = urlopen(url + '/+text')
870 except IOError:
871 # if we are offline, or LP is down, upload will fail anyway, so we
872 # can just as well avoid the upload
873 return url
874
875 line = f.readline()
876 if not line.startswith(b'bug:'):
877 # presumably a 404 etc. page, which happens for private bugs
878 return True
879
880 # check tags
881 for line in f:
882 if line.startswith(b'tags:'):
883 if b'apport-failed-retrace' in line or b'apport-request-retrace' in line:
884 return None
885 else:
886 break
887
888 # stop at the first task, tags are in the first block
889 if not line.strip():
890 break
891
892 return url
893
894 def _subscribe_triaging_team(self, bug, report):
895 '''Subscribe the right triaging team to the bug.'''
896
897 #FIXME: this entire function is an ugly Ubuntu specific hack until LP
898 #gets a real crash db; see https://wiki.ubuntu.com/CrashReporting
899
900 if 'DistroRelease' in report and report['DistroRelease'].split()[0] != 'Ubuntu':
901 return # only Ubuntu bugs are filed private
902
903 #use a url hack here, it is faster
904 person = '%s~%s' % (self.launchpad._root_uri,
905 self.options.get('triaging_team', 'ubuntu-crashes-universe'))
906 bug.subscribe(person=person)
907
908 def _generate_upload_blob(self, report):
909 '''Generate a multipart/MIME temporary file for uploading.
910
911 You have to close the returned file object after you are done with it.
912 '''
913 # set reprocessing tags
914 hdr = {}
915 hdr['Tags'] = 'apport-%s' % report['ProblemType'].lower()
916 a = report.get('PackageArchitecture')
917 if not a or a == 'all':
918 a = report.get('Architecture')
919 if a:
920 hdr['Tags'] += ' ' + a
921 if 'Tags' in report:
922 hdr['Tags'] += ' ' + self._filter_tag_names(report['Tags'])
923
924 # privacy/retracing for distro reports
925 # FIXME: ugly hack until LP has a real crash db
926 if 'DistroRelease' in report:
927 if a and ('VmCore' in report or 'CoreDump' in report or 'LaunchpadPrivate' in report):
928 hdr['Private'] = 'yes'
929 hdr['Subscribers'] = report.get('LaunchpadSubscribe',
930 self.options.get('initial_subscriber', 'apport'))
931 hdr['Tags'] += ' need-%s-retrace' % a
932 elif 'Traceback' in report:
933 hdr['Private'] = 'yes'
934 hdr['Subscribers'] = 'apport'
935 hdr['Tags'] += ' need-duplicate-check'
936 if 'DuplicateSignature' in report and 'need-duplicate-check' not in hdr['Tags']:
937 hdr['Tags'] += ' need-duplicate-check'
938
939 # if we have checkbox submission key, link it to the bug; keep text
940 # reference until the link is shown in Launchpad's UI
941 if 'CheckboxSubmission' in report:
942 hdr['HWDB-Submission'] = report['CheckboxSubmission']
943
944 # order in which keys should appear in the temporary file
945 order = ['ProblemType', 'DistroRelease', 'Package', 'Regression', 'Reproducible',
946 'TestedUpstream', 'ProcVersionSignature', 'Uname', 'NonfreeKernelModules']
947
948 # write MIME/Multipart version into temporary file
949 mime = tempfile.TemporaryFile()
950 report.write_mime(mime, extra_headers=hdr,
951 skip_keys=['Tags', 'LaunchpadPrivate', 'LaunchpadSubscribe'],
952 priority_fields=order)
953 mime.flush()
954 mime.seek(0)
955
956 return mime
957
958 @classmethod
959 def _filter_tag_names(klass, tags):
960 '''Replace characters from tags which are not palatable to Launchpad'''
961
962 res = ''
963 for ch in tags.lower().encode('ASCII', errors='ignore'):
964 if ch in b'abcdefghijklmnopqrstuvwxyz0123456789 ' or (len(res) > 0 and ch in b'+-.'):
965 if _python2:
966 res += ch
967 else:
968 res += chr(ch)
969 else:
970 res += '.'
971
972 return res
973
974#
975# Launchpad storeblob API (should go into launchpadlib, see LP #315358)
976#
977
978_https_upload_callback = None
979
980
981#
982# This progress code is based on KodakLoader by Jason Hildebrand
983# <jason@opensky.ca>. See http://www.opensky.ca/~jdhildeb/software/kodakloader/
984# for details.
985class HTTPSProgressConnection(HTTPSConnection):
986 '''Implement a HTTPSConnection with an optional callback function for
987 upload progress.'''
988
989 def send(self, data):
990 global _https_upload_callback
991
992 # if callback has not been set, call the old method
993 if not _https_upload_callback:
994 HTTPSConnection.send(self, data)
995 return
996
997 sent = 0
998 total = len(data)
999 chunksize = 1024
1000 while sent < total:
1001 _https_upload_callback(sent, total)
1002 t1 = time.time()
1003 HTTPSConnection.send(self, data[sent:(sent + chunksize)])
1004 sent += chunksize
1005 t2 = time.time()
1006
1007 # adjust chunksize so that it takes between .5 and 2
1008 # seconds to send a chunk
1009 if chunksize > 1024:
1010 if t2 - t1 < .5:
1011 chunksize <<= 1
1012 elif t2 - t1 > 2:
1013 chunksize >>= 1
1014
1015
1016class HTTPSProgressHandler(HTTPSHandler):
1017
1018 def https_open(self, req):
1019 return self.do_open(HTTPSProgressConnection, req)
1020
1021
1022def upload_blob(blob, progress_callback=None, hostname='launchpad.net'):
1023 '''Upload blob (file-like object) to Launchpad.
1024
1025 progress_callback can be set to a function(sent, total) which is regularly
1026 called with the number of bytes already sent and total number of bytes to
1027 send. It is called every 0.5 to 2 seconds (dynamically adapted to upload
1028 bandwidth).
1029
1030 Return None on error, or the ticket number on success.
1031
1032 By default this uses the production Launchpad hostname. Set
1033 hostname to 'launchpad.dev' or 'staging.launchpad.net' to use another
1034 instance for testing.
1035 '''
1036 ticket = None
1037 url = 'https://%s/+storeblob' % hostname
1038
1039 global _https_upload_callback
1040 _https_upload_callback = progress_callback
1041
1042 # build the form-data multipart/MIME request
1043 data = email.mime.multipart.MIMEMultipart()
1044
1045 submit = email.mime.text.MIMEText('1')
1046 submit.add_header('Content-Disposition', 'form-data; name="FORM_SUBMIT"')
1047 data.attach(submit)
1048
1049 form_blob = email.mime.base.MIMEBase('application', 'octet-stream')
1050 form_blob.add_header('Content-Disposition', 'form-data; name="field.blob"; filename="x"')
1051 form_blob.set_payload(blob.read().decode('ascii'))
1052 data.attach(form_blob)
1053
1054 data_flat = BytesIO()
1055 if sys.version_info.major == 2:
1056 gen = email.generator.Generator(data_flat, mangle_from_=False)
1057 else:
1058 gen = email.generator.BytesGenerator(data_flat, mangle_from_=False)
1059 gen.flatten(data)
1060
1061 # do the request; we need to explicitly set the content type here, as it
1062 # defaults to x-www-form-urlencoded
1063 req = Request(url, data_flat.getvalue())
1064 req.add_header('Content-Type', 'multipart/form-data; boundary=' + data.get_boundary())
1065 opener = build_opener(HTTPSProgressHandler)
1066 result = opener.open(req)
1067 ticket = result.info().get('X-Launchpad-Blob-Token')
1068
1069 assert ticket
1070 return ticket
1071
1072#
1073# Unit tests
1074#
1075
1076if __name__ == '__main__':
1077 import unittest, atexit, shutil, subprocess
1078 import mock
1079
1080 crashdb = None
1081 _segv_report = None
1082 _python_report = None
1083 _uncommon_description_report = None
1084
1085 class _T(unittest.TestCase):
1086 # this assumes that a source package 'coreutils' exists and builds a
1087 # binary package 'coreutils'
1088 test_package = 'coreutils'
1089 test_srcpackage = 'coreutils'
1090
1091 #
1092 # Generic tests, should work for all CrashDB implementations
1093 #
1094
1095 def setUp(self):
1096 global crashdb
1097 if not crashdb:
1098 crashdb = self._get_instance()
1099 self.crashdb = crashdb
1100
1101 # create a local reference report so that we can compare
1102 # DistroRelease, Architecture, etc.
1103 self.ref_report = apport.Report()
1104 self.ref_report.add_os_info()
1105 self.ref_report.add_user_info()
1106 self.ref_report['SourcePackage'] = 'coreutils'
1107
1108 # Objects tests rely on.
1109 self._create_project('langpack-o-matic')
1110
1111 def _create_project(self, name):
1112 '''Create a project using launchpadlib to be used by tests.'''
1113
1114 project = self.crashdb.launchpad.projects[name]
1115 if not project:
1116 self.crashdb.launchpad.projects.new_project(
1117 description=name + 'description',
1118 display_name=name,
1119 name=name,
1120 summary=name + 'summary',
1121 title=name + 'title')
1122
1123 @property
1124 def hostname(self):
1125 '''Get the Launchpad hostname for the given crashdb.'''
1126
1127 return self.crashdb.get_hostname()
1128
1129 def get_segv_report(self, force_fresh=False):
1130 '''Generate SEGV crash report.
1131
1132 This is only done once, subsequent calls will return the already
1133 existing ID, unless force_fresh is True.
1134
1135 Return the ID.
1136 '''
1137 global _segv_report
1138 if not force_fresh and _segv_report is not None:
1139 return _segv_report
1140
1141 r = self._generate_sigsegv_report()
1142 r.add_package_info(self.test_package)
1143 r.add_os_info()
1144 r.add_gdb_info()
1145 r.add_user_info()
1146 self.assertEqual(r.standard_title(), 'crash crashed with SIGSEGV in f()')
1147
1148 # add some binary gibberish which isn't UTF-8
1149 r['ShortGibberish'] = ' "]\xb6"\n'
1150 r['LongGibberish'] = 'a\nb\nc\nd\ne\n\xff\xff\xff\n\f'
1151
1152 # create a bug for the report
1153 bug_target = self._get_bug_target(self.crashdb, r)
1154 self.assertTrue(bug_target)
1155
1156 id = self._file_bug(bug_target, r)
1157 self.assertTrue(id > 0)
1158
1159 sys.stderr.write('(Created SEGV report: https://%s/bugs/%i) ' % (self.hostname, id))
1160 if not force_fresh:
1161 _segv_report = id
1162 return id
1163
1164 def get_python_report(self):
1165 '''Generate Python crash report.
1166
1167 Return the ID.
1168 '''
1169 global _python_report
1170 if _python_report is not None:
1171 return _python_report
1172
1173 r = apport.Report('Crash')
1174 r['ExecutablePath'] = '/bin/foo'
1175 r['Traceback'] = '''Traceback (most recent call last):
1176 File "/bin/foo", line 67, in fuzz
1177 print(weird)
1178NameError: global name 'weird' is not defined'''
1179 r['Tags'] = 'boogus pybogus'
1180 r.add_package_info(self.test_package)
1181 r.add_os_info()
1182 r.add_user_info()
1183 self.assertEqual(r.standard_title(),
1184 "foo crashed with NameError in fuzz(): global name 'weird' is not defined")
1185
1186 bug_target = self._get_bug_target(self.crashdb, r)
1187 self.assertTrue(bug_target)
1188
1189 id = self._file_bug(bug_target, r)
1190 self.assertTrue(id > 0)
1191 sys.stderr.write('(Created Python report: https://%s/bugs/%i) ' % (self.hostname, id))
1192 _python_report = id
1193 return id
1194
1195 def get_uncommon_description_report(self, force_fresh=False):
1196 '''File a bug report with an uncommon description.
1197
1198 This is only done once, subsequent calls will return the already
1199 existing ID, unless force_fresh is True.
1200
1201 Example taken from real LP bug 269539. It contains only
1202 ProblemType/Architecture/DistroRelease in the description, and has
1203 free-form description text after the Apport data.
1204
1205 Return the ID.
1206 '''
1207 global _uncommon_description_report
1208 if not force_fresh and _uncommon_description_report is not None:
1209 return _uncommon_description_report
1210
1211 desc = '''problem
1212
1213ProblemType: Package
1214Architecture: amd64
1215DistroRelease: Ubuntu 8.10
1216
1217more text
1218
1219and more
1220'''
1221 bug = self.crashdb.launchpad.bugs.createBug(
1222 title=b'mixed description bug'.encode(),
1223 description=desc,
1224 target=self.crashdb.lp_distro)
1225 sys.stderr.write('(Created uncommon description: https://%s/bugs/%i) ' % (self.hostname, bug.id))
1226
1227 if not force_fresh:
1228 _uncommon_description_report = bug.id
1229 return bug.id
1230
1231 def test_1_download(self):
1232 '''download()'''
1233
1234 r = self.crashdb.download(self.get_segv_report())
1235 self.assertEqual(r['ProblemType'], 'Crash')
1236 self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in f()')
1237 self.assertEqual(r['DistroRelease'], self.ref_report['DistroRelease'])
1238 self.assertEqual(r['Architecture'], self.ref_report['Architecture'])
1239 self.assertEqual(r['Uname'], self.ref_report['Uname'])
1240 self.assertEqual(r.get('NonfreeKernelModules'),
1241 self.ref_report.get('NonfreeKernelModules'))
1242 self.assertEqual(r.get('UserGroups'), self.ref_report.get('UserGroups'))
1243 tags = set(r['Tags'].split())
1244 self.assertEqual(tags, set([self.crashdb.arch_tag, 'apport-crash',
1245 apport.packaging.get_system_architecture()]))
1246
1247 self.assertEqual(r['Signal'], '11')
1248 self.assertTrue(r['ExecutablePath'].endswith('/crash'))
1249 self.assertEqual(r['SourcePackage'], self.test_srcpackage)
1250 self.assertTrue(r['Package'].startswith(self.test_package + ' '))
1251 self.assertTrue('f (x=42)' in r['Stacktrace'])
1252 self.assertTrue('f (x=42)' in r['StacktraceTop'])
1253 self.assertTrue('f (x=42)' in r['ThreadStacktrace'])
1254 self.assertTrue(len(r['CoreDump']) > 1000)
1255 self.assertTrue('Dependencies' in r)
1256 self.assertTrue('Disassembly' in r)
1257 self.assertTrue('Registers' in r)
1258
1259 # check tags
1260 r = self.crashdb.download(self.get_python_report())
1261 tags = set(r['Tags'].split())
1262 self.assertEqual(tags, set(['apport-crash', 'boogus', 'pybogus',
1263 'need-duplicate-check', apport.packaging.get_system_architecture()]))
1264
1265 def test_2_update_traces(self):
1266 '''update_traces()'''
1267
1268 r = self.crashdb.download(self.get_segv_report())
1269 self.assertTrue('CoreDump' in r)
1270 self.assertTrue('Dependencies' in r)
1271 self.assertTrue('Disassembly' in r)
1272 self.assertTrue('Registers' in r)
1273 self.assertTrue('Stacktrace' in r)
1274 self.assertTrue('ThreadStacktrace' in r)
1275 self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in f()')
1276
1277 # updating with a useless stack trace retains core dump
1278 r['StacktraceTop'] = '?? ()'
1279 r['Stacktrace'] = 'long\ntrace'
1280 r['ThreadStacktrace'] = 'thread\neven longer\ntrace'
1281 r['FooBar'] = 'bogus'
1282 self.crashdb.update_traces(self.get_segv_report(), r, 'I can has a better retrace?')
1283 r = self.crashdb.download(self.get_segv_report())
1284 self.assertTrue('CoreDump' in r)
1285 self.assertTrue('Dependencies' in r)
1286 self.assertTrue('Disassembly' in r)
1287 self.assertTrue('Registers' in r)
1288 self.assertTrue('Stacktrace' in r) # TODO: ascertain that it's the updated one
1289 self.assertTrue('ThreadStacktrace' in r)
1290 self.assertFalse('FooBar' in r)
1291 self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in f()')
1292
1293 tags = self.crashdb.launchpad.bugs[self.get_segv_report()].tags
1294 self.assertTrue('apport-crash' in tags)
1295 self.assertFalse('apport-collected' in tags)
1296
1297 # updating with a useful stack trace removes core dump
1298 r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
1299 r['Stacktrace'] = 'long\ntrace'
1300 r['ThreadStacktrace'] = 'thread\neven longer\ntrace'
1301 self.crashdb.update_traces(self.get_segv_report(), r, 'good retrace!')
1302 r = self.crashdb.download(self.get_segv_report())
1303 self.assertFalse('CoreDump' in r)
1304 self.assertTrue('Dependencies' in r)
1305 self.assertTrue('Disassembly' in r)
1306 self.assertTrue('Registers' in r)
1307 self.assertTrue('Stacktrace' in r)
1308 self.assertTrue('ThreadStacktrace' in r)
1309 self.assertFalse('FooBar' in r)
1310
1311 # as previous title had standard form, the top function gets
1312 # updated
1313 self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in read()')
1314
1315 # respects title amendments
1316 bug = self.crashdb.launchpad.bugs[self.get_segv_report()]
1317 bug.title = 'crash crashed with SIGSEGV in f() on exit'
1318 try:
1319 bug.lp_save()
1320 except HTTPError:
1321 pass # LP#336866 workaround
1322 r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
1323 self.crashdb.update_traces(self.get_segv_report(), r, 'good retrace with title amendment')
1324 r = self.crashdb.download(self.get_segv_report())
1325 self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in read() on exit')
1326
1327 # does not destroy custom titles
1328 bug = self.crashdb.launchpad.bugs[self.get_segv_report()]
1329 bug.title = 'crash is crashy'
1330 try:
1331 bug.lp_save()
1332 except HTTPError:
1333 pass # LP#336866 workaround
1334
1335 r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
1336 self.crashdb.update_traces(self.get_segv_report(), r, 'good retrace with custom title')
1337 r = self.crashdb.download(self.get_segv_report())
1338 self.assertEqual(r['Title'], 'crash is crashy')
1339
1340 # test various situations which caused crashes
1341 r['Stacktrace'] = '' # empty file
1342 r['ThreadStacktrace'] = '"]\xb6"\n' # not interpretable as UTF-8, LP #353805
1343 r['StacktraceSource'] = 'a\nb\nc\nd\ne\n\xff\xff\xff\n\f'
1344 self.crashdb.update_traces(self.get_segv_report(), r, 'tests')
1345
1346 def test_get_comment_url(self):
1347 '''get_comment_url() for non-ASCII titles'''
1348
1349 # UTF-8 bytestring, works in both python 2.7 and 3
1350 title = b'1\xc3\xa4\xe2\x99\xa52'
1351
1352 # distro, UTF-8 bytestring
1353 r = apport.Report('Bug')
1354 r['Title'] = title
1355 url = self.crashdb.get_comment_url(r, 42)
1356 self.assertTrue(url.endswith('/ubuntu/+filebug/42?field.title=1%C3%A4%E2%99%A52'))
1357
1358 # distro, unicode
1359 r['Title'] = title.decode('UTF-8')
1360 url = self.crashdb.get_comment_url(r, 42)
1361 self.assertTrue(url.endswith('/ubuntu/+filebug/42?field.title=1%C3%A4%E2%99%A52'))
1362
1363 # package, unicode
1364 r['SourcePackage'] = 'coreutils'
1365 url = self.crashdb.get_comment_url(r, 42)
1366 self.assertTrue(url.endswith('/ubuntu/+source/coreutils/+filebug/42?field.title=1%C3%A4%E2%99%A52'))
1367
1368 def test_update_description(self):
1369 '''update() with changing description'''
1370
1371 bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
1372 bug = self.crashdb.launchpad.bugs.createBug(
1373 description='test description for test bug.',
1374 target=bug_target,
1375 title='testbug')
1376 id = bug.id
1377 self.assertTrue(id > 0)
1378 sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
1379
1380 r = apport.Report('Bug')
1381
1382 r['OneLiner'] = b'bogus\xe2\x86\x92'.decode('UTF-8')
1383 r['StacktraceTop'] = 'f()\ng()\nh(1)'
1384 r['ShortGoo'] = 'lineone\nlinetwo'
1385 r['DpkgTerminalLog'] = 'one\ntwo\nthree\nfour\nfive\nsix'
1386 r['VarLogDistupgradeBinGoo'] = b'\x01' * 1024
1387
1388 self.crashdb.update(id, r, 'NotMe', change_description=True)
1389
1390 r = self.crashdb.download(id)
1391
1392 self.assertEqual(r['OneLiner'], b'bogus\xe2\x86\x92'.decode('UTF-8'))
1393 self.assertEqual(r['ShortGoo'], 'lineone\nlinetwo')
1394 self.assertEqual(r['DpkgTerminalLog'], 'one\ntwo\nthree\nfour\nfive\nsix')
1395 self.assertEqual(r['VarLogDistupgradeBinGoo'], b'\x01' * 1024)
1396
1397 self.assertEqual(self.crashdb.launchpad.bugs[id].tags,
1398 ['apport-collected'])
1399
1400 def test_update_comment(self):
1401 '''update() with appending comment'''
1402
1403 bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
1404 # we need to fake an apport description separator here, since we
1405 # want to be lazy and use download() for checking the result
1406 bug = self.crashdb.launchpad.bugs.createBug(
1407 description='Pr0blem\n\n--- \nProblemType: Bug',
1408 target=bug_target,
1409 title='testbug')
1410 id = bug.id
1411 self.assertTrue(id > 0)
1412 sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
1413
1414 r = apport.Report('Bug')
1415
1416 r['OneLiner'] = 'bogus→'
1417 r['StacktraceTop'] = 'f()\ng()\nh(1)'
1418 r['ShortGoo'] = 'lineone\nlinetwo'
1419 r['DpkgTerminalLog'] = 'one\ntwo\nthree\nfour\nfive\nsix'
1420 r['VarLogDistupgradeBinGoo'] = '\x01' * 1024
1421
1422 self.crashdb.update(id, r, 'meow', change_description=False)
1423
1424 r = self.crashdb.download(id)
1425
1426 self.assertFalse('OneLiner' in r)
1427 self.assertFalse('ShortGoo' in r)
1428 self.assertEqual(r['ProblemType'], 'Bug')
1429 self.assertEqual(r['DpkgTerminalLog'], 'one\ntwo\nthree\nfour\nfive\nsix')
1430 self.assertEqual(r['VarLogDistupgradeBinGoo'], '\x01' * 1024)
1431
1432 self.assertEqual(self.crashdb.launchpad.bugs[id].tags,
1433 ['apport-collected'])
1434
1435 def test_update_filter(self):
1436 '''update() with a key filter'''
1437
1438 bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
1439 bug = self.crashdb.launchpad.bugs.createBug(
1440 description='test description for test bug',
1441 target=bug_target,
1442 title='testbug')
1443 id = bug.id
1444 self.assertTrue(id > 0)
1445 sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
1446
1447 r = apport.Report('Bug')
1448
1449 r['OneLiner'] = 'bogus→'
1450 r['StacktraceTop'] = 'f()\ng()\nh(1)'
1451 r['ShortGoo'] = 'lineone\nlinetwo'
1452 r['DpkgTerminalLog'] = 'one\ntwo\nthree\nfour\nfive\nsix'
1453 r['VarLogDistupgradeBinGoo'] = '\x01' * 1024
1454
1455 self.crashdb.update(id, r, 'NotMe', change_description=True,
1456 key_filter=['ProblemType', 'ShortGoo', 'DpkgTerminalLog'])
1457
1458 r = self.crashdb.download(id)
1459
1460 self.assertFalse('OneLiner' in r)
1461 self.assertEqual(r['ShortGoo'], 'lineone\nlinetwo')
1462 self.assertEqual(r['ProblemType'], 'Bug')
1463 self.assertEqual(r['DpkgTerminalLog'], 'one\ntwo\nthree\nfour\nfive\nsix')
1464 self.assertFalse('VarLogDistupgradeBinGoo' in r)
1465
1466 self.assertEqual(self.crashdb.launchpad.bugs[id].tags, [])
1467
1468 def test_get_distro_release(self):
1469 '''get_distro_release()'''
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: