This commit is contained in:
wangsong 2021-08-24 16:23:19 +08:00
commit bba35ffd41
353 changed files with 307482 additions and 0 deletions

19
AUTHORS Normal file
View File

@ -0,0 +1,19 @@
Hackers
=======
Michiel Sikkes <michiel@eyesopened.nl>
Michael Vogt <michael.vogt@canonical.com>
Translators
===========
Jorge Bernal <koke@amedias.org>
Jean Privat <privat@lirmm.fr>
Martin Willemoes Hansen <mwh@sysrq.dk>
Zygmunt Krynicki <zyga@www.suxx.pl>
Technical Author
================
Sean Wheller <sean@inwords.co.za>
Icons
=====
Jakub Steiner <jimmac@novell.com>

5
BUGS Normal file
View File

@ -0,0 +1,5 @@
* aptsources.py:
- when turning off a new sources.list entry for the runing distro
(e.g. multiverse in a full edgy) and it is turned on again, the
source code checkbox becomes only half-checked
- when turning off/on a entry the comments become disordered

340
COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

62
ChangeLog Normal file
View File

@ -0,0 +1,62 @@
2005-04-04 Michael Vogt <mvo@debian.org>
* configure.in: Added "xh" to ALL_LINGUAS.
2005-04-01 Steve Murphy <murf@e-tools.com>
* configure.in: Added "rw" to ALL_LINGUAS.
2005-03-29 Raphael Higino <raphaelh@cvs.gnome.org>
* configure.in: Added pt_BR to ALL_LINGUAS.
2005-03-29 Zygmunt Krynicki <zyga@www.suxx.pl>
* src/dialog_apt_key.py.in: Enabled translation of known keys
2005-03-24 Michiel Sikkes <michiel@eyesopened.nl>
* data/update-manager.glade: Added help button. Put package count next
to reload button above updatelist.
* src/update-manager.in: Implemented help button.
* configure.in: Added ja to ALL_LINGUAS.
2005-03-21 Adam Weinberger <adamw@gnome.org>
* configure.in: Added en_CA to ALL_LINGUAS.
2005-03-21 Christian Rose <menthos@menthos.com>
* configure.in: Added "sv" to ALL_LINGUAS.
Also sorted the language entries in the ALL_LINGUAS line,
so that it will be possible to spot duplicates or omissions.
2005-03-11 Michiel Sikkes <michiel@eyesopened.nl>
* configure.in: Added el (Greek) to ALL_LINGUAS.
2005-03-03 Dan Damian <dand@gnome.ro>
* configure.in: Added ro (Romanian) to ALL_LINGUAS.
2005-03-10 Zygmunt Krynicki <zyga@www.suxx.pl>
* Improved translation support
2005-02-18 Michiel Sikkes <michiel@eyesopened.nl>
* Added window title to the synaptic gtk socket window.
2005-02-18 Michiel Sikkes <michiel@eyesopened.nl>
* Updated strings to have more consistent messages and it's not a pain
in the ass to translate ;).
* Merge from branches/release-37-2.
2005-01-27 Martin Willemoes Hansen <mwh@sysrq.dk>
* da.po: Added Danish translation.
2004-10-25 Michiel Sikkes <michiel@eyesopened.nl>
* Initial release.

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,68 @@
import datetime
import gettext
gettext.install("update-manager")
from gettext import gettext as _
# the day on which the short support HWE stack goes EoL
HWE_EOL_DATE = datetime.date(2025, 4, 30)
# the day on which the next LTS first point release is available
# used to propose a release upgrade
NEXT_LTS_DOT1_DATE = datetime.date(2022, 7, 21)
# end of the month in which this LTS goes EoL
LTS_EOL_DATE = datetime.date(2025, 4, 30)
class Messages:
UM_UPGRADE = _("""
There is a graphics stack installed on this system. An upgrade to a
configuration supported for the full lifetime of the LTS will become
available on %(date)s and can be installed by running 'update-manager'
in the Dash.
""") % {'date': NEXT_LTS_DOT1_DATE.isoformat()}
APT_UPGRADE = _("""
To upgrade to a supported (or longer-supported) configuration:
* Upgrade from Ubuntu 18.04 LTS to Ubuntu 20.04 LTS by running:
sudo do-release-upgrade %s
OR
* Switch to the current security-supported stack by running:
sudo apt-get install %s
and reboot your system.""")
# this message is shown if there is no clear upgrade path via a
# meta pkg that we recognize
APT_SHOW_UNSUPPORTED = _("""
The following packages are no longer supported:
%s
Please upgrade them to a supported HWE stack or remove them if you
no longer need them.
""")
HWE_SUPPORTED = _("Your Hardware Enablement Stack (HWE) is "
"supported until %(month)s %(year)s.") % {
'month': LTS_EOL_DATE.strftime("%B"),
'year': LTS_EOL_DATE.year}
HWE_SUPPORT_ENDS = _("""
Your current Hardware Enablement Stack (HWE) is going out of support
on %s. After this date security updates for critical parts (kernel
and graphics stack) of your system will no longer be available.
For more information, please see:
http://wiki.ubuntu.com/2004_HWE_EOL
""") % HWE_EOL_DATE.isoformat()
HWE_SUPPORT_HAS_ENDED = _("""
WARNING: Security updates for your current Hardware Enablement
Stack ended on %s:
* http://wiki.ubuntu.com/2004_HWE_EOL
""") % HWE_EOL_DATE.isoformat()

510
LGPL Normal file
View File

@ -0,0 +1,510 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations
below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it
becomes a de-facto standard. To achieve this, non-free programs must
be allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control
compilation and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply, and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License
may add an explicit geographical distribution limitation excluding those
countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms
of the ordinary General Public License).
To apply these terms, attach the following notices to the library.
It is safest to attach them to the start of each source file to most
effectively convey the exclusion of warranty; and each file should
have at least the "copyright" line and a pointer to where the full
notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the library,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James
Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

18
README Normal file
View File

@ -0,0 +1,18 @@
Software Updater for apt
------------------------
This is an application which lets you manage available updates for your
computer via apt and python-apt.
It also supports easy release upgrades. The following environment variables
are honored:
DEBUG_UPDATE_MANAGER:
- If set, debug information is printed to sys.stderr. This is useful for
testing e.g. release upgrade checking/fetching.
If the release upgrade is running in text mode and gnu screen is
available it will automatically use gnu screen. This means that if
e.g. the network connection is dropping just running the upgrader
again will just reconnect to the running upgrade.

20
TODO Normal file
View File

@ -0,0 +1,20 @@
* offer removal of no-longer-supported apps
* improve countrymirror detection
UpdateManager.Common.aptsources.py:
- make the distro detection in sources.list more clever by using the
origin informaton to avoid adding full uris to (unofficial/internal)
mirrors
- make it possible to inherit the mirrros from a ParentSuite (for
the childs)
Misc:
- add download size to treeview
- add /etc/apt/software-properties.d dir where the user can
install matchers and templates
- handle cases like "deb http://bla/ dist sec1 sec2 # comment"
- rework the add channel/cdrom dialogs
- d'n'd for key files
- use one row per section and not one per channel in the treeview
- sort the sources by dist

View File

@ -0,0 +1,308 @@
# ChangelogViewer.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2006 Sebastian Heinlein
# 2007 Canonical
#
# Author: Sebastian Heinlein <sebastian.heinlein@web.de>
# Michael Vogt <michael.vogt@ubuntu.com>
#
# This modul provides an inheritance of the Gtk.TextView that is
# aware of http URLs and allows to open them in a browser.
# It is based on the pygtk-demo "hypertext".
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import Pango
from gettext import gettext as _
from DistUpgrade.ReleaseNotesViewer import open_url
class ChangelogViewer(Gtk.TextView):
def __init__(self, changelog=None):
"""Init the ChangelogViewer as an Inheritance of the Gtk.TextView"""
# init the parent
GObject.GObject.__init__(self)
# global hovering over link state
self.hovering = False
self.first = True
# setup the buffer and signals
self.set_property("editable", False)
self.set_cursor_visible(False)
# set some margin
self.set_right_margin(4)
self.set_left_margin(4)
self.set_pixels_above_lines(4)
# fill the area
self.set_vexpand(True)
self.buffer = Gtk.TextBuffer()
self.set_buffer(self.buffer)
self.connect("button-press-event", self.button_press_event)
self.connect("motion-notify-event", self.motion_notify_event)
self.connect("visibility-notify-event", self.visibility_notify_event)
#self.buffer.connect("changed", self.search_links)
self.buffer.connect_after("insert-text", self.on_insert_text)
# search for links in the changelog and make them clickable
if changelog is not None:
self.buffer.set_text(changelog)
def create_context_menu(self, url):
"""Create the context menu to be displayed when links are right
clicked"""
self.menu = Gtk.Menu()
# create menu items
item_grey_link = Gtk.MenuItem()
item_grey_link.set_label(url)
item_grey_link.connect("activate", self.handle_context_menu, "open",
url)
item_seperator = Gtk.MenuItem()
item_open_link = Gtk.MenuItem()
item_open_link.set_label(_("Open Link in Browser"))
item_open_link.connect("activate", self.handle_context_menu, "open",
url)
item_copy_link = Gtk.MenuItem()
item_copy_link.set_label(_("Copy Link to Clipboard"))
item_copy_link.connect("activate", self.handle_context_menu, "copy",
url)
# add menu items
self.menu.add(item_grey_link)
self.menu.add(item_seperator)
self.menu.add(item_open_link)
self.menu.add(item_copy_link)
self.menu.show_all()
def handle_context_menu(self, menuitem, action, url):
"""Handle activate event for the links' context menu"""
if action == "open":
open_url(url)
if action == "copy":
# the following two lines used to be enough - then gtk3/pygi
# came along ...
#cb = Gtk.Clipboard()
#cb.set_text(url)
display = Gdk.Display.get_default()
selection = Gdk.Atom.intern("CLIPBOARD", False)
cb = Gtk.Clipboard.get_for_display(display, selection)
cb.set_text(url, -1)
cb.store()
def tag_link(self, start, end, url):
"""Apply the tag that marks links to the specified buffer selection"""
tags = start.get_tags()
for tag in tags:
url = getattr(tag, "url", None)
if url != "":
return
tag = self.buffer.create_tag(None, foreground="blue",
underline=Pango.Underline.SINGLE)
tag.url = url
self.buffer.apply_tag(tag, start, end)
def on_insert_text(self, buffer, iter_end, content, *args):
"""Search for http URLs in newly inserted text
and tag them accordingly"""
# some convenient urls
MALONE = "https://launchpad.net/bugs/"
DEBIAN = "http://bugs.debian.org/"
CVE = "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-"
# some convinient end-markers
ws = [" ", "\t", "\n"]
brak = [")", "]", ">"]
punct = [",", "!", ":"]
dot = ["."] + punct
dot_cr = [".\n"]
# search items are start-str, list-of-end-strs, url-prefix
# a lot of this search is "TEH SUCK"(tm) because of limitations
# in iter.forward_search()
# - i.e. no insensitive searching, no regexp
search_items = [("http://", ws + brak + punct + dot_cr, "http://"),
("LP#", ws + brak + dot, MALONE),
("lp#", ws + brak + dot, MALONE),
("LP: #", ws + brak + dot, MALONE),
("lp: #", ws + brak + dot, MALONE),
("LP:#", ws + brak + dot, MALONE),
("lp:#", ws + brak + dot, MALONE),
("Malone: #", ws + brak + dot, MALONE),
("Malone:#", ws + brak + dot, MALONE),
("Ubuntu: #", ws + brak + dot, MALONE),
("Ubuntu:#", ws + brak + dot, MALONE),
("Closes: #", ws + brak + dot, DEBIAN),
("Closes:#", ws + brak + dot, DEBIAN),
("closes:#", ws + brak + dot, DEBIAN),
("closes: #", ws + brak + dot, DEBIAN),
("CVE-", ws + brak + dot, CVE),
]
# search for the next match in the buffer
for (start_str, end_list, url_prefix) in search_items:
# init
iter = buffer.get_iter_at_offset(iter_end.get_offset()
- len(content))
while True:
ret = iter.forward_search(start_str,
Gtk.TextSearchFlags.VISIBLE_ONLY,
iter_end)
# if we reach the end break the loop
if not ret:
break
# get the position of the protocol prefix
(match_start, match_end) = ret
match_suffix = match_end.copy()
match_tmp = match_end.copy()
while True:
# extend the selection to the complete search item
if match_tmp.forward_char():
text = match_end.get_text(match_tmp)
if text in end_list:
break
# move one char futher to get two char
# end-markers (and back later) LP: #396393
match_tmp.forward_char()
text = match_end.get_text(match_tmp)
if text in end_list:
break
match_tmp.backward_char()
else:
break
match_end = match_tmp.copy()
# call the tagging method for the complete URL
url = url_prefix + match_suffix.get_text(match_end)
self.tag_link(match_start, match_end, url)
# set the starting point for the next search
iter = match_end
def button_press_event(self, text_view, event):
"""callback for mouse click events"""
# we only react on left or right mouse clicks
if event.button != 1 and event.button != 3:
return False
# try to get a selection
try:
(start, end) = self.buffer.get_selection_bounds()
except ValueError:
pass
else:
if start.get_offset() != end.get_offset():
return False
# get the iter at the mouse position
(x, y) = self.window_to_buffer_coords(Gtk.TextWindowType.WIDGET,
int(event.x), int(event.y))
# call open_url or menu.popup if an URL is assigned to the iter
try:
iter = self.get_iter_at_location(x, y)
tags = iter.get_tags()
except AttributeError:
# GTK > 3.20 added a return type to this function
(over_text, iter) = self.get_iter_at_location(x, y)
tags = iter.get_tags()
for tag in tags:
if hasattr(tag, "url"):
if event.button == 1:
open_url(tag.url)
break
if event.button == 3:
self.create_context_menu(tag.url)
self.menu.popup(None, None, None, None, event.button,
event.time)
return True
def motion_notify_event(self, text_view, event):
"""callback for the mouse movement event, that calls the
check_hovering method with the mouse postition coordiantes"""
x, y = text_view.window_to_buffer_coords(Gtk.TextWindowType.WIDGET,
int(event.x), int(event.y))
self.check_hovering(x, y)
self.get_window(Gtk.TextWindowType.TEXT).get_pointer()
return False
def visibility_notify_event(self, text_view, event):
"""callback if the widgets gets visible (e.g. moves to the foreground)
that calls the check_hovering method with the mouse position
coordinates"""
window = text_view.get_window(Gtk.TextWindowType.TEXT)
(screen, wx, wy, mod) = window.get_pointer()
(bx, by) = text_view.window_to_buffer_coords(Gtk.TextWindowType.WIDGET,
wx, wy)
self.check_hovering(bx, by)
return False
def check_hovering(self, x, y):
"""Check if the mouse is above a tagged link and if yes show
a hand cursor"""
_hovering = False
# get the iter at the mouse position
try:
iter = self.get_iter_at_location(x, y)
tags = iter.get_tags()
except AttributeError:
# GTK > 3.20 added a return type to this function
(over_text, iter) = self.get_iter_at_location(x, y)
tags = iter.get_tags()
# set _hovering if the iter has the tag "url"
for tag in tags:
if hasattr(tag, "url"):
_hovering = True
break
# change the global hovering state
if _hovering != self.hovering or self.first:
self.first = False
self.hovering = _hovering
# Set the appropriate cursur icon
if self.hovering:
self.get_window(Gtk.TextWindowType.TEXT).set_cursor(
Gdk.Cursor.new(Gdk.CursorType.HAND2))
else:
self.get_window(Gtk.TextWindowType.TEXT).set_cursor(
Gdk.Cursor.new(Gdk.CursorType.LEFT_PTR))
if __name__ == "__main__":
w = Gtk.Window()
cv = ChangelogViewer()
changes = cv.get_buffer()
changes.create_tag("versiontag", weight=Pango.Weight.BOLD)
changes.set_text("""
Version 6-14-0ubuntu1.9.04:
* New upstream version. LP: #382918.
Release notes at http://java.sun.com/javase/6/webnotes/ReleaseNotes.html.
""")
w.add(cv)
w.show_all()
Gtk.main()

View File

@ -0,0 +1,99 @@
# AlertWatcher.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2010 Mohamed Amine IL Idrissi
#
# Author: Mohamed Amine IL Idrissi <ilidrissiamine@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import
from gi.repository import GObject
import dbus
from dbus.mainloop.glib import DBusGMainLoop
class AlertWatcher(GObject.GObject):
""" a class that checks for alerts and reports them, like a battery
or network warning """
__gsignals__ = {"network-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_INT,)),
"battery-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_BOOLEAN,)),
"network-3g-alert": (GObject.SignalFlags.RUN_FIRST,
None,
(GObject.TYPE_BOOLEAN,
GObject.TYPE_BOOLEAN,)),
}
def __init__(self):
GObject.GObject.__init__(self)
DBusGMainLoop(set_as_default=True)
self.bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
# make it always connected if NM isn't available
self.network_state = 3
def check_alert_state(self):
try:
obj = self.bus.get_object("org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager")
obj.connect_to_signal(
"StateChanged",
self._on_network_state_changed,
dbus_interface="org.freedesktop.NetworkManager")
interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
self.network_state = interface.Get(
"org.freedesktop.NetworkManager", "State")
self._network_alert(self.network_state)
# power
obj = self.bus.get_object('org.freedesktop.UPower',
'/org/freedesktop/UPower')
obj.connect_to_signal("Changed", self._power_changed,
dbus_interface="org.freedesktop.UPower")
self._power_changed()
# 3g
self._update_3g_state()
except dbus.exceptions.DBusException:
pass
def _on_network_state_changed(self, state):
self._network_alert(state)
self._update_3g_state()
def _update_3g_state(self):
from .roam import NetworkManagerHelper
nm = NetworkManagerHelper()
on_3g = nm.is_active_connection_gsm_or_cdma()
is_roaming = nm.is_active_connection_gsm_or_cdma_roaming()
self._network_3g_alert(on_3g, is_roaming)
def _network_3g_alert(self, on_3g, is_roaming):
self.emit("network-3g-alert", on_3g, is_roaming)
def _network_alert(self, state):
self.network_state = state
self.emit("network-alert", state)
def _power_changed(self):
obj = self.bus.get_object("org.freedesktop.UPower",
"/org/freedesktop/UPower")
interface = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
on_battery = interface.Get("org.freedesktop.UPower", "OnBattery")
self.emit("battery-alert", on_battery)

View File

@ -0,0 +1,118 @@
# LivePatchSocket.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2017 Canonical
#
# Author: Andrea Azzarone <andrea.azzarone@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from gi.repository import GLib
import http.client
import socket
import threading
import yaml
HOST_NAME = '/var/snap/canonical-livepatch/current/livepatchd.sock'
class UHTTPConnection(http.client.HTTPConnection):
def __init__(self, path):
http.client.HTTPConnection.__init__(self, 'localhost')
self.path = path
def connect(self):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self.path)
self.sock = sock
class LivePatchSocket(object):
def __init__(self, http_conn=None):
if http_conn is None:
self.conn = UHTTPConnection(HOST_NAME)
else:
self.conn = http_conn
def get_status(self, on_done):
def do_call():
try:
self.conn.request('GET', '/status?verbose=True')
r = self.conn.getresponse()
active = r.status == 200
data = yaml.safe_load(r.read())
except Exception:
active = False
data = dict()
check_state = LivePatchSocket.get_check_state(data)
patch_state = LivePatchSocket.get_patch_state(data)
fixes = LivePatchSocket.get_fixes(data)
GLib.idle_add(lambda: on_done(
active, check_state, patch_state, fixes))
thread = threading.Thread(target=do_call)
thread.start()
@staticmethod
def get_check_state(data):
try:
status = data['status']
kernel = next((k for k in status if k['running']), None)
return kernel['livepatch']['checkState']
except Exception:
return 'check-failed'
@staticmethod
def get_patch_state(data):
try:
status = data['status']
kernel = next((k for k in status if k['running']), None)
return kernel['livepatch']['patchState']
except Exception:
return 'unknown'
@staticmethod
def get_fixes(data):
try:
status = data['status']
kernel = next((k for k in status if k['running']), None)
fixes = kernel['livepatch']['fixes']
return [LivePatchFix(f)
for f in fixes.replace('* ', '').split('\n') if len(f) > 0]
except Exception:
return list()
class LivePatchFix(object):
def __init__(self, text):
patched_pattern = ' (unpatched)'
self.patched = text.find(patched_pattern) == -1
self.name = text.replace(patched_pattern, '')
def __eq__(self, other):
if isinstance(other, LivePatchFix):
return self.name == other.name and self.patched == other.patched
return NotImplemented
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result

View File

@ -0,0 +1,425 @@
# MetaRelease.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004,2005 Canonical
#
# Author: Michael Vogt <michael.vogt@ubuntu.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import, print_function
import apt
import apt_pkg
import distro_info
try:
import configparser
except ImportError:
import ConfigParser as configparser
try:
from http.client import BadStatusLine
except ImportError:
from httplib import BadStatusLine
import logging
import email.utils
import os
import socket
import sys
import time
import threading
try:
from urllib.parse import quote
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
except ImportError:
from urllib2 import HTTPError, Request, URLError, urlopen, quote
from .utils import (get_lang, get_dist, get_dist_version, get_ubuntu_flavor,
get_ubuntu_flavor_name)
class MetaReleaseParseError(Exception):
pass
class Dist(object):
def __init__(self, name, version, date, supported):
self.name = name
self.version = version
self.date = date
self.supported = supported
self.releaseNotesURI = None
self.releaseNotesHtmlUri = None
self.upgradeTool = None
self.upgradeToolSig = None
# the server may report that the upgrade is broken currently
self.upgrade_broken = None
class MetaReleaseCore(object):
"""
A MetaReleaseCore object abstracts the list of released
distributions.
"""
DEBUG = "DEBUG_UPDATE_MANAGER" in os.environ
# some constants
CONF = "/etc/update-manager/release-upgrades"
CONF_METARELEASE = "/etc/update-manager/meta-release"
def __init__(self,
useDevelopmentRelease=False,
useProposed=False,
forceLTS=False,
forceDownload=False,
cache=None):
self._debug("MetaRelease.__init__() useDevel=%s useProposed=%s" %
(useDevelopmentRelease, useProposed))
# force download instead of sending if-modified-since
self.forceDownload = forceDownload
self.useDevelopmentRelease = useDevelopmentRelease
# information about the available dists
self.downloaded = threading.Event()
self.upgradable_to = None
self.new_dist = None
if cache is None:
cache = apt.Cache()
self.flavor = get_ubuntu_flavor(cache=cache)
self.flavor_name = get_ubuntu_flavor_name(cache=cache)
self.current_dist_name = get_dist()
self.current_dist_version = get_dist_version()
self.no_longer_supported = None
self.prompt = None
# default (if the conf file is missing)
base_uri = "https://changelogs.ubuntu.com/"
self.METARELEASE_URI = base_uri + "meta-release"
self.METARELEASE_URI_LTS = base_uri + "meta-release-lts"
self.METARELEASE_URI_UNSTABLE_POSTFIX = "-development"
self.METARELEASE_URI_PROPOSED_POSTFIX = "-development"
# check the meta-release config first
parser = configparser.ConfigParser()
if os.path.exists(self.CONF_METARELEASE):
try:
parser.read(self.CONF_METARELEASE)
except configparser.Error as e:
sys.stderr.write("ERROR: failed to read '%s':\n%s" % (
self.CONF_METARELEASE, e))
return
# make changing the metarelease file and the location
# for the files easy
if parser.has_section("METARELEASE"):
sec = "METARELEASE"
for k in ["URI",
"URI_LTS",
"URI_UNSTABLE_POSTFIX",
"URI_PROPOSED_POSTFIX"]:
if parser.has_option(sec, k):
self._debug("%s: %s " % (self.CONF_METARELEASE,
parser.get(sec, k)))
setattr(self, "%s_%s" % (sec, k), parser.get(sec, k))
# check the config file first to figure if we want lts upgrades only
parser = configparser.ConfigParser()
if os.path.exists(self.CONF):
try:
parser.read(self.CONF)
except configparser.Error as e:
sys.stderr.write("ERROR: failed to read '%s':\n%s" % (
self.CONF, e))
return
# now check which specific url to use
if parser.has_option("DEFAULT", "Prompt"):
prompt = parser.get("DEFAULT", "Prompt").lower()
if (prompt == "never" or prompt == "no"):
self.prompt = 'never'
# nothing to do for this object
# FIXME: what about no longer supported?
self.downloaded.set()
return
elif prompt == "lts":
self.prompt = 'lts'
# the Prompt=lts setting only makes sense when running on
# a LTS, otherwise it would result in users not receiving
# any distro upgrades
di = distro_info.UbuntuDistroInfo()
if di.is_lts(self.current_dist_name):
self.METARELEASE_URI = self.METARELEASE_URI_LTS
else:
self._debug("Prompt=lts for non-LTS, ignoring")
else:
self.prompt = 'normal'
# needed for the _tryUpgradeSelf() code in DistUpgradeController
if forceLTS:
self.METARELEASE_URI = self.METARELEASE_URI_LTS
# devel and proposed "just" change the postfix
if useDevelopmentRelease:
self.METARELEASE_URI += self.METARELEASE_URI_UNSTABLE_POSTFIX
elif useProposed:
self.METARELEASE_URI += self.METARELEASE_URI_PROPOSED_POSTFIX
self._debug("metarelease-uri: %s" % self.METARELEASE_URI)
self.metarelease_information = None
if not self._buildMetaReleaseFile():
self._debug("_buildMetaReleaseFile failed")
return
# we start the download thread here and we have a timeout
threading.Thread(target=self.download).start()
#threading.Thread(target=self.check).start()
def _buildMetaReleaseFile(self):
# build the metarelease_file name
self.METARELEASE_FILE = os.path.join(
"/var/lib/update-manager/",
os.path.basename(self.METARELEASE_URI))
# check if we can write to the global location, if not,
# write to homedir
try:
open(self.METARELEASE_FILE, "a").close()
except IOError:
cache_dir = os.getenv(
"XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
# Take special care when creating this directory; ~/.cache needs
# to be created with mode 0700, but the other directories do
# not.
cache_parent_dir = os.path.split(cache_dir)[0]
if not os.path.exists(cache_parent_dir):
try:
os.makedirs(cache_parent_dir)
except OSError as e:
sys.stderr.write("mkdir() failed: '%s'" % e)
return False
if not os.path.exists(cache_dir):
try:
os.mkdir(cache_dir, 0o700)
except OSError as e:
sys.stderr.write("mkdir() failed: '%s'" % e)
return False
path = os.path.join(cache_dir, 'update-manager-core')
if not os.path.exists(path):
try:
os.mkdir(path)
except OSError as e:
sys.stderr.write("mkdir() failed: '%s'" % e)
return False
self.METARELEASE_FILE = os.path.join(
path,
os.path.basename(self.METARELEASE_URI))
# if it is empty, remove it to avoid I-M-S hits on empty file
try:
if os.path.getsize(self.METARELEASE_FILE) == 0:
os.unlink(self.METARELEASE_FILE)
except Exception:
pass
return True
def dist_no_longer_supported(self, dist):
""" virtual function that is called when the distro is no longer
supported
"""
self.no_longer_supported = dist
def new_dist_available(self, dist):
""" virtual function that is called when a new distro release
is available
"""
self.new_dist = dist
def parse(self):
self._debug("MetaRelease.parse()")
current_dist_name = self.current_dist_name
self._debug("current dist name: '%s'" % current_dist_name)
current_dist = None
dists = []
# parse the metarelease_information file
index_tag = apt_pkg.TagFile(self.metarelease_information)
try:
while index_tag.step():
for required_key in ("Dist", "Version", "Supported", "Date"):
if required_key not in index_tag.section:
raise MetaReleaseParseError(
"Required key '%s' missing" % required_key)
name = index_tag.section["Dist"]
self._debug("found distro name: '%s'" % name)
rawdate = index_tag.section["Date"]
parseddate = list(email.utils.parsedate(rawdate))
parseddate[8] = 0 # assume no DST
date = time.mktime(tuple(parseddate))
supported = int(index_tag.section["Supported"])
version = index_tag.section["Version"]
# add the information to a new date object
dist = Dist(name, version, date, supported)
if "ReleaseNotes" in index_tag.section:
dist.releaseNotesURI = index_tag.section["ReleaseNotes"]
lang = get_lang()
if lang:
dist.releaseNotesURI += "?lang=%s" % lang
if "ReleaseNotesHtml" in index_tag.section:
dist.releaseNotesHtmlUri = index_tag.section[
"ReleaseNotesHtml"]
query = self._get_release_notes_uri_query_string(dist)
if query:
dist.releaseNotesHtmlUri += query
if "UpgradeTool" in index_tag.section:
dist.upgradeTool = index_tag.section["UpgradeTool"]
if "UpgradeToolSignature" in index_tag.section:
dist.upgradeToolSig = index_tag.section[
"UpgradeToolSignature"]
if "UpgradeBroken" in index_tag.section:
dist.upgrade_broken = index_tag.section["UpgradeBroken"]
dists.append(dist)
if name == current_dist_name:
current_dist = dist
except apt_pkg.Error:
raise MetaReleaseParseError("Unable to parse %s" %
self.METARELEASE_URI)
self.metarelease_information.close()
self.metarelease_information = None
# first check if the current runing distro is in the meta-release
# information. if not, we assume that we run on something not
# supported and silently return
if current_dist is None:
self._debug("current dist not found in meta-release file\n")
return False
# then see what we can upgrade to
upgradable_to = ""
for dist in dists:
if dist.date > current_dist.date:
# Only offer to upgrade to an unsupported release if running
# with useDevelopmentRelease, this way one can upgrade from an
# LTS release to the next supported non-LTS release e.g. from
# 14.04 to 15.04.
if not dist.supported and not self.useDevelopmentRelease:
continue
upgradable_to = dist
self._debug("new dist: %s" % upgradable_to)
break
# only warn if unsupported and a new dist is available (because
# the development version is also unsupported)
if upgradable_to != "" and not current_dist.supported:
self.upgradable_to = upgradable_to
self.dist_no_longer_supported(current_dist)
if upgradable_to != "":
self.upgradable_to = upgradable_to
self.new_dist_available(upgradable_to)
# parsing done and sucessfully
return True
# the network thread that tries to fetch the meta-index file
# can't touch the gui, runs as a thread
def download(self):
self._debug("MetaRelease.download()")
lastmodified = 0
req = Request(self.METARELEASE_URI)
# make sure that we always get the latest file (#107716)
req.add_header("Cache-Control", "No-Cache")
req.add_header("Pragma", "no-cache")
if os.access(self.METARELEASE_FILE, os.W_OK):
try:
lastmodified = os.stat(self.METARELEASE_FILE).st_mtime
except OSError:
pass
if lastmodified > 0 and not self.forceDownload:
req.add_header("If-Modified-Since",
time.asctime(time.gmtime(lastmodified)))
try:
# open
uri = urlopen(req, timeout=20)
# sometime there is a root owned meta-relase file
# there, try to remove it so that we get it
# with proper permissions
if (os.path.exists(self.METARELEASE_FILE)
and not os.access(self.METARELEASE_FILE, os.W_OK)):
try:
os.unlink(self.METARELEASE_FILE)
except OSError as e:
print("Can't unlink '%s' (%s)" % (self.METARELEASE_FILE,
e))
# we may get exception here on e.g. disk full
try:
f = open(self.METARELEASE_FILE, "w+")
for line in uri.readlines():
f.write(line.decode("UTF-8"))
f.flush()
f.seek(0, 0)
self.metarelease_information = f
except IOError:
pass
uri.close()
# http error
except HTTPError as e:
# mvo: only reuse local info on "not-modified"
if e.code == 304 and os.path.exists(self.METARELEASE_FILE):
self._debug("reading file '%s'" % self.METARELEASE_FILE)
self.metarelease_information = open(self.METARELEASE_FILE, "r")
else:
self._debug("result of meta-release download: '%s'" % e)
# generic network error
except (URLError, BadStatusLine, socket.timeout) as e:
self._debug("result of meta-release download: '%s'" % e)
print("Failed to connect to %s. Check your Internet connection "
"or proxy settings" % self.METARELEASE_URI)
# now check the information we have
if self.metarelease_information is not None:
self._debug("have self.metarelease_information")
try:
self.parse()
except Exception:
logging.exception("parse failed for '%s'"
% self.METARELEASE_FILE)
# no use keeping a broken file around
os.remove(self.METARELEASE_FILE)
# we don't want to keep a meta-release file around when it
# has a "Broken" flag, this ensures we are not bitten by
# I-M-S/cache issues
if self.new_dist and self.new_dist.upgrade_broken:
os.remove(self.METARELEASE_FILE)
else:
self._debug("NO self.metarelease_information")
self.downloaded.set()
@property
def downloading(self):
return not self.downloaded.is_set()
def _get_release_notes_uri_query_string(self, dist):
q = "?"
# get the lang
lang = get_lang()
if lang:
q += "lang=%s&" % lang
# get the os
q += "os=%s&" % self.flavor
# get the version to upgrade to
q += "ver=%s" % dist.version
# the archive didn't respond well to ? being %3F
return quote(q, '/?')
def _debug(self, msg):
if self.DEBUG:
sys.stderr.write(msg + "\n")
if __name__ == "__main__":
meta = MetaReleaseCore(False, False)

View File

@ -0,0 +1,462 @@
# MyCache.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004-2008 Canonical
#
# Author: Michael Vogt <mvo@debian.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import, print_function
import warnings
warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
import apt
import apt_pkg
import logging
import os
try:
from urllib.error import HTTPError
from urllib.request import urlopen
from urllib.parse import urlsplit
except ImportError:
from urllib2 import HTTPError, urlopen
from urlparse import urlsplit
try:
from http.client import BadStatusLine
except ImportError:
from httplib import BadStatusLine
import socket
import subprocess
import re
import DistUpgrade.DistUpgradeCache
from gettext import gettext as _
try:
from launchpadlib.launchpad import Launchpad
except ImportError:
Launchpad = None
SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences"
CHANGELOGS_POOL = "https://changelogs.ubuntu.com/changelogs/pool/"
CHANGELOGS_URI = CHANGELOGS_POOL + "%s/%s/%s/%s_%s/%s"
class HttpsChangelogsUnsupportedError(Exception):
""" https changelogs with credentials are unsupported because of the
lack of certitifcation validation in urllib2 which allows MITM
attacks to steal the credentials
"""
pass
class MyCache(DistUpgrade.DistUpgradeCache.MyCache):
CHANGELOG_ORIGIN = "Ubuntu"
def __init__(self, progress, rootdir=None):
apt.Cache.__init__(self, progress, rootdir)
# save for later
self.rootdir = rootdir
# raise if we have packages in reqreinst state
# and let the caller deal with that (runs partial upgrade)
assert len(self.req_reinstall_pkgs) == 0
# check if the dpkg journal is ok (we need to do that here
# too because libapt will only do it when it tries to lock
# the packaging system)
assert(not self._dpkgJournalDirty())
# init the regular cache
self._initDepCache()
self.all_changes = {}
self.all_news = {}
# on broken packages, try to fix via saveDistUpgrade()
if self._depcache.broken_count > 0:
self.saveDistUpgrade()
assert (self._depcache.broken_count == 0
and self._depcache.del_count == 0)
self.launchpad = None
# generate versioned_kernel_pkgs_regexp for later use
apt_versioned_kernel_pkgs = apt_pkg.config.value_list(
"APT::VersionedKernelPackages")
if apt_versioned_kernel_pkgs:
self.versioned_kernel_pkgs_regexp = re.compile("(" + "|".join(
["^" + p for p in apt_versioned_kernel_pkgs]) + ")")
running_kernel_version = subprocess.check_output(
["uname", "-r"], universal_newlines=True).rstrip()
self.running_kernel_pkgs_regexp = re.compile("(" + "|".join(
[("^" + p + ".*" + running_kernel_version)
if not p.startswith(".*") else (running_kernel_version + p)
for p in apt_versioned_kernel_pkgs]) + ")")
else:
self.versioned_kernel_pkgs_regexp = None
self.running_kernel_pkgs_regexp = None
def _dpkgJournalDirty(self):
"""
test if the dpkg journal is dirty
(similar to debSystem::CheckUpdates)
"""
d = os.path.dirname(
apt_pkg.config.find_file("Dir::State::status")) + "/updates"
for f in os.listdir(d):
if re.match("[0-9]+", f):
return True
return False
def _initDepCache(self):
#apt_pkg.config.set("Debug::pkgPolicy","1")
#self.depcache = apt_pkg.GetDepCache(self.cache)
#self._depcache = apt_pkg.GetDepCache(self._cache)
self._depcache.read_pinfile()
if os.path.exists(SYNAPTIC_PINFILE):
self._depcache.read_pinfile(SYNAPTIC_PINFILE)
self._depcache.init()
def clear(self):
self._initDepCache()
@property
def required_download(self):
""" get the size of the packages that are required to download """
pm = apt_pkg.PackageManager(self._depcache)
fetcher = apt_pkg.Acquire()
pm.get_archives(fetcher, self._list, self._records)
return fetcher.fetch_needed
@property
def install_count(self):
return self._depcache.inst_count
def keep_count(self):
return self._depcache.keep_count
@property
def del_count(self):
return self._depcache.del_count
def _check_dependencies(self, target, deps):
"""Return True if any of the dependencies in deps match target."""
# TODO: handle virtual packages
for dep_or in deps:
if not dep_or:
continue
match = True
for base_dep in dep_or:
if (base_dep.name != target.package.shortname
or not apt_pkg.check_dep(
target.version, base_dep.relation, base_dep.version)):
match = False
if match:
return True
return False
def find_removal_justification(self, pkg):
target = pkg.installed
if not target:
return False
for cpkg in self:
candidate = cpkg.candidate
if candidate is not None:
if (self._check_dependencies(
target, candidate.get_dependencies("Conflicts"))
and self._check_dependencies(
target, candidate.get_dependencies("Replaces"))):
logging.info(
"%s Conflicts/Replaces %s; allowing removal" % (
candidate.package.shortname, pkg.shortname))
return True
return False
def saveDistUpgrade(self):
""" this functions mimics a upgrade but will never remove anything """
#self._apply_dselect_upgrade()
self._depcache.upgrade(True)
wouldDelete = self._depcache.del_count
if wouldDelete > 0:
deleted_pkgs = [pkg for pkg in self if pkg.marked_delete]
assert wouldDelete == len(deleted_pkgs)
for pkg in deleted_pkgs:
if self.find_removal_justification(pkg):
wouldDelete -= 1
if wouldDelete > 0:
self.clear()
assert (self._depcache.broken_count == 0
and self._depcache.del_count == 0)
else:
assert self._depcache.broken_count == 0
#self._apply_dselect_upgrade()
self._depcache.upgrade()
return wouldDelete
def _strip_epoch(self, verstr):
" strip of the epoch "
vers_no_epoch = verstr.split(":")
if len(vers_no_epoch) > 1:
verstr = "".join(vers_no_epoch[1:])
return verstr
def _get_changelog_or_news(self, name, fname, strict_versioning=False,
changelogs_uri=None):
" helper that fetches the file in question "
# don't touch the gui in this function, it needs to be thread-safe
pkg = self[name]
# get the src package name
srcpkg = pkg.candidate.source_name
# assume "main" section
src_section = "main"
# use the section of the candidate as a starting point
section = pkg._pcache._depcache.get_candidate_ver(pkg._pkg).section
# get the source version
srcver_epoch = pkg.candidate.source_version
srcver = self._strip_epoch(srcver_epoch)
split_section = section.split("/")
if len(split_section) > 1:
src_section = split_section[0]
# lib is handled special
prefix = srcpkg[0]
if srcpkg.startswith("lib"):
prefix = "lib" + srcpkg[3]
# the changelogs_uri argument overrides the default changelogs_uri,
# this is useful for e.g. PPAs where we construct the changelogs
# path differently
if changelogs_uri:
uri = changelogs_uri
else:
uri = CHANGELOGS_URI % (src_section, prefix, srcpkg, srcpkg,
srcver, fname)
# https uris are not supported when they contain a username/password
# because the urllib2 https implementation will not check certificates
# and so its possible to do a man-in-the-middle attack to steal the
# credentials
res = urlsplit(uri)
if res.scheme == "https" and res.username:
raise HttpsChangelogsUnsupportedError(
"https locations with username/password are not"
"supported to fetch changelogs")
# print("Trying: %s " % uri)
changelog = urlopen(uri)
#print(changelog.read())
# do only get the lines that are new
alllines = ""
regexp = "^%s \\((.*)\\)(.*)$" % (re.escape(srcpkg))
while True:
line = changelog.readline().decode("UTF-8", "replace")
if line == "":
break
match = re.match(regexp, line)
if match:
# strip epoch from installed version
# and from changelog too
installed = getattr(pkg.installed, "version", None)
if installed and ":" in installed:
installed = installed.split(":", 1)[1]
changelogver = match.group(1)
if changelogver and ":" in changelogver:
changelogver = changelogver.split(":", 1)[1]
# we test for "==" here for changelogs
# to ensure that the version
# is actually really in the changelog - if not
# just display it all, this catches cases like:
# gcc-defaults with "binver=4.3.1" and srcver=1.76
#
# for NEWS.Debian we do require the changelogver > installed
if strict_versioning:
if (installed
and apt_pkg.version_compare(changelogver,
installed) < 0):
break
else:
if (installed
and apt_pkg.version_compare(changelogver,
installed) == 0):
break
alllines = alllines + line
return alllines
def _extract_ppa_changelog_uri(self, name):
"""Return the changelog URI from the Launchpad API
Return None in case of an error.
"""
if not Launchpad:
logging.warning("Launchpadlib not available, cannot retrieve PPA "
"changelog")
return None
cdt = self[name].candidate
for uri in cdt.uris:
if urlsplit(uri).hostname != 'ppa.launchpad.net':
continue
match = re.search('http.*/(.*)/(.*)/ubuntu/.*', uri)
if match is not None:
user, ppa = match.group(1), match.group(2)
break
else:
logging.error("Unable to find a valid PPA candidate URL.")
return
# Login on launchpad if we are not already
if self.launchpad is None:
self.launchpad = Launchpad.login_anonymously('update-manager',
'production',
version='devel')
archive = self.launchpad.archives.getByReference(
reference='~%s/ubuntu/%s' % (user, ppa)
)
if archive is None:
logging.error("Unable to retrieve the archive from the Launchpad "
"API.")
return
spphs = archive.getPublishedSources(source_name=cdt.source_name,
exact_match=True,
version=cdt.source_version)
if not spphs:
logging.error("No published sources were retrieved from the "
"Launchpad API.")
return
return spphs[0].changelogUrl()
def _guess_third_party_changelogs_uri_by_source(self, name):
pkg = self[name]
deb_uri = pkg.candidate.uri
if deb_uri is None:
return None
srcrec = pkg.candidate.record.get("Source")
if not srcrec:
return None
# srcpkg can be "apt" or "gcc-default (1.0)"
srcpkg = srcrec.split("(")[0].strip()
if "(" in srcrec:
srcver = srcrec.split("(")[1].rstrip(")")
else:
srcver = pkg.candidate.source_version
base_uri = deb_uri.rpartition("/")[0]
return base_uri + "/%s_%s.changelog" % (srcpkg, srcver)
def _guess_third_party_changelogs_uri_by_binary(self, name):
""" guess changelogs uri based on ArchiveURI by replacing .deb
with .changelog
"""
# there is always a pkg and a pkg.candidate, no need to add
# check here
pkg = self[name]
deb_uri = pkg.candidate.uri
if deb_uri:
return "%s.changelog" % deb_uri.rsplit(".", 1)[0]
return None
def get_news_and_changelog(self, name, lock):
self.get_news(name)
self.get_changelog(name)
try:
lock.release()
except Exception:
pass
def get_news(self, name):
" get the NEWS.Debian file from the changelogs location "
try:
news = self._get_changelog_or_news(name, "NEWS.Debian", True)
except Exception:
return
if news:
self.all_news[name] = news
def _fetch_changelog_for_third_party_package(self, name, origins):
# Special case for PPAs
changelogs_uri_ppa = None
for origin in origins:
if origin.origin.startswith('LP-PPA-'):
try:
changelogs_uri_ppa = self._extract_ppa_changelog_uri(name)
break
except Exception:
logging.exception("Unable to connect to the Launchpad "
"API.")
# Try non official changelog location
changelogs_uri_binary = \
self._guess_third_party_changelogs_uri_by_binary(name)
changelogs_uri_source = \
self._guess_third_party_changelogs_uri_by_source(name)
error_message = ""
for changelogs_uri in [changelogs_uri_ppa,
changelogs_uri_binary,
changelogs_uri_source]:
if changelogs_uri:
try:
changelog = self._get_changelog_or_news(
name, "changelog", False, changelogs_uri)
self.all_changes[name] += changelog
except (HTTPError, HttpsChangelogsUnsupportedError):
# no changelogs_uri or 404
error_message = _(
"This update does not come from a "
"source that supports changelogs.")
except (IOError, BadStatusLine, socket.error):
# network errors and others
logging.exception("error on changelog fetching")
error_message = _(
"Failed to download the list of changes. \n"
"Please check your Internet connection.")
self.all_changes[name] += error_message
def get_changelog(self, name):
" get the changelog file from the changelog location "
origins = self[name].candidate.origins
self.all_changes[name] = _("Changes for %s versions:\n"
"Installed version: %s\n"
"Available version: %s\n\n") % \
(name, getattr(self[name].installed, "version", None),
self[name].candidate.version)
if self.CHANGELOG_ORIGIN not in [o.origin for o in origins]:
self._fetch_changelog_for_third_party_package(name, origins)
return
# fixup epoch handling version
srcpkg = self[name].candidate.source_name
srcver_epoch = self[name].candidate.source_version.replace(':', '%3A')
try:
changelog = self._get_changelog_or_news(name, "changelog")
if len(changelog) == 0:
changelog = _("The changelog does not contain any relevant "
"changes.\n\n"
"Please use http://launchpad.net/ubuntu/+source/"
"%s/%s/+changelog\n"
"until the changes become available or try "
"again later.") % (srcpkg, srcver_epoch)
except HTTPError:
changelog = _("The list of changes is not available yet.\n\n"
"Please use http://launchpad.net/ubuntu/+source/"
"%s/%s/+changelog\n"
"until the changes become available or try again "
"later.") % (srcpkg, srcver_epoch)
except (IOError, BadStatusLine, socket.error) as e:
print("caught exception: ", e)
changelog = _("Failed to download the list "
"of changes. \nPlease "
"check your Internet "
"connection.")
self.all_changes[name] += changelog

View File

@ -0,0 +1,548 @@
# UpdateList.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004-2013 Canonical
#
# Author: Michael Vogt <mvo@debian.org>
# Dylan McCall <dylanmccall@ubuntu.com>
# Michael Terry <michael.terry@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import print_function
import warnings
warnings.filterwarnings("ignore", "Accessed deprecated property",
DeprecationWarning)
from gettext import gettext as _
import apt
import logging
import itertools
import os
import random
import glob
from gi.repository import Gio
from UpdateManager.Core import utils
class UpdateItem():
def __init__(self, pkg, name, icon, to_remove):
self.icon = icon
self.name = name
self.pkg = pkg
self.to_remove = to_remove
def is_selected(self):
if not self.to_remove:
return self.pkg.marked_install or self.pkg.marked_upgrade
else:
return self.pkg.marked_delete
class UpdateGroup(UpdateItem):
_depcache = {}
def __init__(self, pkg, name, icon, to_remove):
UpdateItem.__init__(self, pkg, name, icon, to_remove)
self._items = set()
self._deps = set()
self.core_item = None
if pkg is not None:
self.core_item = UpdateItem(pkg, name, icon, to_remove)
self._items.add(self.core_item)
@property
def items(self):
all_items = []
all_items.extend(self._items)
return sorted(all_items, key=lambda a: a.name.lower())
def add(self, pkg, cache=None, eventloop_callback=None, to_remove=False):
name = utils.get_package_label(pkg)
icon = Gio.ThemedIcon.new("package")
self._items.add(UpdateItem(pkg, name, icon, to_remove))
# If the pkg is in self._deps, stop here. We have already calculated
# the recursive dependencies for this package, no need to do it again.
if cache and pkg.name in cache and pkg.name not in self._deps:
if not self._deps:
# Initial deps haven't been calculated. As we're checking
# whether _deps is empty in is_dependency, we must init now or
# it won't be done at all.
self._init_deps(cache, eventloop_callback)
self._add_deps(pkg, cache, eventloop_callback)
def contains(self, item):
return item in self._items
def _init_deps(self, cache, eventloop_callback):
for item in self._items:
if item.pkg and item.pkg.name not in self._deps:
self._add_deps(item.pkg, cache, eventloop_callback)
def _add_deps(self, pkg, cache, eventloop_callback):
"""Adds pkg and dependencies of pkg to the dependency list."""
if pkg is None or pkg.candidate is None or pkg.name in self._deps:
# This shouldn't really happen. If we land here often, it's a sign
# that something has gone wrong. Unless all pkgs are None it's not
# a critical issue - a hit to the performance at most.
reason = ((not pkg or not pkg.candidate)
and "Package was None or didn't have a candidate."
or "%s already in _deps." % pkg.name)
logging.debug("Useless call to _add_deps. %s" % reason)
return
if len(self._deps) % 200 == 0 and callable(eventloop_callback):
# Don't spin the loop every time _add_deps is called.
eventloop_callback()
self._deps.add(pkg.name)
if pkg.name in self._depcache:
for dep in self._depcache[pkg.name]:
if dep not in self._deps and dep in cache:
self._add_deps(cache[dep], cache, eventloop_callback)
else:
candidate = pkg.candidate
dependencies = candidate.get_dependencies('Depends', 'Recommends')
for dependency_pkg in itertools.chain.from_iterable(dependencies):
name = dependency_pkg.name
if name not in self._deps and name in cache:
self._depcache.setdefault(pkg.name, []).append(name)
self._add_deps(cache[name], cache, eventloop_callback)
def is_dependency(self, maybe_dep, cache=None, eventloop_callback=None):
if not self._deps and cache:
self._init_deps(cache, eventloop_callback)
return maybe_dep.name in self._deps
def packages_are_selected(self):
for item in self.items:
if item.is_selected():
return True
return False
def selection_is_inconsistent(self):
pkgs_installing = [item for item in self.items if item.is_selected()]
return (len(pkgs_installing) > 0
and len(pkgs_installing) < len(self.items))
def get_total_size(self):
if self.to_remove:
return 0
size = 0
for item in self.items:
size += getattr(item.pkg.candidate, "size", 0)
return size
class UpdateApplicationGroup(UpdateGroup):
def __init__(self, pkg, application, to_remove):
name = application.get_display_name()
icon = application.get_icon()
super(UpdateApplicationGroup, self).__init__(pkg, name, icon,
to_remove)
class UpdatePackageGroup(UpdateGroup):
def __init__(self, pkg, to_remove):
name = utils.get_package_label(pkg)
icon = Gio.ThemedIcon.new("package")
super(UpdatePackageGroup, self).__init__(pkg, name, icon, to_remove)
class UpdateSystemGroup(UpdateGroup):
def __init__(self, cache, to_remove):
# Translators: the %s is a distro name, like 'Ubuntu' and 'base' as in
# the core components and packages.
name = _("%s base") % utils.get_ubuntu_flavor_name(cache=cache)
icon = Gio.ThemedIcon.new("distributor-logo")
super(UpdateSystemGroup, self).__init__(None, name, icon, to_remove)
class UpdateOrigin():
def __init__(self, desc, importance):
self.packages = []
self.importance = importance
self.description = desc
class UpdateList():
"""
class that contains the list of available updates in
self.pkgs[origin] where origin is the user readable string
"""
# the key in the debian/control file used to add the phased
# updates percentage
PHASED_UPDATES_KEY = "Phased-Update-Percentage"
# the file that contains the uniq machine id
UNIQ_MACHINE_ID_FILE = "/etc/machine-id"
# use the dbus one as a fallback
UNIQ_MACHINE_ID_FILE_FALLBACK = "/var/lib/dbus/machine-id"
APP_INSTALL_PATTERN = "/usr/share/app-install/desktop/%s:*.desktop"
# the configuration key to turn phased-updates always on
ALWAYS_INCLUDE_PHASED_UPDATES = (
"Update-Manager::Always-Include-Phased-Updates")
# ... or always off
NEVER_INCLUDE_PHASED_UPDATES = (
"Update-Manager::Never-Include-Phased-Updates")
def __init__(self, parent, dist=None):
self.dist = (dist if dist else utils.get_dist())
self.distUpgradeWouldDelete = 0
self.update_groups = []
self.security_groups = []
self.kernel_autoremove_groups = []
self.duplicate_groups = []
self.num_updates = 0
self.random = random.Random()
self.ignored_phased_updates = []
# a stable machine uniq id
try:
with open(self.UNIQ_MACHINE_ID_FILE) as f:
self.machine_uniq_id = f.read()
except FileNotFoundError:
with open(self.UNIQ_MACHINE_ID_FILE_FALLBACK) as f:
self.machine_uniq_id = f.read()
if 'XDG_DATA_DIRS' in os.environ and os.environ['XDG_DATA_DIRS']:
data_dirs = os.environ['XDG_DATA_DIRS']
else:
data_dirs = '/usr/local/share/:/usr/share/'
self.application_dirs = [os.path.join(base, 'applications')
for base in data_dirs.split(':')]
if 'XDG_CURRENT_DESKTOP' in os.environ:
self.current_desktop = os.environ.get('XDG_CURRENT_DESKTOP')
else:
self.current_desktop = ''
self.desktop_cache = {}
def _file_is_application(self, file_path):
# WARNING: This is called often if there's a lot of updates. A poor
# performing call here has a huge impact on the overall performance!
if not file_path.endswith(".desktop"):
# First the obvious case: If the path doesn't end in a .desktop
# extension, this isn't a desktop file.
return False
file_path = os.path.abspath(file_path)
for app_dir in self.application_dirs:
if file_path.startswith(app_dir):
return True
return False
def _rate_application_for_package(self, application, pkg):
score = 0
desktop_file = os.path.basename(application.get_filename())
application_id = os.path.splitext(desktop_file)[0]
if application.should_show():
score += 1
if application_id == pkg.name:
score += 5
return score
def _get_application_for_package(self, pkg):
desktop_files = []
rated_applications = []
for installed_file in pkg.installed_files:
if self._file_is_application(installed_file):
desktop_files.append(installed_file)
if pkg.name in self.desktop_cache:
desktop_files += self.desktop_cache[pkg.name]
for desktop_file in desktop_files:
try:
application = Gio.DesktopAppInfo.new_from_filename(
desktop_file)
application.set_desktop_env(self.current_desktop)
except Exception as e:
logging.warning("Error loading .desktop file %s: %s" %
(desktop_file, e))
continue
score = self._rate_application_for_package(application, pkg)
if score > 0:
rated_applications.append((score, application))
rated_applications.sort(key=lambda app: app[0], reverse=True)
if len(rated_applications) > 0:
return rated_applications[0][1]
else:
return None
def _populate_desktop_cache(self, pkg_names):
if not pkg_names:
# No updates; This shouldn't have happened.
logging.warning("_populate_desktop_cache called with empty list "
"of packages.")
return
elif len(pkg_names) == 1:
# One update; Let glob do the matching.
pattern = self.APP_INSTALL_PATTERN % pkg_names[0]
else:
# More than one update available. Glob all desktop files and store
# those that match an upgradeable package.
pattern = self.APP_INSTALL_PATTERN % "*"
for desktop_file in glob.iglob(pattern):
try:
pkg = desktop_file.split('/')[-1].split(":")[0]
except IndexError:
# app-install-data desktop file had an unexpected naming
# convention. As we can't extract the package name from
# the path, just ignore it.
logging.error("Could not extract package name from '%s'. "
"File ignored." % desktop_file)
continue
if pkg in pkg_names:
self.desktop_cache.setdefault(pkg, []).append(desktop_file)
logging.debug("App candidate for %s: %s" %
(pkg, desktop_file))
def _is_security_update(self, pkg):
""" This will test if the pkg is a security update.
This includes if there is a newer version in -updates, but also
an older update available in -security. For example, if
installed pkg A v1.0 is available in both -updates (as v1.2) and
-security (v1.1). we want to display it as a security update.
:return: True if the update comes from the security pocket
"""
if not self.dist:
return False
inst_ver = pkg._pkg.current_ver
for ver in pkg._pkg.version_list:
# discard is < than installed ver
if (inst_ver
and apt.apt_pkg.version_compare(ver.ver_str,
inst_ver.ver_str) <= 0):
continue
# check if we have a match
for (verFileIter, index) in ver.file_list:
if verFileIter.archive == "%s-security" % self.dist and \
verFileIter.origin == "Ubuntu":
indexfile = pkg._pcache._list.find_index(verFileIter)
if indexfile: # and indexfile.IsTrusted:
return True
return False
def _is_ignored_phased_update(self, pkg):
""" This will test if the pkg is a phased update and if
it needs to get installed or ignored.
:return: True if the updates should be ignored
"""
# allow the admin to override this
if apt.apt_pkg.config.find_b(
self.ALWAYS_INCLUDE_PHASED_UPDATES, False):
return False
if self.PHASED_UPDATES_KEY in pkg.candidate.record:
if apt.apt_pkg.config.find_b(
self.NEVER_INCLUDE_PHASED_UPDATES, False):
logging.info("holding back phased update per configuration")
return True
# its important that we always get the same result on
# multiple runs of the update-manager, so we need to
# feed a seed that is a combination of the pkg/ver/machine
self.random.seed("%s-%s-%s" % (
pkg.candidate.source_name, pkg.candidate.version,
self.machine_uniq_id))
threshold = pkg.candidate.record[self.PHASED_UPDATES_KEY]
percentage = self.random.randint(0, 100)
if percentage > int(threshold):
logging.info("holding back phased update %s (%s < %s)" % (
pkg.name, threshold, percentage))
return True
return False
def _get_linux_packages(self):
"Return all binary packages made by the linux-meta source package"
# Hard code this rather than generate from source info in cache because
# that might only be available if we have deb-src lines. I think we
# could also generate it by iterating over all the binary package info
# we have, but that is costly. These don't change often.
return ['linux',
'linux-cloud-tools-generic',
'linux-cloud-tools-lowlatency',
'linux-cloud-tools-virtual',
'linux-crashdump',
'linux-generic',
'linux-generic-lpae',
'linux-headers-generic',
'linux-headers-generic-lpae',
'linux-headers-lowlatency',
'linux-headers-lowlatency-lpae',
'linux-headers-server',
'linux-headers-virtual',
'linux-image',
'linux-image-extra-virtual',
'linux-image-generic',
'linux-image-generic-lpae',
'linux-image-lowlatency',
'linux-image-virtual',
'linux-lowlatency',
'linux-signed-generic',
'linux-signed-image-generic',
'linux-signed-image-lowlatency',
'linux-signed-lowlatency',
'linux-source',
'linux-tools-generic',
'linux-tools-generic-lpae',
'linux-tools-lowlatency',
'linux-tools-virtual',
'linux-virtual']
def _make_groups(self, cache, pkgs, eventloop_callback, to_remove=False):
if not pkgs:
return []
ungrouped_pkgs = []
app_groups = []
pkg_groups = []
for pkg in pkgs:
app = self._get_application_for_package(pkg)
if app is not None:
app_group = UpdateApplicationGroup(pkg, app, to_remove)
app_groups.append(app_group)
else:
ungrouped_pkgs.append(pkg)
# Stick together applications and their immediate dependencies
for pkg in list(ungrouped_pkgs):
dep_groups = []
for group in app_groups:
if group.is_dependency(pkg, cache, eventloop_callback):
dep_groups.append(group)
if len(dep_groups) > 1:
break
if len(dep_groups) == 1:
dep_groups[0].add(pkg, cache, eventloop_callback, to_remove)
ungrouped_pkgs.remove(pkg)
system_group = None
if ungrouped_pkgs:
# Separate out system base packages. If we have already found an
# application for all updates, don't bother.
meta_group = UpdateGroup(None, None, None, to_remove)
flavor_package = utils.get_ubuntu_flavor_package(cache=cache)
meta_pkgs = [flavor_package, "ubuntu-standard", "ubuntu-minimal"]
meta_pkgs.extend(self._get_linux_packages())
for pkg in meta_pkgs:
if pkg in cache:
meta_group.add(cache[pkg])
for pkg in ungrouped_pkgs:
if meta_group.is_dependency(pkg, cache, eventloop_callback):
if system_group is None:
system_group = UpdateSystemGroup(cache, to_remove)
system_group.add(pkg)
else:
pkg_groups.append(UpdatePackageGroup(pkg, to_remove))
app_groups.sort(key=lambda a: a.name.lower())
pkg_groups.sort(key=lambda a: a.name.lower())
if system_group:
pkg_groups.append(system_group)
return app_groups + pkg_groups
def update(self, cache, eventloop_callback=None, duplicate_packages=[]):
self.held_back = []
# do the upgrade
self.distUpgradeWouldDelete = cache.saveDistUpgrade()
security_pkgs = []
upgrade_pkgs = []
kernel_autoremove_pkgs = []
duplicate_pkgs = []
# Find all upgradable packages
for pkg in cache:
if pkg.is_upgradable or pkg.marked_install:
if getattr(pkg.candidate, "origins", None) is None:
# can happen for e.g. locked packages
# FIXME: do something more sensible here (but what?)
print("WARNING: upgradable but no candidate.origins?!?: ",
pkg.name)
continue
# see if its a phased update and *not* a security update
# keep track of packages for which the update percentage was
# not met for testing
is_security_update = self._is_security_update(pkg)
if not is_security_update:
if self._is_ignored_phased_update(pkg):
self.ignored_phased_updates.append(pkg)
continue
if is_security_update:
security_pkgs.append(pkg)
else:
upgrade_pkgs.append(pkg)
self.num_updates = self.num_updates + 1
if pkg.is_upgradable and not (pkg.marked_upgrade
or pkg.marked_install):
self.held_back.append(pkg.name)
continue
if (pkg.is_auto_removable
and (cache.versioned_kernel_pkgs_regexp
and cache.versioned_kernel_pkgs_regexp.match(pkg.name)
and not cache.running_kernel_pkgs_regexp.match(
pkg.name))):
kernel_autoremove_pkgs.append(pkg)
if (pkg.name in duplicate_packages):
duplicate_pkgs.append(pkg)
# perform operations after the loop to not skip packages which
# changed state due to the resolver
for pkg in kernel_autoremove_pkgs:
pkg.mark_delete()
for pkg in duplicate_pkgs:
pkg.mark_delete()
for pkg in self.ignored_phased_updates:
pkg.mark_keep()
if security_pkgs or upgrade_pkgs:
# There's updates available. Initiate the desktop file cache.
pkg_names = [p.name for p in
security_pkgs + upgrade_pkgs
+ kernel_autoremove_pkgs + duplicate_pkgs]
self._populate_desktop_cache(pkg_names)
#将所有的包进行分组 整理
self.update_groups = self._make_groups(cache, upgrade_pkgs,
eventloop_callback)
self.security_groups = self._make_groups(cache, security_pkgs,
eventloop_callback)
self.kernel_autoremove_groups = self._make_groups(
cache, kernel_autoremove_pkgs, eventloop_callback, True)
self.duplicate_groups = self._make_groups(
cache, duplicate_pkgs, eventloop_callback, True)

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

212
UpdateManager/Core/roam.py Normal file
View File

@ -0,0 +1,212 @@
# utils.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2011 Canonical
#
# Author: Alex Chiang <achiang@canonical.com>
# Michael Vogt <michael.vogt@ubuntu.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import print_function
import dbus
import sys
class ModemManagerHelper(object):
# data taken from
# http://projects.gnome.org/NetworkManager/developers/mm-spec-04.html
MM_DBUS_IFACE = "org.freedesktop.ModemManager"
MM_DBUS_IFACE_MODEM = MM_DBUS_IFACE + ".Modem"
# MM_MODEM_TYPE
MM_MODEM_TYPE_GSM = 1
MM_MODEM_TYPE_CDMA = 2
# GSM
# Not registered, not searching for new operator to register.
MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE = 0
# Registered on home network.
MM_MODEM_GSM_NETWORK_REG_STATUS_HOME = 1
# Not registered, searching for new operator to register with.
MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING = 2
# Registration denied.
MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED = 3
# Unknown registration status.
MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN = 4
# Registered on a roaming network.
MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING = 5
# CDMA
# Registration status is unknown or the device is not registered.
MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN = 0
# Registered, but roaming status is unknown or cannot be provided
# by the device. The device may or may not be roaming.
MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED = 1
# Currently registered on the home network.
MM_MODEM_CDMA_REGISTRATION_STATE_HOME = 2
# Currently registered on a roaming network.
MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING = 3
def __init__(self):
self.bus = dbus.SystemBus()
self.proxy = self.bus.get_object("org.freedesktop.ModemManager",
"/org/freedesktop/ModemManager")
modem_manager = dbus.Interface(self.proxy, self.MM_DBUS_IFACE)
self.modems = modem_manager.EnumerateDevices()
@staticmethod
def get_dbus_property(proxy, interface, property):
props = dbus.Interface(proxy, "org.freedesktop.DBus.Properties")
property = props.Get(interface, property)
return property
def is_gsm_roaming(self):
for m in self.modems:
dev = self.bus.get_object(self.MM_DBUS_IFACE, m)
type = self.get_dbus_property(dev, self.MM_DBUS_IFACE_MODEM,
"Type")
if type != self.MM_MODEM_TYPE_GSM:
continue
net = dbus.Interface(dev,
self.MM_DBUS_IFACE_MODEM + ".Gsm.Network")
reg = net.GetRegistrationInfo()
# Be conservative about roaming. If registration unknown,
# assume yes.
# MM_MODEM_GSM_NETWORK_REG_STATUS
if reg[0] in (self.MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN,
self.MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING):
return True
return False
def is_cdma_roaming(self):
for m in self.modems:
dev = self.bus.get_object(self.MM_DBUS_IFACE, m)
type = self.get_dbus_property(dev, self.MM_DBUS_IFACE_MODEM,
"Type")
if type != self.MM_MODEM_TYPE_CDMA:
continue
cdma = dbus.Interface(dev, self.MM_DBUS_IFACE_MODEM + ".Cdma")
(cmda_1x, evdo) = cdma.GetRegistrationState()
# Be conservative about roaming. If registration unknown,
# assume yes.
# MM_MODEM_CDMA_REGISTRATION_STATE
roaming_states = (self.MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
self.MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING)
# evdo trumps cmda_1x (thanks to Mathieu Trudel-Lapierre)
if evdo in roaming_states:
return True
elif cmda_1x in roaming_states:
return True
return False
class NetworkManagerHelper(object):
NM_DBUS_IFACE = "org.freedesktop.NetworkManager"
# connection states
# Old enum values are for NM 0.7
# The NetworkManager daemon is in an unknown state.
NM_STATE_UNKNOWN = 0
# The NetworkManager daemon is connecting a device.
NM_STATE_CONNECTING_OLD = 2
NM_STATE_CONNECTING = 40
NM_STATE_CONNECTING_LIST = [NM_STATE_CONNECTING_OLD,
NM_STATE_CONNECTING]
# The NetworkManager daemon is connected.
NM_STATE_CONNECTED_OLD = 3
NM_STATE_CONNECTED_LOCAL = 50
NM_STATE_CONNECTED_SITE = 60
NM_STATE_CONNECTED_GLOBAL = 70
NM_STATE_CONNECTED_LIST = [NM_STATE_CONNECTED_OLD,
NM_STATE_CONNECTED_LOCAL,
NM_STATE_CONNECTED_SITE,
NM_STATE_CONNECTED_GLOBAL]
# The device type is unknown.
NM_DEVICE_TYPE_UNKNOWN = 0
# The device is wired Ethernet device.
NM_DEVICE_TYPE_ETHERNET = 1
# The device is an 802.11 WiFi device.
NM_DEVICE_TYPE_WIFI = 2
# The device is a GSM-based cellular WAN device.
NM_DEVICE_TYPE_GSM = 3
# The device is a CDMA/IS-95-based cellular WAN device.
NM_DEVICE_TYPE_CDMA = 4
def __init__(self):
self.bus = dbus.SystemBus()
self.proxy = self.bus.get_object("org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager")
@staticmethod
def get_dbus_property(proxy, interface, property):
props = dbus.Interface(proxy, "org.freedesktop.DBus.Properties")
property = props.Get(interface, property)
return property
def is_active_connection_gsm_or_cdma(self):
res = False
actives = self.get_dbus_property(
self.proxy, self.NM_DBUS_IFACE, 'ActiveConnections')
for a in actives:
active = self.bus.get_object(self.NM_DBUS_IFACE, a)
default_route = self.get_dbus_property(
active, self.NM_DBUS_IFACE + ".Connection.Active", 'Default')
if not default_route:
continue
devs = self.get_dbus_property(
active, self.NM_DBUS_IFACE + ".Connection.Active", 'Devices')
for d in devs:
dev = self.bus.get_object(self.NM_DBUS_IFACE, d)
type = self.get_dbus_property(
dev, self.NM_DBUS_IFACE + ".Device", 'DeviceType')
if type == self.NM_DEVICE_TYPE_GSM:
return True
elif type == self.NM_DEVICE_TYPE_CDMA:
return True
else:
continue
return res
def is_active_connection_gsm_or_cdma_roaming(self):
res = False
if self.is_active_connection_gsm_or_cdma():
mmhelper = ModemManagerHelper()
res |= mmhelper.is_gsm_roaming()
res |= mmhelper.is_cdma_roaming()
return res
if __name__ == "__main__":
# test code
if sys.argv[1:] and sys.argv[1] == "--test":
mmhelper = ModemManagerHelper()
print("is_gsm_roaming", mmhelper.is_gsm_roaming())
print("is_cdma_romaing", mmhelper.is_cdma_roaming())
# roaming?
nmhelper = NetworkManagerHelper()
is_roaming = nmhelper.is_active_connection_gsm_or_cdma_roaming()
print("roam: ", is_roaming)
if is_roaming:
sys.exit(1)
sys.exit(0)

567
UpdateManager/Core/utils.py Normal file
View File

@ -0,0 +1,567 @@
# utils.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2004-2013 Canonical
#
# Authors: Michael Vogt <mvo@debian.org>
# Michael Terry <michael.terry@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import print_function
from gettext import gettext as _
from gettext import ngettext
from stat import (S_IMODE, ST_MODE, S_IXUSR)
from math import ceil
import apt
import apt_pkg
apt_pkg.init_config()
import locale
import logging
import re
import os
import subprocess
import sys
import time
try:
from urllib.request import (
ProxyHandler,
Request,
build_opener,
install_opener,
urlopen,
)
from urllib.parse import urlsplit
except ImportError:
from urllib2 import (
ProxyHandler,
Request,
build_opener,
install_opener,
urlopen,
)
from urlparse import urlsplit
from copy import copy
class ExecutionTime(object):
"""
Helper that can be used in with statements to have a simple
measure of the timing of a particular block of code, e.g.
with ExecutionTime("db flush"):
db.flush()
"""
def __init__(self, info=""):
self.info = info
def __enter__(self):
self.now = time.time()
def __exit__(self, type, value, stack):
print("%s: %s" % (self.info, time.time() - self.now))
def get_string_with_no_auth_from_source_entry(entry):
tmp = copy(entry)
url_parts = urlsplit(tmp.uri)
if url_parts.username:
tmp.uri = tmp.uri.replace(url_parts.username, "hidden-u")
if url_parts.password:
tmp.uri = tmp.uri.replace(url_parts.password, "hidden-p")
return str(tmp)
def is_unity_running():
""" return True if Unity is currently running """
unity_running = False
try:
import dbus
bus = dbus.SessionBus()
unity_running = bus.name_has_owner("com.canonical.Unity")
except Exception:
logging.exception("could not check for Unity dbus service")
return unity_running
def is_child_of_process_name(processname, pid=None):
if not pid:
pid = os.getpid()
while pid > 0:
stat_file = "/proc/%s/stat" % pid
with open(stat_file) as stat_f:
stat = stat_f.read()
# extract command (inside ())
command = stat.partition("(")[2].rpartition(")")[0]
if command == processname:
return True
# get parent (second to the right of command) and check that next
pid = int(stat.rpartition(")")[2].split()[1])
return False
def inside_chroot():
""" returns True if we are inside a chroot
"""
# if there is no proc or no pid 1 we are very likely inside a chroot
if not os.path.exists("/proc") or not os.path.exists("/proc/1"):
return True
# if the inode is differnt for pid 1 "/" and our "/"
return os.stat("/") != os.stat("/proc/1/root")
def wrap(t, width=70, subsequent_indent=""):
""" helpers inspired after textwrap - unfortunately
we can not use textwrap directly because it break
packagenames with "-" in them into new lines
"""
out = ""
for s in t.split():
if (len(out) - out.rfind("\n")) + len(s) > width:
out += "\n" + subsequent_indent
out += s + " "
return out
def twrap(s, **kwargs):
msg = ""
paras = s.split("\n")
for par in paras:
s = wrap(par, **kwargs)
msg += s + "\n"
return msg
def lsmod():
" return list of loaded modules (or [] if lsmod is not found) "
modules = []
# FIXME raise?
if not os.path.exists("/sbin/lsmod"):
return []
p = subprocess.Popen(["/sbin/lsmod"], stdout=subprocess.PIPE,
universal_newlines=True)
lines = p.communicate()[0].split("\n")
# remove heading line: "Modules Size Used by"
del lines[0]
# add lines to list, skip empty lines
for line in lines:
if line:
modules.append(line.split()[0])
return modules
def check_and_fix_xbit(path):
" check if a given binary has the executable bit and if not, add it"
if not os.path.exists(path):
return
mode = S_IMODE(os.stat(path)[ST_MODE])
if not ((mode & S_IXUSR) == S_IXUSR):
os.chmod(path, mode | S_IXUSR)
def country_mirror():
" helper to get the country mirror from the current locale "
# special cases go here
lang_mirror = {'c': ''}
# no lang, no mirror
if 'LANG' not in os.environ:
return ''
lang = os.environ['LANG'].lower()
# check if it is a special case
if lang[:5] in lang_mirror:
return lang_mirror[lang[:5]]
# now check for the most comon form (en_US.UTF-8)
if "_" in lang:
country = lang.split(".")[0].split("_")[1]
if "@" in country:
country = country.split("@")[0]
return country + "."
else:
return lang[:2] + "."
return ''
def get_dist():
" return the codename of the current runing distro "
# support debug overwrite
dist = os.environ.get("META_RELEASE_FAKE_CODENAME")
if dist:
logging.warning("using fake release name '%s' (because of "
"META_RELEASE_FAKE_CODENAME environment) " % dist)
return dist
# then check the real one
from subprocess import Popen, PIPE
p = Popen(["lsb_release", "-c", "-s"], stdout=PIPE,
universal_newlines=True)
res = p.wait()
if res != 0:
sys.stderr.write("lsb_release returned exitcode: %i\n" % res)
return "unknown distribution"
dist = p.stdout.readline().strip()
p.stdout.close()
return dist
def get_dist_version():
" return the version of the current running distro "
# support debug overwrite
desc = os.environ.get("META_RELEASE_FAKE_VERSION")
if desc:
logging.warning("using fake release version '%s' (because of "
"META_RELEASE_FAKE_VERSION environment) " % desc)
return desc
# then check the real one
from subprocess import Popen, PIPE
p = Popen(["lsb_release", "-r", "-s"], stdout=PIPE,
universal_newlines=True)
res = p.wait()
if res != 0:
sys.stderr.write("lsb_release returned exitcode: %i\n" % res)
return "unknown distribution"
desc = p.stdout.readline().strip()
p.stdout.close()
return desc
class HeadRequest(Request):
def get_method(self):
return "HEAD"
def url_downloadable(uri, debug_func=None):
"""
helper that checks if the given uri exists and is downloadable
(supports optional debug_func function handler to support
e.g. logging)
Supports http (via HEAD) and ftp (via size request)
"""
if not debug_func:
lambda x: True
debug_func("url_downloadable: %s" % uri)
(scheme, netloc, path, querry, fragment) = urlsplit(uri)
debug_func("s='%s' n='%s' p='%s' q='%s' f='%s'" % (scheme, netloc, path,
querry, fragment))
if scheme in ("http", "https"):
try:
http_file = urlopen(HeadRequest(uri))
http_file.close()
if http_file.code == 200:
return True
return False
except Exception as e:
debug_func("error from httplib: '%s'" % e)
return False
elif scheme == "ftp":
import ftplib
try:
f = ftplib.FTP(netloc)
f.login()
f.cwd(os.path.dirname(path))
size = f.size(os.path.basename(path))
f.quit()
if debug_func:
debug_func("ftplib.size() returned: %s" % size)
if size != 0:
return True
except Exception as e:
if debug_func:
debug_func("error from ftplib: '%s'" % e)
return False
return False
def init_proxy(gsettings=None):
""" init proxy settings
* use apt.conf http proxy if present,
* otherwise look into synaptics config file,
* otherwise the default behavior will use http_proxy environment
if present
"""
SYNAPTIC_CONF_FILE = "/root/.synaptic/synaptic.conf"
proxies = {}
# generic apt config wins
if apt_pkg.config.find("Acquire::http::Proxy") != '':
proxies["http"] = apt_pkg.config.find("Acquire::http::Proxy")
# then synaptic
elif os.path.exists(SYNAPTIC_CONF_FILE):
cnf = apt_pkg.Configuration()
apt_pkg.read_config_file(cnf, SYNAPTIC_CONF_FILE)
use_proxy = cnf.find_b("Synaptic::useProxy", False)
if use_proxy:
proxy_host = cnf.find("Synaptic::httpProxy")
proxy_port = str(cnf.find_i("Synaptic::httpProxyPort"))
if proxy_host and proxy_port:
proxies["http"] = "http://%s:%s/" % (proxy_host, proxy_port)
if apt_pkg.config.find("Acquire::https::Proxy") != '':
proxies["https"] = apt_pkg.config.find("Acquire::https::Proxy")
elif "http" in proxies:
proxies["https"] = proxies["http"]
# if we have a proxy, set it
if proxies:
# basic verification
for proxy in proxies.values():
if not re.match("https?://\\w+", proxy):
print("proxy '%s' looks invalid" % proxy, file=sys.stderr)
return
proxy_support = ProxyHandler(proxies)
opener = build_opener(proxy_support)
install_opener(opener)
if "http" in proxies:
os.putenv("http_proxy", proxies["http"])
if "https" in proxies:
os.putenv("https_proxy", proxies["https"])
return proxies
def on_battery():
"""
Check via dbus if the system is running on battery.
This function is using UPower per default, if UPower is not
available it falls-back to DeviceKit.Power.
"""
try:
import dbus
bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
try:
devobj = bus.get_object('org.freedesktop.UPower',
'/org/freedesktop/UPower')
dev = dbus.Interface(devobj, 'org.freedesktop.DBus.Properties')
return dev.Get('org.freedesktop.UPower', 'OnBattery')
except dbus.exceptions.DBusException as e:
error_unknown = 'org.freedesktop.DBus.Error.ServiceUnknown'
if e._dbus_error_name != error_unknown:
raise
devobj = bus.get_object('org.freedesktop.DeviceKit.Power',
'/org/freedesktop/DeviceKit/Power')
dev = dbus.Interface(devobj, "org.freedesktop.DBus.Properties")
return dev.Get("org.freedesktop.DeviceKit.Power", "on_battery")
except Exception:
#import sys
#print("on_battery returned error: ", e, file=sys.stderr)
return False
def inhibit_sleep():
"""
Send a dbus signal to logind to not suspend the system, it will be
released when the return value drops out of scope
"""
try:
from gi.repository import Gio, GLib
connection = Gio.bus_get_sync(Gio.BusType.SYSTEM)
var, fdlist = connection.call_with_unix_fd_list_sync(
'org.freedesktop.login1', '/org/freedesktop/login1',
'org.freedesktop.login1.Manager', 'Inhibit',
GLib.Variant('(ssss)',
('shutdown:sleep',
'UpdateManager', 'Updating System',
'block')),
None, 0, -1, None, None)
inhibitor = Gio.UnixInputStream(fd=fdlist.steal_fds()[var[0]])
return inhibitor
except Exception:
#print("could not send the dbus Inhibit signal: %s" % e)
return False
def str_to_bool(str):
if str == "0" or str.upper() == "FALSE":
return False
return True
def get_lang():
import logging
try:
(locale_s, encoding) = locale.getdefaultlocale()
return locale_s
except Exception:
logging.exception("gedefaultlocale() failed")
return None
def get_ubuntu_flavor(cache=None):
""" try to guess the flavor based on the running desktop """
# this will (of course) not work in a server environment,
# but the main use case for this is to show the right
# release notes.
pkg = get_ubuntu_flavor_package(cache=cache)
return pkg.split('-', 1)[0]
def _load_meta_pkg_list():
# This could potentially introduce a circular dependency, but the config
# parser logic is simple, and doesn't rely on any UpdateManager code.
from DistUpgrade.DistUpgradeConfigParser import DistUpgradeConfig
parser = DistUpgradeConfig('/usr/share/ubuntu-release-upgrader')
return parser.getlist('Distro', 'MetaPkgs')
def get_ubuntu_flavor_package(cache=None):
""" try to guess the flavor metapackage based on the running desktop """
# From spec, first if ubuntu-desktop is installed, use that.
# Second, grab first installed one from DistUpgrade.cfg.
# Lastly, fallback to ubuntu-desktop again.
meta_pkgs = ['ubuntu-desktop']
try:
meta_pkgs.extend(sorted(_load_meta_pkg_list()))
except Exception as e:
print('Could not load list of meta packages:', e)
if cache is None:
cache = apt.Cache()
for meta_pkg in meta_pkgs:
cache_pkg = cache[meta_pkg] if meta_pkg in cache else None
if cache_pkg and cache_pkg.is_installed:
return meta_pkg
return 'ubuntu-desktop'
def get_ubuntu_flavor_name(cache=None):
""" try to guess the flavor name based on the running desktop """
pkg = get_ubuntu_flavor_package(cache=cache)
lookup = {'ubuntustudio-desktop': 'Ubuntu Studio'}
if pkg in lookup:
return lookup[pkg]
elif pkg.endswith('-desktop'):
return capitalize_first_word(pkg.rsplit('-desktop', 1)[0])
elif pkg.endswith('-netbook'):
return capitalize_first_word(pkg.rsplit('-netbook', 1)[0])
else:
return 'Ubuntu'
# Unused by update-manager, but still used by ubuntu-release-upgrader
def error(parent, summary, message):
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
d = Gtk.MessageDialog(parent=parent,
flags=Gtk.DialogFlags.MODAL,
type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.CLOSE)
d.set_markup("<big><b>%s</b></big>\n\n%s" % (summary, message))
d.realize()
d.get_window().set_functions(Gdk.WMFunction.MOVE)
d.set_title("")
d.run()
d.destroy()
return False
def humanize_size(bytes):
"""
Convert a given size in bytes to a nicer better readable unit
"""
if bytes < 1000 * 1000:
# to have 0 for 0 bytes, 1 for 0-1000 bytes and for 1 and above
# round up
size_in_kb = int(ceil(bytes / float(1000)))
# TRANSLATORS: download size of small updates, e.g. "250 kB"
return ngettext("%(size).0f kB", "%(size).0f kB", size_in_kb) % {
"size": size_in_kb}
else:
# TRANSLATORS: download size of updates, e.g. "2.3 MB"
return locale.format_string(_("%.1f MB"), bytes / 1000.0 / 1000.0)
def get_arch():
return apt_pkg.config.find("APT::Architecture")
def is_port_already_listening(port):
""" check if the current system is listening on the given tcp port """
# index in the line
INDEX_LOCAL_ADDR = 1
#INDEX_REMOTE_ADDR = 2
INDEX_STATE = 3
# state (st) that we care about
STATE_LISTENING = '0A'
# read the data
with open("/proc/net/tcp") as net_tcp:
for line in net_tcp.readlines():
line = line.strip()
if not line:
continue
# split, values are:
# sl local_address rem_address st tx_queue rx_queue tr
# tm->when retrnsmt uid timeout inode
values = line.split()
state = values[INDEX_STATE]
if state != STATE_LISTENING:
continue
local_port_str = values[INDEX_LOCAL_ADDR].split(":")[1]
local_port = int(local_port_str, 16)
if local_port == port:
return True
return False
def iptables_active():
""" Return True if iptables is active """
# FIXME: is there a better way?
iptables_empty = """Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
"""
if os.getuid() != 0:
raise OSError("Need root to check the iptables state")
if not os.path.exists("/sbin/iptables"):
return False
out = subprocess.Popen(["iptables", "-nL"],
stdout=subprocess.PIPE,
universal_newlines=True).communicate()[0]
if out == iptables_empty:
return False
return True
def capitalize_first_word(string):
""" this uppercases the first word's first letter
"""
if len(string) > 1 and string[0].isalpha() and not string[0].isupper():
return string[0].capitalize() + string[1:]
return string
def get_package_label(pkg):
""" this takes a package synopsis and uppercases the first word's
first letter
"""
name = getattr(pkg.candidate, "summary", "")
return capitalize_first_word(name)
if __name__ == "__main__":
#print(mirror_from_sources_list())
#print(on_battery())
#print(inside_chroot())
#print(iptables_active())
error(None, "bar", "baz")

429
UpdateManager/Dialogs.py Normal file
View File

@ -0,0 +1,429 @@
# Dialogs.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2012 Canonical
#
# Author: Michael Terry <michael.terry@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import, print_function
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Gio
import warnings
warnings.filterwarnings(
"ignore", "Accessed deprecated property", DeprecationWarning)
import logging
import datetime
import dbus
import distro_info
import os
import HweSupportStatus.consts
from .Core.LivePatchSocket import LivePatchSocket
from .Core.utils import get_dist
from gettext import gettext as _
from gettext import ngettext
class Dialog(object):
def __init__(self, window_main):
self.window_main = window_main
def start(self):
pass
def close(self):
return self.stop()
def stop(self):
pass
def run(self, parent=None):
pass
class BuilderDialog(Dialog, Gtk.Alignment):
def __init__(self, window_main, ui_path, root_widget):
Dialog.__init__(self, window_main)
Gtk.Alignment.__init__(self)
builder = self._load_ui(ui_path, root_widget)
self.add(builder.get_object(root_widget))
self.show()
def _load_ui(self, path, root_widget, domain="update-manager"):
builder = Gtk.Builder()
builder.set_translation_domain(domain)
builder.add_objects_from_file(path, [root_widget])
builder.connect_signals(self)
for o in builder.get_objects():
if issubclass(type(o), Gtk.Buildable):
name = Gtk.Buildable.get_name(o)
setattr(self, name, o)
else:
logging.debug("WARNING: can not get name for '%s'" % o)
return builder
def run(self, parent=None):
# FIXME: THIS WILL CRASH!
if parent:
self.window_dialog.set_transient_for(parent)
self.window_dialog.set_modal(True)
self.window_dialog.run()
class InternalDialog(BuilderDialog):
def __init__(self, window_main, content_widget=None):
ui_path = os.path.join(window_main.datadir, "gtkbuilder/Dialog.ui")
BuilderDialog.__init__(self, window_main, ui_path, "pane_dialog")
self.settings = Gio.Settings.new("com.ubuntu.update-manager")
self.focus_button = None
self.set_content_widget(content_widget)
self.connect("realize", self._on_realize)
def _on_realize(self, user_data):
if self.focus_button:
self.focus_button.set_can_default(True)
self.focus_button.set_can_focus(True)
self.focus_button.grab_default()
self.focus_button.grab_focus()
def add_button(self, label, callback, secondary=False):
# from_stock tries stock first and falls back to mnemonic
button = Gtk.Button.new_from_stock(label)
button.connect("clicked", lambda x: callback())
button.show()
self.buttonbox.add(button)
self.buttonbox.set_child_secondary(button, secondary)
return button
def add_settings_button(self):
if os.path.exists("/usr/bin/software-properties-gtk"):
return self.add_button(_("Settings…"),
self.on_settings_button_clicked,
secondary=True)
else:
return None
def on_settings_button_clicked(self):
self.window_main.show_settings()
def set_header(self, label):
if label:
self.label_header.set_markup(
"<span size='larger' weight='bold'>%s</span>" % label)
self.label_header.set_visible(bool(label))
def set_desc(self, label):
if label:
self.label_desc.set_markup(label)
self.label_desc.set_visible(bool(label))
def set_content_widget(self, content_widget):
if content_widget:
self.main_container.add(content_widget)
self.main_container.set_visible(bool(content_widget))
def _is_livepatch_supported(self):
di = distro_info.UbuntuDistroInfo()
codename = get_dist()
return di.is_lts(codename)
def on_livepatch_status_ready(self, active, cs, ps, fixes):
self.set_desc(None)
if not active:
if self._is_livepatch_supported() and \
self.settings_button and \
self.settings.get_int('launch-count') >= 4:
self.set_desc(_("<b>Tip:</b> You can use Livepatch to "
"keep your computer more secure between "
"restarts."))
self.settings_button.set_label(_("Settings & Livepatch…"))
return
needs_reschedule = False
if cs == "needs-check":
needs_reschedule = True
elif cs == "check-failed":
pass
elif cs == "checked":
if ps == "unapplied" or ps == "applying":
needs_reschedule = True
elif ps == "applied":
fixes = [fix for fix in fixes if fix.patched]
d = ngettext("%d Livepatch update applied since the last "
"restart.",
"%d Livepatch updates applied since the last "
"restart.",
len(fixes)) % len(fixes)
self.set_desc(d)
elif ps == "applied-with-bug" or ps == "apply-failed":
fixes = [fix for fix in fixes if fix.patched]
d = ngettext("%d Livepatch update failed to apply since the "
"last restart.",
"%d Livepatch updates failed to apply since the "
"last restart.",
len(fixes)) % len(fixes)
self.set_desc(d)
elif ps == "nothing-to-apply":
pass
elif ps == "unknown":
pass
if needs_reschedule:
self.lp_socket.get_status(self.on_livepatch_status_ready)
def check_livepatch_status(self):
self.lp_socket = LivePatchSocket()
self.lp_socket.get_status(self.on_livepatch_status_ready)
class StoppedUpdatesDialog(InternalDialog):
def __init__(self, window_main):
InternalDialog.__init__(self, window_main)
self.set_header(_("You stopped the check for updates."))
self.add_settings_button()
self.add_button(_("_Check Again"), self.check)
self.focus_button = self.add_button(Gtk.STOCK_OK,
self.window_main.close)
def check(self):
self.window_main.start_update()
class NoUpdatesDialog(InternalDialog):
def __init__(self, window_main, error_occurred=False):
InternalDialog.__init__(self, window_main)
if error_occurred:
self.set_header(_("No software updates are available."))
else:
self.set_header(_("The software on this computer is up to date."))
self.settings_button = self.add_settings_button()
self.focus_button = self.add_button(Gtk.STOCK_OK,
self.window_main.close)
self.check_livepatch_status()
class DistUpgradeDialog(InternalDialog):
def __init__(self, window_main, meta_release):
InternalDialog.__init__(self, window_main)
self.meta_release = meta_release
self.set_header(_("The software on this computer is up to date."))
# Translators: these are Ubuntu version names like "Ubuntu 12.04"
self.set_desc(_("However, %s %s is now available (you have %s).") % (
meta_release.flavor_name,
meta_release.upgradable_to.version,
meta_release.current_dist_version))
self.add_settings_button()
self.add_button(_("Upgrade…"), self.upgrade)
self.focus_button = self.add_button(Gtk.STOCK_OK,
self.window_main.close)
def upgrade(self):
# Pass on several arguments
extra_args = ""
if self.window_main and self.window_main.options:
if self.window_main.options.devel_release:
extra_args = extra_args + " -d"
if self.window_main.options.use_proposed:
extra_args = extra_args + " -p"
os.execl("/bin/sh", "/bin/sh", "-c",
"/usr/bin/do-release-upgrade "
"--frontend=DistUpgradeViewGtk3%s" % extra_args)
class HWEUpgradeDialog(InternalDialog):
def __init__(self, window_main):
InternalDialog.__init__(self, window_main)
self.set_header(_("New important security and hardware support "
"update."))
if datetime.date.today() < HweSupportStatus.consts.HWE_EOL_DATE:
self.set_desc(_(HweSupportStatus.consts.Messages.HWE_SUPPORT_ENDS))
else:
self.set_desc(
_(HweSupportStatus.consts.Messages.HWE_SUPPORT_HAS_ENDED))
self.add_settings_button()
self.add_button(_("_Install…"), self.install)
self.focus_button = self.add_button(Gtk.STOCK_OK,
self.window_main.close)
def install(self):
self.window_main.start_install(hwe_upgrade=True)
class UnsupportedDialog(DistUpgradeDialog):
def __init__(self, window_main, meta_release):
DistUpgradeDialog.__init__(self, window_main, meta_release)
# Translators: this is an Ubuntu version name like "Ubuntu 12.04"
self.set_header(_("Software updates are no longer provided for "
"%s %s.") % (meta_release.flavor_name,
meta_release.current_dist_version))
# Translators: this is an Ubuntu version name like "Ubuntu 12.04"
self.set_desc(_("To stay secure, you should upgrade to %s %s.") % (
meta_release.flavor_name,
meta_release.upgradable_to.version))
def run(self, parent):
# This field is used in tests/test_end_of_life.py
self.window_main.no_longer_supported_nag = self.window_dialog
DistUpgradeDialog.run(self, parent)
class NoUpgradeForYouDialog(InternalDialog):
def __init__(self, window_main, meta_release, arch):
InternalDialog.__init__(self, window_main)
self.set_header(_("Sorry, there are no more upgrades for this system"))
# Translators: this is an Ubuntu version name like "Ubuntu 12.04"
self.set_desc(_("\nThere will not be any further Ubuntu releases "
"for this system's '%s' architecture.\n\n"
"Updates for Ubuntu %s will continue until "
"2023-04-26.\n\nIf you reinstall Ubuntu from "
"ubuntu.com/download, future upgrades will "
"be available.") %
(arch, meta_release.current_dist_version))
self.focus_button = self.add_button(Gtk.STOCK_OK,
self.window_main.close)
class PartialUpgradeDialog(InternalDialog):
def __init__(self, window_main):
InternalDialog.__init__(self, window_main)
self.set_header(_("Not all updates can be installed"))
self.set_desc(_(
"""Run a partial upgrade, to install as many updates as possible.
This can be caused by:
* A previous upgrade which didn't complete
* Problems with some of the installed software
* Unofficial software packages not provided by Ubuntu
* Normal changes of a pre-release version of Ubuntu"""))
self.add_settings_button()
self.add_button(_("_Partial Upgrade"), self.upgrade)
self.focus_button = self.add_button(_("_Continue"), Gtk.main_quit)
def upgrade(self):
os.execl("/bin/sh", "/bin/sh", "-c",
"/usr/lib/ubuntu-release-upgrader/do-partial-upgrade "
"--frontend=DistUpgradeViewGtk3")
def start(self):
Dialog.start(self)
# Block progress until user has answered this question
Gtk.main()
class ErrorDialog(InternalDialog):
def __init__(self, window_main, header, desc=None):
InternalDialog.__init__(self, window_main)
self.set_header(header)
if desc:
self.set_desc(desc)
self.label_desc.set_selectable(True)
self.add_settings_button()
self.focus_button = self.add_button(Gtk.STOCK_OK,
self.window_main.close)
def start(self):
Dialog.start(self)
# The label likes to start selecting everything (b/c it got focus
# before we switched to our default button).
self.label_desc.select_region(0, 0)
class UpdateErrorDialog(ErrorDialog):
def __init__(self, window_main, header, desc=None):
ErrorDialog.__init__(self, window_main, header, desc)
# Get rid of normal error dialog button before adding our own
self.focus_button.destroy()
self.add_button(_("_Try Again"), self.update)
self.focus_button = self.add_button(Gtk.STOCK_OK, self.available)
def update(self):
self.window_main.start_update()
def available(self):
self.window_main.start_available(error_occurred=True)
class NeedRestartDialog(InternalDialog):
def __init__(self, window_main):
InternalDialog.__init__(self, window_main)
self.set_header(
_("The computer needs to restart to finish installing updates."))
self.add_settings_button()
self.focus_button = self.add_button(_("Restart _Later"),
self.window_main.close)
self.add_button(_("_Restart Now"), self.restart)
def start(self):
Dialog.start(self)
# Turn off close button
self.window_main.realize()
self.window_main.get_window().set_functions(Gdk.WMFunction.MOVE
| Gdk.WMFunction.MINIMIZE)
def restart(self, *args, **kwargs):
self._request_reboot_via_session_manager()
self.window_main.close()
def _request_reboot_via_session_manager(self):
try:
bus = dbus.SessionBus()
proxy_obj = bus.get_object("org.gnome.SessionManager",
"/org/gnome/SessionManager")
iface = dbus.Interface(proxy_obj, "org.gnome.SessionManager")
iface.RequestReboot()
except dbus.DBusException:
self._request_reboot_via_consolekit()
except Exception:
pass
def _request_reboot_via_consolekit(self):
try:
bus = dbus.SystemBus()
proxy_obj = bus.get_object("org.freedesktop.ConsoleKit",
"/org/freedesktop/ConsoleKit/Manager")
iface = dbus.Interface(
proxy_obj, "org.freedesktop.ConsoleKit.Manager")
iface.Restart()
except dbus.DBusException:
self._request_reboot_via_logind()
except Exception:
pass
def _request_reboot_via_logind(self):
try:
bus = dbus.SystemBus()
proxy_obj = bus.get_object("org.freedesktop.login1",
"/org/freedesktop/login1")
iface = dbus.Interface(
proxy_obj, "org.freedesktop.login1.Manager")
iface.Reboot(True)
except dbus.DBusException:
pass

View File

@ -0,0 +1,35 @@
# helpviewer.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
import os
import subprocess
# Hardcoded list of available help viewers
# FIXME: khelpcenter support would be nice
#KNOWN_VIEWERS = ["/usr/bin/yelp", "/usr/bin/khelpcenter"]
KNOWN_VIEWERS = ["/usr/bin/yelp"]
class HelpViewer:
def __init__(self, docu):
self.command = []
self.docu = docu
for viewer in KNOWN_VIEWERS:
if os.path.exists(viewer):
self.command = [viewer, "help:%s" % docu]
break
def check(self):
"""check if a viewer is available"""
if self.command == []:
return False
else:
return True
def run(self):
"""open the documentation in the viewer"""
# avoid running the help viewer as root
if os.getuid() == 0 and 'SUDO_USER' in os.environ:
self.command = ['sudo', '-u', os.environ['SUDO_USER']] +\
self.command
subprocess.Popen(self.command)

View File

@ -0,0 +1,59 @@
# Copyright (c) 2004-2007 Canonical
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Author: Michael Vogt <michael.vogt@ubuntu.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import
from gi.repository import GLib
from gi.repository import GObject
from .Core.MetaRelease import MetaReleaseCore
class MetaRelease(MetaReleaseCore, GObject.GObject):
__gsignals__ = {
'new_dist_available': (GObject.SignalFlags.RUN_LAST,
None,
(GObject.TYPE_PYOBJECT,)),
'dist_no_longer_supported': (GObject.SignalFlags.RUN_LAST,
None,
()),
'done_downloading': (GObject.SignalFlags.RUN_LAST,
None,
())
}
def __init__(self, useDevelopmentRelease=False, useProposed=False):
GObject.GObject.__init__(self)
MetaReleaseCore.__init__(self, useDevelopmentRelease, useProposed)
# in the gtk space to test if the download already finished
# this is needed because gtk is not thread-safe
GLib.timeout_add_seconds(1, self.check)
def check(self):
# check if we have a metarelease_information file
if self.no_longer_supported is not None:
self.emit("dist_no_longer_supported")
if self.new_dist is not None:
self.emit("new_dist_available", self.new_dist)
if self.downloading:
return True
else:
self.emit("done_downloading")
return False

View File

@ -0,0 +1,101 @@
# UnitySupport.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2011 Canonical
#
# Author: Michael Vogt <mvo@ubuntu.com>
# Bilal Akhtar <bilalakhtar@ubuntu.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
import logging
from gettext import gettext as _
HAVE_UNITY_SUPPORT = False
try:
import gi
gi.require_version('Dbusmenu', '0.4')
gi.require_version('Unity', '7.0')
from gi.repository import Dbusmenu, Unity
HAVE_UNITY_SUPPORT = True
except (ValueError, ImportError) as e:
logging.warning("can not import unity GI %s" % e)
class IUnitySupport(object):
""" interface for unity support """
def __init__(self, parent=None):
pass
def set_urgency(self, urgent):
pass
def set_install_menuitem_visible(self, visible):
pass
def set_progress(self, progress):
pass
class UnitySupportImpl(IUnitySupport):
""" implementation of unity support (if unity is available) """
def __init__(self, parent=None):
# create launcher and quicklist
um_launcher_entry = Unity.LauncherEntry.get_for_desktop_id(
"update-manager.desktop")
self._unity = um_launcher_entry
if parent:
self._add_quicklist(parent)
def _add_quicklist(self, parent):
quicklist = Dbusmenu.Menuitem.new()
# install
self.install_dbusmenuitem = Dbusmenu.Menuitem.new()
self.install_dbusmenuitem.property_set(
Dbusmenu.MENUITEM_PROP_LABEL,
_("Install All Available Updates"))
self.install_dbusmenuitem.property_set_bool(
Dbusmenu.MENUITEM_PROP_VISIBLE, True)
self.install_dbusmenuitem.connect(
"item-activated", parent.install_all_updates, None)
quicklist.child_append(self.install_dbusmenuitem)
# add it
self._unity.set_property("quicklist", quicklist)
def set_progress(self, progress):
""" set the progress [0,100] """
self._unity.set_property("progress", progress / 100.0)
# hide progress when out of bounds
if progress < 0 or progress > 100:
self._unity.set_property("progress_visible", False)
else:
self._unity.set_property("progress_visible", True)
def set_urgency(self, urgent):
self._unity.set_property("urgent", urgent)
def set_install_menuitem_visible(self, visible):
self.install_dbusmenuitem.property_set_bool(
Dbusmenu.MENUITEM_PROP_VISIBLE, visible)
# check what to export to the clients
if HAVE_UNITY_SUPPORT:
UnitySupport = UnitySupportImpl
else:
# we just provide the empty interface
UnitySupport = IUnitySupport

View File

@ -0,0 +1,449 @@
# UpdateManager.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
# Copyright (c) 2012 Canonical
#
# Author: Michael Terry <michael.terry@canonical.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from __future__ import absolute_import, print_function
from gi.repository import Gtk
from gi.repository import Gdk, GdkX11
from gi.repository import Gio
from gi.repository import GLib
from gi.repository import GObject
GdkX11 # pyflakes
import warnings
warnings.filterwarnings("ignore", "Accessed deprecated property",
DeprecationWarning)
import distro_info
import os
import subprocess
import sys
import time
from gettext import gettext as _
from apt import Cache
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
from .UnitySupport import UnitySupport
from .Dialogs import (DistUpgradeDialog,
ErrorDialog,
HWEUpgradeDialog,
NeedRestartDialog,
NoUpdatesDialog,
NoUpgradeForYouDialog,
PartialUpgradeDialog,
StoppedUpdatesDialog,
UnsupportedDialog,
UpdateErrorDialog)
from .MetaReleaseGObject import MetaRelease
from .UpdatesAvailable import UpdatesAvailable
from .Core.AlertWatcher import AlertWatcher
from .Core.MyCache import MyCache
from .Core.roam import NetworkManagerHelper
from .Core.UpdateList import UpdateList
from .Core.utils import get_arch, get_dist
from .backend import (InstallBackend,
get_backend)
# file that signals if we need to reboot
REBOOT_REQUIRED_FILE = "/var/run/reboot-required"
class UpdateManager(Gtk.Window):
""" This class is the main window and work flow controller. The main
window will show panes, and it will morph between them. """
def __init__(self, datadir, options):
# Public members
self.datadir = datadir
self.options = options
self.controller = None
#cache 池
self.cache = None
#更新列表
self.update_list = None
# self.meta_release = None
self.duplicate_packages = []
#获取系统的架构
self.arch = get_arch()
#设置dbus
self._setup_dbus()
# if self.cache is None:
# self.cache = apt.Cache()
# Look for a new release in a thread
# self.meta_release = MetaRelease(
# self.options and self.options.devel_release,
# self.options and self.options.use_proposed)
def begin_user_resizable(self, stored_width=0, stored_height=0):
self.set_resizable(True)
if stored_width > 0 and stored_height > 0:
# There is a race here. If we immediately resize, it often doesn't
# take. Using idle_add helps, but we *still* occasionally don't
# restore the size correctly. Help needed to track this down!
GLib.idle_add(lambda: self.resize(stored_width, stored_height))
def end_user_resizable(self):
self.set_resizable(False)
def resize_to_standard_width(self):
if self.get_resizable():
return # only size to a specific em if we are a static size
num_em = 33 # per SoftwareUpdates spec
dpi = self.get_screen().get_resolution()
if dpi <= 0:
dpi = 96
ctx = self.get_style_context()
GObject.signal_handler_block(ctx, self.style_changed)
size = ctx.get_property("font-size", Gtk.StateFlags.NORMAL)
width = dpi / 72 * size * num_em
self.set_size_request(width, -1)
GObject.signal_handler_unblock(ctx, self.style_changed)
def on_initial_focus_in(self, widget, event):
"""callback run on initial focus-in (if started unmapped)"""
self.unstick()
self.set_urgency_hint(False)
self.unity.set_urgency(False)
self.disconnect(self.initial_focus_id)
return False
def _start_pane(self, pane):
if self.controller is not None:
self.controller.stop()
if isinstance(self.controller, Gtk.Widget):
self.controller.destroy()
self.controller = pane
self._look_ready()
self.end_user_resizable()
if pane is None:
return
if isinstance(pane, Gtk.Widget):
self.add(pane)
pane.start()
self.show_all()
else:
pane.start()
self.hide()
def _on_close(self, widget, data=None):
return self.close()
def close(self):
if not self.get_sensitive():
return True
if self.controller:
controller_close = self.controller.close()
if controller_close:
return controller_close
self.exit()
def exit(self):
""" exit the application, save the state """
self._start_pane(None)
sys.exit(0)
def show_settings(self):
cmd = ["/usr/bin/software-properties-gtk",
"--open-tab", "2"]
if "WAYLAND_DISPLAY" not in os.environ:
cmd += ["--toplevel", "%s" % self.get_window().get_xid()]
self._look_busy()
try:
p = subprocess.Popen(cmd)
except OSError:
pass
else:
while p.poll() is None:
while Gtk.events_pending():
Gtk.main_iteration()
time.sleep(0.05)
finally:
self.start_available()
#进行更新的操作
def start_update(self):
#不进行update 的操作
if self.options.no_update:
self.start_available()
return
update_backend = get_backend(self, InstallBackend.ACTION_UPDATE)
update_backend.start()
#self._start_pane(update_backend)
#进行安装的操作
def start_install(self, hwe_upgrade=False):
install_backend = get_backend(self, InstallBackend.ACTION_INSTALL)
if hwe_upgrade:
for pkgname in self.hwe_replacement_packages:
try:
self.cache[pkgname].mark_install()
except SystemError:
pass
self._start_pane(install_backend)
#更新结束之后会调到此,获取要更新的
def start_available(self, cancelled_update=False, error_occurred=False):
self.refresh_cache()
pane = self._make_available_pane(self.cache.install_count
+ self.cache.del_count,
os.path.exists(REBOOT_REQUIRED_FILE),
cancelled_update, error_occurred)
self._start_pane(pane)
def _make_available_pane(self, install_count, need_reboot=False,
cancelled_update=False, error_occurred=False):
self._check_hwe_support_status()
if install_count == 0:
# Need Restart > New Release > No Updates
if need_reboot:
return NeedRestartDialog(self)
dist_upgrade = self._check_meta_release()
if dist_upgrade:
return dist_upgrade
elif cancelled_update:
return StoppedUpdatesDialog(self)
elif self.hwe_replacement_packages:
return HWEUpgradeDialog(self)
else:
return NoUpdatesDialog(self, error_occurred=error_occurred)
else:
header = None
desc = None
if error_occurred:
desc = _("Some software couldnt be checked for updates.")
elif cancelled_update:
header = _("You stopped the check for updates.")
desc = _("Updated software is available from "
"a previous check.")
# Display HWE updates first as an old HWE stack is vulnerable
elif self.hwe_replacement_packages:
return HWEUpgradeDialog(self)
return UpdatesAvailable(self, header, desc, need_reboot)
def start_error(self, update_and_retry, header, desc):
if update_and_retry:
self._start_pane(UpdateErrorDialog(self, header, desc))
else:
self._start_pane(ErrorDialog(self, header, desc))
def _look_busy(self):
self.set_sensitive(False)
if self.get_window() is not None:
self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
def _look_ready(self):
self.set_sensitive(True)
if self.get_window() is not None:
self.get_window().set_cursor(None)
self.get_window().set_functions(Gdk.WMFunction.ALL)
def _check_meta_release(self):
if self.meta_release is None:
return None
if self.meta_release.downloading:
# Block until we get an answer
GLib.idle_add(self._meta_release_wait_idle)
Gtk.main()
# Check if there is anything to upgrade to or a known-broken upgrade
next = self.meta_release.upgradable_to
if not next or next.upgrade_broken:
return None
# Check for end-of-life
if self.meta_release.no_longer_supported:
return UnsupportedDialog(self, self.meta_release)
# Check for new fresh release
settings = Gio.Settings.new("com.ubuntu.update-manager")
if (self.meta_release.new_dist
and (self.options.check_dist_upgrades
or settings.get_boolean("check-dist-upgrades"))):
if self.arch == 'i386':
return NoUpgradeForYouDialog(self, self.meta_release,
self.arch)
return DistUpgradeDialog(self, self.meta_release)
return None
def _meta_release_wait_idle(self):
# 'downloading' is changed in a thread, but the signal
# 'done_downloading' is done in our thread's event loop. So we know
# that it won't fire while we're in this function.
if not self.meta_release.downloading:
Gtk.main_quit()
else:
self.meta_release.connect("done_downloading", Gtk.main_quit)
return False
def _check_hwe_support_status(self):
di = distro_info.UbuntuDistroInfo()
codename = get_dist()
lts = di.is_lts(codename)
if not lts:
return None
HWE = "/usr/bin/hwe-support-status"
if not os.path.exists(HWE):
return None
cmd = [HWE, "--show-replacements"]
self._parse_hwe_support_status(cmd)
def _parse_hwe_support_status(self, cmd):
try:
subprocess.check_output(cmd)
# for debugging
# print("nothing unsupported running")
except subprocess.CalledProcessError as e:
if e.returncode == 10:
packages = e.output.strip().split()
self.hwe_replacement_packages = []
for pkgname in packages:
pkgname = pkgname.decode('utf-8')
if pkgname in self.cache and \
not self.cache[pkgname].is_installed:
self.hwe_replacement_packages.append(pkgname)
# for debugging
# print(self.hwe_replacement_packages)
# fixme: we should probably abstract away all the stuff from libapt
def refresh_cache(self):
try:
#会进入这一个分支 初始化
if self.cache is None:
self.cache = MyCache(None)
else:
self.cache.open(None)
self.cache._initDepCache()
except AssertionError:
# if the cache could not be opened for some reason,
# let the release upgrader handle it, it deals
# a lot better with this
self._start_pane(PartialUpgradeDialog(self))
# we assert a clean cache
header = _("Software index is broken")
desc = _("It is impossible to install or remove any software. "
"Please use the package manager \"Synaptic\" or run "
"\"sudo apt-get install -f\" in a terminal to fix "
"this issue at first.")
self.start_error(True, header, desc)
except SystemError as e:
header = _("Could not initialize the package information")
desc = _("An unresolvable problem occurred while "
"initializing the package information.\n\n"
"Please report this bug against the 'update-manager' "
"package and include the following error "
"message:\n") + str(e)
self.start_error(True, header, desc)
# # Let the Gtk event loop breath if it hasn't had a chance.
def iterate():
while Gtk.events_pending():
Gtk.main_iteration()
iterate()
self.update_list = UpdateList(self)
try:
self.update_list.update(self.cache, eventloop_callback=iterate,
duplicate_packages=self.duplicate_packages)
except SystemError as e:
header = _("Could not calculate the upgrade")
desc = _("An unresolvable problem occurred while "
"calculating the upgrade.\n\n"
"Please report this bug against the 'update-manager' "
"package and include the following error "
"message:\n") + str(e)
self.start_error(True, header, desc)
if self.update_list.distUpgradeWouldDelete > 0:
print(self.update_list.distUpgradeWouldDelete)
#self._start_pane(PartialUpgradeDialog(self))
def _setup_dbus(self):
""" this sets up a dbus listener if none is installed already """
# check if there is another g-a-i already and if not setup one
# listening on dbus
try:
bus = dbus.SessionBus()
except Exception:
print("warning: could not initiate dbus")
return
try:
proxy_obj = bus.get_object('org.freedesktop.UpdateManager',
'/org/freedesktop/UpdateManagerObject')
iface = dbus.Interface(proxy_obj,
'org.freedesktop.UpdateManagerIFace')
iface.bringToFront()
#print("send bringToFront")
sys.exit(0)
except dbus.DBusException:
#print("no listening object (%s) " % e)
bus_name = dbus.service.BusName('org.freedesktop.UpdateManager',
bus)
self.dbusController = UpdateManagerDbusController(self, bus_name)
class UpdateManagerDbusController(dbus.service.Object):
""" this is a helper to provide the UpdateManagerIFace """
def __init__(self, parent, bus_name,
object_path='/org/freedesktop/UpdateManagerObject'):
dbus.service.Object.__init__(self, bus_name, object_path)
self.parent = parent
self.alert_watcher = AlertWatcher()
self.alert_watcher.connect("network-alert", self._on_network_alert)
self.connected = False
@dbus.service.method('org.freedesktop.UpdateManagerIFace')
def bringToFront(self):
self.parent.present()
return True
@dbus.service.method('org.freedesktop.UpdateManagerIFace')
def upgrade(self):
try:
self.parent.start_install()
return True
except Exception:
return False
def _on_network_alert(self, watcher, state):
if state in NetworkManagerHelper.NM_STATE_CONNECTED_LIST:
self.connected = True
else:
self.connected = False

View File

@ -0,0 +1,3 @@
# This file isn't used except in local checkouts, but one like it is written
# out in the build directory by setup.py
VERSION = 'bzr'

File diff suppressed because it is too large Load Diff

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,227 @@
#!/usr/bin/env python
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
# (c) 2005-2012 Canonical, GPL
# (C) 2008-2009 Sebastian Heinlein <devel@glatzor.de>
from __future__ import print_function
from gi.repository import Gtk
from aptdaemon import client, errors
from defer import inline_callbacks
from aptdaemon.gtk3widgets import (AptCancelButton,
AptConfigFileConflictDialog,
AptDetailsExpander,
AptMediumRequiredDialog,
AptProgressBar)
from aptdaemon.enums import (EXIT_SUCCESS,
EXIT_FAILED,
STATUS_COMMITTING,
get_error_description_from_enum,
get_error_string_from_enum,
get_status_string_from_enum)
from UpdateManager.backend import InstallBackend
from UpdateManager.UnitySupport import UnitySupport
from UpdateManager.Dialogs import BuilderDialog
from gettext import gettext as _
import dbus
import os
class InstallBackendAptdaemon(InstallBackend, BuilderDialog):
"""Makes use of aptdaemon to refresh the cache and to install updates."""
def __init__(self, window_main, action):
InstallBackend.__init__(self, window_main, action)
#客户端连接aptdeamon的dbus接口
self.client = client.AptClient()
self._expanded_size = None
self.button_cancel = None
self.trans_failed_msg = None
def close(self):
if self.button_cancel and self.button_cancel.get_sensitive():
try:
self.button_cancel.clicked()
except Exception:
# there is not much left to do if the transaction can't be
# canceled
pass
return True
else:
return False
@inline_callbacks
def update(self):
"""Refresh the package list"""
try:
trans = yield self.client.update_cache(defer=True)
yield self._show_transaction(trans, self.ACTION_UPDATE,
_("Checking for updates…"), False)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_UPDATE,
authorized=False, success=False,
error_string=None, error_desc=None)
except Exception:
self._action_done(self.ACTION_UPDATE,
authorized=True, success=False,
error_string=None, error_desc=None)
raise
@inline_callbacks
def commit(self, pkgs_install, pkgs_upgrade, pkgs_remove):
"""Commit a list of package adds and removes"""
try:
reinstall = purge = downgrade = []
trans = yield self.client.commit_packages(
pkgs_install, reinstall, pkgs_remove, purge, pkgs_upgrade,
downgrade, defer=True)
trans.connect("progress-changed", self._on_progress_changed)
yield self._show_transaction(trans, self.ACTION_INSTALL,
_("Installing updates…"), True)
except errors.NotAuthorizedError:
self._action_done(self.ACTION_INSTALL,
authorized=False, success=False,
error_string=None, error_desc=None)
except errors.TransactionFailed as e:
self.trans_failed_msg = str(e)
except dbus.DBusException as e:
#print(e, e.get_dbus_name())
if e.get_dbus_name() != "org.freedesktop.DBus.Error.NoReply":
raise
self._action_done(self.ACTION_INSTALL,
authorized=False, success=False,
error_string=None, error_desc=None)
except Exception:
self._action_done(self.ACTION_INSTALL,
authorized=True, success=False,
error_string=None, error_desc=None)
raise
def _on_progress_changed(self, trans, progress):
#print("_on_progress_changed", progress)
self.unity.set_progress(progress)
def _on_details_changed(self, trans, details):
print(details)
def _on_status_changed(self, trans, status):
print(status)
@inline_callbacks
def _show_transaction(self, trans, action, header, show_details):
trans.connect("status-details-changed", self._on_details_changed)
#状态改变的时候的回调函数
trans.connect("status-changed", self._on_status_changed)
#update 更新完成的时候的回调
trans.connect("finished", self._on_finished, action)
# trans.connect("medium-required", self._on_medium_required)
# trans.connect("config-file-conflict", self._on_config_file_conflict)
yield trans.set_debconf_frontend("kylin")
yield trans.run()
def _on_expanded(self, expander, param):
# Make the dialog resizable if the expander is expanded
# try to restore a previous size
if not expander.get_expanded():
self._expanded_size = (expander.terminal.get_visible(),
self.window_main.get_size())
self.window_main.end_user_resizable()
elif self._expanded_size:
term_visible, (stored_width, stored_height) = self._expanded_size
# Check if the stored size was for the download details or
# the terminal widget
if term_visible != expander.terminal.get_visible():
# The stored size was for the download details, so we need
# get a new size for the terminal widget
self._resize_to_show_details(expander)
else:
self.window_main.begin_user_resizable(stored_width,
stored_height)
else:
self._resize_to_show_details(expander)
def _resize_to_show_details(self, expander):
"""Resize the window to show the expanded details.
Unfortunately the expander only expands to the preferred size of the
child widget (e.g showing all 80x24 chars of the Vte terminal) if
the window is rendered the first time and the terminal is also visible.
If the expander is expanded afterwards the window won't change its
size anymore. So we have to do this manually. See LP#840942
"""
if expander.get_expanded():
win_width, win_height = self.window_main.get_size()
exp_width = expander.get_allocation().width
exp_height = expander.get_allocation().height
if expander.terminal.get_visible():
terminal_width = expander.terminal.get_char_width() * 80
terminal_height = expander.terminal.get_char_height() * 24
new_width = terminal_width - exp_width + win_width
new_height = terminal_height - exp_height + win_height
else:
new_width = win_width + 100
new_height = win_height + 200
self.window_main.begin_user_resizable(new_width, new_height)
def _on_medium_required(self, transaction, medium, drive):
dialog = AptMediumRequiredDialog(medium, drive, self.window_main)
res = dialog.run()
dialog.hide()
if res == Gtk.ResponseType.OK:
transaction.provide_medium(medium)
else:
transaction.cancel()
def _on_config_file_conflict(self, transaction, old, new):
dialog = AptConfigFileConflictDialog(old, new, self.window_main)
res = dialog.run()
dialog.hide()
if res == Gtk.ResponseType.YES:
transaction.resolve_config_file_conflict(old, "replace")
else:
transaction.resolve_config_file_conflict(old, "keep")
def _on_finished(self, trans, status, action):
error_string = None
error_desc = None
trans_failed = False
if status == EXIT_FAILED:
error_string = get_error_string_from_enum(trans.error.code)
error_desc = get_error_description_from_enum(trans.error.code)
if self.trans_failed_msg:
trans_failed = True
error_desc = error_desc + "\n" + self.trans_failed_msg
is_success = (status == EXIT_SUCCESS)
try:
self._action_done(action,
authorized=True, success=is_success,
error_string=error_string, error_desc=error_desc,
trans_failed=trans_failed)
except TypeError:
# this module used to be be lazily imported and in older code
# trans_failed= is not accepted
# TODO: this workaround can be dropped in Ubuntu 20.10
self._action_done(action,
authorized=True, success=is_success,
error_string=error_string, error_desc=error_desc)
if __name__ == "__main__":
import mock
options = mock.Mock()
data_dir = "/usr/share/update-manager"
from UpdateManager.UpdateManager import UpdateManager
app = UpdateManager(data_dir, options)
b = InstallBackendAptdaemon(app, None)
b.commit(["2vcard"], [], [])
Gtk.main()

View File

@ -0,0 +1,342 @@
#!/usr/bin/env python
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
"""Integration of package managers into UpdateManager"""
# (c) 2005-2009 Canonical, GPL
from __future__ import absolute_import
import gi
gi.require_version('Snapd', '1')
from gi.repository import GLib, Gtk, Snapd
from apt import Cache
import json
import logging
import os
import re
import subprocess
from gettext import gettext as _
from threading import Thread
from UpdateManager.Core.MyCache import MyCache
from UpdateManager.Core.utils import inhibit_sleep, get_dist_version
from UpdateManager.Dialogs import Dialog
class InstallBackend(Dialog):
ACTION_UPDATE = 0
ACTION_INSTALL = 1
def __init__(self, window_main, action):
Dialog.__init__(self, window_main)
self.action = action
self.sleep_cookie = None
def start(self):
os.environ["APT_LISTCHANGES_FRONTEND"] = "none"
# Do not suspend during the update process
self.sleep_cookie = inhibit_sleep()
if self.action == self.ACTION_INSTALL:
# Get the packages which should be installed and update
pkgs_install = []
pkgs_upgrade = []
pkgs_remove = []
# Get a fresh cache in case update-manager's is outdated to
# skip operations that already took place
fresh_cache = Cache(rootdir=self.window_main.cache.rootdir)
for pkg in self.window_main.cache:
try:
if pkg.marked_install \
and not fresh_cache[pkg.name].is_installed:
pkgname = pkg.name
if pkg.is_auto_installed:
pkgname += "#auto"
pkgs_install.append(pkgname)
elif (pkg.marked_upgrade
and fresh_cache[pkg.name].is_upgradable):
pkgs_upgrade.append(pkg.name)
elif (pkg.marked_delete
and fresh_cache[pkg.name].is_installed):
pkgs_remove.append(pkg.name)
except KeyError:
# pkg missing from fresh_cache can't be modified
pass
self.commit(pkgs_install, pkgs_upgrade, pkgs_remove)
else:
self.update()
def update(self):
"""Run a update to refresh the package list"""
raise NotImplementedError
def commit(self, pkgs_install, pkgs_upgrade, pkgs_remove):
"""Commit the cache changes """
raise NotImplementedError
def get_snap_seeds(self):
seeded_snaps = {}
unseeded_snaps = {}
curr_channel = "stable/ubuntu-" + get_dist_version()
try:
d2s_file = open(
'/usr/share/ubuntu-release-upgrader/deb2snap.json', 'r')
d2s = json.load(d2s_file)
d2s_file.close()
for snap in d2s["seeded"]:
seed = d2s["seeded"][snap]
deb = seed.get("deb", None)
to_channel = seed.get("to_channel", curr_channel)
seeded_snaps[snap] = (deb, to_channel)
for snap in d2s["unseeded"]:
unseed = d2s["unseeded"][snap]
from_channel = unseed.get("from_channel", curr_channel)
unseeded_snaps[snap] = (from_channel)
except Exception as e:
logging.debug("error reading deb2snap.json file (%s)" % e)
return seeded_snaps, unseeded_snaps
def get_deb2snap_dups(self):
# update and grab the latest cache
try:
if self.window_main.cache is None:
self.window_main.cache = MyCache(None)
else:
self.window_main.cache.open(None)
self.window_main.cache._initDepCache()
cache = self.window_main.cache
except Exception as e:
print("error reading cache (%s)" % e)
cache = None
duplicates = []
seeded_snaps, _ = self.get_snap_seeds()
for snap, (deb, _) in seeded_snaps.items():
# if the deb is installed and was not manually installed,
# replace it
if (deb in cache and cache[deb].is_installed):
deb_is_auto = True
cache[deb].mark_delete()
for pkg in cache.get_changes():
if (pkg.is_installed and pkg.marked_delete
and not pkg.is_auto_installed):
deb_is_auto = False
break
cache.clear()
if deb_is_auto:
duplicates.append(deb)
return duplicates
def get_snap_transitions(self):
# populate snap_list with deb2snap transitions
snap_list = {}
seeded_snaps, unseeded_snaps = self.get_snap_seeds()
for snap, (deb, to_channel) in seeded_snaps.items():
snap_object = {}
# check if the snap is already installed
snap_info = subprocess.Popen(["snap", "info", snap],
universal_newlines=True,
stdout=subprocess.PIPE).communicate()
if re.search("^installed: ", snap_info[0], re.MULTILINE):
logging.debug("Snap %s is installed" % snap)
continue
elif (deb in self.window_main.duplicate_packages):
# install the snap if the deb was just marked delete
snap_object['command'] = 'install'
snap_object['channel'] = to_channel
snap_list[snap] = snap_object
for snap, (from_channel) in unseeded_snaps.items():
snap_object = {}
# check if the snap is already installed
snap_info = subprocess.Popen(["snap", "info", snap],
universal_newlines=True,
stdout=subprocess.PIPE).communicate()
if re.search("^installed: ", snap_info[0], re.MULTILINE):
logging.debug("Snap %s is installed" % snap)
# its not tracking the release channel so don't remove
re_channel = "stable/ubuntu-[0-9][0-9].[0-9][0-9]"
if not re.search(r"^tracking:.*%s" % re_channel,
snap_info[0], re.MULTILINE):
logging.debug("Snap %s is not tracking the release channel"
% snap)
continue
snap_object['command'] = 'remove'
# check if this snap is being used by any other snaps
conns = subprocess.Popen(["snap", "connections", snap],
universal_newlines=True,
stdout=subprocess.PIPE).communicate()
for conn in conns[0].split('\n'):
conn_cols = conn.split()
if len(conn_cols) != 4:
continue
plug = conn_cols[1]
slot = conn_cols[2]
if slot.startswith(snap + ':'):
plug_snap = plug.split(':')[0]
if plug_snap != '-' and \
plug_snap not in unseeded_snaps:
logging.debug("Snap %s is being used by %s. "
"Switching it to stable track"
% (snap, plug_snap))
snap_object['command'] = 'refresh'
snap_object['channel'] = 'stable'
break
snap_list[snap] = snap_object
return snap_list
def update_snap_cb(self, client, change, _, user_data):
index, count, progress_bar = user_data
if not progress_bar:
return
# determine how much of this change has been done
task_total = 0
task_done = 0
for task in change.get_tasks():
task_total += task.get_progress_total()
task_done += task.get_progress_done()
task_fraction = task_done / task_total
# determine how much total progress has been made
total_fraction = (task_fraction / count) + (index / count)
# change.get_tasks() can increase between callbacks so we must
# avoid jumping backward in progress here
if total_fraction > progress_bar.get_fraction():
progress_bar.set_fraction(total_fraction)
def update_snaps(self):
# update status and progress bar
def update_status(status):
GLib.idle_add(self.label_details.set_label, status)
def update_progress(progress_bar):
progress_bar.pulse()
return True
update_status(_("Updating snaps"))
progress_bar = None
progress_timer = None
progress_bars = self.progressbar_slot.get_children()
if progress_bars and isinstance(progress_bars[0], Gtk.ProgressBar):
progress_bar = progress_bars[0]
progress_timer = GLib.timeout_add(100, update_progress,
progress_bar)
# populate snap_list with deb2snap transitions
snap_list = self.get_snap_transitions()
if progress_timer:
GLib.source_remove(progress_timer)
progress_bar.set_fraction(0)
# (un)install (un)seeded snap(s)
try:
client = Snapd.Client()
client.connect_sync()
index = 0
count = len(snap_list)
for snap, snap_object in snap_list.items():
command = snap_object['command']
if command == 'refresh':
update_status(_("Refreshing %s snap" % snap))
client.refresh_sync(snap, snap_object['channel'],
self.update_snap_cb,
progress_callback_data=(index, count,
progress_bar))
elif command == 'remove':
update_status(_("Removing %s snap" % snap))
client.remove_sync(snap, self.update_snap_cb,
progress_callback_data=(index, count,
progress_bar))
else:
update_status(_("Installing %s snap" % snap))
client.install_sync(snap, snap_object['channel'],
self.update_snap_cb,
progress_callback_data=(index, count,
progress_bar))
index += 1
except GLib.Error as e:
logging.debug("error updating snaps (%s)" % e)
GLib.idle_add(self.window_main.start_error, False,
_("Upgrade only partially completed."),
_("An error occurred while updating snaps. "
"Please check your network connection."))
return
# continue with the rest of the updates
GLib.idle_add(self.window_main.start_available)
def _action_done(self, action, authorized, success, error_string,
error_desc, trans_failed=False):
if action == self.ACTION_INSTALL:
if success and os.path.exists("/usr/bin/snap"):
Thread(target=self.update_snaps).start()
elif success:
self.window_main.start_available()
elif error_string:
self.window_main.start_error(trans_failed, error_string,
error_desc)
else:
# exit gracefuly, we can't just exit as this will trigger
# a crash if system.exit() is called in a exception handler
GLib.timeout_add(1, self.window_main.exit)
else:
if error_string:
self.window_main.start_error(True, error_string, error_desc)
# elif success and os.path.exists("/usr/bin/snap"):
# self.window_main.duplicate_packages = self.get_deb2snap_dups()
# self.window_main.start_available()
else:
is_cancelled_update = not success
self.window_main.start_available(is_cancelled_update)
# try aptdaemon
if os.path.exists("/usr/sbin/aptd") \
and "UPDATE_MANAGER_FORCE_BACKEND_SYNAPTIC" not in os.environ:
# check if the gtkwidgets are installed as well
try:
from .InstallBackendAptdaemon import InstallBackendAptdaemon
except ImportError:
logging.exception("importing aptdaemon")
def get_backend(*args, **kwargs):
"""Select and return a package manager backend."""
# try aptdaemon
if (os.path.exists("/usr/sbin/aptd")
and "UPDATE_MANAGER_FORCE_BACKEND_SYNAPTIC" not in os.environ):
# check if the gtkwidgets are installed as well
try:
return InstallBackendAptdaemon(*args, **kwargs)
except NameError:
logging.exception("using aptdaemon failed")
# nothing found, raise
raise Exception("No working backend found, please try installing "
"aptdaemon or synaptic")

View File

@ -0,0 +1,32 @@
# Copyright (c) 2008 Canonical Ltd
#
# Author: Jonathan Riddell <jriddell@ubuntu.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Checks for new releases, run by Adept
from __future__ import absolute_import, print_function
from .Core.MetaRelease import MetaReleaseCore
import time
metaRelease = MetaReleaseCore(False, False)
while metaRelease.downloading:
time.sleep(1)
print("no_longer_supported:" + str(metaRelease.no_longer_supported))
if metaRelease.new_dist is None:
print("new_dist_available:None")
else:
print("new_dist_available:" + str(metaRelease.new_dist.version))

9
data/Makefile Normal file
View File

@ -0,0 +1,9 @@
DOMAIN=update-manager
DESKTOP_IN_FILES := $(wildcard *.desktop.in)
DESKTOP_FILES := $(patsubst %.desktop.in,%.desktop,$(wildcard *.desktop.in))
all: $(DESKTOP_FILES)
%.desktop: %.desktop.in ../po/$(DOMAIN).pot
intltool-merge -d ../po $< $@

View File

@ -0,0 +1,59 @@
<schemalist>
<schema id="com.ubuntu.update-manager" path="/apps/update-manager/">
<key name="show-details" type="b">
<default>false</default>
<summary>Show details of an update</summary>
<description>Stores the state of the expander that contains the list of changes and the description</description>
</key>
<key name="window-width" type="i">
<default>1</default>
<summary>The window width</summary>
<description>Stores the width of the update-manager dialog</description>
</key>
<key name="window-height" type="i">
<default>400</default>
<summary>The window height</summary>
<description>Stores the height of the update-manager dialog</description>
</key>
<key name="check-dist-upgrades" type="b">
<default>true</default>
<summary>*deprecated* Check for new distribution releases</summary>
<description>This key is deprecated in favor of the file /etc/update-manager/release-upgrades Check automatically if a new version of the current distribution is available and offer to upgrade (if possible).</description>
</key>
<key name="autoclose-install-window" type="b">
<default>true</default>
<summary>Auto close the install window after successful install</summary>
<description>If this key is set the install window will be automatically closed on successful installation.</description>
</key>
<key name="show-versions" type="b">
<default>false</default>
<summary>Show version in update list</summary>
<description>If this key is set the main update list window will show version information (from version to version).</description>
</key>
<key name="summary-before-name" type="b">
<default>true</default>
<summary>Show package summary before package name in update list</summary>
<description>If this key is set, the main update list window will show the package summary before the package name.</description>
</key>
<key name="first-run" type="b">
<default>true</default>
<summary>First run welcome message</summary>
<description>If this key is set a first run welcome message will be presented.</description>
</key>
<key name="check-new-release-ignore" type="s">
<default>''</default>
<summary>make check-new-release-gtk ignore a given new release</summary>
<description>This will permanently hide the new release prompt from check-new-release-gtk. Note that the small button in the main update-manager UI will still be there.</description>
</key>
<key name="launch-time" type="x">
<default>0</default>
<summary>Time when update-manager got launched last</summary>
<description>The last time update-manager was run.</description>
</key>
<key name="launch-count" type="i">
<default>0</default>
<summary>Number of times update-manager has been launched</summary>
<description>Number of times update-manager has been launched.</description>
</key>
</schema>
</schemalist>

124
data/gtkbuilder/Dialog.ui Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkWindow" id="window_dialog">
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="pane_dialog">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="border_width">12</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGrid" id="header_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">12</property>
<property name="hexpand">True</property>
<property name="row_spacing">2</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkImage" id="image_logo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="yalign">0</property>
<property name="pixel_size">48</property>
<property name="icon_name">system-software-update</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_header">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="margin_top">6</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="wrap">True</property>
<property name="max_width_chars">20</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_desc">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="wrap">True</property>
<property name="max_width_chars">20</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="main_container">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="margin_bottom">12</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">4</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="buttonbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="spacing">6</property>
<property name="homogeneous">True</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -0,0 +1,408 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkVBox" id="pane_updates_available">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkExpander" id="expander_details">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="spacing">6</property>
<property name="resize_toplevel">True</property>
<child>
<object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="vexpand">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow_update">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="min_content_height">100</property>
<child>
<object class="GtkTreeView" id="treeview_update">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="headers_clickable">False</property>
<property name="rules_hint">True</property>
<child internal-child="accessible">
<object class="AtkObject" id="treeview_update-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">updates</property>
</object>
</child>
<signal name="cursor-changed" handler="on_treeview_update_cursor_changed" swapped="no"/>
<signal name="row-activated" handler="on_treeview_update_row_activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkExpander" id="expander_desc">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkNotebook" id="notebook_details">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow_changes">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Changes</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">6</property>
<property name="shadow_type">in</property>
<property name="min_content_height">80</property>
<child>
<object class="GtkTextView" id="textview_descr">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="pixels_above_lines">6</property>
<property name="editable">False</property>
<property name="wrap_mode">word</property>
<property name="left_margin">6</property>
<property name="right_margin">6</property>
<property name="cursor_visible">False</property>
<property name="accepts_tab">False</property>
<child internal-child="accessible">
<object class="AtkObject" id="textview_descr-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Description</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Description</property>
<child internal-child="accessible">
<object class="AtkObject" id="label9-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Description</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Technical description</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Details of updates</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox_alerts">
<property name="can_focus">False</property>
<property name="spacing">3</property>
<child>
<object class="GtkHBox" id="hbox_downsize">
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="image_downsize">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixel-size">16</property>
<property name="icon_name">aptdaemon-download</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_downsize">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">
</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_restart">
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="image_restart">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">gtk-refresh</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_restart">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">The computer will need to restart.</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_roaming">
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="image_roaming">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">dialog-warning</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_roaming">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">You are connected via roaming and may be charged for the data consumed by this update.</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_on_3g">
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="image_on_3g">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">modem</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_on_3g">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">You may want to wait until youre not using a mobile broadband connection.</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_battery">
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="image_battery">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">battery</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_battery">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Its safer to connect the computer to AC power before updating.</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_offline">
<property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="image_offline">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">network-offline</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_offline">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
</interface>

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkWindow" id="window_update_progress">
<property name="can_focus">False</property>
<child>
<object class="GtkGrid" id="pane_update_progress">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkLabel" id="label_header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_details">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ellipsize">end</property>
<property name="max_width_chars">20</property>
<attributes>
<attribute name="scale" value="0.9"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="progressbar_slot">
<property name="width_request">350</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="yscale">0.5</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="button_cancel_slot">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="expander_slot">
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

72
data/update-manager.8 Normal file
View File

@ -0,0 +1,72 @@
.\" Title : update-manager
.\" Author : Bruno Mangin
.\" August, 2 2007
.\"
.\" First parameter, NAME, should be all caps
.\" other parameters are allowed: see man(7), man(1)
.TH UPDATE-MANAGER 8 "April 15, 2010"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" for manpage-specific macros, see man(7)
.SH NAME
update-manager \- graphical management of software packages updates
.SH SYNOPSIS
\fBupdate-manager\fP [options]
.br
.SH DESCRIPTION
Update-manager is a frontend for the apt package management system. It allows you to perform some actions of the command line tool apt-get in a graphical environment.
.PP
Update-manager is especially designed for upgrading your system, or migrating your system towards a more recent version.
.SH OPTIONS
For a daily use, you may launch update-manager with no options so that your system is just upgraded.
.PP
For migration purposes, update-manager accepts some options:
.TP
\fB-h\fR, \fB\-\-help\fR
Show a similar help message
.TP
\fB-V\fR, \fB\-\-version\fR
Show version
.TP
\fB-\-data-dir=DATA_DIR\fR
Directory that contains the data files
.TP
\fB-c\fR, \fB\-\-check-dist-upgrades\fR
Check if a new distribution release is available
.TP
\fB-d\fR, \fB\-\-devel-release\fR
If using the latest supported release, upgrade to the development release
.TP
\fB-p\fR, \fB\-\-proposed\fR
Upgrade using the latest proposed version of the release upgrader
.TP
\fB-\-no-focus-on-map\fR
Do not focus on map when starting
.SH ACTIONS PERFORMED DURING AN UPGRADE TO A NEW VERSION
* eventually reinstall the package ubuntu-desktop
* switch to an updated sources.list entries
* adds the default user to new groups if needed
.SH SEE ALSO
\fBSynaptic\fR, \fBsources.list\fR, \fBaptitude\fR
.SH AUTHORS
update-manager was developed by Michael Vogt <mvo@ubuntu.com>
with various contributors (see AUTHORS file)
.PP
This manual page was originally written by Bruno Mangin and
Michael Vogt <mvo@ubuntu.com>.
.SH COPYRIGHT
Copyright (C) 2006-2007 Canonical
.PP
There is NO warranty.
You may redistribute this software under the terms of the GNU
General Public License. For more information about these matters, see
the files named COPYING.

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>update-manager.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0+</project_license>
<_name>Software Updater</_name>
<_summary>Show and install available updates</_summary>
<description>
<_p>
Software Updater checks for updates using apt and let you choose which
updates to install.
</_p>
</description>
<kudos>
<kudo>ModernToolkit</kudo>
</kudos>
<url type="bugtracker">https://bugs.launchpad.net/ubuntu/+source/update-manager</url>
<url type="translate">https://translations.launchpad.net/ubuntu/+source/update-manager</url>
<update_contact>ubuntu-desktop_AT_lists.ubuntu.com</update_contact>
<compulsory_for_desktop>GNOME</compulsory_for_desktop>
<compulsory_for_desktop>LXDE</compulsory_for_desktop>
<compulsory_for_desktop>MATE</compulsory_for_desktop>
<compulsory_for_desktop>Unity</compulsory_for_desktop>
<translation type="gettext">update-manager</translation>
</component>

View File

@ -0,0 +1,9 @@
[com.ubuntu.update-manager]
show-details = /apps/update-manager/show_details
check-dist-upgrades = /apps/update-manager/check_dist_upgrades
autoclose-install-window = /apps/update-manager/autoclose_install_window
show-versions = /apps/update-manager/show_versions
summary-before-name = /apps/update-manager/summary_before_name
first-run = /apps/update-manager/first_run
check-new-release-ignore = /apps/update-manager/check_new_release_ignore
launch-time = /apps/update-manager/launch_time

View File

@ -0,0 +1,11 @@
[Desktop Entry]
_Name=Software Updater
_GenericName=Software Updates
_Comment=Show and install available updates
Exec=/usr/bin/update-manager
Icon=system-software-update
Terminal=false
Type=Application
Categories=System;Settings;
X-Ubuntu-Gettext-Domain=update-manager
X-Unity-IconBackgroundColor=#4c9e39

7534
debian/changelog vendored Normal file

File diff suppressed because it is too large Load Diff

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

97
debian/control vendored Normal file
View File

@ -0,0 +1,97 @@
Source: update-manager
Section: gnome
Priority: optional
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Build-Depends: debhelper (>= 9),
dh-python,
python3-all (>= 3.3.0-2),
python3-distutils-extra (>= 2.38),
python3-dbus,
python3-distupgrade,
python3-distro-info,
python3-gi (>= 3.8),
python3-yaml,
gir1.2-gtk-3.0,
gir1.2-snapd-1,
lsb-release,
apt-clone (>= 0.2.3~ubuntu1)
Build-Depends-Indep: libxml-parser-perl,
intltool
Standards-Version: 3.8.0
Vcs-Bzr: http://bazaar.launchpad.net/~ubuntu-core-dev/update-manager/main
XS-Testsuite: autopkgtest
X-Python3-Version: >= 3.2
Package: update-manager-core
Architecture: all
Section: admin
Depends: ${python3:Depends},
${misc:Depends},
python3-update-manager (= ${source:Version}),
python3-distro-info,
distro-info-data,
lsb-release,
ubuntu-release-upgrader-core (>= 1:18.04.9)
Recommends: libpam-modules (>= 1.0.1-9ubuntu3)
Replaces: update-manager (<< 1:0.146.2)
Breaks: update-manager (<< 1:0.146.2),
computer-janitor (<= 1.11-0ubuntu1)
Description: manage release upgrades
This is the core of update-manager and the release upgrader
Package: python3-update-manager
Architecture: all
Section: python
Replaces: update-manager-core (<< 1:0.163)
Breaks: update-manager-core (<< 1:0.163),
python3-distupgrade (<< 1:16.10.10)
Depends: ${python3:Depends},
${misc:Depends},
python3-apt (>= 0.8.5~),
python3-distro-info,
python3-distupgrade,
lsb-release,
Suggests: python3-launchpadlib,
Description: python 3.x module for update-manager
Python module for update-manager (UpdateManager).
.
This package contains the python 3.x version of this module.
Package: update-manager
Architecture: all
Depends: ${python3:Depends}, ${misc:Depends},
update-manager-core (= ${source:Version}),
libgtk3-perl,
python3-aptdaemon.gtk3widgets (>= 1.1.1+bzr982-0ubuntu13) | synaptic,
policykit-1,
python3-dbus,
python3-gi (>= 3.8),
python3-yaml,
gir1.2-gtk-3.0,
gir1.2-snapd-1,
ubuntu-release-upgrader-gtk,
update-notifier,
gnome-shell | policykit-1-gnome | polkit-kde-agent-1 | lxpolkit | lxqt-policykit | mate-polkit | polkit-1-auth-agent
Breaks: update-notifier (<< 3.177)
Recommends: software-properties-gtk (>= 0.71.2),
python3-launchpadlib
Suggests: gir1.2-dbusmenu-glib-0.4,
gir1.2-unity-5.0,
Description: GNOME application that manages apt updates
This is the GNOME apt update manager. It checks for updates and lets the user
choose which to install.
#Package: update-manager-kde
#Architecture: all
#Section: kde
#Depends: ${python3:Depends},
# ${misc:Depends},
# update-manager-core,
# python3-pykde4,
# kdesudo,
# psmisc,
# ubuntu-release-upgrader-qt
#Recommends: python3-launchpadlib
#Description: Support modules for Muon Notifier and Apper
# Support modules for Muon Notifier and Apper to check for new distro releases
# and download the dist-upgrade tool.

22
debian/copyright vendored Normal file
View File

@ -0,0 +1,22 @@
This package was debianized by Michiel Sikkes <michiel@eyesopened.nl> on
Mon, 25 Oct 2004 21:49:07 +0200.
It was downloaded from http://code.launchpad.net/~ubuntu-core-dev/update-manager/main
Upstream Authors:
Michiel Sikkes <michiel@eyesopened.nl>
Michael Vogt <michael.vogt@ubuntu.com>
Sebastian Heinlein <glatzor@ubuntu.com>
Jonathan Riddell <jriddell@ubuntu.com>
Copyright:
2004-2008 Canonical Ltd.
2004-2005 Michiel Sikkes
All code released under the GPL, see /usr/share/common-licenses/GPL
With the exception of
UpdateManager/SimpleGladeApp.py
which is released under the LGPL, see /usr/share/common-licenses/LGPL

3
debian/docs vendored Normal file
View File

@ -0,0 +1,3 @@
README
TODO
AUTHORS

7
debian/python3-update-manager.install vendored Normal file
View File

@ -0,0 +1,7 @@
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/Core
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/UpdateManagerVersion.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/UnitySupport.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/__init__.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/backend
debian/tmp/usr/lib/python3*/dist-packages/HweSupportStatus
debian/tmp/usr/lib/python3*/dist-packages/janitor

6
debian/rules vendored Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/make -f
export PYBUILD_INTERPRETERS=python3
%:
dh $@ --with=python3 --buildsystem=pybuild

64
debian/source_update-manager.py vendored Normal file
View File

@ -0,0 +1,64 @@
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
'''apport package hook for update-manager
(c) 2011 Canonical Ltd.
Author: Brian Murray <brian@ubuntu.com>
'''
import os
import re
import subprocess
from apport.hookutils import (
attach_gsettings_package, attach_root_command_outputs,
attach_file_if_exists, command_available,
recent_syslog)
def run_hwe_command(option):
command = ['hwe-support-status', option]
sp = subprocess.Popen(command, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=None)
out = sp.communicate()[0]
if sp.returncode == 0:
res = out.strip()
# exit code is 10 on unsupported HWE
elif sp.returncode == 10:
res = out.strip()
else:
res = (b'Error: command ' + str(command).encode()
+ b' failed with exit code '
+ str(sp.returncode).encode() + b': ' + out)
return res
def add_info(report, ui):
problem_type = report.get("ProblemType", None)
if problem_type == "Bug":
response = ui.yesno("Is the issue you are reporting one you \
encountered when upgrading Ubuntu from one release to another?")
else:
response = None
if response:
os.execlp('apport-bug', 'apport-bug', 'ubuntu-release-upgrader')
else:
attach_gsettings_package(report, 'update-manager')
attach_file_if_exists(report, '/var/log/apt/history.log',
'DpkgHistoryLog.txt')
attach_file_if_exists(report, '/var/log/apt/term.log',
'DpkgTerminalLog.txt')
attach_root_command_outputs(
report,
{'CurrentDmesg.txt':
'dmesg | comm -13 --nocheck-order /var/log/dmesg -'})
if command_available('hwe-support-status'):
# not using apport's command_output because it doesn't expect a
# return code of 10
unsupported = run_hwe_command('--show-all-unsupported')
if unsupported:
report['HWEunsupported'] = unsupported
report['HWEreplacements'] = \
run_hwe_command('--show-replacements')
report["Aptdaemon"] = recent_syslog(re.compile("AptDaemon"))

2
debian/tests/control vendored Normal file
View File

@ -0,0 +1,2 @@
Tests: nose-tests
Depends: @, aptdaemon, pycodestyle, pyflakes3, python3-mock, python3-nose, xvfb

2
debian/tests/nose-tests vendored Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
xvfb-run nosetests3

3
debian/update-manager-core.dirs vendored Normal file
View File

@ -0,0 +1,3 @@
var/lib/update-manager
var/lib/update-notifier
usr/bin

4
debian/update-manager-core.install vendored Normal file
View File

@ -0,0 +1,4 @@
debian/tmp/usr/bin/ubuntu-security-status
debian/tmp/usr/bin/hwe-support-status
debian/tmp/usr/share/locale
debian/source_update-manager.py /usr/share/apport/package-hooks/

1
debian/update-manager-kde.install vendored Normal file
View File

@ -0,0 +1 @@
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/check-meta-release.py

13
debian/update-manager.install vendored Normal file
View File

@ -0,0 +1,13 @@
debian/tmp/usr/bin/update-manager
debian/tmp/usr/share/update-manager/gtkbuilder
debian/tmp/usr/share/help
debian/tmp/usr/share/glib-2.0/schemas
debian/tmp/usr/share/GConf/gsettings
debian/tmp/usr/share/applications/update-manager.desktop
debian/tmp/usr/share/metainfo/update-manager.appdata.xml
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/ChangelogViewer.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/Dialogs.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/MetaReleaseGObject.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/UpdateManager.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/UpdatesAvailable.py
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/HelpViewer.py

1
debian/update-manager.manpages vendored Normal file
View File

@ -0,0 +1 @@
debian/tmp/usr/share/man/man8/update-manager.8

655
help/C/fdl-appendix.xml Normal file
View File

@ -0,0 +1,655 @@
<!--
The GNU Free Documentation License 1.1 in DocBook
Markup by Eric Baudais <baudais@okstate.edu>
Maintained by the GNOME Documentation Project
http://developer.gnome.org/projects/gdp
Version: 1.0.1
Last Modified: Nov 16, 2000
-->
<appendix id="fdl">
<appendixinfo>
<releaseinfo>
Version 1.1, March 2000
</releaseinfo>
<copyright>
<year>2000</year><holder>Free Software Foundation, Inc.</holder>
</copyright>
<legalnotice id="fdl-legalnotice">
<para>
<address>Free Software Foundation, Inc. <street>59 Temple Place,
Suite 330</street>, <city>Boston</city>, <state>MA</state>
<postcode>02111-1307</postcode> <country>USA</country></address>
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
</para>
</legalnotice>
</appendixinfo>
<title>GNU Free Documentation License</title>
<sect1 id="fdl-preamble">
<title>0. PREAMBLE</title>
<para>
The purpose of this License is to make a manual, textbook, or
other written document <quote>free</quote> in the sense of
freedom: to assure everyone the effective freedom to copy and
redistribute it, with or without modifying it, either
commercially or noncommercially. Secondarily, this License
preserves for the author and publisher a way to get credit for
their work, while not being considered responsible for
modifications made by others.
</para>
<para>
This License is a kind of <quote>copyleft</quote>, which means
that derivative works of the document must themselves be free in
the same sense. It complements the GNU General Public License,
which is a copyleft license designed for free software.
</para>
<para>
We have designed this License in order to use it for manuals for
free software, because free software needs free documentation: a
free program should come with manuals providing the same
freedoms that the software does. But this License is not limited
to software manuals; it can be used for any textual work,
regardless of subject matter or whether it is published as a
printed book. We recommend this License principally for works
whose purpose is instruction or reference.
</para>
</sect1>
<sect1 id="fdl-section1">
<title>1. APPLICABILITY AND DEFINITIONS</title>
<para id="fdl-document">
This License applies to any manual or other work that contains a
notice placed by the copyright holder saying it can be
distributed under the terms of this License. The
<quote>Document</quote>, below, refers to any such manual or
work. Any member of the public is a licensee, and is addressed
as <quote>you</quote>.
</para>
<para id="fdl-modified">
A <quote>Modified Version</quote> of the Document means any work
containing the Document or a portion of it, either copied
verbatim, or with modifications and/or translated into another
language.
</para>
<para id="fdl-secondary">
A <quote>Secondary Section</quote> is a named appendix or a
front-matter section of the <link
linkend="fdl-document">Document</link> that deals exclusively
with the relationship of the publishers or authors of the
Document to the Document's overall subject (or to related
matters) and contains nothing that could fall directly within
that overall subject. (For example, if the Document is in part a
textbook of mathematics, a Secondary Section may not explain any
mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of
legal, commercial, philosophical, ethical or political position
regarding them.
</para>
<para id="fdl-invariant">
The <quote>Invariant Sections</quote> are certain <link
linkend="fdl-secondary"> Secondary Sections</link> whose titles
are designated, as being those of Invariant Sections, in the
notice that says that the <link
linkend="fdl-document">Document</link> is released under this
License.
</para>
<para id="fdl-cover-texts">
The <quote>Cover Texts</quote> are certain short passages of
text that are listed, as Front-Cover Texts or Back-Cover Texts,
in the notice that says that the <link
linkend="fdl-document">Document</link> is released under this
License.
</para>
<para id="fdl-transparent">
A <quote>Transparent</quote> copy of the <link
linkend="fdl-document"> Document</link> means a machine-readable
copy, represented in a format whose specification is available
to the general public, whose contents can be viewed and edited
directly and straightforwardly with generic text editors or (for
images composed of pixels) generic paint programs or (for
drawings) some widely available drawing editor, and that is
suitable for input to text formatters or for automatic
translation to a variety of formats suitable for input to text
formatters. A copy made in an otherwise Transparent file format
whose markup has been designed to thwart or discourage
subsequent modification by readers is not Transparent. A copy
that is not <quote>Transparent</quote> is called
<quote>Opaque</quote>.
</para>
<para>
Examples of suitable formats for Transparent copies include
plain ASCII without markup, Texinfo input format, LaTeX input
format, SGML or XML using a publicly available DTD, and
standard-conforming simple HTML designed for human
modification. Opaque formats include PostScript, PDF,
proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD
and/or processing tools are not generally available, and the
machine-generated HTML produced by some word processors for
output purposes only.
</para>
<para id="fdl-title-page">
The <quote>Title Page</quote> means, for a printed book, the
title page itself, plus such following pages as are needed to
hold, legibly, the material this License requires to appear in
the title page. For works in formats which do not have any title
page as such, <quote>Title Page</quote> means the text near the
most prominent appearance of the work's title, preceding the
beginning of the body of the text.
</para>
</sect1>
<sect1 id="fdl-section2">
<title>2. VERBATIM COPYING</title>
<para>
You may copy and distribute the <link
linkend="fdl-document">Document</link> in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License
applies to the Document are reproduced in all copies, and that
you add no other conditions whatsoever to those of this
License. You may not use technical measures to obstruct or
control the reading or further copying of the copies you make or
distribute. However, you may accept compensation in exchange for
copies. If you distribute a large enough number of copies you
must also follow the conditions in <link
linkend="fdl-section3">section 3</link>.
</para>
<para>
You may also lend copies, under the same conditions stated
above, and you may publicly display copies.
</para>
</sect1>
<sect1 id="fdl-section3">
<title>3. COPYING IN QUANTITY</title>
<para>
If you publish printed copies of the <link
linkend="fdl-document">Document</link> numbering more than 100,
and the Document's license notice requires <link
linkend="fdl-cover-texts">Cover Texts</link>, you must enclose
the copies in covers that carry, clearly and legibly, all these
Cover Texts: Front-Cover Texts on the front cover, and
Back-Cover Texts on the back cover. Both covers must also
clearly and legibly identify you as the publisher of these
copies. The front cover must present the full title with all
words of the title equally prominent and visible. You may add
other material on the covers in addition. Copying with changes
limited to the covers, as long as they preserve the title of the
<link linkend="fdl-document">Document</link> and satisfy these
conditions, can be treated as verbatim copying in other
respects.
</para>
<para>
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto
adjacent pages.
</para>
<para>
If you publish or distribute <link
linkend="fdl-transparent">Opaque</link> copies of the <link
linkend="fdl-document">Document</link> numbering more than 100,
you must either include a machine-readable <link
linkend="fdl-transparent">Transparent</link> copy along with
each Opaque copy, or state in or with each Opaque copy a
publicly-accessible computer-network location containing a
complete Transparent copy of the Document, free of added
material, which the general network-using public has access to
download anonymously at no charge using public-standard network
protocols. If you use the latter option, you must take
reasonably prudent steps, when you begin distribution of Opaque
copies in quantity, to ensure that this Transparent copy will
remain thus accessible at the stated location until at least one
year after the last time you distribute an Opaque copy (directly
or through your agents or retailers) of that edition to the
public.
</para>
<para>
It is requested, but not required, that you contact the authors
of the <link linkend="fdl-document">Document</link> well before
redistributing any large number of copies, to give them a chance
to provide you with an updated version of the Document.
</para>
</sect1>
<sect1 id="fdl-section4">
<title>4. MODIFICATIONS</title>
<para>
You may copy and distribute a <link
linkend="fdl-modified">Modified Version</link> of the <link
linkend="fdl-document">Document</link> under the conditions of
sections <link linkend="fdl-section2">2</link> and <link
linkend="fdl-section3">3</link> above, provided that you release
the Modified Version under precisely this License, with the
Modified Version filling the role of the Document, thus
licensing distribution and modification of the Modified Version
to whoever possesses a copy of it. In addition, you must do
these things in the Modified Version:
</para>
<orderedlist numeration="upperalpha">
<listitem>
<para>
Use in the <link linkend="fdl-title-page">Title
Page</link> (and on the covers, if any) a title distinct
from that of the <link
linkend="fdl-document">Document</link>, and from those of
previous versions (which should, if there were any, be
listed in the History section of the Document). You may
use the same title as a previous version if the original
publisher of that version gives permission.
</para>
</listitem>
<listitem>
<para>
List on the <link linkend="fdl-title-page">Title
Page</link>, as authors, one or more persons or entities
responsible for authorship of the modifications in the
<link linkend="fdl-modified">Modified Version</link>,
together with at least five of the principal authors of
the <link linkend="fdl-document">Document</link> (all of
its principal authors, if it has less than five).
</para>
</listitem>
<listitem>
<para>
State on the <link linkend="fdl-title-page">Title
Page</link> the name of the publisher of the <link
linkend="fdl-modified">Modified Version</link>, as the
publisher.
</para>
</listitem>
<listitem>
<para>
Preserve all the copyright notices of the <link
linkend="fdl-document">Document</link>.
</para>
</listitem>
<listitem>
<para>
Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
</para>
</listitem>
<listitem>
<para>
Include, immediately after the copyright notices, a
license notice giving the public permission to use the
<link linkend="fdl-modified">Modified Version</link> under
the terms of this License, in the form shown in the
Addendum below.
</para>
</listitem>
<listitem>
<para>
Preserve in that license notice the full lists of <link
linkend="fdl-invariant"> Invariant Sections</link> and
required <link linkend="fdl-cover-texts">Cover
Texts</link> given in the <link
linkend="fdl-document">Document's</link> license notice.
</para>
</listitem>
<listitem>
<para>
Include an unaltered copy of this License.
</para>
</listitem>
<listitem>
<para>
Preserve the section entitled <quote>History</quote>, and
its title, and add to it an item stating at least the
title, year, new authors, and publisher of the <link
linkend="fdl-modified">Modified Version </link>as given on
the <link linkend="fdl-title-page">Title Page</link>. If
there is no section entitled <quote>History</quote> in the
<link linkend="fdl-document">Document</link>, create one
stating the title, year, authors, and publisher of the
Document as given on its Title Page, then add an item
describing the Modified Version as stated in the previous
sentence.
</para>
</listitem>
<listitem>
<para>
Preserve the network location, if any, given in the <link
linkend="fdl-document">Document</link> for public access
to a <link linkend="fdl-transparent">Transparent</link>
copy of the Document, and likewise the network locations
given in the Document for previous versions it was based
on. These may be placed in the <quote>History</quote>
section. You may omit a network location for a work that
was published at least four years before the Document
itself, or if the original publisher of the version it
refers to gives permission.
</para>
</listitem>
<listitem>
<para>
In any section entitled <quote>Acknowledgements</quote> or
<quote>Dedications</quote>, preserve the section's title,
and preserve in the section all the substance and tone of
each of the contributor acknowledgements and/or
dedications given therein.
</para>
</listitem>
<listitem>
<para>
Preserve all the <link linkend="fdl-invariant">Invariant
Sections</link> of the <link
linkend="fdl-document">Document</link>, unaltered in their
text and in their titles. Section numbers or the
equivalent are not considered part of the section titles.
</para>
</listitem>
<listitem>
<para>
Delete any section entitled
<quote>Endorsements</quote>. Such a section may not be
included in the <link linkend="fdl-modified">Modified
Version</link>.
</para>
</listitem>
<listitem>
<para>
Do not retitle any existing section as
<quote>Endorsements</quote> or to conflict in title with
any <link linkend="fdl-invariant">Invariant
Section</link>.
</para>
</listitem>
</orderedlist>
<para>
If the <link linkend="fdl-modified">Modified Version</link>
includes new front-matter sections or appendices that qualify as
<link linkend="fdl-secondary">Secondary Sections</link> and
contain no material copied from the Document, you may at your
option designate some or all of these sections as invariant. To
do this, add their titles to the list of <link
linkend="fdl-invariant">Invariant Sections</link> in the
Modified Version's license notice. These titles must be
distinct from any other section titles.
</para>
<para>
You may add a section entitled <quote>Endorsements</quote>,
provided it contains nothing but endorsements of your <link
linkend="fdl-modified">Modified Version</link> by various
parties--for example, statements of peer review or that the text
has been approved by an organization as the authoritative
definition of a standard.
</para>
<para>
You may add a passage of up to five words as a <link
linkend="fdl-cover-texts">Front-Cover Text</link>, and a passage
of up to 25 words as a <link
linkend="fdl-cover-texts">Back-Cover Text</link>, to the end of
the list of <link linkend="fdl-cover-texts">Cover Texts</link>
in the <link linkend="fdl-modified">Modified Version</link>.
Only one passage of Front-Cover Text and one of Back-Cover Text
may be added by (or through arrangements made by) any one
entity. If the <link linkend="fdl-document">Document</link>
already includes a cover text for the same cover, previously
added by you or by arrangement made by the same entity you are
acting on behalf of, you may not add another; but you may
replace the old one, on explicit permission from the previous
publisher that added the old one.
</para>
<para>
The author(s) and publisher(s) of the <link
linkend="fdl-document">Document</link> do not by this License
give permission to use their names for publicity for or to
assert or imply endorsement of any <link
linkend="fdl-modified">Modified Version </link>.
</para>
</sect1>
<sect1 id="fdl-section5">
<title>5. COMBINING DOCUMENTS</title>
<para>
You may combine the <link linkend="fdl-document">Document</link>
with other documents released under this License, under the
terms defined in <link linkend="fdl-section4">section 4</link>
above for modified versions, provided that you include in the
combination all of the <link linkend="fdl-invariant">Invariant
Sections</link> of all of the original documents, unmodified,
and list them all as Invariant Sections of your combined work in
its license notice.
</para>
<para>
The combined work need only contain one copy of this License,
and multiple identical <link linkend="fdl-invariant">Invariant
Sections</link> may be replaced with a single copy. If there are
multiple Invariant Sections with the same name but different
contents, make the title of each such section unique by adding
at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique
number. Make the same adjustment to the section titles in the
list of Invariant Sections in the license notice of the combined
work.
</para>
<para>
In the combination, you must combine any sections entitled
<quote>History</quote> in the various original documents,
forming one section entitled <quote>History</quote>; likewise
combine any sections entitled <quote>Acknowledgements</quote>,
and any sections entitled <quote>Dedications</quote>. You must
delete all sections entitled <quote>Endorsements.</quote>
</para>
</sect1>
<sect1 id="fdl-section6">
<title>6. COLLECTIONS OF DOCUMENTS</title>
<para>
You may make a collection consisting of the <link
linkend="fdl-document">Document</link> and other documents
released under this License, and replace the individual copies
of this License in the various documents with a single copy that
is included in the collection, provided that you follow the
rules of this License for verbatim copying of each of the
documents in all other respects.
</para>
<para>
You may extract a single document from such a collection, and
dispbibute it individually under this License, provided you
insert a copy of this License into the extracted document, and
follow this License in all other respects regarding verbatim
copying of that document.
</para>
</sect1>
<sect1 id="fdl-section7">
<title>7. AGGREGATION WITH INDEPENDENT WORKS</title>
<para>
A compilation of the <link
linkend="fdl-document">Document</link> or its derivatives with
other separate and independent documents or works, in or on a
volume of a storage or distribution medium, does not as a whole
count as a <link linkend="fdl-modified">Modified Version</link>
of the Document, provided no compilation copyright is claimed
for the compilation. Such a compilation is called an
<quote>aggregate</quote>, and this License does not apply to the
other self-contained works thus compiled with the Document , on
account of their being thus compiled, if they are not themselves
derivative works of the Document. If the <link
linkend="fdl-cover-texts">Cover Text</link> requirement of <link
linkend="fdl-section3">section 3</link> is applicable to these
copies of the Document, then if the Document is less than one
quarter of the entire aggregate, the Document's Cover Texts may
be placed on covers that surround only the Document within the
aggregate. Otherwise they must appear on covers around the whole
aggregate.
</para>
</sect1>
<sect1 id="fdl-section8">
<title>8. TRANSLATION</title>
<para>
Translation is considered a kind of modification, so you may
distribute translations of the <link
linkend="fdl-document">Document</link> under the terms of <link
linkend="fdl-section4">section 4</link>. Replacing <link
linkend="fdl-invariant"> Invariant Sections</link> with
translations requires special permission from their copyright
holders, but you may include translations of some or all
Invariant Sections in addition to the original versions of these
Invariant Sections. You may include a translation of this
License provided that you also include the original English
version of this License. In case of a disagreement between the
translation and the original English version of this License,
the original English version will prevail.
</para>
</sect1>
<sect1 id="fdl-section9">
<title>9. TERMINATION</title>
<para>
You may not copy, modify, sublicense, or distribute the <link
linkend="fdl-document">Document</link> except as expressly
provided for under this License. Any other attempt to copy,
modify, sublicense or distribute the Document is void, and will
automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.
</para>
</sect1>
<sect1 id="fdl-section10">
<title>10. FUTURE REVISIONS OF THIS LICENSE</title>
<para>
The <ulink type="http"
url="http://www.gnu.org/fsf/fsf.html">Free Software
Foundation</ulink> may publish new, revised versions of the GNU
Free Documentation License from time to time. Such new versions
will be similar in spirit to the present version, but may differ
in detail to address new problems or concerns. See <ulink
type="http"
url="http://www.gnu.org/copyleft">http://www.gnu.org/copyleft/</ulink>.
</para>
<para>
Each version of the License is given a distinguishing version
number. If the <link linkend="fdl-document">Document</link>
specifies that a particular numbered version of this License
<quote>or any later version</quote> applies to it, you have the
option of following the terms and conditions either of that
specified version or of any later version that has been
published (not as a draft) by the Free Software Foundation. If
the Document does not specify a version number of this License,
you may choose any version ever published (not as a draft) by
the Free Software Foundation.
</para>
</sect1>
<sect1 id="fdl-using">
<title>Addendum</title>
<para>
To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:
</para>
<blockquote>
<para>
Copyright YEAR YOUR NAME.
</para>
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation
License, Version 1.1 or any later version published by the
Free Software Foundation; with the <link
linkend="fdl-invariant">Invariant Sections</link> being LIST
THEIR TITLES, with the <link
linkend="fdl-cover-texts">Front-Cover Texts</link> being LIST,
and with the <link linkend="fdl-cover-texts">Back-Cover
Texts</link> being LIST. A copy of the license is included in
the section entitled <quote>GNU Free Documentation
License</quote>.
</para>
</blockquote>
<para>
If you have no <link linkend="fdl-invariant">Invariant
Sections</link>, write <quote>with no Invariant Sections</quote>
instead of saying which ones are invariant. If you have no
<link linkend="fdl-cover-texts">Front-Cover Texts</link>, write
<quote>no Front-Cover Texts</quote> instead of
<quote>Front-Cover Texts being LIST</quote>; likewise for <link
linkend="fdl-cover-texts">Back-Cover Texts</link>.
</para>
<para>
If your document contains nontrivial examples of program code,
we recommend releasing these examples in parallel under your
choice of free software license, such as the <ulink type="http"
url="http://www.gnu.org/copyleft/gpl.html"> GNU General Public
License</ulink>, to permit their use in free software.
</para>
</sect1>
</appendix>

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
help/C/figures/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

1023
help/C/index.docbook Normal file

File diff suppressed because it is too large Load Diff

76
help/C/legal.xml Normal file
View File

@ -0,0 +1,76 @@
<legalnotice id="legalnotice">
<para>
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation
License (GFDL), Version 1.1 or any later version published
by the Free Software Foundation with no Invariant Sections,
no Front-Cover Texts, and no Back-Cover Texts. You can find
a copy of the GFDL at this <ulink type="help"
url="ghelp:fdl">link</ulink> or in the file COPYING-DOCS
distributed with this manual.
</para>
<para> This manual is part of a collection of GNOME manuals
distributed under the GFDL. If you want to distribute this
manual separately from the collection, you can do so by
adding a copy of the license to the manual, as described in
section 6 of the license.
</para>
<para>
Many of the names used by companies to distinguish their
products and services are claimed as trademarks. Where those
names appear in any GNOME documentation, and the members of
the GNOME Documentation Project are made aware of those
trademarks, then the names are in capital letters or initial
capital letters.
</para>
<para>
DOCUMENT AND MODIFIED VERSIONS OF THE DOCUMENT ARE PROVIDED
UNDER THE TERMS OF THE GNU FREE DOCUMENTATION LICENSE
WITH THE FURTHER UNDERSTANDING THAT:
<orderedlist>
<listitem>
<para>DOCUMENT IS PROVIDED ON AN "AS IS" BASIS,
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
THAT THE DOCUMENT OR MODIFIED VERSION OF THE
DOCUMENT IS FREE OF DEFECTS MERCHANTABLE, FIT FOR
A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE
RISK AS TO THE QUALITY, ACCURACY, AND PERFORMANCE
OF THE DOCUMENT OR MODIFIED VERSION OF THE
DOCUMENT IS WITH YOU. SHOULD ANY DOCUMENT OR
MODIFIED VERSION PROVE DEFECTIVE IN ANY RESPECT,
YOU (NOT THE INITIAL WRITER, AUTHOR OR ANY
CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS
LICENSE. NO USE OF ANY DOCUMENT OR MODIFIED
VERSION OF THE DOCUMENT IS AUTHORIZED HEREUNDER
EXCEPT UNDER THIS DISCLAIMER; AND
</para>
</listitem>
<listitem>
<para>UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL
THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE),
CONTRACT, OR OTHERWISE, SHALL THE AUTHOR,
INITIAL WRITER, ANY CONTRIBUTOR, OR ANY
DISTRIBUTOR OF THE DOCUMENT OR MODIFIED VERSION
OF THE DOCUMENT, OR ANY SUPPLIER OF ANY OF SUCH
PARTIES, BE LIABLE TO ANY PERSON FOR ANY
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES OF ANY CHARACTER
INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS
OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
MALFUNCTION, OR ANY AND ALL OTHER DAMAGES OR
LOSSES ARISING OUT OF OR RELATING TO USE OF THE
DOCUMENT AND MODIFIED VERSIONS OF THE DOCUMENT,
EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF
THE POSSIBILITY OF SUCH DAMAGES.
</para>
</listitem>
</orderedlist>
</para>
</legalnotice>

298
hwe-support-status Executable file
View File

@ -0,0 +1,298 @@
#!/usr/bin/python
from __future__ import print_function
import optparse
import datetime
import distro_info
import os
import re
import subprocess
import sys
import apt
from UpdateManager.Core.utils import twrap, get_dist
# set locale early so that the subsequent imports have localized
# strings
import locale
try:
locale.setlocale(locale.LC_ALL, "")
except:
pass
from gettext import gettext as _
import gettext
gettext.textdomain("update-manager")
from HweSupportStatus.consts import (
Messages,
LTS_EOL_DATE,
HWE_EOL_DATE,
NEXT_LTS_DOT1_DATE,
)
# HWE stack with a short support period
HWE_UNSUPPORTED_BACKPORTS = (
"-lts-utopic",
"-lts-vivid",
"-lts-wily"
)
# from https://wiki.ubuntu.com/Kernel/LTSEnablementStack
UNSUPPORTED_KERNEL_IMAGE_REGEX = \
r'linux-image.*-(3\.16|3\.19|4\.2)(\.[0-9]+)?-.*'
# HWE stack with a long support period
HWE_SUPPORTED_BACKPORT = "-hwe-20.04"
SUPPORTED_KERNEL_IMAGE_REGEX = r'^$' # No fixed backported kernel yet
KERNEL_METAPKGS = (
"linux-generic",
"linux-image-generic",
"linux-signed-generic",
"linux-signed-image-generic",
)
XORG_METAPKGS = (
"xserver-xorg",
"libgl1-mesa-glx",
# LP: #1610434 - Ubuntu GNOME needed libwayland
"libwayland-egl1-mesa",
)
VBOX_METAPKGS = (
"virtualbox-guest-utils",
"virtualbox-guest-source"
)
METAPKGS = KERNEL_METAPKGS + XORG_METAPKGS + VBOX_METAPKGS
class Package:
"""A lightweight apt package """
def __init__(self, name, version, arch, foreign=False):
self.name = name
self.installed_version = version
self.arch = arch
self.foreign = foreign
def find_hwe_packages(installed_packages):
unsupported_hwe_packages = set()
supported_hwe_packages = set()
for pkg in installed_packages:
# metapackages and X are marked with the -lts-$distro string
for name in HWE_UNSUPPORTED_BACKPORTS:
if pkg.name.endswith(name):
unsupported_hwe_packages.add(pkg)
# The individual backported kernels have names like
# linux-image-3.11.0-17-generic
# so we match via a regexp.
#
# The linux-image-generic-lts-$distro metapkg has additional
# dependencies (like linux-firmware) so we can't just walk the
# dependency chain.
if re.match(UNSUPPORTED_KERNEL_IMAGE_REGEX, pkg.name):
unsupported_hwe_packages.add(pkg)
# SUPPORTED
if pkg.name.endswith(HWE_SUPPORTED_BACKPORT):
supported_hwe_packages.add(pkg)
if re.match(SUPPORTED_KERNEL_IMAGE_REGEX, pkg.name):
supported_hwe_packages.add(pkg)
return unsupported_hwe_packages, supported_hwe_packages
def is_unsupported_hwe_kernel_running(unsupported_hwe_package):
# kernels do not conflict with each other, so we need to check
# what version is actually running
running_kernel_ver = os.uname()[2]
# the running kernel without the abi or buildver
running_kernel_ver = running_kernel_ver.split("-")[0]
for pkg in unsupported_hwe_package:
if not pkg.name.startswith("linux-"):
continue
# we only care about the version, not abi or build
if pkg.installed_version.startswith(running_kernel_ver):
return True
return False
def is_unsupported_xstack_running(unsupported_hwe_packages):
# the HWE xstacks conflict with each other, so we can simply test
# for existence in the installed unsupported hwe packages
for pkg in unsupported_hwe_packages:
for xorg_meta in XORG_METAPKGS:
if pkg.name.startswith(xorg_meta):
return True
return False
def find_supported_replacement_hwe_packages(unsupported_hwe_packages,
installed_packages):
unsupported_metapkg_names = set()
replacement_names = set()
for metapkg in METAPKGS:
for unsupported_backport in HWE_UNSUPPORTED_BACKPORTS:
metapkg_name = metapkg + unsupported_backport
for pkg in unsupported_hwe_packages:
if pkg.name == metapkg_name:
replacement_name = metapkg + HWE_SUPPORTED_BACKPORT
if (replacement_name, pkg.arch) not in \
[(p.name, p.arch) for p in installed_packages]:
if pkg.foreign:
replacement_name += ':' + pkg.arch
replacement_names.add(replacement_name)
unsupported_metapkg_names.add(metapkg_name)
return unsupported_metapkg_names, replacement_names
def is_unsupported_hwe_running(unsupported_hwe_packages):
return (is_unsupported_hwe_kernel_running(unsupported_hwe_packages) or
is_unsupported_xstack_running(unsupported_hwe_packages))
def advice_about_hwe_status(unsupported_hwe_packages, supported_hwe_packages,
installed_packages, has_update_manager, today,
verbose):
unsupported_hwe_stack_running = is_unsupported_hwe_running(
unsupported_hwe_packages)
unsupported_hwe_metapkgs, supported_replacement_hwe = \
find_supported_replacement_hwe_packages(unsupported_hwe_packages,
installed_packages)
# we need the "-p" option until the next LTS point release is available
if today < NEXT_LTS_DOT1_DATE:
do_release_upgrade_option = "-p"
else:
do_release_upgrade_option = ""
if unsupported_hwe_stack_running:
if today < HWE_EOL_DATE:
s = Messages.HWE_SUPPORT_ENDS
else:
s = Messages.HWE_SUPPORT_HAS_ENDED
if has_update_manager:
print(s + Messages.UM_UPGRADE)
else:
# bug #1341320 - if no metapkg is left we need to show
# what is no longer supported
if supported_replacement_hwe:
print(s + Messages.APT_UPGRADE % (
do_release_upgrade_option,
" ".join(supported_replacement_hwe)))
else:
print(s + Messages.APT_SHOW_UNSUPPORTED % (
" ".join([pkg.name for pkg in unsupported_hwe_packages])))
# some unsupported package installed but not running and not superseded
# - this is worth reporting
elif (unsupported_hwe_packages and
not supported_hwe_packages and
not unsupported_hwe_stack_running):
s = _("""
You have packages from the Hardware Enablement Stack (HWE) installed that
are going out of support on %s.
""") % HWE_EOL_DATE
if has_update_manager:
print(s + Messages.UM_UPGRADE)
else:
print(s + Messages.APT_UPGRADE % (
do_release_upgrade_option,
" ".join(supported_replacement_hwe)))
elif supported_hwe_packages:
print(Messages.HWE_SUPPORTED)
elif verbose:
print(
_("You are not running a system with a Hardware Enablement Stack. "
"Your system is supported until %(month)s %(year)s.") % {
'month': LTS_EOL_DATE.strftime("%B"),
'year': LTS_EOL_DATE.year})
if __name__ == "__main__":
parser = optparse.OptionParser(description=_("Check HWE support status"))
parser.add_option('--quiet', action='store_true', default=False,
help="No output, exit code 10 on unsupported HWE "
"packages")
parser.add_option('--verbose', action='store_true', default=False,
help="more verbose output")
parser.add_option('--show-all-unsupported', action='store_true',
default=False,
help="Show unsupported HWE packages")
parser.add_option('--show-replacements', action='store_true',
default=False,
help="show what packages need installing to be "
"supported")
# hidden, only useful for testing
parser.add_option(
'--disable-hwe-check-semaphore-file',
default="/var/lib/update-notifier/disable-hwe-eol-messages",
help=optparse.SUPPRESS_HELP)
options, args = parser.parse_args()
if options.quiet:
nullfd = os.open(os.devnull, os.O_WRONLY)
os.dup2(nullfd, sys.stdout.fileno())
# Check to see if we are an LTS release
di = distro_info.UbuntuDistroInfo()
codename = get_dist()
lts = di.is_lts(codename)
if not lts:
if options.verbose:
print("Only LTS releases have Hardware Enablement stacks",
file=sys.stderr)
sys.exit(0)
# request from PSE to be able to disable the hwe check via a special
# semaphore file
HWE_CHECK_DISABLED_FILE = options.disable_hwe_check_semaphore_file
if os.path.exists(HWE_CHECK_DISABLED_FILE):
if options.verbose:
print("Forcefully disabled hwe-support-status via file %s" %
HWE_CHECK_DISABLED_FILE, file=sys.stderr)
sys.exit(0)
foreign_archs = set(subprocess.check_output(
['dpkg', '--print-foreign-architectures'],
universal_newlines=True).split())
# do the actual check
installed_packages = set()
today = datetime.date.today()
tagf = apt.apt_pkg.TagFile("/var/lib/dpkg/status")
while tagf.step():
if tagf.section.find("Status", "") != "install ok installed":
continue
pkgname = tagf.section.find("Package")
version = tagf.section.find("Version")
arch = tagf.section.find("Architecture")
foreign = arch in foreign_archs
installed_packages.add(Package(pkgname, version, arch, foreign))
has_update_manager = "update-manager" in [
pkg.name for pkg in installed_packages]
unsupported_hwe_packages, supported_hwe_packages = find_hwe_packages(
installed_packages)
if options.show_all_unsupported:
if today > HWE_EOL_DATE:
print(twrap(" ".join([
pkg.foreign and pkg.name + ':' + pkg.arch or pkg.name
for pkg in unsupported_hwe_packages])))
if options.show_replacements:
unsupported, replacements = find_supported_replacement_hwe_packages(
unsupported_hwe_packages, installed_packages)
if replacements:
print(" ".join(replacements))
if not options.show_all_unsupported and not options.show_replacements:
advice_about_hwe_status(
unsupported_hwe_packages, supported_hwe_packages,
installed_packages, has_update_manager, today,
options.verbose)
if is_unsupported_hwe_running(unsupported_hwe_packages) and \
today > HWE_EOL_DATE:
sys.exit(10)
sys.exit(0)

24
janitor/__init__.py Normal file
View File

@ -0,0 +1,24 @@
# Copyright (C) 2008-2012 Canonical, Ltd.
#
# The following license applies to all files (including the icons):
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
# This is a namespace package.
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

View File

@ -0,0 +1,7 @@
============================
NEWS for janitor.plugincore
============================
1.0 (2012-XX-XX)
================
* Initial release since refactoring into a separate package.

Some files were not shown because too many files have changed in this diff Show More