init
|
@ -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>
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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()
|
|
@ -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!
|
||||
|
||||
|
|
@ -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.
|
|
@ -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
|
|
@ -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()
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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")
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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 couldn’t 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
|
|
@ -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'
|
|
@ -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()
|
|
@ -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")
|
|
@ -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))
|
|
@ -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 $< $@
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 you’re 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">It’s 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>
|
|
@ -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>
|
|
@ -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.
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
9
|
|
@ -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.
|
|
@ -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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
README
|
||||
TODO
|
||||
AUTHORS
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
export PYBUILD_INTERPRETERS=python3
|
||||
|
||||
%:
|
||||
dh $@ --with=python3 --buildsystem=pybuild
|
|
@ -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"))
|
|
@ -0,0 +1,2 @@
|
|||
Tests: nose-tests
|
||||
Depends: @, aptdaemon, pycodestyle, pyflakes3, python3-mock, python3-nose, xvfb
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
xvfb-run nosetests3
|
|
@ -0,0 +1,3 @@
|
|||
var/lib/update-manager
|
||||
var/lib/update-notifier
|
||||
usr/bin
|
|
@ -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/
|
|
@ -0,0 +1 @@
|
|||
debian/tmp/usr/lib/python3*/dist-packages/UpdateManager/check-meta-release.py
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
debian/tmp/usr/share/man/man8/update-manager.8
|
|
@ -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>
|
||||
|
||||
|
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 31 KiB |
|
@ -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>
|
||||
|
|
@ -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)
|
|
@ -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__)
|
|
@ -0,0 +1,7 @@
|
|||
============================
|
||||
NEWS for janitor.plugincore
|
||||
============================
|
||||
|
||||
1.0 (2012-XX-XX)
|
||||
================
|
||||
* Initial release since refactoring into a separate package.
|