mirror of
https://github.com/clearlinux/swupd-client.git
synced 2026-06-15 18:25:47 +00:00
Initial commit
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
*.kdev4
|
||||
*.lo
|
||||
*.o
|
||||
*.trs
|
||||
*~
|
||||
.cproject
|
||||
.deps
|
||||
.dirstamp
|
||||
.libs/
|
||||
.project
|
||||
DEADJOE
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
ar-lib
|
||||
autom4te.cache/
|
||||
compile
|
||||
config.*
|
||||
configure
|
||||
cscope.*
|
||||
data/check-update.service
|
||||
data/check-update.timer
|
||||
depcomp
|
||||
functional-tests.log
|
||||
fuzzout/
|
||||
install-sh
|
||||
kw*
|
||||
libswupd*
|
||||
libtool
|
||||
ltmain.sh
|
||||
m4/
|
||||
missing
|
||||
regression/*.out
|
||||
stamp-h1
|
||||
swupd
|
||||
swupd-client*tar.gz
|
||||
swupd_*
|
||||
tap-driver.sh
|
||||
test-suite.log
|
||||
test/functional/*/*/*/*/*.tar
|
||||
test/functional/*/*/*/*/*/*.tar
|
||||
@@ -0,0 +1,353 @@
|
||||
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 2 or later 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/>.
|
||||
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, 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.
|
||||
|
||||
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 Lesser 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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General
|
||||
Public License instead of this License.
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
EXTRA_DIST = COPYING findstatic.pl
|
||||
|
||||
AM_CFLAGS = -fPIC -Iinclude/ -O2 -g -Wall -W -Wformat-security -D_FORTIFY_SOURCE=2 -fno-common -std=gnu99
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
bin_PROGRAMS = swupd
|
||||
|
||||
swupd_SOURCES = \
|
||||
src/swupd.c \
|
||||
$(swupd_hashdump_SOURCES) \
|
||||
$(swupd_update_SOURCES) \
|
||||
$(swupd_check_update_SOURCES) \
|
||||
$(swupd_verify_SOURCES) \
|
||||
$(clr_bundle_add_SOURCES) \
|
||||
$(clr_bundle_rm_SOURCES)
|
||||
|
||||
check_PROGRAMS = \
|
||||
swupd_bsdiff_bench \
|
||||
swupd_hashtest \
|
||||
swupd_listtest \
|
||||
swupd_fuzz \
|
||||
swupd_locktest \
|
||||
swupd_sig_verifytest
|
||||
|
||||
SWUPD_COMMON_SOURCES = \
|
||||
src/curl.c \
|
||||
src/delta.c \
|
||||
src/download.c \
|
||||
src/filedesc.c \
|
||||
src/hash.c \
|
||||
src/helpers.c \
|
||||
src/heuristics.c \
|
||||
src/list.c \
|
||||
src/lock.c \
|
||||
src/manifest.c \
|
||||
src/packs.c \
|
||||
src/signature.c \
|
||||
src/staging.c \
|
||||
src/stats.c \
|
||||
src/subscriptions.c \
|
||||
src/update.c \
|
||||
src/version.c \
|
||||
src/xattrs.c \
|
||||
src/globals.c \
|
||||
src/scripts.c \
|
||||
src/bundle.c
|
||||
|
||||
lib_LTLIBRARIES = libswupd.la
|
||||
libswupd_la_SOURCES = $(SWUPD_COMMON_SOURCES)
|
||||
libswupd_la_LIBADD = $(SWUPD_COMPRESSION_LIBS) $(openssl_LIBS) $(curl_LIBS) $(bsdiff_LIBS)
|
||||
# Library version changes according to the libtool convention:
|
||||
# http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
|
||||
LIBSWUPD_CURRENT=1
|
||||
LIBSWUPD_REVISION=0
|
||||
LIBSWUPD_AGE=0
|
||||
libswupd_la_LDFLAGS = \
|
||||
-version-info $(LIBSWUPD_CURRENT):$(LIBSWUPD_REVISION):$(LIBSWUPD_AGE)
|
||||
|
||||
swupd_update_SOURCES = src/main.c
|
||||
swupd_verify_SOURCES = src/verify.c
|
||||
swupd_check_update_SOURCES = src/check_update.c
|
||||
|
||||
clr_bundle_add_SOURCES = src/clr_bundle_add.c
|
||||
clr_bundle_rm_SOURCES = src/clr_bundle_rm.c
|
||||
|
||||
swupd_hashdump_SOURCES = src/hashdump.c
|
||||
|
||||
swupd_bsdiff_bench_SOURCES = test/bsdiff_bench.c
|
||||
swupd_hashtest_SOURCES = test/hash_test.c
|
||||
swupd_listtest_SOURCES = test/listtest.c
|
||||
swupd_fuzz_SOURCES = test/fuzz.c
|
||||
swupd_locktest_SOURCES = test/locktest.c
|
||||
swupd_sig_verifytest_SOURCES = test/signature_verify_test.c
|
||||
|
||||
|
||||
AM_CPPFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include
|
||||
SWUPD_COMPRESSION_LIBS = $(lzma_LIBS) $(zlib_LIBS) $(bzip2_LIBS)
|
||||
SWUPD_CORE_LIBS = libswupd.la ${curl_LIBS} $(openssl_LIBS) $(SWUPD_COMPRESSION_LIBS) $(bsdiff_LIBS)
|
||||
|
||||
swupd_LDADD = $(SWUPD_CORE_LIBS) $(pthread_LIBS)
|
||||
|
||||
swupd_bsdiff_bench_LDADD = $(SWUPD_CORE_LIBS)
|
||||
swupd_hashtest_LDADD = $(SWUPD_CORE_LIBS) $(pthread_LIBS)
|
||||
swupd_listtest_LDADD = $(SWUPD_CORE_LIBS)
|
||||
swupd_fuzz_LDADD = $(SWUPD_CORE_LIBS) $(pthread_LIBS)
|
||||
swupd_locktest_LDADD = $(SWUPD_CORE_LIBS) $(pthread_LIBS)
|
||||
swupd_sig_verifytest_LDADD = $(SWUPD_CORE_LIBS)
|
||||
|
||||
noinst_HEADERS = $(top_srcdir)/include/*
|
||||
|
||||
swupdcertsdir = @swupdcertsdir@
|
||||
SWUPD_CERTS = certs/157753a5.0 \
|
||||
certs/425b0f6b.0 \
|
||||
certs/425b0f6b.key \
|
||||
certs/8d28ae65.0 \
|
||||
certs/d6325660.0 \
|
||||
certs/d6325660.1
|
||||
swupdcerts_DATA = $(SWUPD_CERTS)
|
||||
dist_swupdcerts_DATA = $(SWUPD_CERTS)
|
||||
|
||||
EXTRA_DIST += \
|
||||
data/check-update.service \
|
||||
data/check-update.timer
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdunitdir)
|
||||
|
||||
systemdunitdir = @SYSTEMD_UNITDIR@
|
||||
|
||||
systemdunit_DATA = \
|
||||
data/check-update.service \
|
||||
data/check-update.timer
|
||||
|
||||
systemdunit-install-local:
|
||||
mkdir -p $(DESTDIR)$(systemdunitdir)/multi-user.target.wants/
|
||||
ln -sf ../check-update.timer $(DESTDIR)$(systemdunitdir)/multi-user.target.wants/check-update.timer
|
||||
install-data-local: systemdunit-install-local
|
||||
|
||||
systemdunit-uninstall-local:
|
||||
rm -f $(DESTDIR)$(systemdunitdir)/multi-user.target.wants/check-update.timer
|
||||
uninstall-local: systemdunit-uninstall-local
|
||||
|
||||
distclean-local:
|
||||
rm -rf aclocal.m4 ar-lib autom4te.cache config.guess config.h.in config.h.in~ config.sub configure depcomp install-sh ltmain.sh m4 Makefile.in missing compile
|
||||
|
||||
install-exec-hook:
|
||||
perl findstatic.pl */*.o | grep -v Checking ||:
|
||||
|
||||
|
||||
TEST_EXTENSIONS = .py
|
||||
|
||||
if ENABLE_TESTS
|
||||
tap_driver = env AM_TAP_AWK='$(AWK)' $(SHELL) \
|
||||
$(top_srcdir)/tap-driver.sh
|
||||
|
||||
LOG_DRIVER = $(tap_driver)
|
||||
PY_LOG_DRIVER = $(tap_driver)
|
||||
|
||||
TESTS = $(dist_check_SCRIPTS)
|
||||
|
||||
dist_check_SCRIPTS = \
|
||||
functional-tests.py
|
||||
endif
|
||||
|
||||
release:
|
||||
@git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \
|
||||
if [ "$$?" -eq 0 ]; then \
|
||||
echo "Error: Release for $(PACKAGE_VERSION) already exists."; \
|
||||
echo "Bump version in configure.ac before releasing."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git tag -a -m "$(PACKAGE_NAME) release $(PACKAGE_VERSION)" v$(PACKAGE_VERSION)
|
||||
@printf "\nNew release $(PACKAGE_VERSION) tagged!\n\n"
|
||||
@@ -0,0 +1,5 @@
|
||||
The swupd-client package provides a reference implementation of a software
|
||||
update client which performs file level updates of an OS, preferentially
|
||||
using binary deltas whenever possible for efficiency under an assumption
|
||||
that the OS develops with a release process aimed at rapidly deploying
|
||||
small incremental changes.
|
||||
@@ -0,0 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
|
||||
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
|
||||
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
|
||||
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
|
||||
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
|
||||
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
|
||||
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
|
||||
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
|
||||
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
|
||||
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
|
||||
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
|
||||
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
|
||||
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
|
||||
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
|
||||
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
|
||||
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
|
||||
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
|
||||
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
|
||||
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
|
||||
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
|
||||
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
|
||||
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
|
||||
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,40 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHEzCCBfugAwIBAgIQAfSnvLCU184JB1YkkQ8KkDANBgkqhkiG9w0BAQsFADCB
|
||||
kDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxNjA0BgNV
|
||||
BAMTLUNPTU9ETyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD
|
||||
QTAeFw0xNTA0MzAwMDAwMDBaFw0xODA0MjkyMzU5NTlaMIGqMSEwHwYDVQQLExhE
|
||||
b21haW4gQ29udHJvbCBWYWxpZGF0ZWQxNzA1BgNVBAsTLklzc3VlZCB0aHJvdWdo
|
||||
IEludGVsIENvcnBvcmF0aW9uIEUtUEtJIE1hbmFnZXIxKjAoBgNVBAsTIUNPTU9E
|
||||
TyBTU0wgVW5pZmllZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAxMXZG93bmxvYWQu
|
||||
Y2xlYXJsaW51eC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC5
|
||||
xkCgSShYdkDdT9LsWW3bPHDxtTyDT4fIucCyE+npRPP5IdFYHFbeXIUsiyKm/hiw
|
||||
1egvsczSQcc81IsNUYC/2qKqiA3E+jIYXnP/CXRHVBwXfBvajUjbRiiyY+xMWc5k
|
||||
tRsZ9+eYysH/9YFcLkrRiqBrHl2pzZWsUcZ/ehe5RUFg9Pb2y3Qsc5+TJmS6Bf0l
|
||||
Z5su4X93GCFPATu6Yp5IE20YM8E593egD4tZq1Os/+3TNr9J+aBIjQ0d+AMPvWdF
|
||||
cLX6ax1XKZ/6V40X8yWxb2wmkquogoWjdNGGLYQAFludTCpyTRoXX3QiS3XVDzu/
|
||||
Yq32/hOA4uV5Un2dcTmjfb/0EhcKI2YToAEGr5K0rwXDbUJsxJcJxqkya71WdJ4L
|
||||
3Xy//4JEXuol6XV4YTonclbRDHL/ZYsa4VHp1ziADaDCvM+Z1mOE8K0UgTMQnqy7
|
||||
c/9fcOEhDnBoHiiolmcyISgMM3RJSiFzDTh8BnKAX2eDrb6/xMSROeFs1Eo0USd0
|
||||
N4/pNm31fekrs/nD69JUi2rVCHB1dZhlbQ/wh233nzX1z/L39+HoQeXtUKcamcav
|
||||
LVVv+uOiYIG96B6hZ5CmsinWET01yn1mQx82EvDC8lZIxINp5fpPP4Syvfgukf8V
|
||||
2XtxMcJstKtRm1evzO3Aa0TJwTRUD/5Mq9EXvF2zuwIDAQABo4ICSzCCAkcwHwYD
|
||||
VR0jBBgwFoAUkK9qOpRaC9iQ6hJWc99DtDoo2ucwHQYDVR0OBBYEFFEbI3dnKS70
|
||||
5opGdwihGFg4dNCeMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1Ud
|
||||
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysGAQQBsjEB
|
||||
AgIHMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BT
|
||||
MAgGBmeBDAECATBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vY3JsLmNvbW9kb2Nh
|
||||
LmNvbS9DT01PRE9SU0FEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3Js
|
||||
MIGFBggrBgEFBQcBAQR5MHcwTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jcnQuY29tb2Rv
|
||||
Y2EuY29tL0NPTU9ET1JTQURvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5j
|
||||
cnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTCBmAYDVR0R
|
||||
BIGQMIGNghdkb3dubG9hZC5jbGVhcmxpbnV4Lm9yZ4IYZGVidWdpbmZvLmNsZWFy
|
||||
bGludXgub3JnghRwYWNrcy5jbGVhcmxpbnV4Lm9yZ4ITcGtncy5jbGVhcmxpbnV4
|
||||
Lm9yZ4IVdXBkYXRlLmNsZWFybGludXgub3JnghZ2ZXJzaW9uLmNsZWFybGludXgu
|
||||
b3JnMA0GCSqGSIb3DQEBCwUAA4IBAQAT6AZcA5nNl7J8Aeia6t2pwHOIUx8dsJcJ
|
||||
yK6dDm8C05hfGtg/IMYxE2zKQjhWK3fM4ISdsizRja5IctXBeYxXV22tk+khXPVU
|
||||
m2XgspsN7TLHxlHvMaAoFgC7pcNOET8wPi6yPgjrsoqnjOhJMgADDB4zT+sUApoQ
|
||||
SmZ23xwBm4B2MRmIdgRHzH362W7mxSbpTIF00DGuUBBxoj1xt8PY03q8DvlVxlYn
|
||||
weI/QE+ws8Qe/85gYgFFRjrX4SELZS1qN0A/3IYnVuwosrLK40fRSg1gqCCvwRI3
|
||||
KdfW/WchUc8XfB+AGjaWKGvf3BpB3vt9CnGtRwmEj6MZgzvYhmcy
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAucZAoEkoWHZA3U/S7Flt
|
||||
2zxw8bU8g0+HyLnAshPp6UTz+SHRWBxW3lyFLIsipv4YsNXoL7HM0kHHPNSLDVGA
|
||||
v9qiqogNxPoyGF5z/wl0R1QcF3wb2o1I20YosmPsTFnOZLUbGffnmMrB//WBXC5K
|
||||
0Yqgax5dqc2VrFHGf3oXuUVBYPT29st0LHOfkyZkugX9JWebLuF/dxghTwE7umKe
|
||||
SBNtGDPBOfd3oA+LWatTrP/t0za/SfmgSI0NHfgDD71nRXC1+msdVymf+leNF/Ml
|
||||
sW9sJpKrqIKFo3TRhi2EABZbnUwqck0aF190Ikt11Q87v2Kt9v4TgOLleVJ9nXE5
|
||||
o32/9BIXCiNmE6ABBq+StK8Fw21CbMSXCcapMmu9VnSeC918v/+CRF7qJel1eGE6
|
||||
J3JW0Qxy/2WLGuFR6dc4gA2gwrzPmdZjhPCtFIEzEJ6su3P/X3DhIQ5waB4oqJZn
|
||||
MiEoDDN0SUohcw04fAZygF9ng62+v8TEkTnhbNRKNFEndDeP6TZt9X3pK7P5w+vS
|
||||
VItq1QhwdXWYZW0P8Idt95819c/y9/fh6EHl7VCnGpnGry1Vb/rjomCBvegeoWeQ
|
||||
prIp1hE9Ncp9ZkMfNhLwwvJWSMSDaeX6Tz+Esr34LpH/Fdl7cTHCbLSrUZtXr8zt
|
||||
wGtEycE0VA/+TKvRF7xds7sCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,35 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
|
||||
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
|
||||
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
|
||||
MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
|
||||
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
|
||||
Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
|
||||
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
|
||||
bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
|
||||
Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
|
||||
ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
|
||||
UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
|
||||
c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
|
||||
MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
|
||||
30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
|
||||
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
|
||||
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
|
||||
bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
|
||||
AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
|
||||
T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
|
||||
ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
|
||||
mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
|
||||
e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
|
||||
P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
|
||||
dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
|
||||
2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
|
||||
V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
|
||||
HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
|
||||
j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
|
||||
0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
|
||||
lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
|
||||
+AZxAeKCINT+b72x
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,34 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
|
||||
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
|
||||
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
|
||||
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
|
||||
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
|
||||
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
|
||||
6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
|
||||
pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
|
||||
9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
|
||||
/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
|
||||
Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
|
||||
+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
|
||||
qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
|
||||
SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
|
||||
u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
|
||||
Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
|
||||
crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
|
||||
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
|
||||
/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
|
||||
wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
|
||||
4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
|
||||
2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
|
||||
FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
|
||||
CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
|
||||
boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
|
||||
jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
|
||||
S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
|
||||
QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
|
||||
0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
|
||||
NVOFBkpdn627G190
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
|
||||
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
|
||||
ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
|
||||
eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
|
||||
gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
||||
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
|
||||
VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
|
||||
AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
|
||||
2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
|
||||
ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
|
||||
4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
|
||||
m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
|
||||
vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
|
||||
8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
|
||||
IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
|
||||
KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
|
||||
GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
|
||||
s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
|
||||
JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
|
||||
AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
|
||||
MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
|
||||
bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
|
||||
Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
|
||||
zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
|
||||
Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
|
||||
Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
|
||||
B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
|
||||
PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
|
||||
pu/xO28QOG8=
|
||||
-----END CERTIFICATE-----
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
PKG="swupd-client"
|
||||
LIB="libswupd"
|
||||
|
||||
OLDVERSION=$(git tag -l 'swupd-client*' | sort -t . -k 1,1n -k 2,2n | tail -1)
|
||||
NEWVERSION="HEAD"
|
||||
|
||||
OLDCOMMIT=$(git rev-list -n 1 --abbrev-commit ${OLDVERSION})
|
||||
NEWCOMMIT=$(git rev-list -n 1 --abbrev-commit ${NEWVERSION})
|
||||
|
||||
do_build() {
|
||||
local commit=$1
|
||||
echo "Building ${PKG} at commit ${commit}..."
|
||||
git archive \
|
||||
-o "${TMPDIR}/${PKG}-${commit}.tar.gz" \
|
||||
--prefix="${PKG}-${commit}/" ${commit}
|
||||
(
|
||||
pushd "${TMPDIR}"
|
||||
tar xf ${PKG}-${commit}.tar.gz
|
||||
pushd ${PKG}-${commit}
|
||||
autoreconf -fi
|
||||
./configure
|
||||
make
|
||||
popd
|
||||
popd
|
||||
) &> /dev/null
|
||||
}
|
||||
|
||||
create_xml_desc() {
|
||||
local commit=$1
|
||||
cat > ${TMPDIR}/${PKG}-${commit}.xml << EOF
|
||||
<version>
|
||||
${commit}
|
||||
</version>
|
||||
<headers>
|
||||
${TMPDIR}/${PKG}-${commit}/include/swupd.h
|
||||
</headers>
|
||||
<libs>
|
||||
${TMPDIR}/${PKG}-${commit}/.libs
|
||||
</libs>
|
||||
EOF
|
||||
}
|
||||
|
||||
bump_current() {
|
||||
local current=$(grep '^LIBSWUPD_CURRENT' Makefile.am | cut -d'=' -f2)
|
||||
current=$(expr $current + 1)
|
||||
sed -i "s/^\(LIBSWUPD_CURRENT=\).*$/\1$current/" Makefile.am
|
||||
# Note: the REVISION and AGE fields are not modified at all right now, but
|
||||
# when logic is implemented in the future according to the libtool algorithm,
|
||||
# they must be reset to zero, as below.
|
||||
sed -i "s/^\(LIBSWUPD_REVISION=\).*$/\10/" Makefile.am
|
||||
sed -i "s/^\(LIBSWUPD_AGE=\).*$/\10/" Makefile.am
|
||||
git add Makefile.am
|
||||
git commit -s -m "Bump $LIB major version to $current"
|
||||
}
|
||||
|
||||
do_build ${OLDCOMMIT}
|
||||
do_build ${NEWCOMMIT}
|
||||
|
||||
create_xml_desc ${OLDCOMMIT}
|
||||
create_xml_desc ${NEWCOMMIT}
|
||||
|
||||
abi-compliance-checker \
|
||||
-lib ${LIB} \
|
||||
-report-path ${TMPDIR}/reports/${OLDCOMMIT}_to_${NEWCOMMIT}/report.xml \
|
||||
-log1-path ${TMPDIR}/logs/${OLDCOMMIT}.log \
|
||||
-log2-path ${TMPDIR}/logs/${NEWCOMMIT}.log \
|
||||
-old ${TMPDIR}/${PKG}-${OLDCOMMIT}.xml \
|
||||
-new ${TMPDIR}/${PKG}-${NEWCOMMIT}.xml \
|
||||
-xml
|
||||
|
||||
ret=$?
|
||||
|
||||
if [ $ret -eq 0 ]; then
|
||||
# TODO: should bump AGE and CURRENT for backward-compatible library changes,
|
||||
# and REVISION for any library code changes.
|
||||
echo "No compatibility errors found!"
|
||||
elif [ $ret -eq 1 ]; then
|
||||
echo "Incompatibilities found; bumping lib version."
|
||||
bump_current
|
||||
elif [ $ret -gt 1 ]; then
|
||||
echo "Problem running the ABI check tool: error code ${ret}."
|
||||
fi
|
||||
|
||||
exit $ret
|
||||
|
||||
# vi: ts=8 sw=2 sts=2 et tw=80
|
||||
@@ -0,0 +1,93 @@
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.66])
|
||||
AC_INIT(swupd-client, 3.0.0, timothy.c.pepper@linux.intel.com)
|
||||
AM_PROG_AR
|
||||
LT_INIT
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
AC_LANG(C)
|
||||
AC_ARG_WITH([swupdcertsdir],
|
||||
[AS_HELP_STRING([--with-swupdcertsdir=DIR], [swupdcertsdir files])],
|
||||
[swupdcertsdir=$withval],
|
||||
[swupdcertsdir="/usr/share/clear/update-ca"])
|
||||
AC_SUBST([swupdcertsdir], [$swupdcertsdir])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
PKG_CHECK_MODULES([bsdiff], [bsdiff])
|
||||
PKG_CHECK_MODULES([lzma], [liblzma])
|
||||
PKG_CHECK_MODULES([zlib], [zlib])
|
||||
AC_ARG_ENABLE(
|
||||
bzip2,
|
||||
AS_HELP_STRING([--disable-bzip2],[Do not use bzip2 compression (uses bzip2 by default)]),
|
||||
AC_DEFINE(SWUPD_WITHOUT_BZIP2,1,[Do not use bzip2 compression]) ,
|
||||
AC_DEFINE(SWUPD_WITH_BZIP2,1,[Use bzip2 compression])
|
||||
AC_CHECK_LIB([bz2], [BZ2_bzBuffToBuffCompress], [], [AC_MSG_ERROR([the libbz2 library is missing])])
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[tests],
|
||||
[AS_HELP_STRING([--disable-tests], [Do not enable unit or functional test framework (enabled by default)])]
|
||||
)
|
||||
|
||||
AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
|
||||
[path to systemd system service dir @<:@default=/usr/lib/systemd/system@:>@]), [unitpath=${withval}],
|
||||
[unitpath="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"])
|
||||
test -z "${unitpath}" && unitpath=/usr/lib/systemd/system
|
||||
AC_SUBST(SYSTEMD_UNITDIR, [${unitpath}])
|
||||
|
||||
AS_IF([test "$enable_tests" != "no"], [
|
||||
PKG_CHECK_MODULES([check], [check >= 0.9.12])
|
||||
AC_PATH_PROG([have_python3], [python3])
|
||||
AS_IF([test -z "${have_python3}"], [
|
||||
AC_MSG_ERROR([Must have Python 3 installed to run functional tests])
|
||||
])
|
||||
])
|
||||
AM_CONDITIONAL([ENABLE_TESTS], [test "$enable_tests" != "no"])
|
||||
|
||||
PKG_CHECK_MODULES([curl], [libcurl])
|
||||
PKG_CHECK_MODULES([openssl], [libcrypto >= 0.9.8])
|
||||
AC_CHECK_LIB([pthread], [pthread_create])
|
||||
AC_CHECK_PROGS(TAR, tar)
|
||||
|
||||
# default to Linux rootfs build
|
||||
enable_linux_rootfs_build="yes"
|
||||
|
||||
# document all options for build variants
|
||||
## (1) build variants
|
||||
AH_TEMPLATE([SWUPD_LINUX_ROOTFS],[Enable Linux rootfs build variant])
|
||||
## (2) variant features
|
||||
AH_TEMPLATE([SWUPD_WITH_BINDMNTS],[cope with bind mounts over rootfs])
|
||||
AH_TEMPLATE([SWUPD_WITH_SELINUX],[handle selinux attributes])
|
||||
## (3) variant extra options
|
||||
AH_TEMPLATE([MOUNT_POINT],[The mount point])
|
||||
AH_TEMPLATE([STATE_DIR],[The state directory for swupd content])
|
||||
AH_TEMPLATE([LOG_DIR],[Directory for swupd log files])
|
||||
AH_TEMPLATE([LOCK_DIR],[Directory for lock file])
|
||||
AH_TEMPLATE([BUNDLES_DIR],[Directory to use for bundles])
|
||||
AH_TEMPLATE([STAGING_SUBVOL],[Subvolume for staging content])
|
||||
AH_TEMPLATE([UPDATE_CA_CERTS_PATH],[Location of CA certificates])
|
||||
AH_TEMPLATE([SIGNATURE_CA_CERT],[CA certificate to use])
|
||||
AH_TEMPLATE([MOTD_FILE],[motd file path])
|
||||
|
||||
if test "$enable_linux_rootfs_build" = "yes"; then
|
||||
AC_DEFINE([SWUPD_LINUX_ROOTFS],1)
|
||||
AC_DEFINE([MOUNT_POINT],["/"])
|
||||
AC_DEFINE([STATE_DIR],["/var/lib/swupd"])
|
||||
AC_DEFINE([LOG_DIR],["/var/log/swupd"])
|
||||
AC_DEFINE([LOCK_DIR],["/run/lock"])
|
||||
AC_DEFINE([BUNDLES_DIR],["/usr/share/clear/bundles"])
|
||||
AC_DEFINE([STAGING_SUBVOL],["/"])
|
||||
AC_DEFINE([UPDATE_CA_CERTS_PATH],["/usr/share/clear/update-ca"])
|
||||
AC_DEFINE([SIGNATURE_CA_CERT],["test-do-not-ship-R0-0.pem"])
|
||||
AC_DEFINE([MOTD_FILE],["/usr/lib/motd.d/001-new-release"])
|
||||
else
|
||||
AC_MSG_ERROR([Unknown build variant])
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile data/check-update.service data/check-update.timer])
|
||||
AC_REQUIRE_AUX_FILE([tap-driver.sh])
|
||||
AC_OUTPUT
|
||||
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Check for new OS version
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@prefix@/bin/swupd check-update
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Periodic check for new OS version
|
||||
|
||||
[Timer]
|
||||
OnBootSec=5m
|
||||
OnUnitActiveSec=1h
|
||||
AccuracySec=20m
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/perl -w
|
||||
# find a list of fns and variables in the code that could be static
|
||||
# usually called with something like this:
|
||||
# findstatic.pl `find . -name "*.o"`
|
||||
# Andrew Tridgell <tridge@samba.org>
|
||||
|
||||
use strict;
|
||||
|
||||
# use nm to find the symbols
|
||||
my($saved_delim) = $/;
|
||||
undef $/;
|
||||
my($syms) = `nm -o @ARGV`;
|
||||
$/ = $saved_delim;
|
||||
|
||||
my(@lines) = split(/\n/s, $syms);
|
||||
|
||||
my(%def);
|
||||
my(%undef);
|
||||
my(%stype);
|
||||
|
||||
my(%typemap) = (
|
||||
"T" => "function",
|
||||
"C" => "uninitialised variable",
|
||||
"D" => "initialised variable"
|
||||
);
|
||||
|
||||
|
||||
# parse the symbols into defined and undefined
|
||||
for (my($i)=0; $i <= $#lines; $i++) {
|
||||
my($line) = $lines[$i];
|
||||
if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) {
|
||||
my($fname) = $1;
|
||||
my($symbol) = $3;
|
||||
push(@{$def{$fname}}, $symbol);
|
||||
$stype{$symbol} = $2;
|
||||
}
|
||||
if ($line =~ /(.*):\s* U (.*)/) {
|
||||
my($fname) = $1;
|
||||
my($symbol) = $2;
|
||||
push(@{$undef{$fname}}, $symbol);
|
||||
}
|
||||
}
|
||||
|
||||
# look for defined symbols that are never referenced outside the place they
|
||||
# are defined
|
||||
foreach my $f (keys %def) {
|
||||
print "Checking $f\n";
|
||||
my($found_one) = 0;
|
||||
foreach my $s (@{$def{$f}}) {
|
||||
my($found) = 0;
|
||||
foreach my $f2 (keys %undef) {
|
||||
if ($f2 ne $f) {
|
||||
foreach my $s2 (@{$undef{$f2}}) {
|
||||
if ($s2 eq $s) {
|
||||
$found = 1;
|
||||
$found_one = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($found == 0) {
|
||||
my($t) = $typemap{$stype{$s}};
|
||||
if ($s eq 'main') {
|
||||
# special case: main program
|
||||
$found_one = 1;
|
||||
} else {
|
||||
print " '$s' is unique to $f, should be static? ($t)\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($found_one == 0) {
|
||||
print " all symbols in '$f' are unused (main program?)\n";
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+305
@@ -0,0 +1,305 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# swupd functional test driver
|
||||
#
|
||||
# Things to test:
|
||||
# update
|
||||
# update --status
|
||||
# update --download
|
||||
# verify
|
||||
# verify --fix
|
||||
# verify --install
|
||||
# bundle add
|
||||
# bundle add --list
|
||||
# bundle remove
|
||||
# check-update
|
||||
#
|
||||
# Out of scope (for now at least):
|
||||
# Files going into /boot (integration test)
|
||||
#
|
||||
# Current issues with not running as root:
|
||||
# Use of system /var
|
||||
# chown of files to root for hash comparison
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from tap import TAPTestRunner
|
||||
|
||||
COMMAND_MAP = {'bundleadd': 'bundle-add',
|
||||
'bundleremove': 'bundle-remove',
|
||||
'checkupdate': 'check-update',
|
||||
'update': 'update',
|
||||
'verify': 'verify'}
|
||||
PATH_PREFIX = 'test/functional'
|
||||
PORT = '8089'
|
||||
|
||||
|
||||
def path_from_name(name, path_type):
|
||||
"""Based on a test class name and path_type, create a path"""
|
||||
# Basic conversion looks like:
|
||||
# class name == 'subfunction' + '_' + 'test_name'
|
||||
# Paths will use '-' instead of '_'
|
||||
# So conversion will split on '_' and the first element will be the
|
||||
# subfunction test directory and the rest will be a specific test
|
||||
# folder within that subfunction
|
||||
# So the checkupdate_version_match class would use the
|
||||
# test/functional/checkupdate/version-match/ path
|
||||
# with path_type determining the base folder
|
||||
type_map = {'web': 'web-dir', 'target': 'target-dir'}
|
||||
folders = name.split('_')
|
||||
subfunction_path = folders[0]
|
||||
test_dir = ('-').join(folders[1:])
|
||||
if path_type == 'plain':
|
||||
return os.path.join(PATH_PREFIX, subfunction_path, test_dir)
|
||||
else:
|
||||
return os.path.join(PATH_PREFIX, subfunction_path, test_dir,
|
||||
type_map[path_type])
|
||||
|
||||
|
||||
def http_server(func):
|
||||
"""Create an http server for the lifetime of a test function"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args):
|
||||
"""http_server wrapper"""
|
||||
web_path = path_from_name(args[0].__class__.__name__, 'web')
|
||||
httpd = subprocess.Popen(['python3', '-m', 'http.server', PORT],
|
||||
cwd=web_path,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
# Stupid but give the web server a chance to run
|
||||
time.sleep(.1)
|
||||
try:
|
||||
func(*args)
|
||||
finally:
|
||||
httpd.kill()
|
||||
return wrapper
|
||||
|
||||
|
||||
def with_command(func):
|
||||
"""Add the command variable to the lexical environment of a function"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args):
|
||||
"""with_command wrapper"""
|
||||
target_path = path_from_name(args[0].__class__.__name__, 'target')
|
||||
subcommand = COMMAND_MAP[args[0].__class__.__name__.split('_')[0]]
|
||||
command = ['sudo', './swupd', subcommand, '-u', 'http://localhost/',
|
||||
'-P', PORT, '-p', target_path]
|
||||
func(*args, command)
|
||||
return wrapper
|
||||
|
||||
|
||||
def http_command(**kwargs):
|
||||
"""Make the standard test class method"""
|
||||
def class_wrapper(original_class):
|
||||
"""Update class to add test function"""
|
||||
@http_server
|
||||
@with_command
|
||||
def runTest(*args):
|
||||
subprocess.run('sudo rm -fr /var/lib/swupd'.split())
|
||||
"""Basic test wrapper function"""
|
||||
# Options are for subcommand so insert after the subcommand index.
|
||||
# Currently defaults to the -u option
|
||||
if kwargs.get('option') != "":
|
||||
i = args[1].index('-u')
|
||||
args[1][i:i] = kwargs['option'].split(' ')
|
||||
process = subprocess.run(args[1], stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
args[0].validate(process.stdout.splitlines())
|
||||
msg = kwargs.get('skip')
|
||||
if msg:
|
||||
original_class.runTest = unittest.skip(msg)(runTest)
|
||||
else:
|
||||
original_class.runTest = runTest
|
||||
return original_class
|
||||
return class_wrapper
|
||||
|
||||
|
||||
@http_command(option="test-bundle")
|
||||
class bundleadd_add_directory(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Downloading test-bundle pack for version 10',
|
||||
test_output)
|
||||
self.assertIn('Installing bundle(s) files...', test_output)
|
||||
self.assertIn('Tracking test-bundle bundle on the system', test_output)
|
||||
|
||||
|
||||
@http_command(option="--list")
|
||||
class bundleadd_list(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('os-core', test_output)
|
||||
|
||||
|
||||
@http_command(option="test-bundle")
|
||||
class bundleremove_remove_file(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Deleting bundle files...', test_output)
|
||||
self.assertIn('Total deleted files: 1', test_output)
|
||||
self.assertIn('Untracking bundle from system...', test_output)
|
||||
self.assertIn('Success: Bundle removed', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class checkupdate_version_match(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('There are no updates available', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class checkupdate_new_version(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('There is a new OS version available: 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class checkupdate_no_server_content(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Cannot reach update server', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class checkupdate_no_target_content(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Unable to determine current OS version', test_output)
|
||||
|
||||
|
||||
@http_command(option="--download")
|
||||
class update_download(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn(' changed files : 1', test_output)
|
||||
self.assertIn(' changed manifests : 1', test_output)
|
||||
|
||||
|
||||
@http_command(option="--status")
|
||||
class update_status(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Current OS version: 10', test_output)
|
||||
self.assertIn('Latest server version: 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="--status")
|
||||
class update_status_no_server_content(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Current OS version: 10', test_output)
|
||||
self.assertIn('Cannot get latest the server version.Could not reach server', test_output)
|
||||
|
||||
|
||||
@http_command(option="--status")
|
||||
class update_status_no_target_content(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Cannot determine current OS version', test_output)
|
||||
self.assertIn('Latest server version: 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="--status")
|
||||
class update_status_version_single_quote(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Current OS version: 10', test_output)
|
||||
self.assertIn('Latest server version: 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="--status")
|
||||
class update_status_version_double_quote(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Current OS version: 10', test_output)
|
||||
self.assertIn('Latest server version: 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class update_use_full_file(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn(' changed files : 1', test_output)
|
||||
self.assertIn(' changed manifests : 1', test_output)
|
||||
self.assertIn('Staging file content', test_output)
|
||||
self.assertIn('Update was applied.', test_output)
|
||||
self.assertIn('Update successful. System updated from version 10 to version 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class update_use_pack(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn(' changed files : 1', test_output)
|
||||
self.assertIn(' changed manifests : 1', test_output)
|
||||
self.assertIn('Staging file content', test_output)
|
||||
self.assertIn('Update was applied.', test_output)
|
||||
self.assertIn('Update successful. System updated from version 10 to version 100', test_output)
|
||||
|
||||
|
||||
@http_command(option="--fix")
|
||||
class verify_add_missing_directory(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Verifying version 10', test_output)
|
||||
self.assertIn(' 1 files were missing', test_output)
|
||||
self.assertIn(' 1 of 1 missing files were replaced', test_output)
|
||||
self.assertIn('Fix successful', test_output)
|
||||
|
||||
|
||||
@http_command(option="")
|
||||
class verify_check_missing_directory(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Verifying version 10', test_output)
|
||||
self.assertIn(' 1 files did not match', test_output)
|
||||
self.assertIn('Verify successful', test_output)
|
||||
|
||||
|
||||
@http_command(option="--install -m 10")
|
||||
class verify_install_directory(unittest.TestCase):
|
||||
def validate(self, test_output):
|
||||
self.assertIn('Verifying version 10', test_output)
|
||||
self.assertIn(' 1 files were missing', test_output)
|
||||
self.assertIn(' 1 of 1 missing files were replaced', test_output)
|
||||
self.assertIn('Fix successful', test_output)
|
||||
|
||||
|
||||
def get_test_cases():
|
||||
tests = ['bundleadd', 'bundleremove', 'checkupdate', 'update', 'verify']
|
||||
test_cases = []
|
||||
class_members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
|
||||
for (name, cls) in class_members:
|
||||
if name.split('_')[0] in tests:
|
||||
test_cases.append((name, cls))
|
||||
return test_cases
|
||||
|
||||
|
||||
def run_setups(tests):
|
||||
setups = []
|
||||
for test in tests:
|
||||
test_dir = path_from_name(test[0], 'plain')
|
||||
if os.path.isfile(os.path.join(test_dir, 'setup.sh')):
|
||||
setups.append(subprocess.Popen('./setup.sh', cwd=test_dir,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL))
|
||||
for setup in setups:
|
||||
setup.wait()
|
||||
if setup.returncode != 0:
|
||||
raise Exception('Setup failed')
|
||||
|
||||
|
||||
def run_teardowns(tests):
|
||||
teardowns = []
|
||||
for test in tests:
|
||||
test_dir = path_from_name(test[0], 'plain')
|
||||
if os.path.isfile(os.path.join(test_dir, 'teardown.sh')):
|
||||
teardowns.append(subprocess.Popen('./teardown.sh', cwd=test_dir,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL))
|
||||
for teardown in teardowns:
|
||||
teardown.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
tests = get_test_cases()
|
||||
run_setups(tests)
|
||||
suite = unittest.TestSuite()
|
||||
for test in tests:
|
||||
suite.addTest(test[1]())
|
||||
runner = TAPTestRunner()
|
||||
runner.set_format('{method_name}')
|
||||
runner.set_stream(True)
|
||||
runner.run(suite)
|
||||
run_teardowns(tests)
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef __LIST__
|
||||
#define __LIST__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct list {
|
||||
void *data;
|
||||
struct list *prev;
|
||||
struct list *next;
|
||||
};
|
||||
|
||||
typedef int (*comparison_fn_t)(const void *a, const void *b);
|
||||
typedef void (*list_free_data_fn_t) (void *data);
|
||||
|
||||
/* creates a new list item, store data, and inserts item in list (which can
|
||||
* be NULL). Returns created link, or NULL if failure. Created link can be
|
||||
* used as the list parameter to efficiently append new elements without
|
||||
* having to traverse the whole list to find the last one. */
|
||||
struct list *list_append_data(struct list *list, void *data);
|
||||
struct list *list_prepend_data(struct list *list, void *data);
|
||||
|
||||
/* Returns the head or the tail of a list given anyone of its items */
|
||||
struct list *list_head(struct list *item);
|
||||
struct list *list_tail(struct list *item);
|
||||
|
||||
/* Returns the length of a list given anyone of its items */
|
||||
unsigned int list_len(struct list *list);
|
||||
|
||||
#if 0
|
||||
/* Finds and returns the list item containing the parameter data */
|
||||
struct list *list_find_data(struct list *list, void *data);
|
||||
#endif
|
||||
|
||||
/* Sorts the list using the comparison function. list can be any item in the
|
||||
* list, the complete list will still be sorted. Returns the first item in
|
||||
* the sorted list. */
|
||||
struct list *list_sort(struct list *list, comparison_fn_t comparison_fn);
|
||||
|
||||
/* appends list2 at the tail of list1. Either list1 or list2 can be NULL.
|
||||
* returns the head of the resulting concatenation */
|
||||
struct list *list_concat(struct list *list1, struct list *list2);
|
||||
|
||||
/* unlink item from its list, call list_free_data_fn to free its data,
|
||||
* and free the item itself. Always returns the preceding item, except in
|
||||
* case head is freed - then return the new head. If this was the last item,
|
||||
* returns NULL */
|
||||
struct list *list_free_item(struct list *item, list_free_data_fn_t list_free_data_fn);
|
||||
/* destroy all items in the list, calling list_free_data_fn for each
|
||||
* item to let user free its own item data. list_free_data_fn can be NULL */
|
||||
void list_free_list_and_data(struct list *list, list_free_data_fn_t list_free_data_fn);
|
||||
/* destroy all items in the list without disposing item data */
|
||||
void list_free_list(struct list *list);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef SIGNATURE_H_
|
||||
#define SIGNATURE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* Initialize this module.
|
||||
* @param ca_cert_filename - the file containing the CA certificate
|
||||
* @return true <=> no error
|
||||
*/
|
||||
bool signature_initialize(const char *ca_cert_filename);
|
||||
|
||||
/*
|
||||
* Terminate usage of this module, free resources.
|
||||
*/
|
||||
void signature_terminate(void);
|
||||
|
||||
/*
|
||||
* Verify data file contents against a purported signature.
|
||||
* @param data_filename - the file containing the data
|
||||
* @param sig_filename - the file containing the signature
|
||||
* @return true <=> no error
|
||||
*/
|
||||
bool signature_verify(const char *data_filename, const char *sig_filename);
|
||||
|
||||
/*
|
||||
* The given data file has already been downloaded from the given URL.
|
||||
* Download the corresponding signature file, and verify the data against the signature.
|
||||
* Delete the signature file if and only if the verification fails.
|
||||
* @param data_url - the URL from which the data came
|
||||
* @param data_filename - the file containing the data
|
||||
*/
|
||||
bool signature_download_and_verify(const char *data_url, const char *data_filename);
|
||||
|
||||
/*
|
||||
* Delete the signature file corresponding to given data file.
|
||||
* @param data_filename - the file containing the data
|
||||
*/
|
||||
void signature_delete(const char *data_filename);
|
||||
|
||||
#endif /* SIGNATURE_H_ */
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef __INCLUDE_GUARD_SWUPD_BUILD_VARIANT_H
|
||||
#define __INCLUDE_GUARD_SWUPD_BUILD_VARIANT_H
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "list.h"
|
||||
#include "swupd.h"
|
||||
|
||||
#ifdef SWUPD_WITH_SELINUX
|
||||
#define TAR_PERM_ATTR_ARGS "--preserve-permissions --xattrs --xattrs-include='*' --selinux"
|
||||
#else /* SWUPD_WITHOUT_SELINUX */
|
||||
#define TAR_PERM_ATTR_ARGS "--preserve-permissions --xattrs --xattrs-include='*'"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef __INCLUDE_GUARD_SWUPD_ERROR_H
|
||||
#define __INCLUDE_GUARD_SWUPD_ERROR_H
|
||||
|
||||
#define EBUNDLE_MISMATCH 2 /* at least one local bundle mismatches from MoM */
|
||||
#define EBUNDLE_REMOVE 3 /* cannot delete local bundle filename */
|
||||
#define EMOM_NOTFOUND 4 /* MoM cannot be loaded into memory (this could imply network issue) */
|
||||
#define ETYPE_CHANGED_FILE_RM 5 /* do_staging() couldn't delete a file which must be deleted */
|
||||
#define EDIR_OVERWRITE 6 /* do_staging() couldn't overwrite a directory */
|
||||
#define EDOTFILE_WRITE 7 /* do_staging() couldn't create a dotfile */
|
||||
#define ERECURSE_MANIFEST 8 /* error while recursing a manifest */
|
||||
#define ELOCK_FILE 9 /* cannot get the lock */
|
||||
#define EPREP_MOUNT 10 /* failed to prepare mount points */
|
||||
#define ECURL_INIT 11 /* cannot initialize curl agent */
|
||||
#define EINIT_GLOBALS 12 /* cannot initialize globals */
|
||||
#define EBUNDLE_NOT_TRACKED 13 /* bundle is not tracked on the system */
|
||||
#define EMANIFEST_LOAD 14 /* cannot load manifest into memory */
|
||||
#define EINVALID_OPTION 15 /* invalid command option */
|
||||
#define ENOSWUPDSERVER 16 /* no net connection to swupd server */
|
||||
#define EFULLDOWNLOAD 17 /* full_download problem */
|
||||
#define ENET404 404 /* download 404'd */
|
||||
#define EBUNDLE_INSTALL 18 /* Cannot install bundles */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef __INCLUDE_GUARD_SWUPD_INTERNAL_H
|
||||
#define __INCLUDE_GUARD_SWUPD_INTERNAL_H
|
||||
|
||||
extern int bundle_add_main(int argc, char **argv);
|
||||
extern int bundle_remove_main(int argc, char **argv);
|
||||
extern int hashdump_main(int argc, char **argv);
|
||||
extern int update_main(int argc, char **argv);
|
||||
extern int verify_main(int argc, char **argv);
|
||||
extern int check_update_main(int argc, char **argv);
|
||||
|
||||
#endif
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
#ifndef __INCLUDE_GUARD_SWUPD_H
|
||||
#define __INCLUDE_GUARD_SWUPD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <curl/curl.h>
|
||||
#include "list.h"
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include "swupd-error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* WARNING: keep SWUPD_VERSION_INCR in sync with server definition */
|
||||
#define SWUPD_VERSION_INCR 10
|
||||
#define SWUPD_VERSION_IS_DEVEL(v) (((v) % SWUPD_VERSION_INCR) == 8)
|
||||
#define SWUPD_VERSION_IS_RESVD(v) (((v) % SWUPD_VERSION_INCR) == 9)
|
||||
|
||||
#ifndef LINE_MAX
|
||||
#define LINE_MAX _POSIX2_LINE_MAX
|
||||
#endif
|
||||
|
||||
#define PATH_MAXLEN 4096
|
||||
#define CURRENT_OS_VERSION -1
|
||||
|
||||
#define UNUSED_PARAM __attribute__ ((__unused__))
|
||||
|
||||
#define MAX_TRIES 3
|
||||
|
||||
struct sub {
|
||||
/* name of bundle/component/subscription */
|
||||
char *component;
|
||||
|
||||
/* version meanings:
|
||||
* non-zero -> version has been read from MoM
|
||||
* -1 -> error, local bundle do not match any bundle on MoM */
|
||||
int version;
|
||||
|
||||
/* oldversion: set to 0 by calloc(), possibly overridden by MoM read */
|
||||
int oldversion;
|
||||
};
|
||||
|
||||
struct manifest {
|
||||
int version;
|
||||
int manifest_version;
|
||||
uint64_t contentsize;
|
||||
struct list *files;
|
||||
struct list *manifests; /* struct file for possible manifests */
|
||||
struct list *submanifests; /* struct manifest for subscribed manifests */
|
||||
char *component;
|
||||
};
|
||||
|
||||
struct header;
|
||||
|
||||
extern bool force;
|
||||
extern int verbose;
|
||||
extern int update_count;
|
||||
extern int update_skip;
|
||||
extern bool update_complete;
|
||||
extern bool need_update_boot;
|
||||
extern bool need_update_bootloader;
|
||||
|
||||
struct update_stat {
|
||||
uint64_t st_mode;
|
||||
uint64_t st_uid;
|
||||
uint64_t st_gid;
|
||||
uint64_t st_rdev;
|
||||
uint64_t st_size;
|
||||
};
|
||||
|
||||
#define DIGEST_LEN_SHA256 64
|
||||
/* +1 for null termination */
|
||||
#define SWUPD_HASH_LEN (DIGEST_LEN_SHA256+1)
|
||||
|
||||
struct file {
|
||||
char *filename;
|
||||
char hash[SWUPD_HASH_LEN];
|
||||
bool use_xattrs;
|
||||
int last_change;
|
||||
struct update_stat stat;
|
||||
|
||||
unsigned int is_dir : 1;
|
||||
unsigned int is_file : 1;
|
||||
unsigned int is_link : 1;
|
||||
unsigned int is_deleted : 1;
|
||||
unsigned int is_manifest : 1;
|
||||
|
||||
unsigned int is_config : 1;
|
||||
unsigned int is_state : 1;
|
||||
unsigned int is_boot : 1;
|
||||
unsigned int is_rename : 1;
|
||||
unsigned int is_orphan : 1;
|
||||
unsigned int do_not_update : 1;
|
||||
|
||||
struct file *peer; /* same file in another manifest */
|
||||
struct file *deltapeer; /* the file to do the binary delta against; often same as "peer" except in rename cases */
|
||||
struct header *header;
|
||||
|
||||
char *staging; /* output name used during download & staging */
|
||||
CURL *curl; /* curl handle if downloading */
|
||||
};
|
||||
|
||||
extern bool download_only;
|
||||
extern bool verify_esp_only;
|
||||
extern bool have_manifest_diskspace;
|
||||
extern bool have_network;
|
||||
extern bool verify_bundles_only;
|
||||
extern bool ignore_config;
|
||||
extern bool ignore_state;
|
||||
extern bool ignore_orphans;
|
||||
extern bool fix;
|
||||
extern char *format_string;
|
||||
extern char *path_prefix;
|
||||
extern bool set_format_string(char *userinput);
|
||||
extern bool init_globals(void);
|
||||
extern void free_globals(void);
|
||||
extern bool has_telemetry;
|
||||
extern void *tm_dlhandle;
|
||||
extern char *bundle_to_add;
|
||||
extern struct timeval start_time;
|
||||
|
||||
extern char *version_server_urls[];
|
||||
extern char *preferred_version_url;
|
||||
extern char *content_server_urls[];
|
||||
extern char *preferred_content_url;
|
||||
extern long update_server_port;
|
||||
|
||||
extern void check_root(void);
|
||||
extern void clean_curl_multi_queue(void);
|
||||
extern void increment_retries(int *retries, int *timeout);
|
||||
|
||||
extern int main_update(void);
|
||||
extern int main_verify(int current_version);
|
||||
|
||||
extern void read_versions(int *current_version, int *latest_version, int *server_version, char *path_prefix);
|
||||
extern int check_versions(int *current_version, int *latest_version, int *server_version, char *path_prefix);
|
||||
extern int read_version_from_subvol_file(char *path_prefix);
|
||||
|
||||
extern bool check_network(void);
|
||||
|
||||
extern bool ignore(struct file *file);
|
||||
extern bool is_config(char *filename);
|
||||
extern bool is_state(char *filename);
|
||||
extern void apply_heuristics(struct file *file);
|
||||
|
||||
extern int file_sort_filename(const void *a, const void *b);
|
||||
extern int load_manifests(int current, int version, char *component, struct file *file, struct manifest **manifest);
|
||||
extern struct list *create_update_list(struct manifest *current, struct manifest *server);
|
||||
extern void link_manifests(struct manifest *m1, struct manifest *m2);
|
||||
extern void link_submanifests(struct manifest *m1, struct manifest *m2);
|
||||
extern void free_manifest(struct manifest *manifest);
|
||||
|
||||
extern void account_new_file(void);
|
||||
extern void account_deleted_file(void);
|
||||
extern void account_changed_file(void);
|
||||
extern void account_new_manifest(void);
|
||||
extern void account_deleted_manifest(void);
|
||||
extern void account_changed_manifest(void);
|
||||
extern void account_delta_hit(void);
|
||||
extern void account_delta_miss(void);
|
||||
extern void print_statistics(int version1, int version2);
|
||||
|
||||
extern int download_subscribed_packs(int oldversion, int newversion, bool required);
|
||||
|
||||
extern void try_delta(struct file *file);
|
||||
extern void full_download(struct file *file);
|
||||
extern int start_full_download(bool pipelining);
|
||||
extern struct list *end_full_download(void);
|
||||
|
||||
extern int do_staging(struct file *file);
|
||||
extern int rename_all_files_to_final(struct list *updates);
|
||||
extern int rename_staged_file_to_final(struct file *file);
|
||||
|
||||
extern int update_device_latest_version(int version);
|
||||
|
||||
extern int swupd_curl_init(void);
|
||||
extern void swupd_curl_cleanup(void);
|
||||
extern void swupd_curl_set_current_version(int v);
|
||||
extern void swupd_curl_set_requested_version(int v);
|
||||
extern size_t swupd_download_file(void *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
extern int swupd_curl_get_file(const char *url, char *filename, struct file *file,
|
||||
char *tmp_version, bool pack);
|
||||
#define SWUPD_CURL_LOW_SPEED_LIMIT 1
|
||||
#define SWUPD_CURL_CONNECT_TIMEOUT 30
|
||||
#define SWUPD_CURL_RCV_TIMEOUT 120
|
||||
extern CURLcode swupd_curl_set_basic_options(CURL *curl, const char *url);
|
||||
|
||||
extern struct list *subs;
|
||||
extern void free_subscriptions(void);
|
||||
extern void read_subscriptions(void);
|
||||
extern void read_subscriptions_alt(void);
|
||||
extern int component_subscribed(char *component);
|
||||
extern int subscription_versions_from_MoM(struct manifest *MoM, int is_old);
|
||||
|
||||
extern void hash_assign(char *src, char *dest);
|
||||
extern bool hash_compare(char *hash1, char *hash2);
|
||||
extern bool hash_is_zeros(char *hash);
|
||||
extern int compute_hash_lazy(struct file *file, char *filename);
|
||||
extern int compute_hash(struct file *file, char *filename) __attribute__((warn_unused_result));
|
||||
|
||||
/* manifest.c */
|
||||
extern int recurse_manifest(struct manifest *manifest, const char *component);
|
||||
extern void consolidate_submanifests(struct manifest *manifest);
|
||||
extern void debug_write_manifest(struct manifest *manifest, char *filename);
|
||||
extern void populate_file_struct(struct file *file, char *filename);
|
||||
extern bool verify_file(struct file* file, char *filename);
|
||||
extern void unlink_all_staged_content(struct file *file);
|
||||
extern void link_renames(struct list *newfiles, struct manifest *from_manifest);
|
||||
extern void dump_file_descriptor_leaks(void);
|
||||
extern FILE * fopen_exclusive(const char *filename); /* no mode, opens for write only */
|
||||
extern int rm_staging_dir_contents(const char *rel_path);
|
||||
extern int create_required_dirs(void);
|
||||
extern void dump_file_info(struct file *file);
|
||||
void free_file_data(void *data);
|
||||
void remove_files_in_manifest_from_fs(struct manifest *m);
|
||||
void deduplicate_files_from_manifest(struct manifest **m1, struct manifest *m2);
|
||||
bool manifest_has_component(struct manifest *manifest, const char *component);
|
||||
|
||||
extern char *mounted_dirs;
|
||||
extern void get_mounted_directories(void);
|
||||
extern char *mk_full_filename(const char *prefix, const char *path);
|
||||
extern bool is_directory_mounted(const char *filename);
|
||||
extern bool is_under_mounted_directory(const char *filename);
|
||||
|
||||
extern void run_scripts(void);
|
||||
extern void run_preupdate_scripts(struct manifest *manifest);
|
||||
|
||||
/* lock.c */
|
||||
int p_lockfile(void);
|
||||
void v_lockfile(int fd);
|
||||
|
||||
/* helpers.c */
|
||||
extern int swupd_rm(const char *path);
|
||||
extern int rm_bundle_file(const char *bundle);
|
||||
extern void print_manifest_files(struct manifest *m);
|
||||
extern int swupd_init(int *lock_fd);
|
||||
extern void copyright_header(const char *name);
|
||||
extern void string_or_die(char **strp, const char *fmt, ...);
|
||||
void update_motd(int new_release);
|
||||
void delete_motd(void);
|
||||
extern int is_dirname_link(const char *fullname);
|
||||
|
||||
/* subscription.c */
|
||||
struct list *free_list_file(struct list *item);
|
||||
struct list *free_bundle(struct list *item);
|
||||
extern void create_and_append_subscription(const char *component);
|
||||
|
||||
/* bundle.c */
|
||||
extern int remove_bundle(const char *bundle_name);
|
||||
extern int list_installable_bundles();
|
||||
extern int install_bundles(char **bundles);
|
||||
|
||||
/* some disk sizes constants for the various features:
|
||||
* ...consider adding build automation to catch at build time
|
||||
* if the build's artifacts are larger than these thresholds */
|
||||
#define MANIFEST_REQUIRED_SIZE (1024 * 1024 * 100) /* 100M */
|
||||
#define FREE_MARGIN 10 /* 10% */
|
||||
|
||||
/****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef __INCLUDE_GUARD_XATTRS_H
|
||||
#define __INCLUDE_GUARD_XATTRS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Best effort to copy the extended attributes from one file to another.
|
||||
*
|
||||
* @param src_filename - The source file from which the extended attributes
|
||||
* will be read.
|
||||
* @param dst_filename - The destination file where the source file extended
|
||||
* attributes have to be copied.
|
||||
* @return - None.
|
||||
*/
|
||||
void xattrs_copy(const char *src_filename, const char *dst_filename);
|
||||
|
||||
/*
|
||||
* Attempt to pack into a data blob the extended attributes of the given file.
|
||||
* The data blob will be allocated and possibly filled with first the list
|
||||
* of all the extended attributes names, and then their values.
|
||||
* @param filename - The file from which the extended attributes will be
|
||||
* read and packed into the data blob.
|
||||
* @param blob - The data blob pointer into which the extended attributes will
|
||||
* be packed. (the data blob content has to be freed by the caller).
|
||||
* @param blob_len - The returned data blob length.
|
||||
* @return - None.
|
||||
*/
|
||||
void xattrs_get_blob(const char *filename, char **blob, size_t *blob_len);
|
||||
|
||||
/*
|
||||
* Compare the extended attributes from one file to another.
|
||||
*
|
||||
* @param filename1 - The first file used for the extended attributes
|
||||
* comparison.
|
||||
* @param filename2 - The second file used for the extended attributes
|
||||
* comparison.
|
||||
* @return - 0 if the extended attributes are identical, -1 if they are
|
||||
* different.
|
||||
*/
|
||||
int xattrs_compare(const char *filename1, const char *filename2);
|
||||
|
||||
#endif /* __INCLUDE_GUARD_XATTRS_H */
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright (c) 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Jaime A. Garcia <jaime.garcia.naranjo@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
|
||||
#define MODE_RW_O (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
|
||||
|
||||
/*
|
||||
* list_installable_bundles()
|
||||
* Parse the full manifest for the current version of the OS and print
|
||||
* all available bundles.
|
||||
*/
|
||||
int list_installable_bundles()
|
||||
{
|
||||
struct list *list;
|
||||
struct file *file;
|
||||
struct manifest *MoM = NULL;
|
||||
int current_version;
|
||||
int lock_fd;
|
||||
int ret;
|
||||
|
||||
if (!init_globals()) {
|
||||
return EINIT_GLOBALS;
|
||||
}
|
||||
|
||||
current_version = read_version_from_subvol_file(path_prefix);
|
||||
|
||||
if (swupd_init(&lock_fd) != 0) {
|
||||
printf("Error: Failed updater initialization. Exiting now\n");
|
||||
return ECURL_INIT;
|
||||
}
|
||||
|
||||
ret = create_required_dirs();
|
||||
if (ret != 0) {
|
||||
printf("State directory %s cannot be recreated, aborting removal\n", STATE_DIR);
|
||||
v_lockfile(lock_fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
get_mounted_directories();
|
||||
|
||||
if (!check_network()) {
|
||||
printf("Error: Network issue, unable to download manifest\n");
|
||||
v_lockfile(lock_fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
swupd_curl_set_current_version(current_version);
|
||||
|
||||
ret = load_manifests(current_version, current_version, "MoM", NULL, &MoM);
|
||||
if (ret != 0) {
|
||||
v_lockfile(lock_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list = MoM->manifests;
|
||||
while (list) {
|
||||
file = list->data;
|
||||
list = list->next;
|
||||
printf("%s\n", file->filename);
|
||||
}
|
||||
|
||||
free_manifest(MoM);
|
||||
v_lockfile(lock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* bundle_name: this is the name for the bundle we want to be loaded
|
||||
* version: this is the MoM version from which we pull last changed for bundle manifest
|
||||
* submanifest: where bundle manifest is going to be loaded
|
||||
*
|
||||
* Basically we read MoM version then get the submanifest only for our bundle (component)
|
||||
* put it into submanifest pointer, then dispose MoM data.
|
||||
*/
|
||||
static int load_bundle_manifest(const char *bundle_name, int version, struct manifest **submanifest)
|
||||
{
|
||||
struct list *sub_list = NULL;
|
||||
struct manifest *mom = NULL;
|
||||
int ret = 0;
|
||||
|
||||
*submanifest = NULL;
|
||||
|
||||
swupd_curl_set_current_version(version);
|
||||
ret = load_manifests(version, version, "MoM", NULL, &mom);
|
||||
if (ret != 0) {
|
||||
ret = EMOM_NOTFOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = recurse_manifest(mom, bundle_name);
|
||||
if (ret != 0) {
|
||||
ret = ERECURSE_MANIFEST;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
sub_list = list_head(mom->submanifests);
|
||||
if (sub_list != NULL) {
|
||||
*submanifest = sub_list->data;
|
||||
sub_list->data = NULL;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
free_out:
|
||||
free_manifest(mom);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Finds out whether bundle_name is tracked bundle on
|
||||
* current system.
|
||||
*/
|
||||
static bool is_tracked_bundle(const char *bundle_name)
|
||||
{
|
||||
struct stat statb;
|
||||
char *filename = NULL;
|
||||
bool ret = true;
|
||||
|
||||
string_or_die(&filename, "%s/%s/%s", path_prefix, BUNDLES_DIR, bundle_name);
|
||||
|
||||
if (stat(filename, &statb) == -1) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
free(filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* When loading all tracked bundles into memory, they happen
|
||||
* to be hold in subs global var, for some reasons it is
|
||||
* needed to just pop out one or more of this loaded tracked
|
||||
* bundles, this function search for bundle_name into subs
|
||||
* struct and if it found then free it from the list.
|
||||
*/
|
||||
static int unload_tracked_bundle(const char *bundle_name)
|
||||
{
|
||||
struct list *bundles;
|
||||
struct list *cur_item;
|
||||
struct sub *bundle;
|
||||
|
||||
bundles = list_head(subs);
|
||||
while (bundles) {
|
||||
bundle = bundles->data;
|
||||
cur_item = bundles;
|
||||
bundles = bundles->next;
|
||||
if (strcmp(bundle->component, bundle_name) == 0) {
|
||||
/* unlink (aka untrack) matching bundle name from tracked ones */
|
||||
subs = free_bundle(cur_item);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return EBUNDLE_NOT_TRACKED;
|
||||
}
|
||||
|
||||
/* touch bundle filename in system bundles directory,
|
||||
* this is called after bundle installation to make sure bundle is kept tracked
|
||||
*/
|
||||
static int track_bundle_in_system(char *bundle)
|
||||
{
|
||||
char *filename;
|
||||
int f;
|
||||
int ret = 0;
|
||||
|
||||
string_or_die(&filename, "%s/%s/%s", path_prefix, BUNDLES_DIR, bundle);
|
||||
|
||||
f = open(filename, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY, MODE_RW_O);
|
||||
if (f < 0) {
|
||||
ret = EBUNDLE_NOT_TRACKED;
|
||||
} else {
|
||||
close(f);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function is a fresh new implementation for a bundle
|
||||
* remove without being tied to verify loop, this means
|
||||
* improved speed and space as well as more roubustness and
|
||||
* flexibility. What it does is basically:
|
||||
*
|
||||
* 1) Read MoM and load all submanifests except the one to be
|
||||
* removed and then consolidate them.
|
||||
* 2) Load the removed bundle submanifest.
|
||||
* 3) Order the file list by filename
|
||||
* 4) Deduplicate removed submanifest file list that happens
|
||||
* to be on the MoM (minus bundle to be removed).
|
||||
* 5) iterate over to be removed bundle submanifest file list
|
||||
* performing a unlink(2) for each filename.
|
||||
* 6) Done.
|
||||
*/
|
||||
int remove_bundle(const char *bundle_name)
|
||||
{
|
||||
int lock_fd;
|
||||
int ret = 0;
|
||||
int current_version = CURRENT_OS_VERSION;
|
||||
struct manifest *current_mom, *bundle_manifest;
|
||||
|
||||
/* Initially we don't support format nor path_prefix
|
||||
* for bundle_rm but eventually that will be added, then
|
||||
* set_format_string() and init_globals() must be pulled out
|
||||
* to the caller to properly initialize in case those opts
|
||||
* passed to the command.
|
||||
*/
|
||||
set_format_string(NULL);
|
||||
if (!init_globals()) {
|
||||
return EINIT_GLOBALS;
|
||||
}
|
||||
|
||||
ret = swupd_init(&lock_fd);
|
||||
if (ret != 0) {
|
||||
printf("Failed updater initialization, exiting now.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* os-core bundle not allowed to be removed...
|
||||
* although this is going to be caught later because of all files
|
||||
* being marked as 'duplicated' and note removing anything
|
||||
* anyways, better catch here and return success, no extra work to be done.
|
||||
*/
|
||||
if (strcmp(bundle_name, "os-core") == 0) {
|
||||
ret = EBUNDLE_NOT_TRACKED;
|
||||
goto out_free_curl;
|
||||
}
|
||||
|
||||
if (!is_tracked_bundle(bundle_name)) {
|
||||
ret = EBUNDLE_NOT_TRACKED;
|
||||
goto out_free_curl;
|
||||
}
|
||||
|
||||
current_version = read_version_from_subvol_file(path_prefix);
|
||||
swupd_curl_set_current_version(current_version);
|
||||
|
||||
/* first of all, make sure STATE_DIR is there, recreate if necessary*/
|
||||
ret = create_required_dirs();
|
||||
if (ret != 0) {
|
||||
printf("State directory %s cannot be recreated, aborting removal\n", STATE_DIR);
|
||||
goto out_free_curl;
|
||||
}
|
||||
|
||||
|
||||
ret = load_manifests(current_version, current_version, "MoM", NULL, ¤t_mom);
|
||||
if (ret != 0) {
|
||||
goto out_free_curl;
|
||||
}
|
||||
|
||||
/* load all tracked bundles into memory */
|
||||
read_subscriptions_alt();
|
||||
/* now popout the one to be removed */
|
||||
ret = unload_tracked_bundle(bundle_name);
|
||||
if (ret != 0) {
|
||||
goto out_free_mom;
|
||||
}
|
||||
|
||||
subscription_versions_from_MoM(current_mom, 0);
|
||||
/* load all submanifest minus the one to be removed */
|
||||
recurse_manifest(current_mom, NULL);
|
||||
consolidate_submanifests(current_mom);
|
||||
|
||||
/* Now that we have the consolidated list of all files, load bundle to be removed submanifest*/
|
||||
ret = load_bundle_manifest(bundle_name, current_version, &bundle_manifest);
|
||||
if (ret != 0) {
|
||||
goto out_free_mom;
|
||||
}
|
||||
|
||||
/* deduplication needs file list sorted by filename, do so */
|
||||
bundle_manifest->files = list_sort(bundle_manifest->files, file_sort_filename);
|
||||
deduplicate_files_from_manifest(&bundle_manifest, current_mom);
|
||||
|
||||
printf("Deleting bundle files...\n");
|
||||
remove_files_in_manifest_from_fs(bundle_manifest);
|
||||
|
||||
printf("Untracking bundle from system...\n");
|
||||
rm_bundle_file(bundle_name);
|
||||
|
||||
printf("Success: Bundle removed\n");
|
||||
|
||||
free_manifest(bundle_manifest);
|
||||
out_free_mom:
|
||||
free_manifest(current_mom);
|
||||
out_free_curl:
|
||||
|
||||
if (ret) {
|
||||
printf("Error: Bundle remove failed\n");
|
||||
}
|
||||
|
||||
swupd_curl_cleanup();
|
||||
v_lockfile(lock_fd);
|
||||
dump_file_descriptor_leaks();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Bundle install one ore more bundles passed in bundles
|
||||
* param as a null terminated array of strings
|
||||
*/
|
||||
int install_bundles(char **bundles)
|
||||
{
|
||||
int lock_fd;
|
||||
int ret = 0;
|
||||
int current_version;
|
||||
struct manifest *mom;
|
||||
struct list *iter;
|
||||
struct sub *sub;
|
||||
struct file *file;
|
||||
|
||||
/* step 1: initialize swupd and get current version from OS */
|
||||
|
||||
if (!init_globals()) {
|
||||
return EINIT_GLOBALS;
|
||||
}
|
||||
|
||||
ret = swupd_init(&lock_fd);
|
||||
if (ret != 0) {
|
||||
printf("Failed updater initialization, exiting now.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
current_version = read_version_from_subvol_file(path_prefix);
|
||||
swupd_curl_set_current_version(current_version);
|
||||
|
||||
/* first of all, make sure STATE_DIR is there, recreate if necessary*/
|
||||
ret = create_required_dirs();
|
||||
if (ret != 0) {
|
||||
printf("State directory %s cannot be recreated, aborting installation\n", STATE_DIR);
|
||||
goto clean_and_exit;
|
||||
}
|
||||
|
||||
|
||||
ret = load_manifests(current_version, current_version, "MoM", NULL, &mom);
|
||||
if (ret != 0) {
|
||||
printf("Cannot load official manifest MoM for version %i\n", current_version);
|
||||
ret = EMOM_NOTFOUND;
|
||||
goto clean_and_exit;
|
||||
}
|
||||
|
||||
/* step 2: check bundle args are valid if so populate subs struct */
|
||||
|
||||
int i;
|
||||
for (i = 0; *bundles; ++bundles) {
|
||||
if (is_tracked_bundle(*bundles)) {
|
||||
printf("%s bundle already installed, skipping it\n", *bundles);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!manifest_has_component(mom, *bundles)) {
|
||||
printf("%s bundle name is invalid, skipping it...\n", *bundles);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (component_subscribed(*bundles)) {
|
||||
continue;
|
||||
}
|
||||
create_and_append_subscription(*bundles);
|
||||
i++;
|
||||
printf("Added bundle %s for installation\n", *bundles);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
printf("There are no pending bundles to install, exiting now\n");
|
||||
ret = EBUNDLE_INSTALL;
|
||||
goto clean_manifest_and_exit;
|
||||
}
|
||||
|
||||
subscription_versions_from_MoM(mom, 0);
|
||||
recurse_manifest(mom, NULL);
|
||||
consolidate_submanifests(mom);
|
||||
|
||||
/* step 3: download neccessary packs */
|
||||
|
||||
ret = rm_staging_dir_contents("download");
|
||||
|
||||
printf("Downloading required packs...\n");
|
||||
ret = download_subscribed_packs(0, current_version, true);
|
||||
if (ret != 0) {
|
||||
printf("pack downloads failed, cannot proceed with the installation, exiting.\n");
|
||||
goto clean_subs_and_exit;
|
||||
}
|
||||
|
||||
/* step 4: Install all bundle(s) files into the fs */
|
||||
|
||||
printf("Installing bundle(s) files...\n");
|
||||
iter = list_head(mom->files);
|
||||
while (iter) {
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (file->is_deleted || file->do_not_update || ignore(file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = do_staging(file);
|
||||
if (ret == 0) {
|
||||
rename_staged_file_to_final(file);
|
||||
}
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
/* step 5: create bundle(s) subscription entries to track them
|
||||
*
|
||||
* Strictly speaking each manifest has an entry to write its own bundle filename
|
||||
* and thus tracking automagically, here just making sure.
|
||||
*/
|
||||
|
||||
iter = list_head(subs);
|
||||
while (iter) {
|
||||
sub = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
printf("Tracking %s bundle on the system\n", sub->component);
|
||||
ret = track_bundle_in_system(sub->component);
|
||||
if (ret != 0) {
|
||||
printf("Cannot track %s bundle on the system\n", sub->component);
|
||||
}
|
||||
}
|
||||
|
||||
/* Run any scripts that are needed to complete update */
|
||||
run_scripts();
|
||||
|
||||
ret = 0;
|
||||
printf("Bundle(s) installation done.\n");
|
||||
|
||||
clean_subs_and_exit:
|
||||
free_subscriptions();
|
||||
clean_manifest_and_exit:
|
||||
free_manifest(mom);
|
||||
clean_and_exit:
|
||||
swupd_curl_cleanup();
|
||||
v_lockfile(lock_fd);
|
||||
dump_file_descriptor_leaks();
|
||||
free_globals();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
static void print_help(const char *name)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" swupd %s [options] bundlename\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n");
|
||||
printf(" -u, --url=[URL] RFC-3986 encoded url for version string and content file downloads\n");
|
||||
printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n");
|
||||
printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n");
|
||||
printf(" -x, --force Attempt to proceed even if non-critical errors found\n");
|
||||
printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const struct option prog_opts[] =
|
||||
{
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"url", required_argument, 0, 'u'},
|
||||
{"port", required_argument, 0, 'P'},
|
||||
{"format", required_argument, 0, 'F'},
|
||||
{"force", no_argument, 0, 'x'},
|
||||
{"path", required_argument, 0, 'p'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static bool parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
set_format_string(NULL);
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hxu:P:F:p:", prog_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case '?':
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'u':
|
||||
if (!optarg) {
|
||||
printf("error: invalid --url argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'P':
|
||||
if (sscanf(optarg, "%ld", &update_server_port) != 1) {
|
||||
printf("Invalid --port argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
if (!optarg || !set_format_string(optarg)) {
|
||||
printf("Invalid --format argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'p': /* default empty path_prefix checks the running OS */
|
||||
if (!optarg) {
|
||||
printf("Invalid --path argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (path_prefix) { /* multiple -p options */
|
||||
free(path_prefix);
|
||||
}
|
||||
string_or_die(&path_prefix, "%s", optarg);
|
||||
break;
|
||||
case 'x':
|
||||
force = true;
|
||||
break;
|
||||
default:
|
||||
printf("error: unrecognized option\n\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!init_globals()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
err:
|
||||
print_help(argv[0]);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
static int check_update()
|
||||
{
|
||||
int current_version, server_version;
|
||||
|
||||
check_root();
|
||||
swupd_curl_init();
|
||||
read_versions(¤t_version, ¤t_version, &server_version, path_prefix);
|
||||
|
||||
if (server_version < 0) {
|
||||
printf("Cannot reach update server\n");
|
||||
return -1;
|
||||
} else if (current_version < 0) {
|
||||
printf("Unable to determine current OS version\n");
|
||||
return -1;
|
||||
} else {
|
||||
if (current_version != -1 && current_version < server_version) {
|
||||
printf("There is a new OS version available: %d\n", server_version);
|
||||
update_motd(server_version);
|
||||
} else if (current_version >= server_version) {
|
||||
printf("There are no updates available\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int check_update_main(int argc, char **argv) {
|
||||
int ret;
|
||||
copyright_header("software update checker");
|
||||
|
||||
if (!parse_options(argc, argv)) {
|
||||
free_globals();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = check_update();
|
||||
free_globals();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright (c) 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
* Jaime A. Garcia <jaime.garcia.naranjo@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
#define MODE_RW_O (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
|
||||
#define VERIFY_NOPICKY 0
|
||||
|
||||
bool list = false;
|
||||
static char **bundles;
|
||||
|
||||
static void print_help(const char *name) {
|
||||
printf("Usage:\n");
|
||||
printf(" swupd %s [options] [bundle1 bundle2 (...)]\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n");
|
||||
printf(" -u, --url=[URL] RFC-3986 encoded url for version string and content file downloads\n");
|
||||
printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n");
|
||||
printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n");
|
||||
printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n");
|
||||
printf(" -l, --list List all available bundles for the current version of Clear Linux\n");
|
||||
printf(" -x, --force Attempt to proceed even if non-critical errors found\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const struct option prog_opts[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"url", required_argument, 0, 'u'},
|
||||
{"port", required_argument, 0, 'P'},
|
||||
{"list", no_argument, 0, 'l'},
|
||||
{"path", required_argument, 0, 'p'},
|
||||
{"format", required_argument, 0, 'F'},
|
||||
{"force", no_argument, 0, 'x'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static bool parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
set_format_string(NULL);
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hxu:P:p:F:l", prog_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case '?':
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'u':
|
||||
if (!optarg) {
|
||||
printf("error: invalid --url argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
if (content_server_urls[0]) {
|
||||
free(content_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
string_or_die(&content_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'p': /* default empty path_prefix verifies the running OS */
|
||||
if (!optarg) {
|
||||
printf("Invalid --path argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (path_prefix) { /* multiple -p options */
|
||||
free(path_prefix);
|
||||
}
|
||||
string_or_die(&path_prefix, "%s", optarg);
|
||||
break;
|
||||
case 'P':
|
||||
if (sscanf(optarg, "%ld", &update_server_port) != 1) {
|
||||
printf("Invalid --port argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
if (!optarg || !set_format_string(optarg)) {
|
||||
printf("Invalid --format argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
list = true;
|
||||
break;
|
||||
case 'x':
|
||||
force = true;
|
||||
break;
|
||||
default:
|
||||
printf("error: unrecognized option\n\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!list) {
|
||||
if (argc <= optind) {
|
||||
printf("error: missing bundle(s) to be installed\n\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
bundles = argv + optind;
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
print_help(argv[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
int bundle_add_main(int argc, char **argv)
|
||||
{
|
||||
copyright_header("bundle adder");
|
||||
|
||||
if (!parse_options(argc, argv)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (list) {
|
||||
return list_installable_bundles();
|
||||
} else {
|
||||
return install_bundles(bundles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright (c) 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
* Jaime A. Garcia <jaime.garcia.naranjo@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
#define VERIFY_PICKY 1
|
||||
|
||||
static char *bundle_name = NULL;
|
||||
|
||||
static void print_help(const char *name) {
|
||||
printf("Usage:\n");
|
||||
printf(" swupd %s [options] bundlename\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n");
|
||||
printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n");
|
||||
printf(" -u, --url=[URL] RFC-3986 encoded url for version string and content file downloads\n");
|
||||
printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n");
|
||||
printf(" -x, --force Attempt to proceed even if non-critical errors found\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const struct option prog_opts[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"path", required_argument, 0, 'p'},
|
||||
{"url", required_argument, 0, 'u'},
|
||||
{"port", required_argument, 0, 'P'},
|
||||
{"force", no_argument, 0, 'x'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static bool parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hxp:u:P:", prog_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case '?':
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'p': /* default empty path_prefix removes on the running OS */
|
||||
if (!optarg) {
|
||||
printf("Invalid --path argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (path_prefix) { /* multiple -p options */
|
||||
free(path_prefix);
|
||||
}
|
||||
string_or_die(&path_prefix, "%s", optarg);
|
||||
break;
|
||||
case 'u':
|
||||
if (!optarg) {
|
||||
printf("error: invalid --url argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
if (content_server_urls[0]) {
|
||||
free(content_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
string_or_die(&content_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'P':
|
||||
if (sscanf(optarg, "%ld", &update_server_port) != 1) {
|
||||
printf("Invalid --port argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
force = true;
|
||||
break;
|
||||
default:
|
||||
printf("error: unrecognized option\n\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc == optind) {
|
||||
printf("error: bundle name missing\n\n");
|
||||
goto err;
|
||||
}
|
||||
string_or_die(&bundle_name, "%s", argv[optind]);
|
||||
|
||||
return true;
|
||||
err:
|
||||
print_help(argv[0]);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
int bundle_remove_main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
copyright_header("bundle remover");
|
||||
|
||||
if (!parse_options(argc, argv)) {
|
||||
return EINVALID_OPTION;
|
||||
}
|
||||
|
||||
ret = remove_bundle(bundle_name);
|
||||
free(bundle_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The curl library is great, but it is a little bit of a pain to get it to
|
||||
* reuse connections properly for simple cases. This file will manage our
|
||||
* curl handle properly so that we have a standing chance to get reuse
|
||||
* of our connections.
|
||||
*
|
||||
* NOTE NOTE NOTE
|
||||
*
|
||||
* Only use these from the main thread of the program. For multithreaded
|
||||
* use, you need to manage your own curl mutli environment.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
|
||||
static CURL *curl = NULL;
|
||||
|
||||
static int curr_version = -1;
|
||||
static int req_version = -1;
|
||||
|
||||
int swupd_curl_init(void)
|
||||
{
|
||||
CURLcode curl_ret;
|
||||
|
||||
curl_ret = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl == NULL) {
|
||||
curl_global_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void swupd_curl_cleanup(void)
|
||||
{
|
||||
if (curl) {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
curl = NULL;
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
void swupd_curl_set_current_version(int v)
|
||||
{
|
||||
curr_version = v;
|
||||
}
|
||||
|
||||
void swupd_curl_set_requested_version(int v)
|
||||
{
|
||||
req_version = v;
|
||||
}
|
||||
|
||||
static size_t swupd_download_version_to_memory(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
char *tmp_version = (char *)userdata;
|
||||
size_t data_len = size * nmemb;
|
||||
|
||||
if (data_len >= LINE_MAX) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(tmp_version, ptr, data_len);
|
||||
tmp_version[data_len] = '\0';
|
||||
|
||||
return data_len;
|
||||
}
|
||||
|
||||
/* curl easy CURLOPT_WRITEFUNCTION callback */
|
||||
size_t swupd_download_file(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
struct file *file = (struct file*)userdata;
|
||||
const char *outfile;
|
||||
int fd;
|
||||
FILE *f;
|
||||
size_t written;
|
||||
|
||||
outfile = file->staging;
|
||||
|
||||
fd = open(outfile, O_CREAT | O_RDWR , 00600);
|
||||
if (fd < 0) {
|
||||
printf("Error: Cannot open %s for write: %s\n",
|
||||
outfile, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = fdopen(fd, "a");
|
||||
if (!f) {
|
||||
printf("Error: Cannot fdopen %s for write: %s\n",
|
||||
outfile, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
written = fwrite(ptr, size*nmemb, 1, f);
|
||||
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
|
||||
if (written != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size*nmemb;
|
||||
}
|
||||
|
||||
/* Download a single file SYNCHRONOUSLY
|
||||
* - If (in_memory_version_string != NULL) the file downloaded is expected
|
||||
* to be a version file and it is downloaded to memory instead of disk.
|
||||
* - Packs are big. If (pack == true) then the function allows resuming
|
||||
* a previous/interrupted download.
|
||||
* - This function returns zero or a standard < 0 status code.
|
||||
* - If failure to download, partial download is not deleted.
|
||||
* - NOTE: See full_download() for multi/asynchronous downloading of fullfiles.
|
||||
*/
|
||||
int swupd_curl_get_file(const char *url, char *filename, struct file *file,
|
||||
char *in_memory_version_string, bool pack)
|
||||
{
|
||||
CURLcode curl_ret;
|
||||
long ret = 0;
|
||||
int err;
|
||||
struct file *local = NULL;
|
||||
|
||||
if (!curl) {
|
||||
abort();
|
||||
}
|
||||
curl_easy_reset(curl);
|
||||
|
||||
if (in_memory_version_string == NULL) {
|
||||
// normal file download
|
||||
struct stat stat;
|
||||
|
||||
if (file) {
|
||||
local = file;
|
||||
} else {
|
||||
local = calloc(1, sizeof(struct file));
|
||||
if (!local) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
local->staging = filename;
|
||||
|
||||
if (lstat(filename, &stat) == 0) {
|
||||
if (pack) {
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, (curl_off_t) stat.st_size);
|
||||
} else {
|
||||
unlink(filename);
|
||||
}
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void*)local);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, swupd_download_file);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)local);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
// only download latest version number, storing in the provided pointer
|
||||
printf("Attempting to download version string to memory\n");
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, swupd_download_version_to_memory);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)in_memory_version_string);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_COOKIE, "request=uncached");
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
curl_ret = swupd_curl_set_basic_options(curl, url);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_perform(curl);
|
||||
if (curl_ret == CURLE_OK || curl_ret == CURLE_HTTP_RETURNED_ERROR) {
|
||||
curl_ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (curl_ret == CURLE_OK) {
|
||||
/* curl command succeeded, download might've failed, let our caller handle */
|
||||
switch (ret) {
|
||||
case 200:
|
||||
case 206:
|
||||
err = 0;
|
||||
break;
|
||||
case 403:
|
||||
err = -EACCES;
|
||||
break;
|
||||
case 404:
|
||||
err = -ENET404;
|
||||
break;
|
||||
default:
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
} else { /* download failed but let our caller do it */
|
||||
switch (curl_ret) {
|
||||
case CURLE_COULDNT_RESOLVE_PROXY:
|
||||
case CURLE_COULDNT_RESOLVE_HOST:
|
||||
case CURLE_COULDNT_CONNECT:
|
||||
err = -ENONET;
|
||||
break;
|
||||
case CURLE_PARTIAL_FILE:
|
||||
case CURLE_RECV_ERROR:
|
||||
err = -ENOLINK;
|
||||
break;
|
||||
case CURLE_WRITE_ERROR:
|
||||
err = -EIO;
|
||||
break;
|
||||
case CURLE_OPERATION_TIMEDOUT:
|
||||
err = -ETIMEDOUT;
|
||||
break;
|
||||
default :
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
if (!pack) {
|
||||
unlink(filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (local != file) {
|
||||
free(local);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static CURLcode swupd_curl_set_security_opts(CURL *curl)
|
||||
{
|
||||
CURLcode curl_ret = CURLE_OK;
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// TODO: change this to to use tlsv1.2 when it is supported and enabled
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, "HIGH");
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, "/usr/share/clear/update-ca/425b0f6b.key");
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_CAPATH , UPDATE_CA_CERTS_PATH);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// TODO: add the below when you know the paths:
|
||||
//curl_easy_setopt(curl, CURLOPT_CRLFILE, path-to-cert-revoc-list);
|
||||
//if (curl_ret != CURLE_OK) {
|
||||
// goto exit;
|
||||
//}
|
||||
|
||||
exit:
|
||||
return curl_ret;
|
||||
}
|
||||
|
||||
CURLcode swupd_curl_set_basic_options(CURL *curl, const char *url)
|
||||
{
|
||||
static bool use_ssl = true;
|
||||
|
||||
CURLcode curl_ret = CURLE_OK;
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (update_server_port > 0) {
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_PORT, update_server_port);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(url, content_server_urls[1], strlen(content_server_urls[1])) == 0) {
|
||||
#warning SECURITY HOLE since we can't SSL pin arbitrary servers
|
||||
curl_ret = swupd_curl_set_security_opts(curl);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
if (use_ssl) {
|
||||
use_ssl = false;
|
||||
}
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, SWUPD_CURL_CONNECT_TIMEOUT);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, SWUPD_CURL_LOW_SPEED_LIMIT);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, SWUPD_CURL_RCV_TIMEOUT);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#warning setup a means to validate IPv6 works end to end
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Avoid downloading HTML files for error responses if the HTTP code is >= 400 */
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
return curl_ret;
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <bsdiff.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
|
||||
static void do_delta(struct file* file);
|
||||
|
||||
|
||||
void try_delta(struct file *file)
|
||||
{
|
||||
char *filename;
|
||||
struct stat stat;
|
||||
|
||||
if (file->is_file == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->deltapeer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->deltapeer->is_file == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->deltapeer->is_deleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if the full file is there already, because if it is, don't do the delta */
|
||||
string_or_die(&filename, "%s/staged/%s", STATE_DIR, file->hash);
|
||||
if (lstat(filename, &stat) == 0) {
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
free(filename);
|
||||
|
||||
do_delta(file);
|
||||
}
|
||||
|
||||
static void do_delta(struct file *file)
|
||||
{
|
||||
char *origin;
|
||||
char *dir, *base, *tmp = NULL, *tmp2 = NULL;
|
||||
char *deltafile = NULL;
|
||||
char *filename;
|
||||
int ret;
|
||||
struct stat stat;
|
||||
|
||||
string_or_die(&deltafile, "%s/delta/%i-%i-%s", STATE_DIR,
|
||||
file->deltapeer->last_change, file->last_change, file->hash);
|
||||
|
||||
/* check if the full file is there already, because if it is, don't do the delta */
|
||||
string_or_die(&filename, "%s/staged/%s", STATE_DIR, file->hash);
|
||||
ret = lstat(filename, &stat);
|
||||
if (ret == 0) {
|
||||
unlink(deltafile);
|
||||
free(deltafile);
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp = strdup(file->deltapeer->filename);
|
||||
tmp2 = strdup(file->deltapeer->filename);
|
||||
|
||||
dir = dirname(tmp);
|
||||
base = basename(tmp2);
|
||||
|
||||
string_or_die(&origin, "%s/%s/%s", STAGING_SUBVOL, dir, base);
|
||||
|
||||
ret = apply_bsdiff_delta(origin, filename, deltafile);
|
||||
if (ret) {
|
||||
unlink_all_staged_content(file);
|
||||
goto out;
|
||||
}
|
||||
xattrs_copy(origin, filename);
|
||||
|
||||
if (!verify_file(file, filename)) {
|
||||
unlink_all_staged_content(file);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (xattrs_compare(origin, filename) != 0) {
|
||||
unlink_all_staged_content(file);
|
||||
goto out;
|
||||
}
|
||||
|
||||
unlink(deltafile);
|
||||
|
||||
out:
|
||||
free(origin);
|
||||
free(deltafile);
|
||||
free(filename);
|
||||
free(tmp);
|
||||
free(tmp2);
|
||||
}
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <curl/curl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd-build-variant.h"
|
||||
#include "swupd.h"
|
||||
#include "bsdiff.h"
|
||||
|
||||
/* This file provides a managed download facility for code that needs a set of
|
||||
* update tar files. Such code starts with one call to "start_full_download()",
|
||||
* then makes a series of calls to "full_download()" once per desired file, and
|
||||
* then finishes with one call to "end_full_downlad()" which will block until
|
||||
* the previously queued downloads are completed and and untarred.
|
||||
*/
|
||||
|
||||
static CURLM *mcurl = NULL;
|
||||
static struct list *failed = NULL;
|
||||
|
||||
/*
|
||||
* The following represents a hash data structure used inside download.c
|
||||
* to track file downloads asynchonously via libcurl. In the past a
|
||||
* single linked list was used, but we may have a very large number of
|
||||
* files and repeatedly scan the list could become expensive. The hashmap
|
||||
* gives info on what HASH.tar files will be downloaded. A de-duplicated
|
||||
* set of files is downloaded. The downloads must complete prior to
|
||||
* traversing a manifest, doing hash comparisons, and (re)staging any files
|
||||
* whose hash miscompares.
|
||||
*
|
||||
* file->hash[0] acts as index into the arrays
|
||||
*/
|
||||
struct swupd_curl_hashbucket {
|
||||
pthread_mutex_t mutex;
|
||||
struct list *list;
|
||||
};
|
||||
#define SWUPD_CURL_HASH_BUCKETS 256
|
||||
static struct swupd_curl_hashbucket swupd_curl_hashmap[SWUPD_CURL_HASH_BUCKETS];
|
||||
|
||||
/* try to insert the file into the hashmap download queue
|
||||
* returns 1 if no download is needed
|
||||
* returns 0 if download is needed
|
||||
* returns -1 if error */
|
||||
static int swupd_curl_hashmap_insert(struct file *file) {
|
||||
struct list *iter;
|
||||
struct file *tmp;
|
||||
char *tar_dotfile;
|
||||
char *targetfile;
|
||||
struct stat stat;
|
||||
int hashmap_index = file->hash[0];
|
||||
struct swupd_curl_hashbucket *bucket = &swupd_curl_hashmap[hashmap_index];
|
||||
|
||||
pthread_mutex_lock(&bucket->mutex);
|
||||
|
||||
iter = bucket->list;
|
||||
while (iter) {
|
||||
tmp = iter->data;
|
||||
if (hash_compare(tmp->hash, file->hash)) {
|
||||
// hash already in download queue
|
||||
pthread_mutex_unlock(&bucket->mutex);
|
||||
return 1;
|
||||
}
|
||||
iter = iter->next;
|
||||
}
|
||||
|
||||
// if valid target file is already here, no need to download
|
||||
string_or_die(&targetfile, "%s/staged/%s", STATE_DIR, file->hash);
|
||||
|
||||
if (lstat(targetfile, &stat) == 0) {
|
||||
if (verify_file(file, targetfile)) {
|
||||
free(targetfile);
|
||||
pthread_mutex_unlock(&bucket->mutex);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
free(targetfile);
|
||||
|
||||
// hash not in queue and not present in staged
|
||||
|
||||
// clean up in case any prior download failed in a partial state
|
||||
string_or_die(&tar_dotfile, "%s/download/.%s.tar", STATE_DIR, file->hash);
|
||||
unlink(tar_dotfile);
|
||||
free(tar_dotfile);
|
||||
|
||||
// queue the hash for download
|
||||
iter = bucket->list;
|
||||
if ((iter = list_prepend_data(iter, file)) == NULL) {
|
||||
pthread_mutex_unlock(&bucket->mutex);
|
||||
return -1;
|
||||
}
|
||||
bucket->list = iter;
|
||||
|
||||
pthread_mutex_unlock(&bucket->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hysteresis thresholds */
|
||||
static int MAX_XFER = 25;
|
||||
static int MAX_XFER_BOTTOM = 15;
|
||||
|
||||
int start_full_download(bool pipelining)
|
||||
{
|
||||
int i;
|
||||
|
||||
failed = NULL;
|
||||
for (i = 0; i < SWUPD_CURL_HASH_BUCKETS; i++) {
|
||||
pthread_mutex_init(&swupd_curl_hashmap[i].mutex, NULL);
|
||||
}
|
||||
|
||||
mcurl = curl_multi_init();
|
||||
if (mcurl == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* we want to not do HTTP pipelining once things have failed once.. in case some transpoxy in the middle
|
||||
* is even more broken than average. This at least will allow the user to update, albeit slowly.
|
||||
*/
|
||||
if (pipelining) {
|
||||
curl_multi_setopt(mcurl, CURLMOPT_PIPELINING, 1);
|
||||
} else {
|
||||
/* survival: don't go too parallel in verify/fix loop */
|
||||
MAX_XFER = 1;
|
||||
MAX_XFER_BOTTOM = 1;
|
||||
}
|
||||
|
||||
printf("Starting download of remaining update content. This may take a while...\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_curl_list_data(void *data)
|
||||
{
|
||||
struct file *file = (struct file*)data;
|
||||
CURL *curl = file->curl;
|
||||
if (curl != NULL) {
|
||||
curl_multi_remove_handle(mcurl, curl);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
||||
void clean_curl_multi_queue(void)
|
||||
{
|
||||
int i;
|
||||
struct swupd_curl_hashbucket* bucket;
|
||||
|
||||
for (i = 0; i < SWUPD_CURL_HASH_BUCKETS; i++) {
|
||||
bucket = &swupd_curl_hashmap[i];
|
||||
pthread_mutex_lock(&bucket->mutex);
|
||||
list_free_list_and_data(bucket->list, free_curl_list_data);
|
||||
bucket->list = NULL;
|
||||
pthread_mutex_unlock(&bucket->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* list the tarfile content, and verify it contains only one line equal to the expected hash.
|
||||
* loop through all the content to detect the case where archive contains more than one file.
|
||||
*/
|
||||
static int check_tarfile_content(struct file *file, const char *tarfilename)
|
||||
{
|
||||
int err;
|
||||
char *tarcommand;
|
||||
FILE *tar;
|
||||
int count = 0;
|
||||
|
||||
string_or_die(&tarcommand, "tar -tf %s/download/%s.tar 2> /dev/null", STATE_DIR, file->hash);
|
||||
|
||||
err = access(tarfilename, R_OK);
|
||||
if (err) {
|
||||
goto free_tarcommand;
|
||||
}
|
||||
|
||||
tar = popen(tarcommand, "r");
|
||||
if (tar == NULL) {
|
||||
err = -1;
|
||||
goto free_tarcommand;
|
||||
}
|
||||
|
||||
while (!feof(tar)) {
|
||||
char *c;
|
||||
char buffer[PATH_MAXLEN];
|
||||
|
||||
if (fgets(buffer, PATH_MAXLEN, tar) == NULL) {
|
||||
if (count != 1) {
|
||||
err = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
c = strchr(buffer, '\n');
|
||||
if (c) {
|
||||
*c = 0;
|
||||
}
|
||||
if (c && (c != buffer) && (*(c-1)=='/')) {
|
||||
/* strip trailing '/' from directory tar */
|
||||
*(c-1) = 0;
|
||||
}
|
||||
if (strcmp(buffer, file->hash) != 0) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
pclose(tar);
|
||||
free_tarcommand:
|
||||
free(tarcommand);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function will break if the same HASH.tar full file is downloaded
|
||||
* multiple times in parallel. */
|
||||
static void untar_full_download(void *data)
|
||||
{
|
||||
struct file *file = data;
|
||||
char *tarfile;
|
||||
char *tar_dotfile;
|
||||
char *targetfile;
|
||||
struct stat stat;
|
||||
int err;
|
||||
char *tarcommand;
|
||||
|
||||
string_or_die(&tar_dotfile, "%s/download/.%s.tar", STATE_DIR, file->hash);
|
||||
string_or_die(&tarfile, "%s/download/%s.tar", STATE_DIR, file->hash);
|
||||
string_or_die(&targetfile, "%s/staged/%s", STATE_DIR, file->hash);
|
||||
|
||||
/* If valid target file already exists, we're done.
|
||||
* NOTE: this should NEVER happen given the checking that happens
|
||||
* ahead of queueing a download. But... */
|
||||
if (lstat(targetfile, &stat) == 0) {
|
||||
if (verify_file(file, targetfile)) {
|
||||
unlink(tar_dotfile);
|
||||
unlink(tarfile);
|
||||
free(tar_dotfile);
|
||||
free(tarfile);
|
||||
free(targetfile);
|
||||
return;
|
||||
} else {
|
||||
unlink(tarfile);
|
||||
unlink(targetfile);
|
||||
}
|
||||
} else if (lstat(tarfile, &stat) == 0) {
|
||||
/* remove tar file from possible past failure */
|
||||
unlink(tarfile);
|
||||
}
|
||||
|
||||
err = rename(tar_dotfile, tarfile);
|
||||
if (err) {
|
||||
free(tar_dotfile);
|
||||
goto exit;
|
||||
}
|
||||
free(tar_dotfile);
|
||||
|
||||
err = check_tarfile_content(file, tarfile);
|
||||
if (err) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* modern tar will automatically determine the compression type used */
|
||||
string_or_die(&tarcommand, "tar -C %s/staged/ " TAR_PERM_ATTR_ARGS " -xf %s 2> /dev/null",
|
||||
STATE_DIR, tarfile);
|
||||
|
||||
err = system(tarcommand);
|
||||
if (WIFEXITED(err)) {
|
||||
err = WEXITSTATUS(err);
|
||||
}
|
||||
free(tarcommand);
|
||||
if (err) {
|
||||
printf("ignoring tar extract failure for fullfile %s.tar (ret %d)\n",
|
||||
file->hash, err);
|
||||
goto exit;
|
||||
/* FIXME: can we respond meaningfully to tar error codes?
|
||||
* symlink untars may have perm/xattr complaints and non-zero
|
||||
* tar return, but symlink (probably?) untarred ok.
|
||||
*
|
||||
* Also getting complaints on some new regular files?
|
||||
*
|
||||
* Either way we verify the hash later, so on error there,
|
||||
* something could try to recover? */
|
||||
} else {
|
||||
/* Only unlink when tar succeeded, so we can examine the tar file
|
||||
* in the failure case. */
|
||||
unlink(tarfile);
|
||||
}
|
||||
|
||||
err = lstat(targetfile, &stat);
|
||||
exit:
|
||||
free(tarfile);
|
||||
free(targetfile);
|
||||
if (err) {
|
||||
unlink_all_staged_content(file);
|
||||
}
|
||||
}
|
||||
|
||||
static int perform_curl_io_and_complete(int *left)
|
||||
{
|
||||
CURLMsg *msg;
|
||||
long ret;
|
||||
CURLMcode curlm_ret;
|
||||
CURLcode curl_ret;
|
||||
|
||||
curlm_ret = curl_multi_perform(mcurl, left);
|
||||
if (curlm_ret != CURLM_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
CURL *handle;
|
||||
struct file *file;
|
||||
|
||||
msg = curl_multi_info_read(mcurl, left);
|
||||
if (!msg) {
|
||||
break;
|
||||
}
|
||||
if (msg->msg != CURLMSG_DONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handle = msg->easy_handle;
|
||||
ret = 404;
|
||||
curl_ret = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &ret);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char **)&file);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
curl_easy_cleanup(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The easy handle may have an error set, even if the server returns
|
||||
* HTTP 200, so retry the download for this case. */
|
||||
if (ret == 200 && msg->data.result != CURLE_OK) {
|
||||
printf("Error for %s download: %s\n", file->hash,
|
||||
curl_easy_strerror(msg->data.result));
|
||||
failed = list_prepend_data(failed, file);
|
||||
} else if (ret == 200) {
|
||||
/* When both web server and CURL report success, only then
|
||||
* proceed to uncompress. */
|
||||
untar_full_download(file);
|
||||
} else if (ret == 0) {
|
||||
/* For this case, the CURLINFO_RESPONSE_CODE(3) man page states:
|
||||
* "The stored value will be zero if no server response has been received."
|
||||
* This seems to indicate misuse of libcurl, so report this condition.
|
||||
*/
|
||||
printf("error: received http \"0\" response for %s download\n",
|
||||
file->hash);
|
||||
failed = list_prepend_data(failed, file);
|
||||
} else {
|
||||
char *url = NULL;
|
||||
|
||||
printf("error: received %ld response for download\n", ret);
|
||||
failed = list_prepend_data(failed, file);
|
||||
curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
|
||||
|
||||
unlink_all_staged_content(file);
|
||||
}
|
||||
if (file->staging) {
|
||||
free(file->staging);
|
||||
file->staging = NULL;
|
||||
}
|
||||
|
||||
/* NOTE: Intentionally no removal of file from hashmap. All needed files
|
||||
* need determined and queued in one complete preparation phase. Once all
|
||||
* needed files are all present, they can be staged. Otherwise a complex
|
||||
* datastructure and retries are needed to insure only one download of a file
|
||||
* happens fully to success AND a HASH.tar is uncompressed to and HASH and
|
||||
* staged to the _multiple_ filenames with that hash. */
|
||||
|
||||
curl_multi_remove_handle(mcurl, handle);
|
||||
curl_easy_cleanup(handle);
|
||||
file->curl = NULL;
|
||||
}
|
||||
|
||||
curlm_ret = curl_multi_perform(mcurl, left);
|
||||
if (curlm_ret != CURLM_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2 limits, so that we can have hysteresis in behavior. We let the caller
|
||||
* add new transfer up until the queue reaches the high threshold. At this point
|
||||
* we don't return to the caller and instead process the queue until its len
|
||||
* gets below the low threshold */
|
||||
static int poll_fewer_than(int xfer_queue_high, int xfer_queue_low)
|
||||
{
|
||||
int left;
|
||||
CURLMcode curlm_ret;
|
||||
|
||||
curlm_ret = curl_multi_perform(mcurl, &left);
|
||||
if (curlm_ret != CURLM_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (left <= xfer_queue_high) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (left > xfer_queue_low) {
|
||||
usleep(500); /* TODO this really ought to be a select() statement */
|
||||
if (perform_curl_io_and_complete(&left) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* full_download() attempts to enqueue a file for later asynchronous download
|
||||
- NOTE: See swupd_curl_get_file() for single file synchronous downloads. */
|
||||
void full_download(struct file *file)
|
||||
{
|
||||
char *url = NULL;
|
||||
CURL *curl = NULL;
|
||||
int ret = -EFULLDOWNLOAD;
|
||||
char *filename = NULL;
|
||||
CURLMcode curlm_ret = CURLM_OK;
|
||||
CURLcode curl_ret = CURLE_OK;
|
||||
|
||||
ret = swupd_curl_hashmap_insert(file);
|
||||
if (ret > 0) { /* no download needed */
|
||||
/* File already exists - report success */
|
||||
ret = 0;
|
||||
goto out_good;
|
||||
} else if (ret < 0) { /* error */
|
||||
goto out_bad;
|
||||
} /* else (ret == 0) download needed */
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl == NULL) {
|
||||
goto out_bad;
|
||||
}
|
||||
file->curl = curl;
|
||||
|
||||
ret = poll_fewer_than(MAX_XFER, MAX_XFER_BOTTOM);
|
||||
if (ret) {
|
||||
clean_curl_multi_queue();
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
string_or_die(&url, "%s/%i/files/%s.tar", preferred_content_url, file->last_change, file->hash);
|
||||
|
||||
string_or_die(&filename, "%s/download/.%s.tar", STATE_DIR, file->hash);
|
||||
file->staging = filename;
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void*)file);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto out_bad;
|
||||
}
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, swupd_download_file);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto out_bad;
|
||||
}
|
||||
curl_ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)file);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
curl_ret = swupd_curl_set_basic_options(curl, url);
|
||||
if (curl_ret != CURLE_OK) {
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
curlm_ret = curl_multi_add_handle(mcurl, curl);
|
||||
if (curlm_ret != CURLM_OK) {
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if (poll_fewer_than(MAX_XFER + 10, MAX_XFER) != 0) {
|
||||
clean_curl_multi_queue();
|
||||
}
|
||||
ret = 0;
|
||||
goto out_good;
|
||||
|
||||
out_bad:
|
||||
failed = list_prepend_data(failed, file);
|
||||
if (curl != NULL) {
|
||||
/* Must remove handle out of multi queue first!*/
|
||||
curl_multi_remove_handle(mcurl, curl);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
free(filename);
|
||||
out_good:
|
||||
free(url);
|
||||
}
|
||||
|
||||
struct list *end_full_download(void)
|
||||
{
|
||||
int left;
|
||||
int err;
|
||||
|
||||
printf("Finishing download of update content...\n");
|
||||
|
||||
if (poll_fewer_than(0, 0) == 0) {
|
||||
err = perform_curl_io_and_complete(&left);
|
||||
if (err) {
|
||||
clean_curl_multi_queue();
|
||||
}
|
||||
}
|
||||
|
||||
curl_multi_cleanup(mcurl);
|
||||
return failed;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
|
||||
void dump_file_descriptor_leaks(void)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
dir = opendir("/proc/self/fd");
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
char *filename;
|
||||
char buffer[PATH_MAXLEN + 1];
|
||||
entry = readdir(dir);
|
||||
size_t size;
|
||||
if (!entry) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(entry->d_name, ".") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
/* skip stdin/out/err */
|
||||
if (strcmp(entry->d_name, "0") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (strcmp(entry->d_name, "1") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (strcmp(entry->d_name, "2") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we hold an fd open, the one from opendir above */
|
||||
sprintf(buffer, "%i", dirfd(dir));
|
||||
if (strcmp(entry->d_name, buffer) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string_or_die(&filename, "/proc/self/fd/%s", entry->d_name);
|
||||
memset(&buffer, 0, sizeof(buffer));
|
||||
size = readlink(filename, buffer, PATH_MAXLEN);
|
||||
if (size) {
|
||||
printf("Possible filedescriptor leak: fd_number=\"%s\",fd_details=\"%s\"\n", entry->d_name, buffer);
|
||||
}
|
||||
free(filename);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2014-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Regis Merlino <regis.merlino@intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool force = false;
|
||||
bool verify_esp_only;
|
||||
bool verify_bundles_only = false;
|
||||
int update_count = 0;
|
||||
int update_skip = 0;
|
||||
bool need_update_boot = false;
|
||||
bool need_update_bootloader = false;
|
||||
bool update_complete = false;
|
||||
#if 0
|
||||
/* disabled unused global variables */
|
||||
bool ignore_config = true;
|
||||
bool ignore_state = true;
|
||||
#endif
|
||||
bool ignore_orphans = true;
|
||||
bool fix = false;
|
||||
char *format_string = NULL;
|
||||
char *path_prefix = NULL; /* must always end in '/' */
|
||||
char *mounted_dirs = NULL;
|
||||
char *bundle_to_add = NULL;
|
||||
struct timeval start_time;
|
||||
|
||||
/* NOTE: Today the content and version server urls are the same in
|
||||
* all cases. It is highly likely these will eventually differ, eg:
|
||||
* swupd-version.01.org and swupd-files.01.org as this enables
|
||||
* different quality of server and control of the servers
|
||||
*/
|
||||
bool download_only;
|
||||
bool have_manifest_diskspace = false; /* assume no until checked */
|
||||
bool have_network = false; /* assume no access until proved */
|
||||
#define URL_COUNT 2
|
||||
char *version_server_urls[URL_COUNT] = {
|
||||
NULL,
|
||||
"https://download.clearlinux.org/update",
|
||||
};
|
||||
char *content_server_urls[URL_COUNT] = {
|
||||
NULL,
|
||||
"https://download.clearlinux.org/update",
|
||||
};
|
||||
char *preferred_version_url;
|
||||
char *preferred_content_url;
|
||||
long update_server_port = -1;
|
||||
|
||||
#define SWUPD_DEFAULT_FORMAT "3"
|
||||
bool set_format_string(char *userinput)
|
||||
{
|
||||
int version;
|
||||
|
||||
if (userinput == NULL) {
|
||||
if (format_string) {
|
||||
free(format_string);
|
||||
}
|
||||
string_or_die(&format_string, "%s", SWUPD_DEFAULT_FORMAT);
|
||||
return true;
|
||||
}
|
||||
|
||||
// allow "staging" as a format string
|
||||
if ((strcmp(userinput, "staging") == 0)) {
|
||||
if (format_string) {
|
||||
free(format_string);
|
||||
}
|
||||
string_or_die(&format_string, "%s", userinput);
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, expect a positive integer
|
||||
errno = 0;
|
||||
version = strtoull(userinput, NULL, 10);
|
||||
if ((errno < 0) || (version <= 0)) {
|
||||
return false;
|
||||
}
|
||||
if (format_string) {
|
||||
free(format_string);
|
||||
}
|
||||
string_or_die(&format_string, "%d", version);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init_globals(void)
|
||||
{
|
||||
struct stat statbuf;
|
||||
int ret;
|
||||
|
||||
gettimeofday(&start_time, NULL);
|
||||
|
||||
/* pick urls simply from user specified or default */
|
||||
if (version_server_urls[0] != NULL) {
|
||||
preferred_version_url = version_server_urls[0];
|
||||
} else {
|
||||
preferred_version_url = version_server_urls[1];
|
||||
}
|
||||
if (content_server_urls[0] != NULL) {
|
||||
preferred_content_url = content_server_urls[0];
|
||||
} else {
|
||||
preferred_content_url = content_server_urls[1];
|
||||
}
|
||||
|
||||
/* insure path_prefix is absolute, at least '/', ends in '/',
|
||||
* and is a valid dir */
|
||||
if (path_prefix != NULL) {
|
||||
int len;
|
||||
char *tmp;
|
||||
|
||||
if (path_prefix[0] != '/') {
|
||||
char *cwd;
|
||||
|
||||
cwd = get_current_dir_name();
|
||||
if (cwd == NULL) {
|
||||
printf("Unable to getwd() (%s)\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
string_or_die(&tmp, "%s/%s", cwd, path_prefix);
|
||||
|
||||
free(path_prefix);
|
||||
path_prefix = tmp;
|
||||
free(cwd);
|
||||
}
|
||||
|
||||
len = strlen(path_prefix);
|
||||
if (!len || (path_prefix[len-1] != '/')) {
|
||||
string_or_die(&tmp, "%s/", path_prefix);
|
||||
free(path_prefix);
|
||||
path_prefix = tmp;
|
||||
}
|
||||
} else {
|
||||
string_or_die(&path_prefix, "/");
|
||||
}
|
||||
ret = stat(path_prefix, &statbuf);
|
||||
if (ret != 0 || !S_ISDIR(statbuf.st_mode)) {
|
||||
printf("Bad path_prefix %s (%s), cannot continue.\n",
|
||||
path_prefix, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_globals(void)
|
||||
{
|
||||
free(content_server_urls[0]);
|
||||
free(version_server_urls[0]);
|
||||
free(path_prefix);
|
||||
free(format_string);
|
||||
free(mounted_dirs);
|
||||
if (bundle_to_add != NULL) {
|
||||
free(bundle_to_add);
|
||||
}
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include "swupd.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
void hash_assign(char *src, char *dst)
|
||||
{
|
||||
memcpy(dst, src, SWUPD_HASH_LEN-1);
|
||||
dst[SWUPD_HASH_LEN-1] = '\0';
|
||||
}
|
||||
|
||||
bool hash_compare(char *hash1, char *hash2)
|
||||
{
|
||||
if (bcmp(hash1, hash2, SWUPD_HASH_LEN-1) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hash_is_zeros(char *hash)
|
||||
{
|
||||
return hash_compare("0000000000000000000000000000000000000000000000000000000000000000", hash);
|
||||
}
|
||||
|
||||
static void hash_set_zeros(char *hash)
|
||||
{
|
||||
hash_assign("0000000000000000000000000000000000000000000000000000000000000000", hash);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static bool hash_is_ones(char *hash)
|
||||
{
|
||||
return hash_compare("1111111111111111111111111111111111111111111111111111111111111111", hash);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hash_set_ones(char *hash)
|
||||
{
|
||||
hash_assign("1111111111111111111111111111111111111111111111111111111111111111", hash);
|
||||
}
|
||||
|
||||
static void hmac_sha256_for_data(char *hash,
|
||||
const unsigned char *key, size_t key_len,
|
||||
const unsigned char *data, size_t data_len)
|
||||
{
|
||||
unsigned char digest[EVP_MAX_MD_SIZE];
|
||||
unsigned int digest_len = 0;
|
||||
char *digest_str;
|
||||
unsigned int i;
|
||||
|
||||
if (data == NULL) {
|
||||
hash_set_zeros(hash);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HMAC(EVP_sha256(), (const void *)key, key_len, data, data_len, digest, &digest_len) == NULL) {
|
||||
hash_set_zeros(hash);
|
||||
return;
|
||||
}
|
||||
|
||||
digest_str = calloc((digest_len * 2) + 1, sizeof(char));
|
||||
if (digest_str == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
for (i = 0; i < digest_len; i++) {
|
||||
sprintf(&digest_str[i * 2], "%02x", (unsigned int)digest[i]);
|
||||
}
|
||||
|
||||
hash_assign(digest_str, hash);
|
||||
free(digest_str);
|
||||
}
|
||||
|
||||
static void hmac_sha256_for_string(char *hash,
|
||||
const unsigned char *key, size_t key_len,
|
||||
const char *str)
|
||||
{
|
||||
if (str == NULL) {
|
||||
hash_set_zeros(hash);
|
||||
return;
|
||||
}
|
||||
|
||||
hmac_sha256_for_data(hash, key, key_len, (const unsigned char *)str, strlen(str));
|
||||
}
|
||||
|
||||
static void hmac_compute_key(const char *filename,
|
||||
const struct update_stat *updt_stat,
|
||||
char *key, size_t *key_len, bool use_xattrs)
|
||||
{
|
||||
char *xattrs_blob = (void *)0xdeadcafe;
|
||||
size_t xattrs_blob_len = 0;
|
||||
|
||||
if (use_xattrs) {
|
||||
xattrs_get_blob(filename, &xattrs_blob, &xattrs_blob_len);
|
||||
}
|
||||
|
||||
hmac_sha256_for_data(key, (const unsigned char *)updt_stat,
|
||||
sizeof(struct update_stat),
|
||||
(const unsigned char *)xattrs_blob,
|
||||
xattrs_blob_len);
|
||||
|
||||
if (hash_is_zeros(key)) {
|
||||
*key_len = 0;
|
||||
} else {
|
||||
*key_len = SWUPD_HASH_LEN-1;
|
||||
}
|
||||
|
||||
if (xattrs_blob_len != 0) {
|
||||
free(xattrs_blob);
|
||||
}
|
||||
}
|
||||
|
||||
/* provide a wrapper for compute_hash() because we want a cheap-out option in
|
||||
* case we are looking for missing files only:
|
||||
* zeros hash: file missing
|
||||
* ones hash: file present */
|
||||
int compute_hash_lazy(struct file *file, char *filename)
|
||||
{
|
||||
struct stat sb;
|
||||
if (lstat(filename, &sb) == 0) {
|
||||
hash_set_ones(file->hash);
|
||||
} else {
|
||||
hash_set_zeros(file->hash);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function MUST be kept in sync with the server
|
||||
* return is -1 if there was an error. If the file does not exist,
|
||||
* a "0000000..." hash is returned as is our convention in the manifest
|
||||
* for deleted files. Otherwise file->hash is set to a non-zero hash. */
|
||||
/* TODO: how should we properly handle compute_hash() failures? */
|
||||
int compute_hash(struct file *file, char *filename)
|
||||
{
|
||||
int ret;
|
||||
char key[SWUPD_HASH_LEN];
|
||||
size_t key_len;
|
||||
unsigned char *blob;
|
||||
FILE *fl;
|
||||
|
||||
if (file->is_deleted) {
|
||||
hash_set_zeros(file->hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hash_set_zeros(key);
|
||||
|
||||
if (file->is_link) {
|
||||
char link[PATH_MAXLEN];
|
||||
memset(link, 0, PATH_MAXLEN);
|
||||
|
||||
ret = readlink(filename, link, PATH_MAXLEN - 1);
|
||||
|
||||
if (ret >= 0) {
|
||||
hmac_compute_key(filename, &file->stat, key, &key_len, file->use_xattrs);
|
||||
hmac_sha256_for_string(file->hash,
|
||||
(const unsigned char *)key,
|
||||
key_len,
|
||||
link);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->is_dir) {
|
||||
hmac_compute_key(filename, &file->stat, key, &key_len, file->use_xattrs);
|
||||
hmac_sha256_for_string(file->hash,
|
||||
(const unsigned char *)key,
|
||||
key_len,
|
||||
file->filename); //file->filename not filename
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if we get here, this is a regular file */
|
||||
fl = fopen(filename, "r");
|
||||
if (!fl) {
|
||||
return -1;
|
||||
}
|
||||
blob = mmap(NULL, file->stat.st_size, PROT_READ, MAP_PRIVATE, fileno(fl), 0);
|
||||
if (blob == MAP_FAILED && file->stat.st_size != 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
hmac_compute_key(filename, &file->stat, key, &key_len, file->use_xattrs);
|
||||
hmac_sha256_for_data(file->hash,
|
||||
(const unsigned char *)key,
|
||||
key_len,
|
||||
blob,
|
||||
file->stat.st_size);
|
||||
munmap(blob, file->stat.st_size);
|
||||
fclose(fl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool verify_file(struct file *file, char *filename)
|
||||
{
|
||||
struct file *local = calloc(1, sizeof(struct file));
|
||||
|
||||
if (local == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
local->filename = file->filename;
|
||||
local->use_xattrs = true;
|
||||
|
||||
populate_file_struct(local, filename);
|
||||
if (compute_hash(local, filename) != 0) {
|
||||
free(local);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if manifest hash matches local file hash */
|
||||
if (hash_compare(file->hash, local->hash)) {
|
||||
free(local);
|
||||
return true;
|
||||
} else {
|
||||
free(local);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2013-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
||||
* cguiraud <christophe.guiraud@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
/* outputs the hash of a file */
|
||||
|
||||
static struct option opts[] = {
|
||||
{ "no-xattrs", 0, NULL, 'n' },
|
||||
{ "basepath", 1, NULL, 'b' },
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
static void usage(const char *name) {
|
||||
printf("Usage:\n");
|
||||
printf(" swupd %s [OPTION...] filename\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n\n");
|
||||
printf("Application Options:\n");
|
||||
printf(" -n, --no-xattrs Ignore extended attributes\n");
|
||||
printf(" -b, --basepath Optional argument for leading path to filename\n");
|
||||
printf("\n");
|
||||
printf("The filename is the name as it would appear in a Manifest file.\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int hashdump_main(int argc, char **argv)
|
||||
{
|
||||
struct file* file;
|
||||
char *fullname;
|
||||
int ret;
|
||||
|
||||
file = calloc(1, sizeof(struct file));
|
||||
if (!file) {
|
||||
abort();
|
||||
}
|
||||
|
||||
file->use_xattrs = true;
|
||||
|
||||
while (1) {
|
||||
int c;
|
||||
int i;
|
||||
|
||||
c = getopt_long(argc, argv, "nb:h", opts, &i);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch(c) {
|
||||
case 'n':
|
||||
file->use_xattrs = false;
|
||||
break;
|
||||
case 'b':
|
||||
if (!optarg) {
|
||||
printf("Invalid --path argument\n\n");
|
||||
free(file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (path_prefix) { /* multiple -p options */
|
||||
free(path_prefix);
|
||||
}
|
||||
string_or_die(&path_prefix, "%s", optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!init_globals()) {
|
||||
free_globals();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
usage(argv[0]);
|
||||
free_globals();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// mk_full_filename expects absolute filenames (eg: from Manifest)
|
||||
if (argv[optind][0] == '/') {
|
||||
file->filename = strdup(argv[optind]);
|
||||
if (!file->filename) {
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
string_or_die(&file->filename, "/%s", argv[optind]);
|
||||
}
|
||||
|
||||
printf("Calculating hash %s xattrs for: (%s) ... %s\n",
|
||||
(file->use_xattrs ? "with":"without"), path_prefix, file->filename);
|
||||
fullname = mk_full_filename(path_prefix, file->filename);
|
||||
printf("fullname=%s\n", fullname);
|
||||
populate_file_struct(file, fullname);
|
||||
ret = compute_hash(file, fullname);
|
||||
if (ret != 0) {
|
||||
printf("compute_hash() failed\n");
|
||||
} else {
|
||||
printf("%s\n", file->hash);
|
||||
if (file->is_dir) {
|
||||
if (is_directory_mounted(fullname)) {
|
||||
printf("!! dumped hash might not match a manifest hash because a mount is active\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(fullname);
|
||||
free(file->filename);
|
||||
free(file);
|
||||
free_globals();
|
||||
return 0;
|
||||
}
|
||||
+666
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
|
||||
|
||||
void check_root(void)
|
||||
{
|
||||
if (getuid() != 0) {
|
||||
printf("This program must be run as root..aborting.\n\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the contents of a staging directory (eg: /mnt/swupd/update/780 or
|
||||
* /mnt/swupd/update/delta) which are not supposed to contain
|
||||
* subdirectories containing files, ie: no need for true recursive removal.
|
||||
* Just the relative path (et: "780" or "delta" is passed as a parameter).
|
||||
*
|
||||
* return: 0 on success, non-zero on error
|
||||
*/
|
||||
int rm_staging_dir_contents(const char *rel_path)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent entry;
|
||||
struct dirent *result;
|
||||
char *filename;
|
||||
char *abs_path;
|
||||
int ret;
|
||||
|
||||
string_or_die(&abs_path, "%s/%s", STATE_DIR, rel_path);
|
||||
|
||||
dir = opendir(abs_path);
|
||||
if (dir == NULL) {
|
||||
free(abs_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((ret = readdir_r(dir, &entry, &result)) == 0) {
|
||||
if (result == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(entry.d_name, ".") ||
|
||||
!strcmp(entry.d_name, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string_or_die(&filename, "%s/%s", abs_path, entry.d_name);
|
||||
|
||||
ret = remove(filename);
|
||||
if (ret != 0) {
|
||||
free(filename);
|
||||
break;
|
||||
}
|
||||
free(filename);
|
||||
}
|
||||
|
||||
free(abs_path);
|
||||
closedir(dir);
|
||||
|
||||
sync();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unlink_all_staged_content(struct file *file)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
/* downloaded tar file */
|
||||
string_or_die(&filename, "%s/download/%s.tar", STATE_DIR, file->hash);
|
||||
unlink(filename);
|
||||
free(filename);
|
||||
string_or_die(&filename, "%s/download/.%s.tar", STATE_DIR, file->hash);
|
||||
unlink(filename);
|
||||
free(filename);
|
||||
|
||||
/* downloaded and un-tar'd file */
|
||||
string_or_die(&filename, "%s/staged/%s", STATE_DIR, file->hash);
|
||||
if (file->is_dir) {
|
||||
rmdir(filename);
|
||||
} else {
|
||||
unlink(filename);
|
||||
}
|
||||
free(filename);
|
||||
|
||||
/* delta file */
|
||||
if (file->peer) {
|
||||
string_or_die(&filename, "%s/delta/%i-%i-%s", STATE_DIR,
|
||||
file->peer->last_change, file->last_change, file->hash);
|
||||
unlink(filename);
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
|
||||
FILE * fopen_exclusive(const char *filename) /* no mode, opens for write only */
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(filename,O_CREAT | O_EXCL | O_RDWR , 00600);
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return fdopen(fd, "w");
|
||||
}
|
||||
|
||||
int create_required_dirs(void)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
char *dir;
|
||||
#define STATE_DIR_COUNT 3
|
||||
const char *dirs[] = {"delta","staged","download"};
|
||||
struct stat buf;
|
||||
bool missing = false;
|
||||
|
||||
// check for existance
|
||||
ret = stat(STATE_DIR, &buf);
|
||||
if (ret && (errno == ENOENT)) {
|
||||
missing = true;
|
||||
}
|
||||
for (i = 0; i < STATE_DIR_COUNT; i++) {
|
||||
string_or_die(&dir, "%s/%s", STATE_DIR, dirs[i]);
|
||||
ret = stat(dir, &buf);
|
||||
if (ret) {
|
||||
missing = true;
|
||||
}
|
||||
free(dir);
|
||||
}
|
||||
|
||||
if (missing) { // (re)create dirs
|
||||
char *cmd;
|
||||
|
||||
// laziness here for want of a simple "mkdir -p"
|
||||
string_or_die(&cmd, "mkdir -p %s/{delta,staged,download}", STATE_DIR);
|
||||
ret = system(cmd);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
free(cmd);
|
||||
|
||||
// chmod 700
|
||||
ret = chmod(STATE_DIR, S_IRWXU);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < STATE_DIR_COUNT; i++) {
|
||||
string_or_die(&dir, "%s/%s", STATE_DIR, dirs[i]);
|
||||
ret = chmod(dir, S_IRWXU);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
free(dir);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* store a colon separated list of current mountpoint into
|
||||
* variable mounted_dirs, this function do not return a value.
|
||||
*
|
||||
* e.g: :/proc:/mnt/acct:
|
||||
*/
|
||||
void get_mounted_directories(void)
|
||||
{
|
||||
FILE *file;
|
||||
char *line = NULL;
|
||||
char *mnt;
|
||||
char *tmp;
|
||||
ssize_t ret;
|
||||
char *c;
|
||||
size_t n;
|
||||
|
||||
file = fopen("/proc/self/mountinfo", "r");
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!feof(file)) {
|
||||
ret = getline(&line, &n, file);
|
||||
if ((ret < 0) || (line == NULL)) {
|
||||
break;
|
||||
}
|
||||
|
||||
c = strchr(line, '\n');
|
||||
if (c) {
|
||||
*c = 0;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
mnt = strtok(line, " ");
|
||||
while (mnt != NULL) {
|
||||
if (n == 4) {
|
||||
/* The "4" assumes today's mountinfo form of:
|
||||
* 16 36 0:3 / /proc rw,relatime master:7 - proc proc rw
|
||||
* where the fifth field is the mountpoint. */
|
||||
if (strcmp(mnt, "/") == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mounted_dirs == NULL) {
|
||||
string_or_die(&mounted_dirs, "%s", ":");
|
||||
}
|
||||
tmp = mounted_dirs;
|
||||
string_or_die(&mounted_dirs, "%s%s:", tmp, mnt);
|
||||
free(tmp);
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
mnt = strtok(NULL, " ");
|
||||
}
|
||||
free(line);
|
||||
line = NULL;
|
||||
}
|
||||
free(line);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
// prepends prefix to an path (eg: the global path_prefix to a
|
||||
// file->filename or some other path prefix and path), insuring there
|
||||
// is no duplicate '/' at the strings' junction and no trailing '/'
|
||||
char *mk_full_filename(const char *prefix, const char *path)
|
||||
{
|
||||
char *fname = NULL;
|
||||
char *abspath;
|
||||
|
||||
if (path[0] == '/') {
|
||||
abspath = strdup(path);
|
||||
} else {
|
||||
string_or_die(&abspath, "/%s", path);
|
||||
}
|
||||
if (abspath == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
// The prefix is a minimum of "/" or "". If the prefix is only that,
|
||||
// just use abspath. If the prefix is longer than the minimal, insure
|
||||
// it ends in not "/" and append abspath.
|
||||
if ((strcmp(prefix, "/") == 0) ||
|
||||
(strcmp(prefix, "") == 0)) {
|
||||
// rootfs, use absolute path
|
||||
fname = strdup(abspath);
|
||||
if (fname == NULL) {
|
||||
abort();
|
||||
}
|
||||
} else if (strcmp(&prefix[strlen(prefix)-1], "/") == 0) {
|
||||
// chroot and need to strip trailing "/" from prefix
|
||||
char *tmp = strdup(prefix);
|
||||
if (tmp == NULL) {
|
||||
abort();
|
||||
}
|
||||
tmp[strlen(tmp) - 1] = '\0';
|
||||
|
||||
string_or_die(&fname, "%s%s", tmp, abspath);
|
||||
free(tmp);
|
||||
} else {
|
||||
// chroot and no need to strip trailing "/" from prefix
|
||||
string_or_die(&fname, "%s%s", prefix, abspath);
|
||||
}
|
||||
free(abspath);
|
||||
return fname;
|
||||
}
|
||||
|
||||
// expects filename w/o path_prefix prepended
|
||||
bool is_directory_mounted(const char *filename)
|
||||
{
|
||||
char *fname;
|
||||
bool ret = false;
|
||||
char *tmp;
|
||||
|
||||
if (mounted_dirs == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = mk_full_filename(path_prefix, filename);
|
||||
string_or_die(&fname, ":%s:", tmp);
|
||||
free(tmp);
|
||||
|
||||
if (strstr(mounted_dirs, fname)) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
free(fname);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// expects filename w/o path_prefix prepended
|
||||
bool is_under_mounted_directory(const char *filename)
|
||||
{
|
||||
bool ret = false;
|
||||
int err;
|
||||
char *token;
|
||||
char *mountpoint;
|
||||
char *dir;
|
||||
char *fname;
|
||||
char *tmp;
|
||||
|
||||
if (mounted_dirs == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dir = strdup(mounted_dirs);
|
||||
if (dir == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
token = strtok(dir + 1, ":");
|
||||
while (token != NULL) {
|
||||
string_or_die(&mountpoint, "%s/", token);
|
||||
|
||||
tmp = mk_full_filename(path_prefix, filename);
|
||||
string_or_die(&fname, ":%s:", tmp);
|
||||
free(tmp);
|
||||
|
||||
err = strncmp(fname, mountpoint, strlen(mountpoint));
|
||||
free(fname);
|
||||
if (err == 0) {
|
||||
free(mountpoint);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
token = strtok(NULL, ":");
|
||||
|
||||
free(mountpoint);
|
||||
}
|
||||
|
||||
free(dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int swupd_rm_file(const char *path)
|
||||
{
|
||||
int err = unlink(path);
|
||||
if (err) {
|
||||
if (errno == ENOENT) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int swupd_rm_dir(const char *path)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent entry;
|
||||
struct dirent *result;
|
||||
char *filename = NULL;
|
||||
int ret, err;
|
||||
|
||||
dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
if (errno == ENOENT) {
|
||||
ret = 0;
|
||||
goto exit;
|
||||
} else {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
while ((ret = readdir_r(dir, &entry, &result)) == 0) {
|
||||
if (result == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(entry.d_name, ".") ||
|
||||
!strcmp(entry.d_name, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
free(filename);
|
||||
string_or_die(&filename, "%s/%s", path, entry.d_name);
|
||||
|
||||
err = swupd_rm(filename);
|
||||
if (err) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete directory once it's empty */
|
||||
err = rmdir(path);
|
||||
if (err) {
|
||||
if (errno == ENOENT) {
|
||||
} else {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
closedir(dir);
|
||||
free(filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int swupd_rm(const char *filename) {
|
||||
struct stat stat;
|
||||
int ret;
|
||||
|
||||
ret = lstat(filename, &stat);
|
||||
if (ret) {
|
||||
if (errno == ENOENT) {
|
||||
// Quiet, no real failure here
|
||||
return -ENOENT;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (S_ISDIR(stat.st_mode)) {
|
||||
ret = swupd_rm_dir(filename);
|
||||
} else {
|
||||
ret = swupd_rm_file(filename);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rm_bundle_file(const char *bundle)
|
||||
{
|
||||
char *filename = NULL;
|
||||
int ret = 0;
|
||||
struct stat statb;
|
||||
|
||||
string_or_die(&filename, "%s/%s/%s", path_prefix, BUNDLES_DIR, bundle);
|
||||
|
||||
if (stat(filename, &statb) == -1) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (S_ISREG(statb.st_mode)) {
|
||||
if (unlink(filename) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(filename);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
void dump_file_info(struct file *file)
|
||||
{
|
||||
printf("%s:\n", file->filename);
|
||||
printf("\t%s\n", file->hash);
|
||||
printf("\t%d\n", file->last_change);
|
||||
|
||||
if (file->use_xattrs) {
|
||||
printf("\tuse_xattrs\n");
|
||||
}
|
||||
if (file->is_dir) {
|
||||
printf("\tis_dir\n");
|
||||
}
|
||||
if (file->is_file) {
|
||||
printf("\tis_file\n");
|
||||
}
|
||||
if (file->is_link) {
|
||||
printf("\tis_link\n");
|
||||
}
|
||||
if (file->is_deleted) {
|
||||
printf("\tis_deleted\n");
|
||||
}
|
||||
if (file->is_manifest) {
|
||||
printf("\tis_manifest\n");
|
||||
}
|
||||
if (file->is_config) {
|
||||
printf("\tis_config\n");
|
||||
}
|
||||
if (file->is_state) {
|
||||
printf("\tis_state\n");
|
||||
}
|
||||
if (file->is_boot) {
|
||||
printf("\tis_boot\n");
|
||||
}
|
||||
if (file->is_rename) {
|
||||
printf("\tis_rename\n");
|
||||
}
|
||||
if (file->is_orphan) {
|
||||
printf("\tis_orphan\n");
|
||||
}
|
||||
if (file->do_not_update) {
|
||||
printf("\tdo_not_update\n");
|
||||
}
|
||||
|
||||
if (file->peer) {
|
||||
printf("\tpeer %s(%s)\n", file->peer->filename, file->peer->hash);
|
||||
}
|
||||
if (file->deltapeer) {
|
||||
printf("\tdeltapeer %s(%s)\n", file->deltapeer->filename, file->deltapeer->hash);
|
||||
}
|
||||
if (file->staging) {
|
||||
printf("\tstaging %s\n", file->staging);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void free_file_data(void *data)
|
||||
{
|
||||
struct file *file = (struct file *) data;
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* peer and deltapeer are pointers to files contained
|
||||
* in another list and must not be disposed */
|
||||
|
||||
if (file->filename) {
|
||||
free(file->filename);
|
||||
}
|
||||
|
||||
if (file->header) {
|
||||
free(file->header);
|
||||
}
|
||||
|
||||
if (file->staging) {
|
||||
free(file->staging);
|
||||
}
|
||||
|
||||
free(file);
|
||||
}
|
||||
|
||||
/* this function is intended to encapsulate the basic swupd
|
||||
* initializations for the majority of commands, that is:
|
||||
* - Make sure root is the user running the code
|
||||
* - Initialize log facility
|
||||
* - Get the lock
|
||||
* - initialize mounted directories
|
||||
* - Initialize curl
|
||||
*/
|
||||
int swupd_init(int *lock_fd)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
check_root();
|
||||
*lock_fd = p_lockfile();
|
||||
if (*lock_fd < 0) {
|
||||
ret = ELOCK_FILE;
|
||||
goto out_fds;
|
||||
}
|
||||
|
||||
get_mounted_directories();
|
||||
|
||||
if (swupd_curl_init() != 0) {
|
||||
ret = ECURL_INIT;
|
||||
goto out_close_lock;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
out_close_lock:
|
||||
v_lockfile(*lock_fd);
|
||||
out_fds:
|
||||
dump_file_descriptor_leaks();
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* this function prints the initial message for all utils
|
||||
*/
|
||||
void copyright_header(const char *name)
|
||||
{
|
||||
printf(PACKAGE " %s " VERSION "\n", name);
|
||||
printf(" Copyright (C) 2012-2016 Intel Corporation\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void string_or_die(char **strp, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(strp, fmt, ap) <= 0) {
|
||||
abort();
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void update_motd(int new_release)
|
||||
{
|
||||
FILE *motd_fp = NULL;
|
||||
|
||||
motd_fp = fopen(MOTD_FILE, "w");
|
||||
|
||||
if (motd_fp != NULL) {
|
||||
fprintf(motd_fp, "There is a new OS version available: %d\n", new_release);
|
||||
fprintf(motd_fp, "Upgrade to the latest version using 'swupd update'\n");
|
||||
fclose(motd_fp);
|
||||
}
|
||||
}
|
||||
|
||||
void delete_motd(void)
|
||||
{
|
||||
if (unlink(MOTD_FILE) == ENOMEM) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int is_dirname_link(const char *fullname)
|
||||
{
|
||||
int ret = -1;
|
||||
char *real_path = NULL;
|
||||
real_path = realpath(fullname, NULL);
|
||||
if (!real_path) {
|
||||
printf("Failed to get real path of %s\n", fullname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(real_path, fullname) != 0) {
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
free(real_path);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
/* trailing slash is to indicate dir itself is expected to exist, but
|
||||
* contents are ignored */
|
||||
bool is_config(char *filename)
|
||||
{
|
||||
if (strncmp(filename, "/etc/", 5) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void config_file_heuristics(struct file *file)
|
||||
{
|
||||
if (is_config(file->filename)) {
|
||||
file->is_config = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* trailing slash is to indicate dir itself is expected to exist, but
|
||||
* contents are ignored */
|
||||
bool is_state(char *filename)
|
||||
{
|
||||
if (is_directory_mounted(filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_under_mounted_directory(filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((strlen(filename) == 14) && (strncmp(filename, "/usr/src/debug", 14) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((strncmp(filename, "/data", 5) == 0) ||
|
||||
(strncmp(filename, "/dev/", 5) == 0) ||
|
||||
(strncmp(filename, "/home/", 6) == 0) ||
|
||||
(strncmp(filename, "/lost+found", 11) == 0) ||
|
||||
(strncmp(filename, "/proc/", 6) == 0) ||
|
||||
(strncmp(filename, "/root/", 6) == 0) ||
|
||||
(strncmp(filename, "/run/", 5) == 0) ||
|
||||
(strncmp(filename, "/sys/", 5) == 0) ||
|
||||
(strncmp(filename, "/tmp/", 5) == 0) ||
|
||||
(strncmp(filename, "/usr/src/", 9) == 0) ||
|
||||
(strncmp(filename, "/var/", 5) == 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void runtime_state_heuristics(struct file *file)
|
||||
{
|
||||
if (is_state(file->filename)) {
|
||||
file->is_state = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void boot_file_heuristics(struct file *file)
|
||||
{
|
||||
if ((strncmp(file->filename, "/boot/", 6) == 0) ||
|
||||
(strncmp(file->filename, "/usr/lib/modules/", 17) == 0)) {
|
||||
file->is_boot = 1;
|
||||
}
|
||||
|
||||
if (strncmp(file->filename, "/usr/lib/kernel/", 16) == 0) {
|
||||
file->is_boot = 1;
|
||||
need_update_boot = true;
|
||||
}
|
||||
|
||||
if ((strncmp(file->filename, "/usr/lib/gummiboot", 18) == 0) ||
|
||||
(strncmp(file->filename, "/usr/bin/gummiboot", 18) == 0) ||
|
||||
(strncmp(file->filename, "/usr/bin/bootctl", 16) == 0) ||
|
||||
(strncmp(file->filename, "/usr/lib/systemd/boot", 21) == 0)) {
|
||||
file->is_boot = 1;
|
||||
need_update_bootloader = true;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_heuristics(struct file *file)
|
||||
{
|
||||
runtime_state_heuristics(file);
|
||||
boot_file_heuristics(file);
|
||||
config_file_heuristics(file);
|
||||
}
|
||||
|
||||
bool ignore(struct file *file)
|
||||
{
|
||||
if ((file->is_config) ||
|
||||
is_config(file->filename) || // ideally we trust the manifest but short term reapply check here
|
||||
(file->is_state) ||
|
||||
is_state(file->filename) || // ideally we trust the manifest but short term reapply check here
|
||||
(file->is_boot && fix && file->is_deleted) || // shouldn't happen
|
||||
(file->is_boot && !fix && !file->is_deleted) || // default ignore
|
||||
(ignore_orphans && file->is_orphan)) {
|
||||
update_skip++;
|
||||
file->do_not_update = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* pplaquet <paul.plaquette@intel.com>
|
||||
* Eric Lapuyade <eric.lapuyade@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "list.h"
|
||||
|
||||
static struct list *list_append_item(struct list *list, struct list *item)
|
||||
{
|
||||
list = list_tail(list);
|
||||
if (list) {
|
||||
list->next = item;
|
||||
item->prev = list;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static struct list *list_prepend_item(struct list *list, struct list *item)
|
||||
{
|
||||
list = list_head(list);
|
||||
if (list) {
|
||||
list->prev = item;
|
||||
item->next = list;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static struct list *list_alloc_item(void *data)
|
||||
{
|
||||
struct list *item;
|
||||
|
||||
item = (struct list *)malloc(sizeof(struct list));
|
||||
if (item) {
|
||||
item->data = data;
|
||||
item->next = NULL;
|
||||
item->prev = NULL;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// Merges two sorted lists
|
||||
static struct list *list_merge(struct list *list1, struct list *list2, comparison_fn_t comparison_fn) {
|
||||
struct list *merged_list = NULL;
|
||||
struct list *merged_list_head = NULL;
|
||||
while (list1 && list2) {
|
||||
if (comparison_fn(list1->data, list2->data) < 0) {
|
||||
if (merged_list) {
|
||||
merged_list->next = list1;
|
||||
list1->prev = merged_list;
|
||||
}
|
||||
merged_list = list1;
|
||||
list1 = list1->next;
|
||||
} else {
|
||||
if (merged_list) {
|
||||
merged_list->next = list2;
|
||||
list2->prev = merged_list;
|
||||
}
|
||||
merged_list = list2;
|
||||
list2 = list2->next;
|
||||
}
|
||||
if (!merged_list_head) {
|
||||
merged_list_head = merged_list;
|
||||
}
|
||||
}
|
||||
if (list1 != NULL) {
|
||||
merged_list->next = list1;
|
||||
list1->prev = merged_list;
|
||||
}
|
||||
if (list2 != NULL) {
|
||||
merged_list->next = list2;
|
||||
list2->prev = merged_list;
|
||||
}
|
||||
return merged_list_head;
|
||||
}
|
||||
|
||||
/* Splits a list into two halves to be merged */
|
||||
static struct list *list_merge_sort(struct list *left, unsigned int len, comparison_fn_t comparison_fn)
|
||||
{
|
||||
struct list *right;
|
||||
unsigned int left_len = len / 2;
|
||||
unsigned int right_len = len / 2 + len % 2;
|
||||
unsigned int i;
|
||||
|
||||
if (!left) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len == 1) {
|
||||
return left;
|
||||
}
|
||||
|
||||
right = left;
|
||||
|
||||
/* Split into left and right lists */
|
||||
for (i = 0; i < left_len; i++) {
|
||||
right = right->next;
|
||||
}
|
||||
right->prev->next = NULL;
|
||||
right->prev = NULL;
|
||||
|
||||
/* Recurse */
|
||||
left = list_merge_sort(left, left_len, comparison_fn);
|
||||
right = list_merge_sort(right, right_len, comparison_fn);
|
||||
return list_merge(left, right, comparison_fn);
|
||||
}
|
||||
|
||||
/* ------------ Public API ------------ */
|
||||
|
||||
struct list *list_append_data(struct list *list, void *data)
|
||||
{
|
||||
struct list *item = NULL;
|
||||
|
||||
item = list_alloc_item(data);
|
||||
if (item) {
|
||||
item = list_append_item(list, item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
struct list *list_prepend_data(struct list *list, void *data)
|
||||
{
|
||||
struct list *item = NULL;
|
||||
|
||||
item = list_alloc_item(data);
|
||||
if (item) {
|
||||
item = list_prepend_item(list, item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
struct list *list_head(struct list *item)
|
||||
{
|
||||
if (item) {
|
||||
while (item->prev) {
|
||||
item = item->prev;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
struct list *list_tail(struct list *item)
|
||||
{
|
||||
if (item) {
|
||||
while (item->next) {
|
||||
item = item->next;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
unsigned int list_len(struct list *list)
|
||||
{
|
||||
unsigned int len;
|
||||
struct list *item;
|
||||
|
||||
if (list == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = 1;
|
||||
|
||||
item = list;
|
||||
while ((item = item->next) != NULL) {
|
||||
len++;
|
||||
}
|
||||
|
||||
item = list;
|
||||
while ((item = item->prev) != NULL) {
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct list *list_find_data(struct list *list, void *data)
|
||||
{
|
||||
list = list_head(list);
|
||||
while (list) {
|
||||
if (list->data == data) {
|
||||
return list;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct list *list_sort(struct list *list, comparison_fn_t comparison_fn)
|
||||
{
|
||||
list = list_head(list);
|
||||
unsigned int len = list_len(list);
|
||||
return list_merge_sort(list, len, comparison_fn);
|
||||
}
|
||||
|
||||
struct list *list_concat(struct list *list1, struct list *list2)
|
||||
{
|
||||
struct list *tail;
|
||||
|
||||
list2 = list_head(list2);
|
||||
|
||||
if (list1 == NULL) {
|
||||
return list2;
|
||||
}
|
||||
|
||||
list1 = list_head(list1);
|
||||
|
||||
if (list2) {
|
||||
tail = list_tail(list1);
|
||||
|
||||
tail->next = list2;
|
||||
list2->prev = tail;
|
||||
}
|
||||
|
||||
return list1;
|
||||
}
|
||||
|
||||
struct list *list_free_item(struct list *item, list_free_data_fn_t list_free_data_fn)
|
||||
{
|
||||
struct list *ret_item;
|
||||
|
||||
if (item->prev) {
|
||||
item->prev->next = item->next;
|
||||
ret_item = item->prev;
|
||||
} else {
|
||||
ret_item = item->next;
|
||||
}
|
||||
|
||||
if (item->next) {
|
||||
item->next->prev = item->prev;
|
||||
}
|
||||
|
||||
if (list_free_data_fn) {
|
||||
list_free_data_fn(item->data);
|
||||
}
|
||||
|
||||
free(item);
|
||||
|
||||
return ret_item;
|
||||
}
|
||||
|
||||
void list_free_list_and_data(struct list *list, list_free_data_fn_t list_free_data_fn)
|
||||
{
|
||||
struct list *item = list_head(list);
|
||||
|
||||
while (item) {
|
||||
item = list_free_item(item, list_free_data_fn);
|
||||
}
|
||||
}
|
||||
|
||||
void list_free_list(struct list *list)
|
||||
{
|
||||
list_free_list_and_data(list, NULL);
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
* Eric Lapuyade <eric.lapuyade@intel.com>
|
||||
* Jim Kukunas <james.t.kukunas@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
|
||||
static int mklockdir(void)
|
||||
{
|
||||
char *cmd;
|
||||
int ret;
|
||||
struct stat buf;
|
||||
|
||||
memset(&buf, 0, sizeof(struct stat));
|
||||
ret = stat(LOCK_DIR, &buf);
|
||||
if ((ret == 0) && (S_ISDIR(buf.st_mode))) {
|
||||
return 0;
|
||||
} else if (force) {
|
||||
/* lock dir is not a directory, so with force, clean it up */
|
||||
unlink(LOCK_DIR);
|
||||
}
|
||||
|
||||
string_or_die(&cmd, "mkdir -m 755 -p %s", LOCK_DIR);
|
||||
|
||||
ret = system(cmd);
|
||||
free(cmd);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to get a write lock region on the lock file. Returns:
|
||||
* >= 0 an fcntl region lock'd fd or exits with a positive error
|
||||
* code and a recommended course of action for user.
|
||||
*/
|
||||
int p_lockfile(void)
|
||||
{
|
||||
int lock_fd, ret;
|
||||
pid_t pid = getpid();
|
||||
struct flock fl = {
|
||||
.l_type = F_WRLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
.l_pid = pid,
|
||||
};
|
||||
char *lockfile;
|
||||
|
||||
if (mklockdir() < 0) {
|
||||
printf("Error: Unable to create lock dir: '%s'\n", LOCK_DIR);
|
||||
if (!force) {
|
||||
printf(" Solution: Create manually or re-run with '--force'\n");
|
||||
printf("Operation Failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf(" --force used. Attempting to continue\n");
|
||||
}
|
||||
|
||||
string_or_die(&lockfile, "%s/swupd_lock", LOCK_DIR);
|
||||
|
||||
/* open lock file */
|
||||
lock_fd = open(lockfile, O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||
free(lockfile);
|
||||
|
||||
if (lock_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* try to get an advisory region lock for the file */
|
||||
ret = fcntl(lock_fd, F_SETLK, &fl);
|
||||
if (ret == -1) {
|
||||
if ((errno == EAGAIN) || (errno == EACCES)) {
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
close(lock_fd);
|
||||
return ret;
|
||||
} else {
|
||||
/* speculatively dump our pid in the file,
|
||||
* that may be useful for debug */
|
||||
ret = ftruncate(lock_fd, 0);
|
||||
ret = write(lock_fd, &pid, sizeof(pid));
|
||||
|
||||
/* our lock_fd represents the lock */
|
||||
return lock_fd;
|
||||
}
|
||||
}
|
||||
|
||||
/* closes lock fd and must not unlink lock file (else race allowed) */
|
||||
void v_lockfile(int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <libgen.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
static bool cmd_line_status = false;
|
||||
|
||||
static const struct option prog_opts[] = {
|
||||
{"download", no_argument, 0, 'd'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"url", required_argument, 0, 'u'},
|
||||
{"port", required_argument, 0, 'P'},
|
||||
{"contenturl", required_argument, 0, 'c'},
|
||||
{"versionurl", required_argument, 0, 'v'},
|
||||
{"status", no_argument, 0, 's'},
|
||||
{"format", required_argument, 0, 'F'},
|
||||
{"path", required_argument, 0, 'p'},
|
||||
{"force", no_argument, 0, 'x'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static void print_help(const char *name) {
|
||||
printf("Usage:\n");
|
||||
printf(" swupd %s [OPTION...]\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n\n");
|
||||
printf("Application Options:\n");
|
||||
printf(" -d, --download Download all content, but do not actually install the update\n");
|
||||
#warning remove user configurable url when alternative exists
|
||||
printf(" -u, --url=[URL] RFC-3986 encoded url for version string and content file downloads\n");
|
||||
printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n");
|
||||
#warning remove user configurable content url when alternative exists
|
||||
printf(" -c, --contenturl=[URL] RFC-3986 encoded url for content file downloads\n");
|
||||
#warning remove user configurable version url when alternative exists
|
||||
printf(" -v, --versionurl=[URL] RFC-3986 encoded url for version string download\n");
|
||||
printf(" -s, --status Show current OS version and latest version available on server\n");
|
||||
printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n");
|
||||
printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n");
|
||||
printf(" -x, --force Attempt to proceed even if non-critical errors found\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static bool parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
//set default initial values
|
||||
set_format_string(NULL);
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hxdu:P:c:v:sF:p:", prog_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case '?':
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'd':
|
||||
download_only = true;
|
||||
break;
|
||||
case 'u':
|
||||
if (!optarg) {
|
||||
printf("Invalid --url argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
if (content_server_urls[0]) {
|
||||
free(content_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
string_or_die(&content_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'P':
|
||||
if (sscanf(optarg, "%ld", &update_server_port) != 1) {
|
||||
printf("Invalid --port argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (!optarg) {
|
||||
printf("Invalid --contenturl argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (content_server_urls[0]) {
|
||||
free(content_server_urls[0]);
|
||||
}
|
||||
string_or_die(&content_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'v':
|
||||
if (!optarg) {
|
||||
printf("Invalid --versionurl argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 's':
|
||||
cmd_line_status = true;
|
||||
break;
|
||||
case 'F':
|
||||
if (!optarg || !set_format_string(optarg)) {
|
||||
printf("Invalid --format argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'p': /* default empty path_prefix updates the running OS */
|
||||
if (!optarg) {
|
||||
printf("Invalid --path argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (path_prefix) { /* multiple -p options */
|
||||
free(path_prefix);
|
||||
}
|
||||
string_or_die(&path_prefix, "%s", optarg);
|
||||
break;
|
||||
case 'x':
|
||||
force = true;
|
||||
break;
|
||||
default:
|
||||
printf("Unrecognized option\n\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!init_globals()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
print_help(argv[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void print_versions()
|
||||
{
|
||||
int current_version, server_version;
|
||||
|
||||
swupd_curl_init();
|
||||
read_versions(¤t_version, ¤t_version, &server_version, path_prefix);
|
||||
|
||||
if (current_version < 0) {
|
||||
printf("Cannot determine current OS version\n");
|
||||
} else {
|
||||
printf("Current OS version: %d\n", current_version);
|
||||
}
|
||||
|
||||
if (server_version < 0) {
|
||||
printf("Cannot get latest the server version.Could not reach server\n");
|
||||
} else {
|
||||
printf("Latest server version: %d\n", server_version);
|
||||
}
|
||||
}
|
||||
|
||||
int update_main(int argc, char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
copyright_header("software update");
|
||||
|
||||
if (!parse_options(argc, argv)) {
|
||||
free_globals();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (cmd_line_status) {
|
||||
print_versions();
|
||||
} else {
|
||||
ret = main_update();
|
||||
}
|
||||
free_globals();
|
||||
return ret;
|
||||
}
|
||||
+1130
File diff suppressed because it is too large
Load Diff
+134
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd-build-variant.h"
|
||||
#include "swupd.h"
|
||||
#include "signature.h"
|
||||
|
||||
|
||||
static int download_pack(int oldversion, int newversion, char *module)
|
||||
{
|
||||
FILE *tarfile = NULL;
|
||||
char *tar = NULL;
|
||||
char *url = NULL;
|
||||
int err = -1;
|
||||
char *filename;
|
||||
struct stat stat;
|
||||
|
||||
string_or_die(&filename, "%s/pack-%s-from-%i-to-%i.tar", STATE_DIR, module, oldversion, newversion);
|
||||
|
||||
err = lstat(filename, &stat);
|
||||
if (err == 0 && stat.st_size == 0) {
|
||||
free(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Downloading %s pack for version %i\n", module, newversion);
|
||||
|
||||
string_or_die(&url, "%s/%i/pack-%s-from-%i.tar", preferred_content_url, newversion, module, oldversion);
|
||||
|
||||
err = swupd_curl_get_file(url, filename, NULL, NULL, true);
|
||||
if (err) {
|
||||
free(url);
|
||||
if ((lstat(filename, &stat) == 0) && (stat.st_size == 0)) {
|
||||
unlink(filename);
|
||||
}
|
||||
free(filename);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!signature_download_and_verify(url, filename)) {
|
||||
free(url);
|
||||
unlink(filename);
|
||||
free(filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(url);
|
||||
|
||||
printf("Extracting pack.\n");
|
||||
string_or_die(&tar, "tar -C %s " TAR_PERM_ATTR_ARGS " -xf %s/pack-%s-from-%i-to-%i.tar 2> /dev/null",
|
||||
STATE_DIR, STATE_DIR, module, oldversion, newversion);
|
||||
|
||||
err = system(tar);
|
||||
if (WIFEXITED(err)) {
|
||||
err = WEXITSTATUS(err);
|
||||
}
|
||||
free(tar);
|
||||
unlink(filename);
|
||||
/* make a zero sized file to prevent redownload */
|
||||
tarfile = fopen(filename, "w");
|
||||
free(filename);
|
||||
if (tarfile) {
|
||||
fclose(tarfile);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* pull in packs for base and any subscription */
|
||||
int download_subscribed_packs(int oldversion, int UNUSED_PARAM newversion, bool required)
|
||||
{
|
||||
struct list *iter;
|
||||
struct sub *sub = NULL;
|
||||
int err;
|
||||
|
||||
if (!check_network()) {
|
||||
return -ENOSWUPDSERVER;
|
||||
}
|
||||
|
||||
iter = list_head(subs);
|
||||
while (iter) {
|
||||
sub = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (sub->oldversion == sub->version) { // pack didn't change in this release
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldversion != 0) {
|
||||
oldversion = sub->oldversion;
|
||||
}
|
||||
|
||||
err = download_pack(oldversion, sub->version, sub->component);
|
||||
if (err < 0) {
|
||||
if (required) {
|
||||
return err;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
||||
* Tudor Marcu <tudor.marcu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
static void update_kernel(void)
|
||||
{
|
||||
char *kernel_update_cmd = NULL;
|
||||
|
||||
if (strcmp("/", path_prefix) == 0) {
|
||||
string_or_die(&kernel_update_cmd, "/usr/bin/kernel_updater.sh");
|
||||
} else {
|
||||
string_or_die(&kernel_update_cmd, "%s/usr/bin/kernel_updater.sh --path %s", path_prefix, path_prefix);
|
||||
}
|
||||
|
||||
system(kernel_update_cmd);
|
||||
free(kernel_update_cmd);
|
||||
}
|
||||
|
||||
static void update_bootloader(void)
|
||||
{
|
||||
char *bootloader_update_cmd = NULL;
|
||||
|
||||
if (strcmp("/", path_prefix) == 0) {
|
||||
string_or_die(&bootloader_update_cmd, "/usr/bin/gummiboot_updaters.sh");
|
||||
} else {
|
||||
string_or_die(&bootloader_update_cmd, "%s/usr/bin/gummiboot_updaters.sh --path %s", path_prefix, path_prefix);
|
||||
}
|
||||
|
||||
system(bootloader_update_cmd);
|
||||
free(bootloader_update_cmd);
|
||||
|
||||
if (strcmp("/", path_prefix) == 0) {
|
||||
string_or_die(&bootloader_update_cmd, "/usr/bin/systemdboot_updater.sh");
|
||||
} else {
|
||||
string_or_die(&bootloader_update_cmd, "%s/usr/bin/systemdboot_updater.sh --path %s", path_prefix, path_prefix);
|
||||
}
|
||||
|
||||
system(bootloader_update_cmd);
|
||||
free(bootloader_update_cmd);
|
||||
}
|
||||
|
||||
static void update_triggers(void)
|
||||
{
|
||||
system("/usr/bin/systemctl daemon-reload");
|
||||
system("/usr/bin/systemctl restart update-triggers.target");
|
||||
}
|
||||
|
||||
void run_scripts(void)
|
||||
{
|
||||
printf("Calling post-update helper scripts.\n");
|
||||
|
||||
/* path_prefix aware helper */
|
||||
if (need_update_boot) {
|
||||
update_kernel();
|
||||
} else {
|
||||
printf("No kernel update needed, skipping helper call out.\n");
|
||||
}
|
||||
|
||||
if (need_update_bootloader) {
|
||||
update_bootloader();
|
||||
} else {
|
||||
printf("No bootloader update needed, skipping helper call out.\n");
|
||||
}
|
||||
|
||||
/* helpers which don't run when path_prefix is set */
|
||||
if (strcmp("/", path_prefix) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Crudely call post-update hooks after every update...FIXME */
|
||||
update_triggers();
|
||||
}
|
||||
|
||||
/* Run any "mandatory" pre-update scripts needed. In this case, mandatory
|
||||
* means the script must run, but it is not yet fatal if the script does not
|
||||
* return success */
|
||||
void run_preupdate_scripts(struct manifest *manifest)
|
||||
{
|
||||
struct list *iter = list_tail(manifest->files);
|
||||
struct file *file;
|
||||
struct stat sb;
|
||||
char *script;
|
||||
|
||||
string_or_die(&script, "/usr/bin/clr_pre_update.sh");
|
||||
|
||||
if (stat(script, &sb) == -1) {
|
||||
free(script);
|
||||
return;
|
||||
}
|
||||
|
||||
while (iter != NULL) {
|
||||
file = iter->data;
|
||||
iter = iter->prev;
|
||||
|
||||
if (strcmp(file->filename, script) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check that system file matches file in manifest */
|
||||
if (verify_file(file, script)) {
|
||||
system(script);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(script);
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Tom Keel <thomas.keel@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "signature.h"
|
||||
#include "swupd.h"
|
||||
|
||||
|
||||
/*
|
||||
* Implementation flavors:
|
||||
* FAKE ..... do nothing, always return success
|
||||
* FORGIVE .. do everything, always return success
|
||||
* REAL ..... do everything, return the real status
|
||||
*/
|
||||
#define IMPL_FAKE 0
|
||||
#define IMPL_FORGIVE 1
|
||||
#define IMPL_REAL 2
|
||||
|
||||
#warning TODO pick signing scheme
|
||||
#if defined(SWUPD_LINUX_ROOTFS)
|
||||
#define IMPL IMPL_FAKE
|
||||
#endif
|
||||
|
||||
#if IMPL != IMPL_FAKE
|
||||
|
||||
static X509_STORE *create_store(const char *, const char *, const char *);
|
||||
|
||||
static char *VERIF_FAIL = "Signature verification failed";
|
||||
static char *XSTORE_FAIL = "XSTORE creation failed";
|
||||
|
||||
static bool initialized = false;
|
||||
|
||||
static X509_STORE *x509_store = NULL;
|
||||
|
||||
bool signature_initialize(const char *ca_cert_filename)
|
||||
{
|
||||
if (initialized) {
|
||||
return true;
|
||||
}
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
x509_store = create_store(ca_cert_filename, NULL, NULL);
|
||||
if (x509_store == NULL) {
|
||||
ERR_free_strings(); // undoes ERR_load_crypto_strings
|
||||
EVP_cleanup(); // undoes OpenSSL_add_all_algorithms
|
||||
return false || (IMPL == IMPL_FORGIVE);
|
||||
}
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void signature_terminate(void)
|
||||
{
|
||||
if (initialized) {
|
||||
X509_STORE_free(x509_store); // undocumented...
|
||||
ERR_free_strings(); // undoes ERR_load_crypto_strings
|
||||
EVP_cleanup(); // undoes OpenSSL_add_all_algorithms
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool signature_verify(const char *data_filename, const char *sig_filename)
|
||||
{
|
||||
BIO *bio_data = NULL;
|
||||
BIO *bio_sig = NULL;
|
||||
PKCS7 *pkcs7 = NULL;
|
||||
int ret;
|
||||
bool result = false;
|
||||
|
||||
if (!initialized) {
|
||||
return false || (IMPL == IMPL_FORGIVE);
|
||||
}
|
||||
bio_data = BIO_new_file(data_filename, "r"); // i.e. fopen
|
||||
if (bio_data == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
bio_sig = BIO_new_file(sig_filename, "r"); // i.e. fopen
|
||||
if (bio_sig == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
pkcs7 = PEM_read_bio_PKCS7(bio_sig, NULL, NULL, NULL);
|
||||
if (pkcs7 == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
ret = PKCS7_verify(pkcs7, NULL, x509_store, bio_data, NULL, 0);
|
||||
if (ret != 1) {
|
||||
goto exit;
|
||||
}
|
||||
result = true;
|
||||
exit:
|
||||
/*
|
||||
* The free functions below tolerate NULL arguments.
|
||||
* The documentation doesn't really say so, but both testing and
|
||||
* examination of openssl source code confirm that such is the case.
|
||||
*/
|
||||
PKCS7_free(pkcs7); // undocumented...
|
||||
BIO_free(bio_sig); // i.e. fclose
|
||||
BIO_free(bio_data); // i.e. fclose
|
||||
return result || (IMPL == IMPL_FORGIVE);
|
||||
}
|
||||
|
||||
static X509_STORE *create_store(const char *ca_filename, const char *ca_dirname,
|
||||
const char *crl_filename)
|
||||
{
|
||||
X509_STORE *store = X509_STORE_new();
|
||||
|
||||
if (!store) {
|
||||
return NULL;
|
||||
}
|
||||
if (X509_STORE_load_locations(store, ca_filename, ca_dirname) != 1) {
|
||||
goto err;
|
||||
}
|
||||
if (X509_STORE_set_default_paths(store) != 1) {
|
||||
goto err;
|
||||
}
|
||||
if (crl_filename) {
|
||||
X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
|
||||
if (!lookup) {
|
||||
goto err;
|
||||
}
|
||||
if (X509_load_crl_file(lookup, crl_filename, X509_FILETYPE_PEM) != 1) {
|
||||
goto err;
|
||||
}
|
||||
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
|
||||
}
|
||||
return store;
|
||||
err:
|
||||
X509_STORE_free(x509_store);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool signature_download_and_verify(const char *data_url, const char *data_filename)
|
||||
{
|
||||
char *sig_url;
|
||||
char *sig_filename;
|
||||
int ret;
|
||||
bool result;
|
||||
|
||||
string_or_die(&sig_url, "%s.signed", data_url);
|
||||
|
||||
string_or_die(&sig_filename, "%s.signed", data_filename);
|
||||
|
||||
ret = swupd_curl_get_file(sig_url, sig_filename, NULL, NULL, false);
|
||||
if (ret) {
|
||||
result = false;
|
||||
} else {
|
||||
result = signature_verify(data_filename, sig_filename);
|
||||
}
|
||||
if (!result) {
|
||||
unlink(sig_filename);
|
||||
}
|
||||
free(sig_filename);
|
||||
free(sig_url);
|
||||
return result || (IMPL == IMPL_FORGIVE);
|
||||
}
|
||||
|
||||
void signature_delete(const char *data_filename)
|
||||
{
|
||||
char *sig_filename;
|
||||
|
||||
string_or_die(&sig_filename, "%s.signed", data_filename);
|
||||
|
||||
unlink(sig_filename);
|
||||
|
||||
free(sig_filename);
|
||||
}
|
||||
|
||||
#else // IMPL == IMPL_FAKE
|
||||
|
||||
bool signature_initialize(const char UNUSED_PARAM *ca_cert_filename)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void signature_terminate(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool signature_verify(const char UNUSED_PARAM *data_filename, const char UNUSED_PARAM *sig_filename)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool signature_download_and_verify(const char UNUSED_PARAM *data_url, const char UNUSED_PARAM *data_filename)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void signature_delete(const char UNUSED_PARAM *data_filename)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // IMPL == IMPL_FAKE
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd-build-variant.h"
|
||||
#include "swupd.h"
|
||||
|
||||
/* clean then recreate temporary folder for tar renames */
|
||||
static int create_staging_renamedir(char *rename_tmpdir)
|
||||
{
|
||||
int ret;
|
||||
char *rmcommand = NULL;
|
||||
|
||||
string_or_die(&rmcommand, "rm -fr %s", rename_tmpdir);
|
||||
if (!system(rmcommand)) {
|
||||
/* Not fatal but pretty scary, likely to really fail at the
|
||||
* next command too. Pass for now as printing may just cause
|
||||
* confusion */
|
||||
;
|
||||
}
|
||||
free(rmcommand);
|
||||
|
||||
ret = mkdir(rename_tmpdir, S_IRWXU);
|
||||
if (ret == -1 && errno != EEXIST) {
|
||||
ret = -errno;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do the staging of new files into the filesystem */
|
||||
#warning do_staging is currently not able to be run in parallel
|
||||
/* Consider adding a remove_leftovers() that runs in verify/fix in order to
|
||||
* allow this function to mkdtemp create folders for parallel build */
|
||||
int do_staging(struct file *file)
|
||||
{
|
||||
char *statfile = NULL, *tmp = NULL, *tmp2 = NULL;
|
||||
char *dir, *base, *rel_dir;
|
||||
char *tarcommand = NULL;
|
||||
char *original = NULL;
|
||||
char *target = NULL;
|
||||
char *targetpath = NULL;
|
||||
char *symbase = NULL;
|
||||
char *rename_target = NULL;
|
||||
char *rename_tmpdir = NULL;
|
||||
int ret;
|
||||
struct stat s;
|
||||
|
||||
tmp = strdup(file->filename);
|
||||
tmp2 = strdup(file->filename);
|
||||
|
||||
dir = dirname(tmp);
|
||||
base = basename(tmp2);
|
||||
|
||||
rel_dir = dir;
|
||||
if (*dir == '/') {
|
||||
rel_dir = dir + 1;
|
||||
}
|
||||
|
||||
string_or_die(&original, "%s/staged/%s", STATE_DIR, file->hash);
|
||||
|
||||
string_or_die(&targetpath, "%s%s", path_prefix, rel_dir);
|
||||
ret = stat(targetpath, &s);
|
||||
|
||||
if (S_ISLNK(s.st_mode)) {
|
||||
/* Follow symlink to ultimate target and redo stat */
|
||||
symbase = realpath(targetpath, NULL);
|
||||
if (symbase != NULL) {
|
||||
free(targetpath);
|
||||
targetpath = strdup(symbase);
|
||||
ret = stat(targetpath, &s);
|
||||
free(symbase);
|
||||
}
|
||||
}
|
||||
|
||||
/* For now, just report on error conditions. Once we implement
|
||||
* verify_fix_path(char *path, int targetversion), we'll want to call it here */
|
||||
if ((ret == -1) && (errno == ENOENT)) {
|
||||
printf("Error: Update target directory does not exist: %s\n", targetpath);
|
||||
} else if (!S_ISDIR(s.st_mode)) {
|
||||
printf("Error: Update target exists but is NOT a directory: %s\n", targetpath);
|
||||
}
|
||||
|
||||
free(targetpath);
|
||||
string_or_die(&target, "%s%s/.update.%s", path_prefix, rel_dir, base);
|
||||
ret = swupd_rm(target);
|
||||
if (ret < 0 && ret != -ENOENT) {
|
||||
printf("Error: Failed to remove %s\n", target);
|
||||
}
|
||||
|
||||
string_or_die(&statfile, "%s%s", path_prefix, file->filename);
|
||||
|
||||
memset(&s, 0, sizeof(struct stat));
|
||||
ret = lstat(statfile, &s);
|
||||
if (ret == 0) {
|
||||
if ((file->is_dir && !S_ISDIR(s.st_mode)) ||
|
||||
(file->is_link && !S_ISLNK(s.st_mode)) ||
|
||||
(file->is_file && !S_ISREG(s.st_mode))) {
|
||||
//file type changed, move old out of the way for new
|
||||
ret = swupd_rm(statfile);
|
||||
if (ret < 0) {
|
||||
ret = -ETYPE_CHANGED_FILE_RM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(statfile);
|
||||
|
||||
if (file->is_dir || S_ISDIR(s.st_mode)) {
|
||||
/* In the btrfs only scenario there was an implicit
|
||||
* "create_or_update_dir()" via un-tar-ing a directory.tar after
|
||||
* download and the untar happens in the staging subvolume which
|
||||
* then gets promoted to a "real" usable subvolume. But for
|
||||
* a live rootfs the directory needs copied out of staged
|
||||
* and into the rootfs. Tar is a way to copy with
|
||||
* attributes and it includes internal logic that does the
|
||||
* right thing to overlay a directory onto something
|
||||
* pre-existing: */
|
||||
/* In order to avoid tar transforms with directories, rename
|
||||
* the directory before and after the tar command */
|
||||
string_or_die(&rename_tmpdir, "%s/tmprenamedir", STATE_DIR);
|
||||
ret = create_staging_renamedir(rename_tmpdir);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
string_or_die(&rename_target, "%s/%s", rename_tmpdir, base);
|
||||
if (rename(original, rename_target)) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
string_or_die(&tarcommand, "tar -C %s " TAR_PERM_ATTR_ARGS " -cf - '%s' 2> /dev/null | "
|
||||
"tar -C %s%s " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
|
||||
rename_tmpdir, base, path_prefix, rel_dir);
|
||||
ret = system(tarcommand);
|
||||
if (WIFEXITED(ret)) {
|
||||
ret = WEXITSTATUS(ret);
|
||||
}
|
||||
free(tarcommand);
|
||||
if (rename(rename_target, original)) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
if (ret < 0) {
|
||||
ret = -EDIR_OVERWRITE;
|
||||
goto out;
|
||||
}
|
||||
} else { /* (!file->is_dir && !S_ISDIR(stat.st_mode)) */
|
||||
/* can't naively hard link(): Non-read-only files with same hash must remain
|
||||
* separate copies otherwise modifications to one instance of the file
|
||||
* propagate to all instances of the file perhaps causing subtle data corruption from
|
||||
* a user's perspective. In practice the rootfs is stateless and owned by us.
|
||||
* Additionally cross-mount hardlinks fail and it's hard to know what an admin
|
||||
* might have for overlaid mounts. The use of tar is a simple way to copy, but
|
||||
* inefficient. So prefer hardlink and fall back if needed: */
|
||||
ret = -1;
|
||||
if (!file->is_config && !file->is_state && !file->use_xattrs) {
|
||||
ret = link(original, target);
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* either the hardlink failed, or it was undesirable (config), do a tar-tar dance */
|
||||
/* In order to avoid tar transforms, rename the file
|
||||
* before and after the tar command */
|
||||
string_or_die(&rename_target, "%s/staged/.update.%s", STATE_DIR, base);
|
||||
ret = rename(original, rename_target);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
string_or_die(&tarcommand, "tar -C %s/staged " TAR_PERM_ATTR_ARGS " -cf - '.update.%s' 2> /dev/null | "
|
||||
"tar -C %s%s " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
|
||||
STATE_DIR, base, path_prefix, rel_dir);
|
||||
ret = system(tarcommand);
|
||||
if (WIFEXITED(ret)) {
|
||||
ret = WEXITSTATUS(ret);
|
||||
}
|
||||
free(tarcommand);
|
||||
ret = rename(rename_target, original);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
struct stat buf;
|
||||
int err;
|
||||
|
||||
if (file->staging) {
|
||||
/* this must never happen...full file never finished download */
|
||||
free(file->staging);
|
||||
file->staging = NULL;
|
||||
}
|
||||
|
||||
string_or_die(&file->staging, "%s%s/.update.%s", path_prefix, rel_dir, base);
|
||||
|
||||
err = lstat(file->staging, &buf);
|
||||
if (err != 0) {
|
||||
free(file->staging);
|
||||
file->staging = NULL;
|
||||
ret = -EDOTFILE_WRITE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(target);
|
||||
free(original);
|
||||
free(rename_target);
|
||||
free(rename_tmpdir);
|
||||
free(tmp);
|
||||
free(tmp2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* caller should not call this function for do_not_update marked files */
|
||||
int rename_staged_file_to_final(struct file *file) {
|
||||
int ret;
|
||||
char *target;
|
||||
|
||||
string_or_die(&target, "%s%s", path_prefix, file->filename);
|
||||
|
||||
if (!file->staging && !file->is_deleted && !file->is_dir) {
|
||||
free(target);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file->is_deleted) {
|
||||
ret = swupd_rm(target);
|
||||
|
||||
/* don't count missing ones as errors...
|
||||
* if somebody already deleted them for us then all is well */
|
||||
if ((ret == -ENOENT) || (ret == -ENOTDIR)) {
|
||||
ret = 0;
|
||||
}
|
||||
} else if (file->is_dir) {
|
||||
ret = 0;
|
||||
} else {
|
||||
struct stat stat;
|
||||
ret = lstat(target, &stat);
|
||||
|
||||
/* If the file was previously a directory but no longer, then
|
||||
* we need to move it out of the way.
|
||||
* This should not happen because the server side complains
|
||||
* when creating update content that includes such a state
|
||||
* change. But...you never know. */
|
||||
|
||||
if ((ret == 0) && (S_ISDIR(stat.st_mode))) {
|
||||
char *lostnfound;
|
||||
char *base;
|
||||
|
||||
string_or_die(&lostnfound, "%slost+found", path_prefix);
|
||||
ret = mkdir(lostnfound, S_IRWXU);
|
||||
if ((ret != 0) && (errno != EEXIST)) {
|
||||
free(lostnfound);
|
||||
free(target);
|
||||
return ret;
|
||||
}
|
||||
free(lostnfound);
|
||||
|
||||
base = basename(file->filename);
|
||||
string_or_die(&lostnfound, "%slost+found/%s", path_prefix, base);
|
||||
/* this will fail if the directory was not already emptied */
|
||||
ret = rename(target, lostnfound);
|
||||
if (ret < 0 && errno != ENOTEMPTY && errno != EEXIST) {
|
||||
printf("Error: failed to move %s to lost+found: %s\n",
|
||||
base, strerror(errno));
|
||||
}
|
||||
free(lostnfound);
|
||||
} else {
|
||||
ret = rename(file->staging, target);
|
||||
if (ret < 0) {
|
||||
printf("Error: failed to rename staged %s to final: %s\n",
|
||||
file->hash, strerror(errno));
|
||||
}
|
||||
unlink(file->staging);
|
||||
}
|
||||
}
|
||||
|
||||
free(target);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rename_all_files_to_final(struct list *updates)
|
||||
{
|
||||
int ret, update_errs = 0, update_good = 0, skip = 0;
|
||||
struct list *list;
|
||||
|
||||
list = list_head(updates);
|
||||
while (list) {
|
||||
struct file *file;
|
||||
file = list->data;
|
||||
list = list->next;
|
||||
if (file->do_not_update) {
|
||||
skip += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = rename_staged_file_to_final(file);
|
||||
if (ret != 0) {
|
||||
update_errs += 1;
|
||||
} else {
|
||||
update_good += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return update_count - update_good - update_errs - (update_skip - skip);
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
|
||||
|
||||
|
||||
static int new_files;
|
||||
static int deleted_files;
|
||||
static int changed_files;
|
||||
static int new_manifests;
|
||||
static int deleted_manifests;
|
||||
static int changed_manifests;
|
||||
static int delta_miss;
|
||||
static int delta_hit;
|
||||
|
||||
|
||||
void account_new_file(void)
|
||||
{
|
||||
new_files++;
|
||||
}
|
||||
|
||||
void account_deleted_file(void)
|
||||
{
|
||||
deleted_files++;
|
||||
}
|
||||
|
||||
void account_changed_file(void)
|
||||
{
|
||||
changed_files++;
|
||||
}
|
||||
|
||||
void account_new_manifest(void)
|
||||
{
|
||||
new_manifests++;
|
||||
}
|
||||
|
||||
void account_deleted_manifest(void)
|
||||
{
|
||||
deleted_manifests++;
|
||||
}
|
||||
|
||||
void account_changed_manifest(void)
|
||||
{
|
||||
changed_manifests++;
|
||||
}
|
||||
|
||||
void account_delta_hit(void)
|
||||
{
|
||||
delta_hit++;
|
||||
}
|
||||
|
||||
|
||||
void account_delta_miss(void)
|
||||
{
|
||||
delta_miss++;
|
||||
}
|
||||
|
||||
|
||||
void print_statistics(int version1, int version2)
|
||||
{
|
||||
printf("\n");
|
||||
printf("Statistics for going from version %i to version %i:\n\n", version1, version2);
|
||||
printf(" changed manifests : %i\n", changed_manifests);
|
||||
printf(" new manifests : %i\n", new_manifests);
|
||||
printf(" deleted manifests : %i\n\n", deleted_manifests);
|
||||
printf(" changed files : %i\n", changed_files);
|
||||
printf(" new files : %i\n", new_files);
|
||||
printf(" deleted files : %i\n", deleted_files);
|
||||
printf("\n");
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
|
||||
struct list *subs;
|
||||
|
||||
static void free_subscription_data(void *data)
|
||||
{
|
||||
struct sub *sub = (struct sub *) data;
|
||||
|
||||
free(sub->component);
|
||||
free(sub);
|
||||
}
|
||||
|
||||
|
||||
void free_subscriptions(void)
|
||||
{
|
||||
list_free_list_and_data(subs, free_subscription_data);
|
||||
subs = NULL;
|
||||
}
|
||||
|
||||
struct list *free_bundle(struct list *item)
|
||||
{
|
||||
return list_free_item(item, free_subscription_data);
|
||||
}
|
||||
|
||||
struct list *free_list_file(struct list *item)
|
||||
{
|
||||
return list_free_item(item, free_file_data);
|
||||
}
|
||||
|
||||
void read_subscriptions_alt(void)
|
||||
{
|
||||
char *path = NULL;
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
string_or_die(&path, "%s/%s", path_prefix, BUNDLES_DIR);
|
||||
|
||||
dir = opendir(path);
|
||||
if (dir) {
|
||||
while ((ent = readdir(dir))) {
|
||||
if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) {
|
||||
continue;
|
||||
}
|
||||
if (ent->d_type == DT_REG) {
|
||||
if (component_subscribed(ent->d_name)) {
|
||||
/* This is considered odd since means two files same name on same folder */
|
||||
continue;
|
||||
}
|
||||
|
||||
create_and_append_subscription(ent->d_name);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
free(path);
|
||||
|
||||
/* if nothing was picked up from bundles directory then add os-core by default */
|
||||
if (list_len(subs) == 0) {
|
||||
create_and_append_subscription("os-core");
|
||||
}
|
||||
}
|
||||
|
||||
int component_subscribed(char *component)
|
||||
{
|
||||
struct list *list;
|
||||
struct sub *sub;
|
||||
|
||||
list = list_head(subs);
|
||||
while (list) {
|
||||
sub = list->data;
|
||||
list = list->next;
|
||||
|
||||
if (strcmp(sub->component, component) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int subscription_versions_from_MoM(struct manifest *MoM, int is_old)
|
||||
{
|
||||
struct list *list;
|
||||
struct list *list2;
|
||||
struct file *file;
|
||||
struct sub *sub;
|
||||
bool bundle_found;
|
||||
int ret = 0;
|
||||
|
||||
list = list_head(subs);
|
||||
while (list) {
|
||||
bundle_found = false;
|
||||
sub = list->data;
|
||||
list = list->next;
|
||||
|
||||
list2 = MoM->manifests;
|
||||
while (list2 && !bundle_found) {
|
||||
file = list2->data;
|
||||
list2 = list2->next;
|
||||
|
||||
if (strcmp(sub->component, file->filename) == 0) {
|
||||
if (is_old) {
|
||||
sub->oldversion = file->last_change;
|
||||
} else {
|
||||
sub->version = file->last_change;
|
||||
}
|
||||
bundle_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bundle_found) {
|
||||
sub->version = -1;
|
||||
printf("ERROR: Bundle not in MoM |\"component=%s\"\n", sub->component);
|
||||
ret = EBUNDLE_MISMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void create_and_append_subscription(const char *component)
|
||||
{
|
||||
struct sub *sub;
|
||||
|
||||
sub = calloc(1, sizeof(struct sub));
|
||||
if (!sub) {
|
||||
abort();
|
||||
}
|
||||
|
||||
sub->component = strdup(component);
|
||||
if (sub->component == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
sub->version = 0;
|
||||
sub->oldversion = 0;
|
||||
subs = list_prepend_data(subs, sub);
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2015-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE // for basename()
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "swupd.h"
|
||||
#include "swupd-internal.h"
|
||||
|
||||
struct subcmd {
|
||||
char *name;
|
||||
char *doc;
|
||||
int (*mainfunc)(int, char **);
|
||||
};
|
||||
|
||||
static struct subcmd commands[] = {
|
||||
{ "bundle-add", "Install a new bundle", bundle_add_main },
|
||||
{ "bundle-remove", "Uninstall a bundle", bundle_remove_main },
|
||||
{ "hashdump", "Dumps the HMAC hash of a file", hashdump_main },
|
||||
{ "update", "Update to latest OS version", update_main },
|
||||
{ "verify", "Verify content for OS version", verify_main },
|
||||
{ "check-update", "Checks if a new OS version is available", check_update_main},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct option prog_opts[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "version", no_argument, 0, 'v' },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void print_help(const char *name) {
|
||||
printf("Usage:\n");
|
||||
printf(" %s [OPTION...]\n", basename((char*)name));
|
||||
printf(" or %s [OPTION...] SUBCOMMAND [OPTION...]\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n");
|
||||
printf(" -v, --version Output version information and exit\n\n");
|
||||
printf("Subcommands:\n");
|
||||
|
||||
struct subcmd *entry = commands;
|
||||
|
||||
while (entry->name != NULL) {
|
||||
printf(" %-20s %-30s\n", entry->name, entry->doc);
|
||||
entry++;
|
||||
}
|
||||
printf("\n");
|
||||
printf("To view subcommand options, run `%s SUBCOMMAND --help'\n", basename((char*)name));
|
||||
}
|
||||
|
||||
static int subcmd_index(char *arg)
|
||||
{
|
||||
struct subcmd *entry = commands;
|
||||
int i = 0;
|
||||
size_t input_len = strlen(arg);
|
||||
size_t cmd_len;
|
||||
|
||||
while (entry->name != NULL) {
|
||||
cmd_len = strlen(entry->name);
|
||||
if (cmd_len == input_len && strcmp(arg, entry->name) == 0) {
|
||||
return i;
|
||||
}
|
||||
entry++;
|
||||
i++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_options(int argc, char **argv, int *index)
|
||||
{
|
||||
int opt;
|
||||
int ret;
|
||||
|
||||
/* The leading "-" in the optstring is required to preserve option parsing order */
|
||||
while ((opt = getopt_long(argc, argv, "-hv", prog_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'v':
|
||||
copyright_header("swupd");
|
||||
exit(EXIT_SUCCESS);
|
||||
case '\01':
|
||||
/* found a subcommand, or a random non-option argument */
|
||||
ret = subcmd_index(optarg);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "error: unrecognized subcommand `%s'\n\n",
|
||||
optarg);
|
||||
goto error;
|
||||
} else {
|
||||
*index = ret;
|
||||
return 0;
|
||||
}
|
||||
case '?':
|
||||
/* for unknown options, an error message is printed automatically */
|
||||
printf("\n");
|
||||
goto error;
|
||||
default:
|
||||
/* should be unreachable */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* no subcommands implies -h/--help */
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
error:
|
||||
print_help(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int index;
|
||||
int ret;
|
||||
|
||||
if (parse_options(argc, argv, &index) < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Reset optind to 0 (instead of the default value, 1) at this point,
|
||||
* because option parsing is restarted for the given subcommand, and
|
||||
* subcommand optstrings are not prefixed with "-", which is a GNU
|
||||
* extension. See the getopt_long(3) NOTES section.
|
||||
*/
|
||||
optind = 0;
|
||||
|
||||
ret = commands[index].mainfunc(argc - 1, argv + 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
#include "signature.h"
|
||||
|
||||
void increment_retries(int *retries, int *timeout)
|
||||
{
|
||||
(*retries)++;
|
||||
sleep(*timeout);
|
||||
*timeout *= 2;
|
||||
}
|
||||
|
||||
static void try_delta_loop(struct list *updates)
|
||||
{
|
||||
struct list *iter;
|
||||
struct file *file;
|
||||
|
||||
/* need update list in filename order to insure directories are
|
||||
* created before their contents */
|
||||
updates = list_sort(updates, file_sort_filename);
|
||||
|
||||
iter = list_head(updates);
|
||||
while (iter) {
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (!file->is_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try_delta(file);
|
||||
}
|
||||
}
|
||||
|
||||
static struct list *full_download_loop(struct list *updates, int isfailed)
|
||||
{
|
||||
struct list *iter;
|
||||
struct file *file;
|
||||
|
||||
iter = list_head(updates);
|
||||
while (iter) {
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (file->is_deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
full_download(file);
|
||||
}
|
||||
|
||||
if (isfailed) {
|
||||
list_free_list(updates);
|
||||
}
|
||||
|
||||
return end_full_download();
|
||||
}
|
||||
|
||||
static int update_loop(struct list *updates)
|
||||
{
|
||||
int ret;
|
||||
struct file *file;
|
||||
struct list *iter;
|
||||
struct list *failed = NULL;
|
||||
int err;
|
||||
int retries = 0; /* We only want to go through the download loop once */
|
||||
int timeout = 10; /* Amount of seconds for first download retry */
|
||||
|
||||
TRY_DOWNLOAD:
|
||||
err = start_full_download(true);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (failed != NULL) {
|
||||
try_delta_loop(failed);
|
||||
failed = full_download_loop(failed, 1);
|
||||
} else {
|
||||
try_delta_loop(updates);
|
||||
failed = full_download_loop(updates, 0);
|
||||
}
|
||||
|
||||
/* if (rm_staging_dir_contents("download")) {
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Set retries only if failed downloads exist, and only retry a fixed
|
||||
amount of times */
|
||||
if (list_head(failed) != NULL && retries < MAX_TRIES) {
|
||||
increment_retries(&retries, &timeout);
|
||||
printf("Starting download retry #%d\n", retries);
|
||||
clean_curl_multi_queue();
|
||||
goto TRY_DOWNLOAD;
|
||||
}
|
||||
|
||||
if (retries >= MAX_TRIES) {
|
||||
printf("ERROR: Could not download all files, aborting update\n");
|
||||
list_free_list(failed);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (download_only) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*********** rootfs critical section starts ***************************
|
||||
NOTE: the next loop calls do_staging() which can remove files, starting a critical section
|
||||
which ends after rename_all_files_to_final() succeeds
|
||||
*/
|
||||
|
||||
/* from here onward we're doing real update work modifying "the disk" */
|
||||
|
||||
/* starting at list_head in the filename alpha-sorted updates list
|
||||
* means node directories are added before leaf files */
|
||||
printf("Staging file content\n");
|
||||
iter = list_head(updates);
|
||||
while (iter) {
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (file->do_not_update || file->is_deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* for each file: fdatasync to persist changed content over reboot, or maybe a global sync */
|
||||
/* for each file: check hash value; on mismatch delete and queue full download */
|
||||
/* todo: hash check */
|
||||
|
||||
ret = do_staging(file);
|
||||
if (ret < 0) {
|
||||
printf("File staging failed: %s\n", file->filename);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* check policy, and if policy says, "ask", ask the user at this point */
|
||||
/* check for reboot need - if needed, wait for reboot */
|
||||
|
||||
/* sync */
|
||||
sync();
|
||||
|
||||
/* rename to apply update */
|
||||
ret = rename_all_files_to_final(updates);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: do we need to optimize directory-permission-only changes (directories
|
||||
* are now sent as tar's so permissions are handled correctly, even
|
||||
* if less than efficiently)? */
|
||||
|
||||
sync();
|
||||
|
||||
//NOTE: critical section starts when update_loop() calls do_staging()
|
||||
/*************************************************** critical section ends ***************************************************/
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main_update()
|
||||
{
|
||||
int current_version = -1, server_version = -1, latest_version = -1;
|
||||
struct manifest *current_manifest = NULL, *server_manifest = NULL;
|
||||
struct list *updates = NULL;
|
||||
int ret;
|
||||
int lock_fd;
|
||||
int retries = 0;
|
||||
int timeout = 10;
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
ret = swupd_init(&lock_fd);
|
||||
if (ret != 0) {
|
||||
/* being here means we already close log by a previously caught error */
|
||||
printf("Updater failed to initialize, exiting now.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!check_network()) {
|
||||
printf("Error: Network issue, unable to proceed with update\n");
|
||||
ret = EXIT_FAILURE;
|
||||
goto clean_curl;
|
||||
}
|
||||
|
||||
printf("Update started.\n");
|
||||
read_subscriptions_alt();
|
||||
|
||||
if (!signature_initialize(UPDATE_CA_CERTS_PATH "/" SIGNATURE_CA_CERT)) {
|
||||
goto clean_curl;
|
||||
}
|
||||
|
||||
/* Step 1: get versions */
|
||||
|
||||
ret = check_versions(¤t_version, &latest_version, &server_version, path_prefix);
|
||||
|
||||
if (ret < 0) {
|
||||
goto clean_curl;
|
||||
}
|
||||
if (server_version <= latest_version) {
|
||||
printf("Version on server (%i) is not newer than system version (%i)\n", server_version, latest_version);
|
||||
ret = EXIT_SUCCESS;
|
||||
goto clean_curl;
|
||||
}
|
||||
|
||||
printf("Preparing to update from %i to %i\n", latest_version, server_version);
|
||||
|
||||
/* Step 2: housekeeping */
|
||||
|
||||
if (create_required_dirs()) {
|
||||
goto clean_curl;
|
||||
}
|
||||
|
||||
if (rm_staging_dir_contents("download")) {
|
||||
goto clean_curl;
|
||||
}
|
||||
|
||||
/* Step 3: setup manifests */
|
||||
|
||||
load_current_manifests:
|
||||
/* get the from/to MoM manifests */
|
||||
printf("Querying current manifest.\n");
|
||||
ret = load_manifests(latest_version, latest_version, "MoM", NULL, ¤t_manifest);
|
||||
if (ret) {
|
||||
/* TODO: possibly remove this as not getting a "from" manifest is not fatal
|
||||
* - we just don't apply deltas */
|
||||
if (retries < MAX_TRIES) {
|
||||
increment_retries(&retries, &timeout);
|
||||
printf("Retry #%d downloading from/to MoM Manifests\n", retries);
|
||||
goto load_current_manifests;
|
||||
}
|
||||
printf("Failure retrieving manifest from server\n");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
/* Reset the retries and timeout for subsequent download calls */
|
||||
if (retries != 0) {
|
||||
retries = 0;
|
||||
timeout = 10;
|
||||
}
|
||||
|
||||
load_server_manifests:
|
||||
printf("Querying server manifest.\n");
|
||||
|
||||
ret = load_manifests(latest_version, server_version, "MoM", NULL, &server_manifest);
|
||||
if (ret) {
|
||||
if (retries < MAX_TRIES) {
|
||||
increment_retries(&retries, &timeout);
|
||||
printf("Retry #%d downloading server Manifests\n", retries);
|
||||
goto load_server_manifests;
|
||||
}
|
||||
printf("Failure retrieving manifest from server\n");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (current_manifest == NULL || server_manifest == NULL) {
|
||||
printf("Unable to load manifest after retrying (config or network problem?)\n");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (retries != 0) {
|
||||
retries = 0;
|
||||
timeout = 10;
|
||||
}
|
||||
|
||||
subscription_versions_from_MoM(current_manifest, 1);
|
||||
subscription_versions_from_MoM(server_manifest, 0);
|
||||
|
||||
link_submanifests(current_manifest, server_manifest);
|
||||
|
||||
/* updating subscribed manifests is done as part of recurse_manifest */
|
||||
|
||||
/* read the current collective of manifests that we are subscribed to */
|
||||
ret = recurse_manifest(current_manifest, NULL);
|
||||
if (ret != 0) {
|
||||
printf("Cannot load current MoM sub-manifests, ret = %d (%s), exiting\n", ret, strerror(errno));
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
/* consolidate the current collective manifests down into one in memory */
|
||||
consolidate_submanifests(current_manifest);
|
||||
|
||||
/* read the new collective of manifests that we are subscribed to */
|
||||
ret = recurse_manifest(server_manifest, NULL);
|
||||
if (ret != 0) {
|
||||
printf("Error: Cannot load server MoM sub-manifests, ret = %d (%s), exiting\n", ret, strerror(errno));
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
/* consolidate the new collective manifests down into one in memory */
|
||||
consolidate_submanifests(server_manifest);
|
||||
|
||||
/* prepare for an update process based on comparing two in memory manifests */
|
||||
link_manifests(current_manifest, server_manifest);
|
||||
#if 0
|
||||
debug_write_manifest(current_manifest, "debug_manifest_current.txt");
|
||||
debug_write_manifest(server_manifest, "debug_manifest_server.txt");
|
||||
#endif
|
||||
/* Step 4: check disk state before attempting update */
|
||||
|
||||
run_preupdate_scripts(server_manifest);
|
||||
|
||||
download_packs:
|
||||
/* Step 5: get the packs and untar */
|
||||
ret = download_subscribed_packs(latest_version, server_version, false);
|
||||
if (ret == -ENONET) {
|
||||
// packs don't always exist, tolerate that but not ENONET
|
||||
if (retries < MAX_TRIES) {
|
||||
increment_retries(&retries, &timeout);
|
||||
printf("Retry #%d downloading packs\n", retries);
|
||||
goto download_packs;
|
||||
}
|
||||
printf("No network, or server unavailable for pack downloads\n");
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
/* Step 6: some more housekeeping */
|
||||
|
||||
/* TODO: consider trying to do less sorting of manifests */
|
||||
|
||||
updates = create_update_list(current_manifest, server_manifest);
|
||||
|
||||
link_renames(updates, current_manifest); /* TODO: Have special lists for candidate and renames */
|
||||
|
||||
print_statistics(latest_version, server_version);
|
||||
|
||||
/* Step 7: apply the update */
|
||||
|
||||
ret = update_loop(updates);
|
||||
if (ret == 0) {
|
||||
ret = update_device_latest_version(server_version);
|
||||
printf("Update was applied.\n");
|
||||
}
|
||||
|
||||
if ((latest_version < server_version) && (ret == 0)) {
|
||||
printf("Update successful. System updated from version %d to version %d\n",
|
||||
latest_version, server_version);
|
||||
} else if (ret == 0) {
|
||||
printf("Update complete. System already up-to-date at version %d\n", latest_version);
|
||||
}
|
||||
|
||||
delete_motd();
|
||||
|
||||
/* Run any scripts that are needed to complete update */
|
||||
run_scripts();
|
||||
|
||||
clean_exit:
|
||||
list_free_list(updates);
|
||||
free_manifest(current_manifest);
|
||||
free_manifest(server_manifest);
|
||||
|
||||
clean_curl:
|
||||
signature_terminate();
|
||||
swupd_curl_cleanup();
|
||||
free_subscriptions();
|
||||
|
||||
printf("Update exiting.\n");
|
||||
|
||||
v_lockfile(lock_fd);
|
||||
|
||||
dump_file_descriptor_leaks();
|
||||
|
||||
return ret;
|
||||
}
|
||||
+777
@@ -0,0 +1,777 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
#include "signature.h"
|
||||
|
||||
|
||||
static bool cmdline_option_fix = false;
|
||||
static bool cmdline_option_install = false;
|
||||
static bool cmdline_option_quick = false;
|
||||
|
||||
static int version;
|
||||
|
||||
/* Count of how many files we managed to not fix */
|
||||
static int file_checked_count;
|
||||
static int file_missing_count;
|
||||
static int file_replaced_count;
|
||||
static int file_not_replaced_count;
|
||||
static int file_mismatch_count;
|
||||
static int file_fixed_count;
|
||||
static int file_not_fixed_count;
|
||||
static int file_extraneous_count;
|
||||
static int file_deleted_count;
|
||||
static int file_not_deleted_count;
|
||||
|
||||
|
||||
static const struct option prog_opts[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"manifest", required_argument, 0, 'm'},
|
||||
{"path", required_argument, 0, 'p'},
|
||||
{"url", required_argument, 0, 'u'},
|
||||
{"port", required_argument, 0, 'P'},
|
||||
{"contenturl", required_argument, 0, 'c'},
|
||||
{"versionurl", required_argument, 0, 'v'},
|
||||
{"fix", no_argument, 0, 'f'},
|
||||
{"install", no_argument, 0, 'i'},
|
||||
{"format", required_argument, 0, 'F'},
|
||||
{"quick", no_argument, 0, 'q'},
|
||||
{"force", no_argument, 0, 'x'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static void print_help(const char *name) {
|
||||
printf("Usage:\n");
|
||||
printf(" swupd %s [OPTION...]\n\n", basename((char*)name));
|
||||
printf("Help Options:\n");
|
||||
printf(" -h, --help Show help options\n\n");
|
||||
printf("Application Options:\n");
|
||||
printf(" -m, --manifest=M Verify against manifest version M\n");
|
||||
printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n");
|
||||
printf(" -u, --url=[URL] RFC-3986 encoded url for version string and content file downloads\n");
|
||||
printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n");
|
||||
printf(" -c, --contenturl=[URL] RFC-3986 encoded url for content file downloads\n");
|
||||
printf(" -v, --versionurl=[URL] RFC-3986 encoded url for version file downloads\n");
|
||||
printf(" -f, --fix Fix local issues relative to server manifest (will not modify ignored files)\n");
|
||||
printf(" -i, --install Similar to \"--fix\" but optimized for install all files to empty directory\n");
|
||||
printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n");
|
||||
printf(" -q, --quick Don't compare hashes, only fix missing files\n");
|
||||
printf(" -x, --force Attempt to proceed even if non-critical errors found\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static bool parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
//set default initial values
|
||||
set_format_string(NULL);
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hxm:p:u:P:c:v:fiF:q", prog_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case '?':
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'm':
|
||||
if (sscanf(optarg, "%i", &version) != 1) {
|
||||
printf("Invalid --manifest argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'p': /* default empty path_prefix verifies the running OS */
|
||||
if (!optarg) {
|
||||
printf("Invalid --path argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (path_prefix) { /* multiple -p options */
|
||||
free(path_prefix);
|
||||
}
|
||||
string_or_die(&path_prefix, "%s", optarg);
|
||||
break;
|
||||
case 'u':
|
||||
if (!optarg) {
|
||||
printf("Invalid --url argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
if (content_server_urls[0]) {
|
||||
free(content_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
string_or_die(&content_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'P':
|
||||
if (sscanf(optarg, "%ld", &update_server_port) != 1) {
|
||||
printf("Invalid --port argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (!optarg) {
|
||||
printf("Invalid --contenturl argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (content_server_urls[0]) {
|
||||
free(content_server_urls[0]);
|
||||
}
|
||||
string_or_die(&content_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'v':
|
||||
if (!optarg) {
|
||||
printf("Invalid --versionurl argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
if (version_server_urls[0]) {
|
||||
free(version_server_urls[0]);
|
||||
}
|
||||
string_or_die(&version_server_urls[0], "%s", optarg);
|
||||
break;
|
||||
case 'f':
|
||||
cmdline_option_fix = true;
|
||||
break;
|
||||
case 'i':
|
||||
cmdline_option_install = true;
|
||||
cmdline_option_quick = true;
|
||||
break;
|
||||
case 'F':
|
||||
if (!optarg || !set_format_string(optarg)) {
|
||||
printf("Invalid --format argument\n\n");
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
cmdline_option_quick = true;
|
||||
break;
|
||||
case 'x':
|
||||
force = true;
|
||||
break;
|
||||
default:
|
||||
printf("Unrecognized option\n\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdline_option_install) {
|
||||
if (version == 0) {
|
||||
printf("--install option requires -m version option\n");
|
||||
return false;
|
||||
}
|
||||
if (path_prefix == NULL) {
|
||||
printf("--install option requires --path option\n");
|
||||
return false;
|
||||
}
|
||||
if (cmdline_option_fix) {
|
||||
printf("--install and --fix options are mutually exclusive\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!init_globals()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
err:
|
||||
print_help(argv[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hash_needs_work(struct file *file, char *hash)
|
||||
{
|
||||
if (cmdline_option_quick) {
|
||||
if (hash_is_zeros(hash)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (hash_compare(file->hash, hash)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_all_files(int version, struct manifest *official_manifest)
|
||||
{
|
||||
int ret;
|
||||
struct list *iter;
|
||||
|
||||
/* for install we need everything so synchronously download zero packs */
|
||||
ret = download_subscribed_packs(0, version, true);
|
||||
if (ret < 0) { // require zero pack
|
||||
/* If we hit this point, we know we have a network connection, therefore
|
||||
* the error is server-side. This is also a critical error, so detailed
|
||||
* logging needed */
|
||||
printf("zero pack downloads failed. \n");
|
||||
printf("Failed - Server-side error, cannot download necessary files\n");
|
||||
return ret;
|
||||
}
|
||||
iter = list_head(official_manifest->files);
|
||||
while (iter) {
|
||||
struct file *file;
|
||||
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (file->is_deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
file_checked_count++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct list *download_loop(struct list *files, int isfailed)
|
||||
{
|
||||
int ret;
|
||||
struct file local;
|
||||
struct list *iter;
|
||||
|
||||
iter = list_head(files);
|
||||
while (iter) {
|
||||
struct file *file;
|
||||
char *fullname;
|
||||
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (file->is_deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fullname = mk_full_filename(path_prefix, file->filename);
|
||||
if (fullname == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
memset(&local, 0, sizeof(struct file));
|
||||
local.filename = file->filename;
|
||||
populate_file_struct(&local, fullname);
|
||||
|
||||
if (cmdline_option_quick) {
|
||||
ret = compute_hash_lazy(&local, fullname);
|
||||
} else {
|
||||
ret = compute_hash(&local, fullname);
|
||||
}
|
||||
if (ret != 0) {
|
||||
free(fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hash_needs_work(file, local.hash)) {
|
||||
full_download(file);
|
||||
} else {
|
||||
/* mark the file as good to save time later */
|
||||
file->do_not_update = 1;
|
||||
}
|
||||
free(fullname);
|
||||
}
|
||||
|
||||
if (isfailed) {
|
||||
list_free_list(files);
|
||||
}
|
||||
|
||||
return end_full_download();
|
||||
}
|
||||
|
||||
static int get_missing_files(struct manifest *official_manifest)
|
||||
{
|
||||
int ret;
|
||||
struct list *failed = NULL;
|
||||
int retries = 0; /* We only want to go through the download loop once */
|
||||
int timeout = 10; /* Amount of seconds for first download retry */
|
||||
|
||||
/* when fixing (not installing): queue download and mark any files
|
||||
* which are already verified OK */
|
||||
RETRY_DOWNLOADS:
|
||||
ret = start_full_download(true);
|
||||
if (ret != 0) {
|
||||
/* If we hit this point, the network is accessible but we were
|
||||
* unable to download the needed files. This is a terminal error
|
||||
* and we need good logging */
|
||||
printf("Error: Unable to download neccessary files for this OS release\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (failed != NULL) {
|
||||
failed = download_loop(failed, 1);
|
||||
} else {
|
||||
failed = download_loop(official_manifest->files, 0);
|
||||
}
|
||||
|
||||
/* Set retries only if failed downloads exist, and only retry a fixed
|
||||
amount of times */
|
||||
if (list_head(failed) != NULL && retries < MAX_TRIES) {
|
||||
increment_retries(&retries, &timeout);
|
||||
printf("Starting download retry #%d\n", retries);
|
||||
clean_curl_multi_queue();
|
||||
goto RETRY_DOWNLOADS;
|
||||
}
|
||||
|
||||
if (retries >= MAX_TRIES) {
|
||||
printf("ERROR: Could not download all files, aborting update\n");
|
||||
list_free_list(failed);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* allow optimization of install case */
|
||||
static int get_required_files(int version, struct manifest *official_manifest)
|
||||
{
|
||||
if (cmdline_option_install) {
|
||||
return get_all_files(version, official_manifest);
|
||||
}
|
||||
|
||||
if (cmdline_option_fix) {
|
||||
return get_missing_files(official_manifest);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for each missing but expected file, (re)add the file */
|
||||
static void add_missing_files(struct manifest *official_manifest)
|
||||
{
|
||||
int ret;
|
||||
struct file local;
|
||||
struct list *iter;
|
||||
|
||||
iter = list_head(official_manifest->files);
|
||||
while (iter) {
|
||||
struct file *file;
|
||||
char *fullname;
|
||||
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if ((file->is_deleted) ||
|
||||
(file->do_not_update)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fullname = mk_full_filename(path_prefix, file->filename);
|
||||
if (fullname == NULL) {
|
||||
abort();
|
||||
}
|
||||
memset(&local, 0, sizeof(struct file));
|
||||
local.filename = file->filename;
|
||||
populate_file_struct(&local, fullname);
|
||||
ret = compute_hash_lazy(&local, fullname);
|
||||
if (ret != 0) {
|
||||
file_not_replaced_count++;
|
||||
free(fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* compare the hash and report mismatch */
|
||||
if (hash_is_zeros(local.hash)) {
|
||||
file_missing_count++;
|
||||
printf("Missing file: %s\n", fullname);
|
||||
} else {
|
||||
free(fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* install the new file (on miscompare + fix) */
|
||||
ret = do_staging(file);
|
||||
if (ret == 0) {
|
||||
rename_staged_file_to_final(file);
|
||||
}
|
||||
|
||||
/* verify the hash again to judge success */
|
||||
populate_file_struct(&local, fullname);
|
||||
if (cmdline_option_quick) {
|
||||
ret = compute_hash_lazy(&local, fullname);
|
||||
} else {
|
||||
ret = compute_hash(&local, fullname);
|
||||
}
|
||||
if ((ret != 0) || hash_needs_work(file, local.hash)) {
|
||||
file_not_replaced_count++;
|
||||
printf("\tnot fixed\n");
|
||||
} else {
|
||||
file_replaced_count++;
|
||||
file->do_not_update = 1;
|
||||
printf("\tfixed\n");
|
||||
}
|
||||
free(fullname);
|
||||
}
|
||||
}
|
||||
|
||||
static void deal_with_hash_mismatches(struct manifest *official_manifest, bool repair)
|
||||
{
|
||||
int ret;
|
||||
struct list *iter;
|
||||
|
||||
/* for each expected and present file which hash-mismatches vs the manifest, replace the file */
|
||||
iter = list_head(official_manifest->files);
|
||||
while (iter) {
|
||||
struct file *file;
|
||||
char *fullname;
|
||||
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if (file->is_deleted ||
|
||||
ignore(file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
file_checked_count++;
|
||||
|
||||
// do_not_update set by earlier check, so account as checked
|
||||
if (file->do_not_update) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* compare the hash and report mismatch */
|
||||
fullname = mk_full_filename(path_prefix, file->filename);
|
||||
if (fullname == NULL) {
|
||||
abort();
|
||||
}
|
||||
if (verify_file(file, fullname)) {
|
||||
free(fullname);
|
||||
continue;
|
||||
} else {
|
||||
file_mismatch_count++;
|
||||
printf("Hash mismatch for file: %s\n", fullname);
|
||||
}
|
||||
|
||||
/* if not repairing, we're done */
|
||||
if (!repair) {
|
||||
free(fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* install the new file (on miscompare + fix) */
|
||||
ret = do_staging(file);
|
||||
if (ret == 0) {
|
||||
rename_staged_file_to_final(file);
|
||||
}
|
||||
|
||||
/* at the end of all this, verify the hash again to judge success */
|
||||
if (verify_file(file, fullname)) {
|
||||
file_fixed_count++;
|
||||
printf("\tfixed\n");
|
||||
} else {
|
||||
file_not_fixed_count++;
|
||||
printf("\tnot fixed\n");
|
||||
}
|
||||
free(fullname);
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_orphaned_files(struct manifest *official_manifest)
|
||||
{
|
||||
int ret;
|
||||
struct list *iter;
|
||||
|
||||
iter = list_head(official_manifest->files);
|
||||
while (iter) {
|
||||
struct file *file;
|
||||
char *fullname;
|
||||
struct stat sb;
|
||||
|
||||
file = iter->data;
|
||||
iter = iter->next;
|
||||
|
||||
if ((!file->is_deleted) ||
|
||||
(file->is_config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fullname = mk_full_filename(path_prefix, file->filename);
|
||||
if (fullname == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
if (lstat(fullname, &sb) != 0) {
|
||||
/* correctly, the file is not present */
|
||||
free(fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
file_extraneous_count++;
|
||||
if (is_dirname_link(fullname)) {
|
||||
free(fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!file->is_dir) {
|
||||
ret = unlink(fullname);
|
||||
if (!ret && errno != ENOENT && errno != EISDIR) {
|
||||
printf("Failed to remove %s (%i: %s)\n", fullname, errno, strerror(errno));
|
||||
file_not_deleted_count++;
|
||||
} else {
|
||||
printf("Deleted %s \n", fullname);
|
||||
file_deleted_count++;
|
||||
}
|
||||
} else {
|
||||
ret = rmdir(fullname);
|
||||
if (ret) {
|
||||
file_not_deleted_count++;
|
||||
if (errno != ENOTEMPTY) {
|
||||
printf("Failed to remove empty folder %s (%i: %s)\n",
|
||||
fullname, errno, strerror(errno));
|
||||
} else {
|
||||
//FIXME: Add force removal option?
|
||||
printf("Couldn't remove directory containing untracked files: %s\n", fullname);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(fullname);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function does a simple verification of files listed in the
|
||||
* subscribed bundle manifests. If the optional "fix" or "install" parameter
|
||||
* is specified, the disk will be modified at each point during the
|
||||
* sequential comparison of manifest files to disk files, where the disk is
|
||||
* found to not match the manifest. This is notably different from update,
|
||||
* which attempts to atomically (or nearly atomically) activate a set of
|
||||
* pre-computed and validated staged changes as a group. */
|
||||
int verify_main(int argc, char **argv)
|
||||
{
|
||||
struct manifest *official_manifest = NULL;
|
||||
int ret;
|
||||
int lock_fd;
|
||||
|
||||
copyright_header("software verify");
|
||||
|
||||
if (!parse_options(argc, argv) ||
|
||||
create_required_dirs()) {
|
||||
free_globals();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* parse command line options */
|
||||
assert(argc >= 0);
|
||||
assert(argv != NULL);
|
||||
|
||||
/* Gather current manifests */
|
||||
if (!version) {
|
||||
version = read_version_from_subvol_file(path_prefix);
|
||||
if (!version) {
|
||||
printf("Cannot determine current version\n");
|
||||
free_globals();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ret = swupd_init(&lock_fd);
|
||||
if (ret != 0) {
|
||||
printf("Failed verify initialization, exiting now.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
printf("Verifying version %i\n", version);
|
||||
|
||||
if (!check_network()) {
|
||||
printf("Error: Network issue, unable to download manifest\n");
|
||||
v_lockfile(lock_fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
read_subscriptions_alt();
|
||||
get_mounted_directories();
|
||||
|
||||
/*
|
||||
* FIXME: We need a command line option to override this in case the
|
||||
* certificate is hosed and the admin knows it and wants to recover.
|
||||
*/
|
||||
if (!signature_initialize(UPDATE_CA_CERTS_PATH "/" SIGNATURE_CA_CERT)) {
|
||||
printf("Can't initialize the SSL certificates\n");
|
||||
goto brick_the_system_and_clean_curl;
|
||||
}
|
||||
|
||||
ret = rm_staging_dir_contents("download");
|
||||
if (ret != 0) {
|
||||
printf("Failed to remove prior downloads, carrying on anyway\n");
|
||||
}
|
||||
|
||||
ret = load_manifests(version, version, "MoM", NULL, &official_manifest);
|
||||
|
||||
if (ret != 0) {
|
||||
/* This should never get hit, since network issues are identified by the
|
||||
* check_network() call earlier */
|
||||
printf("Unable to download %d Manifest.MoM\n", version);
|
||||
goto brick_the_system_and_clean_curl;
|
||||
}
|
||||
|
||||
subscription_versions_from_MoM(official_manifest, 0);
|
||||
recurse_manifest(official_manifest, NULL);
|
||||
consolidate_submanifests(official_manifest);
|
||||
|
||||
/* preparation work complete. */
|
||||
|
||||
/*
|
||||
* NOTHING ELSE IS ALLOWED TO FAIL/ABORT after this line.
|
||||
* This tool is there to recover a nearly-bricked system. Aborting
|
||||
* from this point forward, for any reason, will result in a bricked system.
|
||||
*
|
||||
* I don't care what your static analysis tools says
|
||||
* I don't care what valgrind tells you
|
||||
*
|
||||
* There shall be no "goto fail;" from this point on.
|
||||
*
|
||||
* *** THE SHOW MUST GO ON ***
|
||||
*/
|
||||
|
||||
if (cmdline_option_fix || cmdline_option_install) {
|
||||
/* when fixing or installing we need input files. */
|
||||
ret = get_required_files(version, official_manifest);
|
||||
if (ret != 0) {
|
||||
goto brick_the_system_and_clean_curl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Next put the files in place that are missing completely.
|
||||
* This is to avoid updating a symlink to a library before the new full file
|
||||
* is already there. It's also the most safe operation, adding files rarely
|
||||
* has unintended side effect. So lets do the safest thing first.
|
||||
*/
|
||||
printf("Adding any missing files\n");
|
||||
add_missing_files(official_manifest);
|
||||
}
|
||||
|
||||
if (cmdline_option_quick) {
|
||||
/* quick only replaces missing files, so it is done here */
|
||||
goto brick_the_system_and_clean_curl;
|
||||
}
|
||||
|
||||
if (cmdline_option_fix) {
|
||||
bool repair = true;
|
||||
|
||||
printf("Fixing modified files\n");
|
||||
deal_with_hash_mismatches(official_manifest, repair);
|
||||
|
||||
/* removing files could be risky, so only do it if the
|
||||
* prior phases had no problems */
|
||||
if ((file_not_fixed_count == 0) && (file_not_replaced_count == 0)) {
|
||||
remove_orphaned_files(official_manifest);
|
||||
}
|
||||
} else {
|
||||
bool repair = false;
|
||||
|
||||
printf("Verifying files\n");
|
||||
deal_with_hash_mismatches(official_manifest, repair);
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
|
||||
/*
|
||||
* naming convention: All exit goto labels must follow the "brick_the_system_and_FOO:" pattern
|
||||
*/
|
||||
|
||||
brick_the_system_and_clean_curl:
|
||||
|
||||
/* report a summary of what we managed to do and not do */
|
||||
printf("Inspected %i files\n", file_checked_count);
|
||||
|
||||
if (cmdline_option_fix || cmdline_option_install) {
|
||||
printf(" %i files were missing\n", file_missing_count);
|
||||
if (file_missing_count) {
|
||||
printf(" %i of %i missing files were replaced\n", file_replaced_count, file_missing_count);
|
||||
printf(" %i of %i missing files were not replaced\n", file_not_replaced_count, file_missing_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmdline_option_quick && file_mismatch_count > 0) {
|
||||
printf(" %i files did not match\n", file_mismatch_count);
|
||||
if (cmdline_option_fix) {
|
||||
printf(" %i of %i files were fixed\n", file_fixed_count, file_mismatch_count);
|
||||
printf(" %i of %i files were not fixed\n", file_not_fixed_count, file_mismatch_count);
|
||||
}
|
||||
}
|
||||
|
||||
if ((file_not_fixed_count == 0) && (file_not_replaced_count == 0) &&
|
||||
cmdline_option_fix && !cmdline_option_quick) {
|
||||
printf(" %i files found which should be deleted\n", file_extraneous_count);
|
||||
if (file_extraneous_count) {
|
||||
printf(" %i of %i files were deleted\n", file_deleted_count, file_extraneous_count);
|
||||
printf(" %i of %i files were not deleted\n", file_not_deleted_count, file_extraneous_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdline_option_fix || cmdline_option_install) {
|
||||
// always run in a fix or install case
|
||||
need_update_boot = true;
|
||||
need_update_bootloader = true;
|
||||
run_scripts();
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
if ((file_not_fixed_count == 0) &&
|
||||
(file_not_replaced_count == 0) &&
|
||||
(file_not_deleted_count == 0)) {
|
||||
ret = EXIT_SUCCESS;
|
||||
} else {
|
||||
ret = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* this concludes the critical section, after this point it's clean up time, the disk content is finished and final */
|
||||
|
||||
if (ret == EXIT_SUCCESS) {
|
||||
if (cmdline_option_fix || cmdline_option_install) {
|
||||
printf("Fix successful\n");
|
||||
} else {
|
||||
/* This is just a verification */
|
||||
printf("Verify successful\n");
|
||||
}
|
||||
} else {
|
||||
if (cmdline_option_fix || cmdline_option_install) {
|
||||
printf("Error: Fix did not fully succeed\n");
|
||||
} else {
|
||||
/* This is just a verification */
|
||||
printf("Error: Verify did not fully succeed\n");
|
||||
}
|
||||
}
|
||||
swupd_curl_cleanup();
|
||||
free_subscriptions();
|
||||
free_manifest(official_manifest);
|
||||
|
||||
v_lockfile(lock_fd);
|
||||
dump_file_descriptor_leaks();
|
||||
free_globals();
|
||||
return ret;
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Tim Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "swupd.h"
|
||||
|
||||
/* this function attempts to download the latest server version string file from
|
||||
* the preferred server to a memory buffer, returning either a negative integer
|
||||
* error code or >= 0 representing the server version */
|
||||
static int try_version_download(void)
|
||||
{
|
||||
char *url = NULL;
|
||||
char *path = NULL;
|
||||
int ret = 0;
|
||||
char *tmp_version;
|
||||
|
||||
tmp_version = malloc(LINE_MAX);
|
||||
if (tmp_version == NULL) {
|
||||
abort();
|
||||
}
|
||||
|
||||
string_or_die(&url, "%s/version/format%s/latest", preferred_version_url, format_string);
|
||||
|
||||
string_or_die(&path, "%s/server_version", STATE_DIR);
|
||||
|
||||
unlink(path);
|
||||
|
||||
ret = swupd_curl_get_file(url, path, NULL, tmp_version, false);
|
||||
if (ret) {
|
||||
goto out;
|
||||
} else {
|
||||
ret = strtol(tmp_version, NULL, 10);
|
||||
}
|
||||
|
||||
out:
|
||||
free(path);
|
||||
free(url);
|
||||
free(tmp_version);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool check_network(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!have_network) {
|
||||
ret = try_version_download();
|
||||
if (ret < 0) {
|
||||
have_network = false;
|
||||
} else {
|
||||
have_network = true;
|
||||
}
|
||||
}
|
||||
|
||||
return have_network;
|
||||
}
|
||||
|
||||
int read_version_from_subvol_file(char *path_prefix)
|
||||
{
|
||||
char line[LINE_MAX];
|
||||
FILE *file;
|
||||
int v = -1;
|
||||
char *buildstamp;
|
||||
char *src, *dest;
|
||||
|
||||
string_or_die(&buildstamp, "%s/usr/lib/os-release", path_prefix);
|
||||
file = fopen(buildstamp, "rm");
|
||||
if (!file) {
|
||||
string_or_die(&buildstamp, "%s/etc/os-release", path_prefix);
|
||||
file = fopen(buildstamp, "rm");
|
||||
if (!file) {
|
||||
free(buildstamp);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
while (!feof(file)) {
|
||||
line[0] = 0;
|
||||
if (fgets(line, LINE_MAX, file) == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp(line, "VERSION_ID=", 11) == 0) {
|
||||
src = &line[11];
|
||||
|
||||
/* Drop quotes and newline in value */
|
||||
dest = src;
|
||||
while (*src) {
|
||||
if (*src == '\'' || *src == '"' || *src == '\n') {
|
||||
++src;
|
||||
} else {
|
||||
*dest = *src;
|
||||
++dest;
|
||||
++src;
|
||||
}
|
||||
}
|
||||
*dest = 0;
|
||||
|
||||
v = strtoull(&line[11], NULL, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buildstamp);
|
||||
fclose(file);
|
||||
return v;
|
||||
}
|
||||
|
||||
void read_versions(int *current_version,
|
||||
int *latest_version,
|
||||
int *server_version,
|
||||
char *path_prefix)
|
||||
{
|
||||
*current_version = *latest_version = read_version_from_subvol_file(path_prefix);
|
||||
|
||||
printf("Querying server version.\n");
|
||||
*server_version = try_version_download();
|
||||
}
|
||||
|
||||
int check_versions(int *current_version,
|
||||
int *latest_version,
|
||||
int *server_version,
|
||||
char *path_prefix)
|
||||
{
|
||||
|
||||
read_versions(current_version, latest_version, server_version, path_prefix);
|
||||
|
||||
if (*latest_version < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (*latest_version == 0) {
|
||||
printf("Update from version 0 not supported yet.\n");
|
||||
return -1;
|
||||
}
|
||||
if (SWUPD_VERSION_IS_DEVEL(*current_version) || SWUPD_VERSION_IS_RESVD(*current_version)) {
|
||||
printf("Update of dev build not supported %d\n", *current_version);
|
||||
return -1;
|
||||
}
|
||||
swupd_curl_set_current_version(*latest_version);
|
||||
|
||||
/* set preferred version and content server urls */
|
||||
if (*server_version < 0) {
|
||||
have_network = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
have_network = true;
|
||||
|
||||
//TODO allow policy layer to send us to intermediate version?
|
||||
|
||||
swupd_curl_set_requested_version(*server_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_device_latest_version(int version)
|
||||
{
|
||||
FILE *file = NULL;
|
||||
char *path = NULL;
|
||||
|
||||
string_or_die(&path, "%s/version", STATE_DIR);
|
||||
file = fopen(path, "w");
|
||||
if (!file) {
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(file, "%i\n", version);
|
||||
fflush(file);
|
||||
fdatasync(fileno(file));
|
||||
fclose(file);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Software Update - client side
|
||||
*
|
||||
* File Extended Attributes Helpers
|
||||
*
|
||||
* Copyright © 2014-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Christophe Guiraud <christophe.guiraud@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "swupd.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
enum xattrs_action_type_t_ {
|
||||
XATTRS_ACTION_COPY,
|
||||
XATTRS_ACTION_GET_BLOB
|
||||
};
|
||||
|
||||
typedef enum xattrs_action_type_t_ xattrs_action_type_t;
|
||||
|
||||
static int xattr_get_value(const char *path, const char *name, char **blob,
|
||||
size_t *blob_len, xattrs_action_type_t action)
|
||||
{
|
||||
char *value;
|
||||
ssize_t len;
|
||||
|
||||
len = lgetxattr(path, name, NULL, 0);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*blob == NULL) {
|
||||
*blob_len = 0;
|
||||
}
|
||||
|
||||
/* realloc needed len + 1 in case we need to add final zero
|
||||
* to ensure consistent blob */
|
||||
value = realloc(*blob, *blob_len + len +
|
||||
(action == XATTRS_ACTION_GET_BLOB ? 1 : 0));
|
||||
if (!value) {
|
||||
abort();
|
||||
}
|
||||
|
||||
*blob = value;
|
||||
|
||||
value = value + *blob_len;
|
||||
len = lgetxattr(path, name, value, len);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* in the xattrs system, value is just an arbitrary binary blob. If it
|
||||
* is a string, it can already be null terminated or not, depending
|
||||
* on the len that was passed when the attribute was set. So, make
|
||||
* sure we use a consistent value by adding a final zero if not
|
||||
* already there.
|
||||
* note: this must not be done when copying attributes. In this case
|
||||
* we want to keep them unchanged. It must only be done when
|
||||
* getting the value blob to use for key computation
|
||||
*/
|
||||
if (action == XATTRS_ACTION_GET_BLOB && len && value[len - 1] != 0) {
|
||||
value[len] = 0;
|
||||
len++;
|
||||
}
|
||||
|
||||
*blob_len += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_xattr_name_count(const char *names_list, ssize_t len)
|
||||
{
|
||||
int count = 0;
|
||||
const char *name;
|
||||
|
||||
for (name = names_list; name < (names_list + len); name += strlen(name) + 1) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int cmp_xattr_name_ptrs(const void *ptr1, const void *ptr2)
|
||||
{
|
||||
return strcmp(*(char * const *)ptr1, *(char * const *)ptr2);
|
||||
}
|
||||
|
||||
static const char **get_sorted_xattr_name_table(const char *names, int n)
|
||||
{
|
||||
const char **table;
|
||||
int i;
|
||||
|
||||
table = calloc(1, n * sizeof(char*));
|
||||
if (!table) {
|
||||
abort();
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
table[i] = names;
|
||||
names += strlen(names) + 1;
|
||||
}
|
||||
|
||||
qsort(table, n, sizeof(char*), cmp_xattr_name_ptrs);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/* copy an xattr blob from a file to another file or to a buffer
|
||||
*
|
||||
* returned blob_len==0 indicates the blob pointer does not
|
||||
* contain valid data. Given quirks in xattr implemenations (or lack there
|
||||
* of on some kernels or filesystems or OS's) and SSL quirks (the blob and
|
||||
* blob length are passed to a hasing function) the blob pointer is set to a
|
||||
* canary instead of being left as NULL, simplifying code elsewhere. */
|
||||
static void xattrs_do_action(xattrs_action_type_t action,
|
||||
const char *src_filename,
|
||||
const char *dst_filename,
|
||||
char **blob, size_t *blob_len) {
|
||||
ssize_t len;
|
||||
char *list;
|
||||
int ret = 0;
|
||||
char *value = NULL;
|
||||
size_t value_len = 0;
|
||||
const char **sorted_list = NULL;
|
||||
int count;
|
||||
int i;
|
||||
int offset = 0;
|
||||
|
||||
len = llistxattr(src_filename, NULL, 0);
|
||||
if (len <= 0) {
|
||||
if (action == XATTRS_ACTION_GET_BLOB) {
|
||||
*blob_len = 0;
|
||||
*blob = (void *)0xdeadcafe;
|
||||
}
|
||||
return; // no xattrs, this is OK
|
||||
}
|
||||
|
||||
list = calloc(1, len);
|
||||
if (!list) {
|
||||
abort();
|
||||
}
|
||||
|
||||
len = llistxattr(src_filename, list, len);
|
||||
if (len <= 0) {
|
||||
if (action == XATTRS_ACTION_GET_BLOB) {
|
||||
*blob_len = 0;
|
||||
*blob = (void *)0xdeadcafe;
|
||||
}
|
||||
free(list);
|
||||
return; // no xattrs, this is OK
|
||||
}
|
||||
|
||||
count = get_xattr_name_count(list, len);
|
||||
sorted_list = get_sorted_xattr_name_table(list, count);
|
||||
|
||||
if (action == XATTRS_ACTION_GET_BLOB) {
|
||||
value = calloc(1, len);
|
||||
if (!value) {
|
||||
abort();
|
||||
}
|
||||
|
||||
value_len = len;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
len = strlen(sorted_list[i]) + 1;
|
||||
memcpy(value + offset, sorted_list[i], len);
|
||||
offset += len;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
|
||||
/* In the XATTRS_ACTION_COPY case the xattr_get_value(...) calls
|
||||
* are always performed with 'value = NULL' and 'value_len = 0'.
|
||||
*/
|
||||
ret = xattr_get_value(src_filename, sorted_list[i], &value, &value_len,
|
||||
action);
|
||||
if (ret < 0) {
|
||||
free(value);
|
||||
value_len = 0;
|
||||
value = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (action == XATTRS_ACTION_COPY) {
|
||||
/* 'value' contains only the attribute value in this
|
||||
* case. */
|
||||
ret = lsetxattr(dst_filename, sorted_list[i],
|
||||
value, value_len, 0);
|
||||
free(value);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
value_len = 0;
|
||||
value = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == XATTRS_ACTION_GET_BLOB) {
|
||||
if (value_len != 0) {
|
||||
*blob_len = value_len;
|
||||
*blob = value;
|
||||
} else {
|
||||
*blob_len = 0;
|
||||
*blob = (void *)0xdeadcafe;
|
||||
}
|
||||
}
|
||||
|
||||
free(list);
|
||||
free(sorted_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void xattrs_copy(const char *src_filename, const char *dst_filename)
|
||||
{
|
||||
xattrs_do_action(XATTRS_ACTION_COPY, src_filename, dst_filename,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
void xattrs_get_blob(const char *filename, char **blob, size_t *blob_len)
|
||||
{
|
||||
xattrs_do_action(XATTRS_ACTION_GET_BLOB, filename, NULL,
|
||||
blob, blob_len);
|
||||
}
|
||||
|
||||
int xattrs_compare(const char *filename1, const char *filename2)
|
||||
{
|
||||
char *new_xattrs;
|
||||
char *old_xattrs;
|
||||
size_t new_xattrs_len;
|
||||
size_t old_xattrs_len;
|
||||
int ret = 0;
|
||||
|
||||
xattrs_get_blob(filename1, &old_xattrs, &old_xattrs_len);
|
||||
xattrs_get_blob(filename2, &new_xattrs, &new_xattrs_len);
|
||||
|
||||
if ((old_xattrs_len == 0) && (new_xattrs_len == 0)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (old_xattrs_len != new_xattrs_len) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(old_xattrs, new_xattrs, old_xattrs_len) != 0) {
|
||||
ret = -1;
|
||||
}
|
||||
out:
|
||||
if (old_xattrs_len != 0) {
|
||||
free(old_xattrs);
|
||||
}
|
||||
|
||||
if (new_xattrs_len != 0) {
|
||||
free(new_xattrs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Uncomment if this file uses macros from config.h */
|
||||
#if 0
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "swupd.h"
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Software Updater - client side
|
||||
*
|
||||
* Copyright © 2012-2016 Intel Corporation.
|
||||
*
|
||||
* 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 2 or later 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/>.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Lapuyade <eric.lapuyade@intel.com>
|
||||
* cguiraud <christophe.guiraud@intel.com>
|
||||
* Timothy C. Pepper <timothy.c.pepper@linux.intel.com>
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <bsdiff.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "swupd.h"
|
||||
|
||||
double a[5], b[5];
|
||||
int size[5];
|
||||
double count[5];
|
||||
|
||||
int main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
|
||||
{
|
||||
int ret;
|
||||
int algo;
|
||||
struct timeval before, after;
|
||||
struct stat st1, st2;
|
||||
struct file *file1, *file2;
|
||||
|
||||
ret = stat(argv[1], &st1);
|
||||
if (ret)
|
||||
exit(0);
|
||||
ret = stat(argv[2], &st2);
|
||||
if (ret)
|
||||
exit(0);
|
||||
|
||||
file1 = calloc(1, sizeof(struct file));
|
||||
assert(file1);
|
||||
file1->use_xattrs = true;
|
||||
file1->filename = strdup(argv[2]);
|
||||
|
||||
file2 = calloc(1, sizeof(struct file));
|
||||
assert(file2);
|
||||
file2->use_xattrs = true;
|
||||
file1->filename = strdup("result");
|
||||
|
||||
populate_file_struct(file1, argv[2]);
|
||||
ret = compute_hash(file1, argv[2]);
|
||||
if ((ret != 0) || hash_is_zeros(file1->hash)) {
|
||||
printf("Hash computation failed\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
for (algo = 0; algo < BSDIFF_ENC_LAST; algo++) {
|
||||
struct stat bu;
|
||||
int i;
|
||||
time_t start;
|
||||
|
||||
unlink("output.bsdiff");
|
||||
make_bsdiff_delta(argv[1], argv[2], "output.bsdiff", algo);
|
||||
|
||||
stat("output.bsdiff", &bu);
|
||||
|
||||
start = time(NULL);
|
||||
gettimeofday(&before, NULL);
|
||||
for (i = 0; i < 10000; i++) {
|
||||
ret = apply_bsdiff_delta(argv[1], "result", "output.bsdiff");
|
||||
if (i > 500 && time(NULL) - start > 5)
|
||||
break;
|
||||
}
|
||||
gettimeofday(&after, NULL);
|
||||
populate_file_struct(file1, "result");
|
||||
ret = compute_hash(file2, "result");
|
||||
if ((ret != 0) || hash_is_zeros(file2->hash)) {
|
||||
printf("Hash computation failed\n");
|
||||
exit(0);
|
||||
}
|
||||
if (!hash_compare(file1->hash, file2->hash)) {
|
||||
printf("Hash mismatch for algorithm %i \n", algo);
|
||||
exit(0);
|
||||
}
|
||||
unlink("result");
|
||||
|
||||
b[algo] = before.tv_sec + before.tv_usec / 1000000.0;
|
||||
a[algo] = after.tv_sec + after.tv_usec / 1000000.0;
|
||||
count[algo] = i / 5000.0;
|
||||
size[algo] = bu.st_size;
|
||||
}
|
||||
|
||||
printf("file, %s, orgsize, %i, best, %i, unc, %i, %5.3f, bzip, %i, %5.3f, gzip, %i, %5.3f, xz, %i, %5.3f, zeros, %i, %5.3f\n",
|
||||
argv[1], (int)(st1.st_size+st2.st_size)/2, size[0],
|
||||
size[1], (a[1] - b[1])/count[1],
|
||||
size[2], (a[2] - b[2])/count[2],
|
||||
size[3], (a[3] - b[3])/count[3],
|
||||
size[4], (a[4] - b[4])/count[4],
|
||||
size[5], (a[5] - b[5])/count[5]);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
diffs/
|
||||
errors/
|
||||
failed/
|
||||
fulldownload/
|
||||
patched/
|
||||
results/
|
||||
Executable
+124
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
|
||||
input=/usr/bin/bash
|
||||
enc="full"
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo -e "\tUsage: creatediffs.sh folder-to-diff [<optional-file-to-diff-against>] <optional_encoding>\n"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $# -ge 2 ]; then
|
||||
input=$2
|
||||
fi
|
||||
|
||||
if [ $# -eq 3 ]; then
|
||||
enc=$3
|
||||
fi
|
||||
|
||||
folder=$1
|
||||
|
||||
# Using find because rm fails when there are > 4096 files
|
||||
echo "* Cleaning up output folders..."
|
||||
find diffs/* -name "*" -delete > /dev/null 2>&1
|
||||
find failed/* -name "*" -delete > /dev/null 2>&1
|
||||
find fulldownload/* -name "*" -delete > /dev/null 2>&1
|
||||
find patched/* -name "*" -delete > /dev/null 2>&1
|
||||
rm -rf errors/errordiffs errors/falsepositivediffs errors/hashfails RESULTS.txt
|
||||
|
||||
differr=0
|
||||
patcherr=0
|
||||
falsepos=0
|
||||
hasherr=0
|
||||
|
||||
# Run bsdiff with every supported encoding
|
||||
echo "* Running bsdiff..."
|
||||
if [ "$enc" == "full" ]; then
|
||||
for f in $(ls $folder);
|
||||
do
|
||||
for t in $(cat types);
|
||||
do
|
||||
echo "$input ----> $f TYPE: $t"
|
||||
sudo bsdiff $input $folder/$f diffs/$f.$t $t #> /dev/null
|
||||
ret=$?
|
||||
if [ $ret -eq 255 ]; then
|
||||
echo -e "***ERROR: $ret\n"
|
||||
sudo mv diffs/$f.$t failed/$f.$t
|
||||
let differr=differr+1
|
||||
elif [ $ret -eq 1 ]; then
|
||||
echo -e "\t* FULL DOWNLOAD requested"
|
||||
sudo mv diffs/$f.$t fulldownload/$f.$t
|
||||
fi
|
||||
done
|
||||
done
|
||||
else
|
||||
# Do diffs with ONLY the specified encoding if given
|
||||
echo "IN MINIMAL"
|
||||
for f in $(ls $folder);
|
||||
do
|
||||
echo "$input ----> $f TYPE: $enc"
|
||||
sudo valgrind bsdiff $input $folder/$f diffs/$f.$enc $enc
|
||||
ret=$?
|
||||
if [ $ret -eq 255 ]; then
|
||||
echo -e "***ERROR: $ret\n"
|
||||
sudo mv diffs/$f failed/$f
|
||||
let differr=differr+1
|
||||
elif [ $ret -eq 1 ]; then
|
||||
sudo mv diffs/$f fulldownload/$f
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Check if the successful diffs REALLY do apply cleanly
|
||||
echo -e "\n* Applying created diffs..."
|
||||
|
||||
for f in $(ls diffs);
|
||||
do
|
||||
sudo bspatch $input patched/$f diffs/$f
|
||||
ret=$?
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo -e "Failed to apply diff $f\n"
|
||||
let patcherr=patcherr+1
|
||||
sudo echo "$f, $ret" >> errors/errordiffs
|
||||
fi
|
||||
done
|
||||
echo -e "Finished!\n"
|
||||
|
||||
# Check if any failed diffs apply cleanly to mark false positives
|
||||
echo -e "* Applying failed diffs..."
|
||||
for f in $(ls failed);
|
||||
do
|
||||
sudo bspatch $input patched/$f-FAILED failed/$f
|
||||
ret=$?
|
||||
|
||||
if [ $ret -eq 0 ]; then
|
||||
echo "FALSEPOSITIVE: $f applied successfully"
|
||||
echo "$f, $ret" >> errors/falsepositivediffs
|
||||
let falsepos=falsepos+1
|
||||
fi
|
||||
done
|
||||
echo -e "Finished!\n"
|
||||
|
||||
# Check that the patched file hashes match the original file hashes
|
||||
for f in $(ls patched);
|
||||
do
|
||||
newhash=$(sudo swupd hashdump --basepath ./ patched/$f | tail -1)
|
||||
# strip the encoding type off the filename so we can match the original file
|
||||
oldfile=$(echo $f | sed 's/\.[a-z0-9]*$//')
|
||||
oldhash=$(sudo swupd hashdump --basepath $folder $oldfile | tail -1)
|
||||
|
||||
if [[ "$newhash" != "$oldhash" ]]; then
|
||||
echo -e "\n*** ERROR: hash mismatch **\n$input/$oldfile\n"
|
||||
echo -e "patched/$f\nHas Hash: $newhash\nExpected: $oldhash\n" >> errors/hashfails
|
||||
echo -e "NEWHASH: $newhash\nOLDHASH: $oldhash"
|
||||
let hasherr=hasherr+1
|
||||
fi
|
||||
done
|
||||
|
||||
# Report the number of failures since a lot of output was probably produced
|
||||
echo "Failed Diffs: $differr" | tee -a RESULTS.txt
|
||||
echo "Failed patches: $patcherr" | tee -a RESULTS.txt
|
||||
echo "False positive diffs: $falsepos" | tee -a RESULTS.txt
|
||||
echo "Hash Mismatches: $hasherr" | tee -a RESULTS.txt
|
||||
echo
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Use one of the available template source options to specify the template to be used in creating this stack." %}</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "You may update the editable properties of your port here." %}</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "You may update the editable properties of your network here." %}</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,28 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}select_template{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:project:stacks:change_template' stack.id %}{% endblock %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Select Template" %}{% endblock %}
|
||||
{% block modal_id %}select_template_modal{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Use one of the available template source options to specify the template to be used in creating this stack." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Next" %}" />
|
||||
<a href="{% url 'horizon:project:stacks:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}update_network_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:networks:update' network_id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Edit Network" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "You may update the editable properties of your network here." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
|
||||
<a href="{% url 'horizon:admin:networks:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,30 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}update_port_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:networks:editport' network_id port_id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Edit Port" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<dl>
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ port_id }}</dd>
|
||||
</dl>
|
||||
<hr>
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "You may update the editable properties of your port here." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
|
||||
<a href="{% url 'horizon:admin:networks:detail' network_id %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Router Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Router Details") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% include "project/routers/_detail_overview.html" %}
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Router Details" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{% include "admin/routers/_detail_overview.html" %}
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *mem = malloc(atoi(argv[1]));
|
||||
|
||||
if (!mem) {
|
||||
fprintf(stderr, "failed to allocate, mem = %p\n", mem);
|
||||
exit(1);
|
||||
}
|
||||
printf("mem: %p\n", mem);
|
||||
free(mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
any
|
||||
@@ -0,0 +1,61 @@
|
||||
Advanced configuration interface
|
||||
********************************
|
||||
|
||||
|
||||
Configuration basics
|
||||
====================
|
||||
|
||||
The default configuration method for all services is automatic or something
|
||||
like DHCP. In almost every case that should be just good enough, but if it
|
||||
is not, Connection Manager supports manual configuration for Ethernet and
|
||||
IP settings.
|
||||
|
||||
|
||||
Configuration interface
|
||||
=======================
|
||||
|
||||
Every service contains two properties. One represents the current active
|
||||
configuration and the other one allows manual configuration via the user.
|
||||
|
||||
For IPv4 they are named "IPv4" and IPv4.Configuration".
|
||||
|
||||
[ /profile/default/wifi_001122334455_42696720696e204a6170616e_managed_psk ]
|
||||
Type = wifi
|
||||
Name = Big in Japan
|
||||
Mode = managed
|
||||
Strength = 82
|
||||
Security = rsn
|
||||
Favorite = true
|
||||
State = ready
|
||||
IPv4.Configuration = { Method=dhcp }
|
||||
IPv4 = { Netmask=255.255.255.0 Method=dhcp Address=192.168.1.198 }
|
||||
|
||||
The above WiFi network shows how the default configuration would look like
|
||||
with a connected service. The configuration method is DHCP and the current
|
||||
IP address is 192.168.1.198.
|
||||
|
||||
The "IPv4" property is read-only and will emit PropertyChanged signals in
|
||||
case the IP address of this interface changes. The "IPv4.Configuration"
|
||||
property is read-write and allows changes. For example to use a static IP
|
||||
configuration this call could be used:
|
||||
|
||||
service.SetProperty("IPv4.Configuration", { "Method": "manual",
|
||||
"Address": "192.168.1.100",
|
||||
"Netmask": "255.255.255.0" })
|
||||
|
||||
The configuration itself is a dictionary with various fields. Not all of
|
||||
them need to be present. A lot of combinations are valid.
|
||||
|
||||
For example the "Method" field has valid settings of "off", "fixed", "manual"
|
||||
and "dhcp". The "fixed" value however can not be set by any user program. It
|
||||
is an internal value that some 3G cards require. Switching to "off" will
|
||||
remove any IP configuration from the interface. The "manual" method allows
|
||||
for static address configuration. And "dhcp" will use DHCP to retrieve all
|
||||
required information automatically.
|
||||
|
||||
With a manual configuration, the fields "Address" and "Netmask" should be
|
||||
given. In case "Netmask" is left out, the best netmask will be calculated.
|
||||
|
||||
The "Gateway" field can be used to indicate the default route/gateway for
|
||||
this interface.
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
Agent hierarchy
|
||||
===============
|
||||
|
||||
Service unique name
|
||||
Interface net.connman.Agent
|
||||
Object path freely definable
|
||||
|
||||
Methods void Release()
|
||||
|
||||
This method gets called when the service daemon
|
||||
unregisters the agent. An agent can use it to do
|
||||
cleanup tasks. There is no need to unregister the
|
||||
agent, because when this method gets called it has
|
||||
already been unregistered.
|
||||
|
||||
void ReportError(object service, string error)
|
||||
|
||||
This method gets called when an error has to be
|
||||
reported to the user.
|
||||
|
||||
A special return value can be used to trigger a
|
||||
retry of the failed transaction.
|
||||
|
||||
Possible Errors: net.connman.Agent.Error.Retry
|
||||
|
||||
void ReportPeerError(object peer, string error)
|
||||
|
||||
This method gets called when an error has to be
|
||||
reported to the user about a peer connection.
|
||||
|
||||
A special return value can be used to trigger a
|
||||
retry of the failed transaction.
|
||||
|
||||
Possible Errors: net.connman.Agent.Error.Retry
|
||||
|
||||
void RequestBrowser(object service, string url)
|
||||
|
||||
This method gets called when it is required
|
||||
to ask the user to open a website to procceed
|
||||
with login handling.
|
||||
|
||||
This can happen if connected to a hotspot portal
|
||||
page without WISPr support.
|
||||
|
||||
Possible Errors: net.connman.Agent.Error.Canceled
|
||||
|
||||
dict RequestInput(object service, dict fields)
|
||||
|
||||
This method gets called when trying to connect to
|
||||
a service and some extra input is required. For
|
||||
example a passphrase or the name of a hidden network.
|
||||
|
||||
The return value should be a dictionary where the
|
||||
keys are the field names and the values are the
|
||||
actual fields. Alternatively an error indicating that
|
||||
the request got canceled can be returned.
|
||||
|
||||
Most common return field names are "Name" and of
|
||||
course "Passphrase".
|
||||
|
||||
The dictionary arguments contains field names with
|
||||
their input parameters.
|
||||
|
||||
In case of WISPr credentials requests and if the user
|
||||
prefers to login through the browser by himself, agent
|
||||
will have to return a LaunchBrowser error (see below).
|
||||
|
||||
Possible Errors: net.connman.Agent.Error.Canceled
|
||||
net.connman.Agent.Error.LaunchBrowser
|
||||
|
||||
dict RequestPeerAuthorization(object peer, dict fields) [experimental]
|
||||
|
||||
This method gets called when trying to connect to a
|
||||
peer or when an incoming peer connection is requested,
|
||||
for which some extra input is required. In this case,
|
||||
it will only deal with WPS input as well as accepting
|
||||
or rejecting an incoming connection.
|
||||
|
||||
The return value should be a dictionary where the
|
||||
keys are the field names and the values are the
|
||||
actual fields. Alternatively an error indicating that
|
||||
the request got canceled or rejected can be returned.
|
||||
|
||||
The dictionary arguments contains field names with
|
||||
their input parameters.
|
||||
|
||||
Possible Errors: net.connman.Agent.Error.Canceled
|
||||
net.connman.Agent.Error.Rejected
|
||||
|
||||
void Cancel()
|
||||
|
||||
This method gets called to indicate that the agent
|
||||
request failed before a reply was returned.
|
||||
|
||||
Fields string Name
|
||||
|
||||
The name of a network. This field will be requested
|
||||
when trying to connect to a hidden network.
|
||||
|
||||
array{byte} SSID
|
||||
|
||||
This field is an alternative to "Name" for WiFi
|
||||
networks and can be used to return the exact binary
|
||||
representation of a network name.
|
||||
|
||||
Normally returning the "Name" field is the better
|
||||
option here.
|
||||
|
||||
string Identity
|
||||
|
||||
Identity (username) for EAP authentication methods.
|
||||
|
||||
string Passphrase
|
||||
|
||||
The passphrase for authentication. For example a WEP
|
||||
key, a PSK passphrase or a passphrase for EAP
|
||||
authentication methods.
|
||||
|
||||
string PreviousPassphrase
|
||||
|
||||
The previous passphrase successfully saved, i.e.
|
||||
which lead to a successfull connection. This field is
|
||||
provided as an informational argument when connecting
|
||||
with it does not work anymore, for instance when it
|
||||
has been changed on the AP. Such argument appears when
|
||||
a RequestInput is raised after a retry. In case of WPS
|
||||
association through PIN method: when retrying, the
|
||||
previous wpspin will be provided.
|
||||
|
||||
string WPS
|
||||
|
||||
This field requests the use of WPS to get associated.
|
||||
This is an alternate choice against Passphrase when
|
||||
requested service supports WPS. The reply can contain
|
||||
either empty pin, if user wants to use push-button
|
||||
method, or a pin code if user wants to use the pin
|
||||
method.
|
||||
|
||||
In case of a RequestPeerAuthorization, this field will
|
||||
be set as mandatory.
|
||||
|
||||
string Username
|
||||
|
||||
Username for WISPr authentication. This field will be
|
||||
requested when connecting to a WISPr-enabled hotspot.
|
||||
|
||||
string Password
|
||||
|
||||
Password for WISPr authentication. This field will be
|
||||
requested when connecting to a WISPr-enabled hotspot.
|
||||
|
||||
Arguments string Type
|
||||
|
||||
Contains the type of a field. For example "psk", "wep"
|
||||
"passphrase", "response", "ssid", "wpspin" or plain
|
||||
"string".
|
||||
|
||||
string Requirement
|
||||
|
||||
Contains the requirement option. Valid values are
|
||||
"mandatory", "optional", "alternate" or
|
||||
"informational".
|
||||
|
||||
The "alternate" value specifies that this field can be
|
||||
returned as an alternative to another one. An example
|
||||
would be the network name or SSID.
|
||||
|
||||
All "mandatory" fields must be returned, while the
|
||||
"optional" can be returned if available.
|
||||
|
||||
Nothing needs to be returned for "informational", as it
|
||||
is here only to provide an information so a value is
|
||||
attached to it.
|
||||
|
||||
array{string} Alternates
|
||||
|
||||
Contains the list of alternate field names this
|
||||
field can be represented by.
|
||||
|
||||
string Value
|
||||
|
||||
Contains data as a string, relatively to an
|
||||
"informational" argument.
|
||||
|
||||
Examples Requesting a passphrase for WPA2 network
|
||||
|
||||
RequestInput("/service1",
|
||||
{ "Passphrase" : { "Type" : "psk",
|
||||
"Requirement" : "mandatory"
|
||||
}
|
||||
}
|
||||
==> { "Passphrase" : "secret123" }
|
||||
|
||||
Requesting a passphrase after an error on the previous one:
|
||||
|
||||
RequestInput("/service1",
|
||||
{ "Passphrase" : { "Type" : "psk",
|
||||
"Requirement" : "mandatory"
|
||||
},
|
||||
"PreviousPassphrase" :
|
||||
{ "Type" : "psk",
|
||||
"Requirement : "informational",
|
||||
"Value" : "secret123"
|
||||
}
|
||||
}
|
||||
|
||||
Requesting name for hidden network
|
||||
|
||||
RequestInput("/service2",
|
||||
{ "Name" : { "Type" : "string",
|
||||
"Requirement" : "mandatory",
|
||||
"Alternates" : [ "SSID" ]
|
||||
},
|
||||
"SSID" : { "Type" : "ssid",
|
||||
"Requirement" : "alternate"
|
||||
}
|
||||
}
|
||||
==> { "Name" : "My hidden network" }
|
||||
|
||||
Requesting a passphrase for a WPA2 network with WPS alternative:
|
||||
|
||||
RequestInput("/service3",
|
||||
{ "Passphrase" : { "Type" : "psk",
|
||||
"Requirement" : "mandatory",
|
||||
"Alternates" : [ "WPS" ]
|
||||
},
|
||||
"WPS" : { "Type" : "wpspin",
|
||||
"Requirement" : "alternate"
|
||||
}
|
||||
}
|
||||
|
||||
==> { "WPS" : "123456" }
|
||||
|
||||
Requesting a passphrase for a WPA2 network with WPS alternative
|
||||
after an error on the previous one:
|
||||
|
||||
RequestInput("/service3",
|
||||
{ "Passphrase" : { "Type" : "psk",
|
||||
"Requirement" : "mandatory",
|
||||
"Alternates" : [ "WPS" ]
|
||||
},
|
||||
"WPS" : { "Type" : "wpspin",
|
||||
"Requirement" : "alternate"
|
||||
}
|
||||
"PreviousPassphrase" :
|
||||
{ "Type" : "wpspin",
|
||||
"Requirement : "informational",
|
||||
"Value" : "123456"
|
||||
}
|
||||
|
||||
Requesting passphrase for a WPA-Enterprise network:
|
||||
|
||||
RequestInput("/service4",
|
||||
{ "Identity" : { "Type" : "string",
|
||||
"Requirement" : "mandatory"
|
||||
},
|
||||
"Passphrase" : { "Type" : "passphrase",
|
||||
"Requirement" : "mandatory"
|
||||
}
|
||||
}
|
||||
|
||||
==> { "Identity" : "alice", "Passphrase": "secret123" }
|
||||
|
||||
Requesting challenge response for a WPA-Enterprise network:
|
||||
|
||||
RequestInput("/service4",
|
||||
{ "Identity" : { "Type" : "string",
|
||||
"Requirement" : "mandatory"
|
||||
},
|
||||
"Passphrase" : { "Type" : "response",
|
||||
"Requirement" : "mandatory"
|
||||
}
|
||||
}
|
||||
|
||||
==> { "Identity" : "bob", "Passphrase": "secret123" }
|
||||
|
||||
Requesting username and password for a WISPr-enabled hotspot:
|
||||
|
||||
RequestInput("/service5",
|
||||
{ "Username" : { "Type" : "string",
|
||||
"Requirement" : "mandatory"
|
||||
},
|
||||
"Password" : { "Type" : "passphrase",
|
||||
"Requirement" : "mandatory"
|
||||
}
|
||||
}
|
||||
|
||||
==> { "Username" : "foo", "Password": "secret" }
|
||||
|
||||
Requesting a answer about an inconming peer connection:
|
||||
|
||||
RequestPeerAuthorization("/peer3", {})
|
||||
|
||||
==> { }
|
||||
|
||||
Requesting the WPS details when connecting to a peer:
|
||||
|
||||
RequestPeerAuthorization("/peer4",
|
||||
{ "WPS":
|
||||
{ "Type" : "wpspin",
|
||||
"Requirement" : "mandatory"
|
||||
}
|
||||
}
|
||||
|
||||
==> { "WPS" : "" }
|
||||
@@ -0,0 +1,28 @@
|
||||
ConnMan backtraces
|
||||
******************
|
||||
|
||||
ConnMan dumps backtraces upon segmentation faults, bus errors and other
|
||||
crashing signals. Regardless of the debug level you started connmand with, the
|
||||
backtrace will be dumped to syslog.
|
||||
|
||||
The ConnMan backtraces start with the following line:
|
||||
-------- backtrace --------
|
||||
and will try to display function names if those can be resolved from the stack
|
||||
addresses. All static functions name will not appear for example.
|
||||
|
||||
For a more complete and useful stack frame output you can use the
|
||||
test/backtrace script. It takes the actual binary that crashed and the
|
||||
connmand logs. The logs can contain any connman debug strings on top of the
|
||||
backtrace.
|
||||
|
||||
Here is an example of the backtrace script usage:
|
||||
|
||||
me@localhost:[~]$ backtrace /sbin/connmand connman.log
|
||||
-------- backtrace --------
|
||||
[0]: __connman_debug_list_available() [log.c:117]
|
||||
[1]: connman_driver_register() [element.c:515]
|
||||
[2]: __connman_driver_rescan() [element.c:490]
|
||||
[3]: disable_technology() [manager.c:391]
|
||||
[4]: generic_message() [object.c:262]
|
||||
-----------------------------------
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Interface behavior description
|
||||
******************************
|
||||
|
||||
|
||||
Ethernet service
|
||||
================
|
||||
|
||||
The Ethernet based service is a special case since it has no children, but
|
||||
still can be manually connected and disconnected while also has an implicit
|
||||
behavior when physically plugging in or removing an Ethernet cable.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
Clock hierarchy
|
||||
===============
|
||||
|
||||
Service net.connman
|
||||
Interface net.connman.Clock
|
||||
Object path /
|
||||
|
||||
Methods dict GetProperties() [experimental]
|
||||
|
||||
Returns all system clock properties. See the
|
||||
properties section for available properties.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void SetProperty(string name, variant value) [experimental]
|
||||
|
||||
Changes the value of the specified property. Only
|
||||
properties that are listed as read-write are
|
||||
changeable. On success a PropertyChanged signal
|
||||
will be emitted.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.InvalidProperty
|
||||
|
||||
Signals PropertyChanged(string name, variant value) [experimental]
|
||||
|
||||
This signal indicates a changed value of the given
|
||||
property.
|
||||
|
||||
|
||||
Properties uint64 Time [readonly or readwrite] [experimental]
|
||||
|
||||
Current system time in seconds since epoch.
|
||||
|
||||
This value is present for changing the system time
|
||||
if TimeUpdates is set to manual.
|
||||
|
||||
It is not present for driving an updated display
|
||||
of the system time. PropertyChanged signal for this
|
||||
value are only send out if it gets changed or jumps
|
||||
unexpectedly.
|
||||
|
||||
In general application interested in the current
|
||||
time should be using gettimeofday() and related
|
||||
system calls.
|
||||
|
||||
string TimeUpdates [readwrite] [experimental]
|
||||
|
||||
Possible values are "manual" and "auto" to indicate
|
||||
time update policy.
|
||||
|
||||
With the "auto" setting the system tries to use as
|
||||
many sources as possible to determine the correct
|
||||
and updated time.
|
||||
|
||||
string Timezone [readonly or readwrite] [experimental]
|
||||
|
||||
Current system timezone string. Allowed values
|
||||
are from the standard timezone data (tzdata)
|
||||
package under /usr/share/zoneinfo. For example
|
||||
strings like "America/Vancouver" or "Europe/Berlin".
|
||||
|
||||
This value is present for changing the timezone
|
||||
if TimezoneUpdates is set to manual.
|
||||
|
||||
When the timezone gets changed a PropertyChanged
|
||||
signal will be send out.
|
||||
|
||||
string TimezoneUpdates [readwrite] [experimental]
|
||||
|
||||
Possible values are "manual" and "auto" to indicate
|
||||
timezone update policy.
|
||||
|
||||
With the "auto" setting the system tries to use as
|
||||
many sources as possible to determine the correct
|
||||
timezone.
|
||||
|
||||
array{string} Timeservers [readwrite] [experimental]
|
||||
|
||||
List of global default NTP servers. The list should
|
||||
be sorted in order of preference.
|
||||
|
||||
If a service configuration provides NTP servers,
|
||||
then they are preferred over the global ones.
|
||||
|
||||
This list of servers is used when TimeUpdates is set
|
||||
to auto.
|
||||
@@ -0,0 +1,344 @@
|
||||
Every project has its coding style, and ConnMan is not an exception. This
|
||||
document describes the preferred coding style for ConnMan code, in order to keep
|
||||
some level of consistency among developers so that code can be easily
|
||||
understood and maintained, and also to help your code survive under
|
||||
maintainer's fastidious eyes so that you can get a passport for your patch
|
||||
ASAP.
|
||||
|
||||
First of all, ConnMan coding style must follow every rule for Linux kernel
|
||||
(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
|
||||
named checkpatch.pl to help you check the compliance with it. Just type
|
||||
"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
|
||||
to clean up all the warnings and errors except this one: "ERROR: Missing
|
||||
Signed-off-by: line(s)". ConnMan does not used Signed-Off lines, so including
|
||||
them is actually an error. In certain circumstances one can ignore the 80
|
||||
character per line limit. This is generally only allowed if the alternative
|
||||
would make the code even less readable.
|
||||
|
||||
Besides the kernel coding style above, ConnMan has special flavors for its own.
|
||||
Some of them are mandatory (marked as 'M'), while some others are optional
|
||||
(marked as 'O'), but generally preferred.
|
||||
|
||||
M1: Blank line before and after an if/while/do/for statement
|
||||
============================================================
|
||||
There should be a blank line before if statement unless the if is nested and
|
||||
not preceded by an expression or variable declaration.
|
||||
|
||||
Example:
|
||||
1)
|
||||
a = 1;
|
||||
if (b) { // wrong
|
||||
|
||||
2)
|
||||
a = 1
|
||||
|
||||
if (b) {
|
||||
}
|
||||
a = 2; // wrong
|
||||
|
||||
3)
|
||||
if (a) {
|
||||
if (b) // correct
|
||||
|
||||
4)
|
||||
b = 2;
|
||||
|
||||
if (a) { // correct
|
||||
|
||||
}
|
||||
|
||||
b = 3;
|
||||
|
||||
The only exception to this rule applies when a variable is being allocated:
|
||||
array = g_try_new0(int, 20);
|
||||
if (!array) // Correct
|
||||
return;
|
||||
|
||||
|
||||
M2: Multiple line comment
|
||||
=========================
|
||||
If your comments have more then one line, please start it from the second line.
|
||||
|
||||
Example:
|
||||
/*
|
||||
* first line comment // correct
|
||||
* ...
|
||||
* last line comment
|
||||
*/
|
||||
|
||||
|
||||
M3: Space before and after operator
|
||||
===================================
|
||||
There should be a space before and after each operator.
|
||||
|
||||
Example:
|
||||
a + b; // correct
|
||||
|
||||
|
||||
M4: Wrap long lines
|
||||
===================
|
||||
If your condition in if, while, for statement or a function declaration is too
|
||||
long to fit in one line, the new line needs to be indented not aligned with the
|
||||
body.
|
||||
|
||||
Example:
|
||||
1)
|
||||
if (call->status == CALL_STATUS_ACTIVE ||
|
||||
call->status == CALL_STATUS_HELD) { // wrong
|
||||
connman_dbus_dict_append();
|
||||
|
||||
2)
|
||||
if (call->status == CALL_STATUS_ACTIVE ||
|
||||
call->status == CALL_STATUS_HELD) { // correct
|
||||
connman_dbus_dict_append();
|
||||
|
||||
3)
|
||||
gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
|
||||
enum sim_ust_service index) // wrong
|
||||
{
|
||||
int a;
|
||||
...
|
||||
}
|
||||
|
||||
4)
|
||||
gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
|
||||
enum sim_ust_service index) // correct
|
||||
{
|
||||
int a;
|
||||
...
|
||||
}
|
||||
|
||||
If the line being wrapped is a function call or function declaration, the
|
||||
preferred style is to indent at least past the opening parenthesis. Indenting
|
||||
further is acceptable as well (as long as you don't hit the 80 character
|
||||
limit).
|
||||
|
||||
If this is not possible due to hitting the 80 character limit, then indenting
|
||||
as far as possible to the right without hitting the limit is preferred.
|
||||
|
||||
Example:
|
||||
|
||||
1)
|
||||
gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
|
||||
enum sim_ust_service index); // worse
|
||||
|
||||
2)
|
||||
gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
|
||||
enum sim_ust_service index);
|
||||
// better
|
||||
|
||||
M5: Git commit message 50/72 formatting
|
||||
=======================================
|
||||
The commit message header should be within 50 characters. And if you have
|
||||
detailed explanatory text, wrap it to 72 character.
|
||||
|
||||
|
||||
M6: Space when doing type casting
|
||||
=================================
|
||||
There should be a space between new type and variable.
|
||||
|
||||
Example:
|
||||
1)
|
||||
a = (int *)b; // wrong
|
||||
2)
|
||||
a = (int *) b; // correct
|
||||
|
||||
|
||||
M7: Don't initialize variable unnecessarily
|
||||
===========================================
|
||||
When declaring a variable, try not to initialize it unless necessary.
|
||||
|
||||
Example:
|
||||
int i = 1; // wrong
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
}
|
||||
|
||||
|
||||
M8: Use g_try_malloc instead of g_malloc
|
||||
========================================
|
||||
When g_malloc fails, the whole program would exit. Most of time, this is not
|
||||
the expected behavior, and you may want to use g_try_malloc instead.
|
||||
|
||||
Example:
|
||||
additional = g_try_malloc(len - 1); // correct
|
||||
if (!additional)
|
||||
return FALSE;
|
||||
|
||||
|
||||
M9: Follow the order of include header elements
|
||||
===============================================
|
||||
When writing an include header the various elements should be in the following
|
||||
order:
|
||||
- #includes
|
||||
- forward declarations
|
||||
- #defines
|
||||
- enums
|
||||
- typedefs
|
||||
- function declarations and inline function definitions
|
||||
|
||||
|
||||
M10: Internal headers must not use include guards
|
||||
=================================================
|
||||
Any time when creating a new header file with non-public API, that header
|
||||
must not contain include guards.
|
||||
|
||||
|
||||
M11: Naming of enums
|
||||
====================
|
||||
|
||||
Enums must have a descriptive name. The enum type should be small caps and
|
||||
it should not be typedef-ed. Enum contents should be in CAPITAL letters and
|
||||
prefixed by the enum type name.
|
||||
|
||||
Example:
|
||||
|
||||
enum animal_type {
|
||||
ANIMAL_TYPE_FOUR_LEGS,
|
||||
ANIMAL_TYPE_EIGHT_LEGS,
|
||||
ANIMAL_TYPE_TWO_LEGS,
|
||||
};
|
||||
|
||||
If the enum contents have values (e.g. from specification) the formatting
|
||||
should be as follows:
|
||||
|
||||
enum animal_type {
|
||||
ANIMAL_TYPE_FOUR_LEGS = 4,
|
||||
ANIMAL_TYPE_EIGHT_LEGS = 8,
|
||||
ANIMAL_TYPE_TWO_LEGS = 2,
|
||||
};
|
||||
|
||||
M12: Enum as switch variable
|
||||
====================
|
||||
|
||||
If the variable of a switch is an enum, you must not include a default in
|
||||
switch body. The reason for this is: If later on you modify the enum by adding
|
||||
a new type, and forget to change the switch accordingly, the compiler will
|
||||
complain the new added type hasn't been handled.
|
||||
|
||||
Example:
|
||||
|
||||
enum animal_type {
|
||||
ANIMAL_TYPE_FOUR_LEGS = 4,
|
||||
ANIMAL_TYPE_EIGHT_LEGS = 8,
|
||||
ANIMAL_TYPE_TWO_LEGS = 2,
|
||||
};
|
||||
|
||||
enum animal_type t;
|
||||
|
||||
switch (t) {
|
||||
case ANIMAL_TYPE_FOUR_LEGS:
|
||||
...
|
||||
break;
|
||||
case ANIMAL_TYPE_EIGHT_LEGS:
|
||||
...
|
||||
break;
|
||||
case ANIMAL_TYPE_TWO_LEGS:
|
||||
...
|
||||
break;
|
||||
default: // wrong
|
||||
break;
|
||||
}
|
||||
|
||||
However if the enum comes from an external header file outside ConnMan
|
||||
we cannot make any assumption of how the enum is defined and this
|
||||
rule might not apply.
|
||||
|
||||
M13: Check for pointer being NULL
|
||||
=================================
|
||||
|
||||
When checking if a pointer or a return value is NULL, use the
|
||||
check with "!" operator.
|
||||
|
||||
Example:
|
||||
1)
|
||||
array = g_try_new0(int, 20);
|
||||
if (!array) // Correct
|
||||
return;
|
||||
|
||||
2)
|
||||
if (!g_at_chat_get_slave(chat)) // Correct
|
||||
return -EINVAL;
|
||||
|
||||
3)
|
||||
array = g_try_new0(int, 20);
|
||||
if (array == NULL) // Wrong
|
||||
return;
|
||||
|
||||
|
||||
M14: Always use parenthesis with sizeof
|
||||
=======================================
|
||||
The expression argument to the sizeof operator should always be in
|
||||
parenthesis, too.
|
||||
|
||||
Example:
|
||||
1)
|
||||
memset(stuff, 0, sizeof(*stuff));
|
||||
|
||||
2)
|
||||
memset(stuff, 0, sizeof *stuff); // Wrong
|
||||
|
||||
|
||||
M15: Use void if function has no parameters
|
||||
===========================================================
|
||||
A function with no parameters must use void in the parameter list.
|
||||
|
||||
Example:
|
||||
1)
|
||||
void foo(void)
|
||||
{
|
||||
}
|
||||
|
||||
2)
|
||||
void foo() // Wrong
|
||||
{
|
||||
}
|
||||
|
||||
M16: Don't use hex value with shift operators
|
||||
==============================================
|
||||
The expression argument to the shift operators should not be in hex.
|
||||
|
||||
Example:
|
||||
|
||||
1)
|
||||
1 << y
|
||||
|
||||
2)
|
||||
0x1 << y // Wrong
|
||||
|
||||
O1: Shorten the name
|
||||
====================
|
||||
Better to use abbreviation, rather than full name, to name a variable,
|
||||
function, struct, etc.
|
||||
|
||||
Example:
|
||||
supplementary_service // too long
|
||||
ss // better
|
||||
|
||||
O2: Try to avoid complex if body
|
||||
================================
|
||||
It's better not to have a complicated statement for if. You may judge its
|
||||
contrary condition and return | break | continue | goto ASAP.
|
||||
|
||||
Example:
|
||||
1)
|
||||
if (a) { // worse
|
||||
struct voicecall *v;
|
||||
call = synthesize_outgoing_call(vc, vc->pending);
|
||||
v = voicecall_create(vc, call);
|
||||
v->detect_time = time(NULL);
|
||||
DBG("Registering new call: %d", call->id);
|
||||
voicecall_dbus_register(v);
|
||||
} else
|
||||
return;
|
||||
|
||||
2)
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
struct voicecall *v;
|
||||
call = synthesize_outgoing_call(vc, vc->pending);
|
||||
v = voicecall_create(vc, call);
|
||||
v->detect_time = time(NULL);
|
||||
DBG("Registering new call: %d", call->id);
|
||||
voicecall_dbus_register(v);
|
||||
@@ -0,0 +1,155 @@
|
||||
Connman configuration file format
|
||||
*********************************
|
||||
|
||||
Connman uses configuration files to provision existing services. Connman will
|
||||
be looking for its configuration files at STORAGEDIR which by default points
|
||||
to /var/lib/connman/. Configuration file names must not include other
|
||||
characters than letters or numbers and must have a .config suffix.
|
||||
Those configuration files are text files with a simple key-value pair format,
|
||||
organized into sections. Values do not comprise leading or trailing whitespace.
|
||||
We typically have one file per provisioned network.
|
||||
|
||||
If the config file is removed, then Connman tries to remove the
|
||||
provisioned services. If an individual service inside a config is removed,
|
||||
then the corresponding provisioned service is removed. If a service section
|
||||
is changed, then the corresponding service is removed and immediately
|
||||
re-provisioned.
|
||||
|
||||
|
||||
Global section [global]
|
||||
=======================
|
||||
|
||||
These files can have an optional global section describing the actual file.
|
||||
The two allowed fields for this section are:
|
||||
- Name: Name of the network.
|
||||
- Description: Description of the network.
|
||||
|
||||
|
||||
Service sections [service_*]
|
||||
============================
|
||||
|
||||
Each provisioned service must start with the [service_*] tag. Replace * with
|
||||
an identifier unique to the config file.
|
||||
|
||||
Allowed fields:
|
||||
- Type: Service type. We currently only support wifi and ethernet.
|
||||
- IPv4: The IPv4 address, netmask and gateway. Format of the entry
|
||||
is network/netmask/gateway. The mask length can be used instead
|
||||
of netmask. The gateway can be omitted if necessary.
|
||||
The IPv4 field can also contain the string "off" or "dhcp".
|
||||
If the setting is "off", then no IPv4 address is set to the interface.
|
||||
If the setting is "dhcp", then DHCPv4 address resolution is activated.
|
||||
Example: 192.168.1.2/24/192.168.1.1
|
||||
192.168.200.100/255.255.255.0/192.168.200.1
|
||||
10.0.0.2/24
|
||||
- IPv6: The IPv6 address, prefix length and gateway. Format of the entry
|
||||
is network/prefixlen/gateway. For IPv6 addresses only prefix length is
|
||||
accepted. The gateway can be omitted if necessary.
|
||||
The IPv6 field can also contain the string "off" or "auto".
|
||||
If the setting is "off", then no IPv6 address is set to the interface.
|
||||
If the setting is "auto", then SLAAC or DHCPv6 is used.
|
||||
Example: 2001:db8::2/64/2001:db8::1
|
||||
2001:db8::1:2:3:4/64
|
||||
- IPv6.Privacy: IPv6 privacy option. Value can be either "disabled",
|
||||
"enabled" or "preferred" (or the misspelled "prefered"). See use_tempaddr
|
||||
variable description in Linux kernel Documentation/networking/ip-sysctl.txt
|
||||
file.
|
||||
- MAC: MAC address of the interface where this setting should be applied.
|
||||
The MAC address is optional and if it is missing, then the first found
|
||||
interface is used. The byte values must have prefix 0 added,
|
||||
the bytes must be separated by ":" char and its length must be
|
||||
exactly 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 = 17 characters.
|
||||
- Nameservers: Comma separated list of nameservers
|
||||
- SearchDomains: Comma separated list of DNS search domains
|
||||
- Timeservers: Comma separated list of timeservers
|
||||
- Domain: Domain name to be used
|
||||
|
||||
If IPv4 address is missing then DHCP is used. If IPv6 address is missing,
|
||||
then SLAAC or DHCPv6 is used.
|
||||
|
||||
The following options are valid if Type is "wifi"
|
||||
- Name: A string representation of an 802.11 SSID. If the SSID field is
|
||||
present, the Name field is ignored.
|
||||
- SSID: A hexadecimal representation of an 802.11 SSID. Use this format to
|
||||
encode special characters including starting or ending spaces. If the SSID
|
||||
field is omitted, the Name field is used instead.
|
||||
- EAP: EAP type. We currently only support tls, ttls or peap.
|
||||
- CACertFile: File path to CA certificate file (PEM/DER).
|
||||
- ClientCertFile: File path to client certificate file (PEM/DER).
|
||||
- PrivateKeyFile: File path to client private key file (PEM/DER/PFX).
|
||||
- PrivateKeyPassphrase: Password/passphrase for private key file.
|
||||
- PrivateKeyPassphraseType: We only support the fsid passphrase type for now.
|
||||
This is for private keys generated by using their own filesystem UUID as the
|
||||
passphrase. The PrivateKeyPassphrase field is ignored when this field is set
|
||||
to fsid.
|
||||
- Identity: Identity string for EAP.
|
||||
- Phase2: Phase2 (inner authentication with TLS tunnel) authentication method.
|
||||
Prefix the value with "EAP-" to indicate the usage of an EAP-based inner
|
||||
authentication method (should only be used with EAP = TTLS).
|
||||
- Passphrase: RSN/WPA/WPA2 Passphrase
|
||||
- Security: The security type of the network. Possible values are 'psk'
|
||||
(WPA/WPA2 PSK), 'ieee8021x' (WPA EAP), 'none' and 'wep'. When not set, the
|
||||
default value is 'ieee8021x' if an EAP type is configured, 'psk' if a
|
||||
passphrase is present and 'none' otherwise.
|
||||
- Hidden: If set to true, then this AP is hidden. If missing or set to false,
|
||||
then AP is not hidden.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
This is a configuration file for a network providing EAP-TLS, EAP-TTLS and
|
||||
EAP-PEAP services. The respective SSIDs are tls_ssid, ttls_ssid and peap_ssid
|
||||
and the file name is example.config.
|
||||
|
||||
Please note that the SSID entry is for hexadecimal encoded SSID (e.g. "SSID =
|
||||
746c735f73736964"). If your SSID does not contain any exotic character then
|
||||
you should use the Name entry instead (e.g. "Name = tls_ssid").
|
||||
|
||||
example@example:[~]$ cat /var/lib/connman/example.config
|
||||
[global]
|
||||
Name = Example
|
||||
Description = Example network configuration
|
||||
|
||||
[service_tls]
|
||||
Type = wifi
|
||||
SSID = 746c735f73736964
|
||||
EAP = tls
|
||||
CACertFile = /home/user/.certs/ca.pem
|
||||
ClientCertFile = /home/user/devlp/.certs/client.pem
|
||||
PrivateKeyFile = /home/user/.certs/client.fsid.pem
|
||||
PrivateKeyPassphraseType = fsid
|
||||
Identity = user
|
||||
|
||||
[service_ttls]
|
||||
Type = wifi
|
||||
Name = ttls_ssid
|
||||
EAP = ttls
|
||||
CACertFile = /home/user/.cert/ca.pem
|
||||
Phase2 = MSCHAPV2
|
||||
Identity = user
|
||||
|
||||
[service_peap]
|
||||
Type = wifi
|
||||
Name = peap_ssid
|
||||
EAP = peap
|
||||
CACertFile = /home/user/.cert/ca.pem
|
||||
Phase2 = MSCHAPV2
|
||||
Identity = user
|
||||
|
||||
[service_home_ethernet]
|
||||
Type = ethernet
|
||||
IPv4 = 192.168.1.42/255.255.255.0/192.168.1.1
|
||||
IPv6 = 2001:db8::42/64/2001:db8::1
|
||||
MAC = 01:02:03:04:05:06
|
||||
Nameservers = 10.2.3.4,192.168.1.99
|
||||
SearchDomains = my.home,isp.net
|
||||
Timeservers = 10.172.2.1,ntp.my.isp.net
|
||||
Domain = my.home
|
||||
|
||||
[service_home_wifi]
|
||||
Type = wifi
|
||||
Name = my_home_wifi
|
||||
Passphrase = secret
|
||||
IPv4 = 192.168.2.2/255.255.255.0/192.168.2.1
|
||||
MAC = 06:05:04:03:02:01
|
||||
@@ -0,0 +1,70 @@
|
||||
Counter hierarchy
|
||||
=================
|
||||
|
||||
Service unique name
|
||||
Interface net.connman.Counter
|
||||
Object path freely definable
|
||||
|
||||
Methods void Release()
|
||||
|
||||
This method gets called when the service daemon
|
||||
unregisters the counter. A counter can use it to do
|
||||
cleanup tasks. There is no need to unregister the
|
||||
counter, because when this method gets called it has
|
||||
already been unregistered.
|
||||
|
||||
void Usage(object service, dict home, dict roaming)
|
||||
|
||||
This signal indicates a change in the counter values
|
||||
for the service object. The counter is reset by calling
|
||||
the service ResetCounters method.
|
||||
|
||||
When registering a new counter this method will be
|
||||
called once with all details for "home" and "roaming"
|
||||
counters filled in. Every further method call will
|
||||
only include the changed values.
|
||||
|
||||
When "home" counter is active, then "roaming" counter
|
||||
will contain an empty dictionary and vise-versa.
|
||||
|
||||
The dictionary argument contains the following entries:
|
||||
|
||||
RX.Packets
|
||||
|
||||
Total number of packets received.
|
||||
|
||||
TX.Bytes
|
||||
|
||||
Total number of packets sent.
|
||||
|
||||
RX.Bytes
|
||||
|
||||
Total number of bytes received.
|
||||
|
||||
TX.Bytes
|
||||
|
||||
Total number of bytes sent.
|
||||
|
||||
RX.Errors
|
||||
|
||||
Total number of erronous packets
|
||||
received.
|
||||
|
||||
TX.Errors
|
||||
|
||||
Total number of erronous packets
|
||||
sent.
|
||||
|
||||
RX.Dropped
|
||||
|
||||
Total number of dropped packets
|
||||
while receiving.
|
||||
|
||||
TX.Dropped
|
||||
|
||||
Total number of dropped packets
|
||||
while sending.
|
||||
|
||||
Time
|
||||
|
||||
Total number of seconds online.
|
||||
@@ -0,0 +1,41 @@
|
||||
IP configuration handling
|
||||
*************************
|
||||
|
||||
|
||||
IP basics
|
||||
=========
|
||||
|
||||
The core IP handling is designed around network interfaces or more precisely
|
||||
what the Linux kernel handles as struct net_device. Via RTNL every interface
|
||||
is tracked and an IP device created for it.
|
||||
|
||||
+--------+ +---- eth0 -----+
|
||||
| | | |
|
||||
| RTNL +-----+---->| IP device |
|
||||
| | | | |
|
||||
+--------+ | +---------------+
|
||||
|
|
||||
| +---- wlan0 ----+
|
||||
| | |
|
||||
+---->| IP device |
|
||||
| |
|
||||
+---------------+
|
||||
|
||||
The IP device tracks link configuration, IP address setting and routing
|
||||
information for that interface. Every IP device also contains a configuration
|
||||
element. That element contains an operation table for callbacks based on
|
||||
different events.
|
||||
|
||||
struct connman_ipconfig_ops {
|
||||
void (*up) (struct connman_ipconfig *);
|
||||
void (*down) (struct connman_ipconfig *);
|
||||
void (*lower_up) (struct connman_ipconfig *);
|
||||
void (*lower_down) (struct connman_ipconfig *);
|
||||
void (*ip_bound) (struct connman_ipconfig *);
|
||||
void (*ip_release) (struct connman_ipconfig *);
|
||||
};
|
||||
|
||||
All configuration objects created directly by RTNL are tightly bound to the
|
||||
IP device. They will trigger DHCP or other configuration helpers.
|
||||
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
Manager hierarchy
|
||||
=================
|
||||
|
||||
Service net.connman
|
||||
Interface net.connman.Manager
|
||||
Object path /
|
||||
|
||||
Methods dict GetProperties()
|
||||
|
||||
Returns all global system properties. See the
|
||||
properties section for available properties.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void SetProperty(string name, variant value)
|
||||
|
||||
Changes the value of the specified property. Only
|
||||
properties that are listed as read-write are
|
||||
changeable. On success a PropertyChanged signal
|
||||
will be emitted.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.InvalidProperty
|
||||
|
||||
array{object,dict} GetTechnologies()
|
||||
|
||||
Returns a list of tuples with technology object
|
||||
path and dictionary of technology properties.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
array{object,dict} GetServices()
|
||||
|
||||
Returns a sorted list of tuples with service
|
||||
object path and dictionary of service properties.
|
||||
|
||||
This list will not contain sensitive information
|
||||
like passphrases etc.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
array{object,dict} GetPeers() [experimental]
|
||||
|
||||
Returns a sorted list of tuples with peer object path
|
||||
and dictionary of peer properties
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
object ConnectProvider(dict provider) [deprecated]
|
||||
|
||||
Connect to a VPN specified by the given provider
|
||||
properties.
|
||||
|
||||
When successful this method will return the object
|
||||
path of the VPN service object.
|
||||
|
||||
This method can also be used to connect to an
|
||||
already existing VPN.
|
||||
|
||||
This method call will only return in case of an
|
||||
error or when the service is fully connected. So
|
||||
setting a longer D-Bus timeout might be a really
|
||||
good idea.
|
||||
|
||||
When 'SessionMode' property is enabled, this method
|
||||
call is disallowed.
|
||||
|
||||
This API is deprecated and should not be used.
|
||||
The VPN configuration API is provided by ConnMan
|
||||
VPN daemon and user should use that one instead.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void RemoveProvider(object path) [deprecated]
|
||||
|
||||
Remove a VPN specified by the object path.
|
||||
|
||||
void RegisterAgent(object path)
|
||||
|
||||
Register new agent for handling user requests.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void UnregisterAgent(object path)
|
||||
|
||||
Unregister an existing agent.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void RegisterCounter(object path, uint32 accuracy, uint32 period) [experimental]
|
||||
|
||||
Register a new counter for user notifications.
|
||||
|
||||
The accuracy is specified in kilo-bytes and defines
|
||||
a threshold for counter updates. Together with the
|
||||
period value it defines how often user space needs
|
||||
to be updated. The period value is in seconds.
|
||||
|
||||
This interface is not meant for time tracking. If
|
||||
the time needs to be tracked down to the second, it
|
||||
is better to have a real timer running inside the
|
||||
application than using this interface.
|
||||
|
||||
Also getting notified for every kilo-byte is a bad
|
||||
choice (even if the interface supports it). Something
|
||||
like 10 kilo-byte units or better 1 mega-byte seems
|
||||
to be a lot more reasonable and better for the user.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void UnregisterCounter(object path) [experimental]
|
||||
|
||||
Unregister an existing counter.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
object CreateSession(dict settings, object notifier) [experimental]
|
||||
|
||||
Create a new session for the application. Every
|
||||
application can create multiple session with
|
||||
different settings. The settings are described
|
||||
as part of the session interface.
|
||||
|
||||
The notifier allows asynchronous notification about
|
||||
session specific changes. These changes can be
|
||||
for online/offline state or IP address changes or
|
||||
similar things the application is required to
|
||||
handle.
|
||||
|
||||
Every application should at least create one session
|
||||
to inform about its requirements and it purpose.
|
||||
|
||||
void DestroySession(object session) [experimental]
|
||||
|
||||
Remove the previously created session.
|
||||
|
||||
If an application exits unexpectatly the session
|
||||
will be automatically destroyed.
|
||||
|
||||
object path, dict, fd RequestPrivateNetwork(dict options)
|
||||
[experimental]
|
||||
|
||||
Request a new Private Network, which includes the
|
||||
creation of a tun/tap interface, and IP
|
||||
configuration, NAT and IP forwarding on that
|
||||
interface.
|
||||
An object path, a dictionnary and a file descriptor
|
||||
with IP settings are returned.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.NotSupported
|
||||
|
||||
void ReleasePrivateNetwork(object path) [experimental]
|
||||
|
||||
Releases a private network.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void RegisterPeerService(array{byte} specification, boolean master) [experimental]
|
||||
|
||||
Registers a local P2P Peer service
|
||||
|
||||
If p2p technology is not available, this will raise a
|
||||
'not supported' error. This behavior does not apply if
|
||||
such technology is just disabled.
|
||||
|
||||
A Peer service belongs to the process that registers
|
||||
it, thus if that process dies, its Peer services will
|
||||
be destroyed as well.
|
||||
|
||||
"specification" is the TLV formated byte array
|
||||
describing the WiFi P2P service.
|
||||
|
||||
ConnMan will be able to determine in most cases
|
||||
whether to be the P2P Group Owner or not. If the
|
||||
service must belong to a group that this device
|
||||
manages, the "master" property can be set. Do not set
|
||||
the "master" property unless you are absolutely sure
|
||||
you know what you are doing.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.NotSupported
|
||||
|
||||
void UnregisterPeerService(array{byte} specification) [experimental]
|
||||
|
||||
Unregisters an existing local P2P Peer service
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
Signals TechnologyAdded(object path, dict properties)
|
||||
|
||||
Signal that is sent when a new technology is added.
|
||||
|
||||
It contains the object path of the technology and
|
||||
also its properties.
|
||||
|
||||
TechnologyRemoved(object path)
|
||||
|
||||
Signal that is sent when a technology has been removed.
|
||||
|
||||
The object path is no longer accessible after this
|
||||
signal and only emitted for reference.
|
||||
|
||||
ServicesChanged(array{object, dict}, array{object})
|
||||
|
||||
Signals a list of services that have been changed
|
||||
via the first array. And a list of service that
|
||||
have been removed via the second array.
|
||||
|
||||
The list of added services is sorted. The dictionary
|
||||
with the properties might be empty in case none of
|
||||
the properties have changed. Or only contains the
|
||||
properties that have changed.
|
||||
|
||||
For newly added services the whole set of properties
|
||||
will be present.
|
||||
|
||||
The list of removed services can be empty.
|
||||
|
||||
This signal will only be triggered when the sort
|
||||
order of the service list or the number of services
|
||||
changes. It will not be emitted if only a property
|
||||
of the service object changes. For that it is
|
||||
required to watch the PropertyChanged signal of
|
||||
the service object.
|
||||
|
||||
PeersChanged(array{object, dict}, array{object}) [experimental]
|
||||
|
||||
Signals a list of peers that have been changed via the
|
||||
first array. And a list of peer that have been removed
|
||||
via the second array.
|
||||
|
||||
The list of changed peers is sorted. The dictionary
|
||||
with the properties might be empty in case none of the
|
||||
properties have changed. Or only contains the
|
||||
properties that have changed.
|
||||
|
||||
For newly added peers the whole set of properties will
|
||||
be present.
|
||||
|
||||
The list of removed peers can be empty.
|
||||
|
||||
This signal will only be triggered when the sort order
|
||||
of the peer list or the number of peers changes. It
|
||||
will not be emitted if only a property of the peer
|
||||
object changes. For that it is required to watch the
|
||||
PropertyChanged signal of the peer object.
|
||||
|
||||
PropertyChanged(string name, variant value)
|
||||
|
||||
This signal indicates a changed value of the given
|
||||
property.
|
||||
|
||||
Properties string State [readonly]
|
||||
|
||||
The global connection state of a system. Possible
|
||||
values are "offline", "idle", "ready" and "online".
|
||||
|
||||
If the device is in offline mode, the value "offline"
|
||||
indicates this special global state. It can also be
|
||||
retrieved via the OfflineMode property, but is kept
|
||||
here for consistency and to differentiate from "idle".
|
||||
|
||||
However when OfflineMode property is true, the State
|
||||
property can still be "idle", "ready" or "online"
|
||||
since it is possible by the end user to re-enable
|
||||
individual technologies like WiFi and Bluetooth while
|
||||
in offline mode.
|
||||
|
||||
The states "idle", "ready" and "online" match to
|
||||
states from the services. If no service is in
|
||||
either "ready" or "online" state it will indicate
|
||||
the "idle" state.
|
||||
|
||||
If at least one service is in "ready" state and no
|
||||
service is in "online" state, then it will indicate
|
||||
the "ready" state.
|
||||
|
||||
When at least one service is in "online" state,
|
||||
this property will indicate "online" as well.
|
||||
|
||||
boolean OfflineMode [readwrite]
|
||||
|
||||
The offline mode indicates the global setting for
|
||||
switching all radios on or off. Changing offline mode
|
||||
to true results in powering down all devices. When
|
||||
leaving offline mode the individual policy of each
|
||||
device decides to switch the radio back on or not.
|
||||
|
||||
During offline mode, it is still possible to switch
|
||||
certain technologies manually back on. For example
|
||||
the limited usage of WiFi or Bluetooth devices might
|
||||
be allowed in some situations.
|
||||
|
||||
boolean SessionMode [readwrite] [experminental][deprecated]
|
||||
|
||||
This property exists only for compatibility reasons
|
||||
and does not affect ConnMan in any way.
|
||||
|
||||
The default value is false.
|
||||
@@ -0,0 +1,435 @@
|
||||
Application programming interface
|
||||
*********************************
|
||||
|
||||
|
||||
Service basics
|
||||
==============
|
||||
|
||||
Inside Connection Manager there exists one advanced interface to allow the
|
||||
user interface an easy access to networking details and user chosen
|
||||
preferences. This is the service list and interface.
|
||||
|
||||
The basic idea is that Connection Manager maintains a single flat and sorted
|
||||
list of all available, preferred or previously used services. A service here
|
||||
can be either a Ethernet device, a WiFi network or a remote Bluetooth device
|
||||
(for example a mobile phone).
|
||||
|
||||
This list of service is sorted by Connection Manager and there is no need
|
||||
for the user interface to implement its own sorting. User decisions will
|
||||
need to be done via Connection Manager and it is then responsible to update
|
||||
the order of services in this list.
|
||||
|
||||
+---------------------------------------+
|
||||
| Ethernet |
|
||||
+---------------------------------------+
|
||||
| Bluetooth phone |
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) |
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) |
|
||||
+---------------------------------------+
|
||||
| Other AP (strength 70, rsn) |
|
||||
+---------------------------------------+
|
||||
| Friends AP (strength 70, wep) |
|
||||
+---------------------------------------+
|
||||
|
||||
If none of the services has been used before the sorting order will be done
|
||||
with these priorities:
|
||||
|
||||
1. Ethernet (lower index numbers first)
|
||||
2. Bluetooth (last used devices first)
|
||||
3. GSM/UTMS/3G (if SIM card is present, activated and not roaming)
|
||||
3. WiFi (signal strength first, then more secure network
|
||||
first)
|
||||
|
||||
The Ethernet devices are always sorted first since they are physically built
|
||||
into the system and will be always present. In cases they are switched off
|
||||
manually they will not be showing in this list.
|
||||
|
||||
Since every Bluetooth device has to be configured/paired first, the user
|
||||
already made a choice here that these are important. Connection Manager will
|
||||
only show devices with PAN or DUN profile support. While Bluetooth devices
|
||||
do have a signal strength, it is mostly unknown since background scanning
|
||||
in Bluetooth is too expensive. The choice here is to sort the last used
|
||||
Bluetooth device before the others.
|
||||
|
||||
WiFi networks closer in the proximity should be shown first since it is more
|
||||
likely they are selected. The signal strength value is normalized to 0-100
|
||||
(effectively a percentage) and allows an easy sorting.
|
||||
|
||||
WiFi networks with the same signal strength are then sorted by their security
|
||||
setting. WPA2 encrypted networks should be preferred over WPA/WEP and also
|
||||
unencrypted ones. After that they will be sorted by the SSID in alphabetical
|
||||
order.
|
||||
|
||||
In the case the WiFi network uses WPS for setup and it is clearly detectable
|
||||
that a network waits for Connection Manager to connect to it (for example via
|
||||
a push-to-connect button press on the AP), then this network should be shown
|
||||
first before any other WiFi networks. The reason here is that the user already
|
||||
made a choice via the access point. However this depends on technical details
|
||||
if it is possible to detect these situations.
|
||||
|
||||
|
||||
Service order
|
||||
=============
|
||||
|
||||
All unused services will have the internal order number of 0 and then will
|
||||
be sorted according to the rules above. For Bluetooth the user already made
|
||||
the decision to setup their device and by that means select it. However
|
||||
until the first connection attempt it might have been setup for total
|
||||
different reason (like audio usage) and thus it still counts as unused from
|
||||
a networking point of view.
|
||||
|
||||
Selecting the "My WiFi AP" and successfully connecting to it makes it a
|
||||
favorite device and it will become an order number bigger than 0. All
|
||||
order numbers are internally. They are given only to service that are marked
|
||||
as favorite. For WiFi and Bluetooth a successful connection attempt makes
|
||||
these services automatically a favorite. For Ethernet the plugging of a cable
|
||||
makes it a favorite. Disconnecting from a network doesn't remove the favorite
|
||||
setting. It is a manual operation and is equal to users pressing
|
||||
delete/remove button.
|
||||
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) | order=1 - favorite=yes
|
||||
+---------------------------------------+
|
||||
| Ethernet | order=0
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
Ethernet is special here since the unplugging of the network cable will
|
||||
remove the service from the list
|
||||
|
||||
+---------------------------------------+
|
||||
| Ethernet with cable | order=1 - favorite=yes
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
This means that all services with an order > 0 have favorite=yes and all
|
||||
others have favorite=no setting. The favorite setting is exposed via a
|
||||
property over the service interface. As mentioned above, the order number
|
||||
is only used internally.
|
||||
|
||||
Within Connection Manager many services can be connected at the same time and
|
||||
also have an IP assignment. However only one can have the default route. The
|
||||
service with the default route will always be sorted at the top of the
|
||||
list.
|
||||
|
||||
+---------------------------------------+
|
||||
| Ethernet | order=2 - connected=yes
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) | order=1 - connected=yes
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
To change the default connection to your access point, the user needs to
|
||||
manually drag the access point service to the top of the list. Connection
|
||||
Manager will not take down default routes if there is no reason to do so.
|
||||
A working connection is considered top priority.
|
||||
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) | order=2 - connected=yes
|
||||
+---------------------------------------+
|
||||
| Ethernet | order=1 - connected=yes
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
Another possible user interaction would be to disconnect the Ethernet service
|
||||
and in this case the service falls back down in the list.
|
||||
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) | order=1 - connected=yes
|
||||
+---------------------------------------+
|
||||
| Ethernet | order=1 - connected=no
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
If the service on the top of the list changes the default route will be
|
||||
automatically adjusted as needed. The user can trigger this by disconnecting
|
||||
from a network, if the network becomes unavailable (out of range) or if the
|
||||
cable gets unplugged.
|
||||
|
||||
As described above, the pure case of disconnecting from a network will not
|
||||
remove the favorite setting. So previously selected networks are still present
|
||||
and are sorted above all others.
|
||||
|
||||
+---------------------------------------+
|
||||
| Ethernet | order=2 - connected=yes
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) | order=1 - connected=no
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
Unplugging the Ethernet cable will remove the Ethernet service.
|
||||
|
||||
+---------------------------------------+
|
||||
| My WiFi AP (strength 80, rsn) | order=1 - connected=no
|
||||
+---------------------------------------+
|
||||
| Guest (strength 90, none) | order=0
|
||||
+---------------------------------------+
|
||||
| |
|
||||
|
||||
|
||||
Service tweaks
|
||||
==============
|
||||
|
||||
The interfaces of Connection Manager will always export all services that are
|
||||
currently known. The Ethernet devices with no cable plugged are actually not
|
||||
included in this list. They will only show up once a carrier is detected.
|
||||
|
||||
The service interface is not meant for basic device configuration task. So
|
||||
switching a device on and off (via RFKILL for example) should be done via
|
||||
the technology interface. See "Technology interfaces" chapter in this document.
|
||||
|
||||
Due to limited screen size of small devices and the big amount of WiFi
|
||||
access points that are deployed right now it might be sensible to not show
|
||||
certain WiFi networks in the user interface.
|
||||
|
||||
The choice to hide a WiFi network from the user interface should be purely
|
||||
done by the signal strength. The optimal cut-off value here still has to be
|
||||
determined, but in the end that is a user interface policy.
|
||||
|
||||
|
||||
Service naming
|
||||
==============
|
||||
|
||||
Every service will have a name property that allows the user interface to
|
||||
display them directly. All names will be already converted into UTF-8. It
|
||||
derives from the netork details.
|
||||
|
||||
In case of WiFi this will be the SSID value. The SSID is a binary array and
|
||||
will be converted into printable form. Unprintable characters are replaced
|
||||
with spaces.
|
||||
|
||||
In addition to WiFi naming, WiFi networks are subject to a grouping policy
|
||||
performed around SSID and security type. This means that one service will be
|
||||
seen for N WiFi networks providing the same SSID and the same security metod.
|
||||
For instance, if 5 APs are servicing an SSID called "TEST" with WPA2
|
||||
authentication and 3 APs are servicing the same SSID with open authentication
|
||||
method, the user will see only two services listed with the name "TEST"
|
||||
differentiated by their security type, which are "psk" and "none". Such
|
||||
policy is also applied to hidden networks, where hidden services will have an
|
||||
empty name and will be differentiated by the security type. The user has then
|
||||
to select the one with the right security and the Agent API will request any
|
||||
required information such as the SSID for the network (See "Application
|
||||
basics" below).
|
||||
|
||||
For Bluetooth the device alias is used. The alias is different since it
|
||||
can be overwritten by the user via the Bluetooth service. The identification
|
||||
is still done based on its address, but the display name might change. In
|
||||
most cases the alias is equal to the Bluetooth remote friendly name.
|
||||
|
||||
For Ethernet device no name will be provided. The type property will indicate
|
||||
that this service is Ethernet and then it is up to the user interface to
|
||||
provide a proper localized name for it.
|
||||
|
||||
|
||||
Service states
|
||||
==============
|
||||
|
||||
Every service can have multiple states that indicate what is currently
|
||||
going on with it. The choice to have multiple states instead of a simple
|
||||
connected yes/no value comes from the fact that it is important to let the
|
||||
user interface name if a service is in process of connecting/disconnecting.
|
||||
|
||||
The basic state of every service is "idle". This means that this service
|
||||
is not in use at all at the moment. It also is not attempting to connect
|
||||
or do anything else.
|
||||
|
||||
The "association" state indicates that this service tries to establish a
|
||||
low-level connection to the network. For example associating/connecting
|
||||
with a WiFi access point.
|
||||
|
||||
With the "configuration" state the service indicates that it is trying
|
||||
to retrieve/configure IP settings.
|
||||
|
||||
The "ready" state signals a successful connected device. This doesn't mean
|
||||
it has the default route, but basic IP operations will succeed.
|
||||
|
||||
With the "disconnect" state a service indicates that it is going to terminate
|
||||
the current connection and will return to the "idle" state.
|
||||
|
||||
In addition a "failure" state indicates a wrong behavior. It is similar to
|
||||
the "idle" state since the service is not connected.
|
||||
|
||||
+---------------+
|
||||
| idle |<-------------------------------+
|
||||
+---------------+ |
|
||||
| |
|
||||
| +-------------+ |
|
||||
+----------------------| failure | |
|
||||
| service.Connect() +-------------+ |
|
||||
V A |
|
||||
+---------------+ | |
|
||||
| association |-----------------+ |
|
||||
+---------------+ error | |
|
||||
| | |
|
||||
| success | |
|
||||
V | |
|
||||
+---------------+ | |
|
||||
| configuration |-----------------+ |
|
||||
+---------------+ error |
|
||||
| |
|
||||
| success |
|
||||
V |
|
||||
+---------------+ |
|
||||
| ready | |
|
||||
+---------------+ |
|
||||
| |
|
||||
| success |
|
||||
| |
|
||||
V |
|
||||
+---------------+ |
|
||||
| online |<----------------+ |
|
||||
+---------------+ | |
|
||||
| | |
|
||||
| service.Disconnect() | |
|
||||
V | |
|
||||
+---------------+ | |
|
||||
| disconnect |-----------------+ |
|
||||
+---------------+ error |
|
||||
| |
|
||||
+------------------------------------------+
|
||||
|
||||
The different states should no be used by the user interface to trigger
|
||||
advanced actions. The state transitions are provided for the sole purpose
|
||||
to give the user feedback on what is currently going on. Especially in
|
||||
cases where networks are flaky or DHCP servers take a long time these
|
||||
information are helpful for the user.
|
||||
|
||||
Some services might require special authentication procedure like a web
|
||||
based confirmation. The LoginRequired property should be used to check
|
||||
for this.
|
||||
|
||||
|
||||
Application basics
|
||||
==================
|
||||
|
||||
All applications should use D-Bus to communicate with Connection Manager. The
|
||||
main entry point is the manager object. Currently the manager object is
|
||||
located at "/", but this might change to allow full namespacing of the API
|
||||
in the future. The manager interface is documented in manager-api.txt and
|
||||
contains a set of global properties and methods.
|
||||
|
||||
A simple way to retrieve all global properties looks like this:
|
||||
|
||||
bus = dbus.SystemBus()
|
||||
|
||||
manager = dbus.Interface(bus.get_object("net.connman", "/"),
|
||||
"net.connman.Manager")
|
||||
|
||||
properties = manager.GetProperties()
|
||||
|
||||
Changing a global property is also pretty simple. For example enabling the
|
||||
so called offline mode (aka flight mode) it is enough to just set that
|
||||
property:
|
||||
|
||||
manager.SetProperty("OfflineMode", dbus.Boolean(1))
|
||||
|
||||
The manager object contains references to profiles, devices, services and
|
||||
connections. All these references represent other interfaces that allow
|
||||
detailed control of Connection Manager. The profiles and devices interfaces
|
||||
are more for advanced features and most applications don't need them at all.
|
||||
|
||||
The services are represented as a list of object paths. Every of these object
|
||||
paths contains a service interface. A service is a global collection for
|
||||
Ethernet devices, WiFi networks, Bluetooth services etc. and all these
|
||||
different types are treated equally.
|
||||
|
||||
Every local Ethernet card will show up as exactly one service. WiFi networks
|
||||
will be grouped by SSID, mode and security setting. Bluetooth PAN and DUN
|
||||
service will show up per remote device. This creates a simple list that can
|
||||
be directly displayed to the users since these are the exact details users
|
||||
should care about.
|
||||
|
||||
properties = manager.GetProperties()
|
||||
|
||||
for path in properties["Services"]:
|
||||
service = dbus.Interface(bus.get_object("net.connman", path),
|
||||
"net.connman.Service")
|
||||
|
||||
service_properties = service.GetProperties()
|
||||
|
||||
The service interface is documented in service-api.txt and contains common
|
||||
properties valid for all services. It also contains method to connect or
|
||||
disconnect a specific service. This allows users to select a specific service.
|
||||
Connection Manager can also auto-connect services based on his policies or
|
||||
via external events (like plugging in an Ethernet cable).
|
||||
|
||||
Connecting (or disconnecting) a specific service manually is as simple as
|
||||
just telling it to actually connect:
|
||||
|
||||
service.Connect() or service.Disconnect()
|
||||
|
||||
It is possible to connect multiple services if the underlying technology
|
||||
allows it. For example it would be possible to connect to a WiFi network
|
||||
and a Bluetooth service at the same time. Trying to connect to a second WiFi
|
||||
network with the same WiFi hardware would result in an automatic disconnect
|
||||
of the currently connected network. Connection Manager handles all of this
|
||||
for the applications in the background. Trying to connect an Ethernet service
|
||||
will result in an error if no cable is plugged in. All connection attempts
|
||||
can fail for one reason or another. Application should be able to handle
|
||||
such errors and will also be notified of changes via signals.
|
||||
|
||||
Connection Manager will interact with an agent via the Agent API to confirm
|
||||
certain transactions with the user. If Connection Manager needs extra
|
||||
information, it will ask the user for exactly the information it requires,
|
||||
i.e. passphrase, network's name (for hidden WiFi networks) and more depending
|
||||
on the use case (e.g. WPS, EAP). Therefore an application environment using
|
||||
Connection Manager should implement one dedicated Connection Manager agent
|
||||
according to the Agent API in order to interact with the user. Please see
|
||||
agent-api.txt for implementation details.
|
||||
|
||||
To monitor the current status of a service the state property can be used. It
|
||||
gives detailed information about the current progress.
|
||||
|
||||
properties = service.GetProperties()
|
||||
|
||||
print properties["State"]
|
||||
|
||||
All state changes are also sent via the PropertyChanged signal on the
|
||||
service interface. This allows asynchronous monitoring without having to poll
|
||||
Connection Manager for changes.
|
||||
|
||||
|
||||
Technology interfaces
|
||||
=====================
|
||||
|
||||
When ConnMan is started first time, all technologies except ethernet are
|
||||
powered off by default. The reason is that the user needs to decide which
|
||||
technologies are relevant to him and what bearers the user wants to use.
|
||||
User can use the Technology Powered property to turn on or off a given
|
||||
technology. See doc/technology-api.txt document for details.
|
||||
|
||||
User can activate offline (flight) mode via Manager OfflineMode property.
|
||||
While in offline mode, all the technologies including ethernet are
|
||||
powered off. During the offline mode, the user can temporarily activate
|
||||
individual technologies by using the Technology Powered property or by
|
||||
using the rfkill command or Fn-Fx key combination found in some laptops.
|
||||
|
||||
If the host supports rfkill switch, then all the radios can be turned off
|
||||
by the kernel when the switch is activated. ConnMan will notice this and
|
||||
remove corresponding technologies from D-Bus. Technologies cannot be
|
||||
activated while rfkill switch is turned on. When rfkill switch is turned
|
||||
off (radios are activated), then ConnMan restores the original Powered
|
||||
status for each activated technology.
|
||||
|
||||
User can use the rfkill command from command line or indirectly via
|
||||
some UI component to activate/deactivate individual radios found in
|
||||
the host. ConnMan will listen these rfkill events and set the Powered
|
||||
property accordingly. ConnMan will not save the rfkill status it has
|
||||
received. This means that after restarting ConnMan, the original and
|
||||
saved technology status is used when deciding which technologies should
|
||||
be powered. If the user uses the Technology D-Bus API to set the Powered
|
||||
property, then that information is saved and used when ConnMan is restarted.
|
||||
@@ -0,0 +1,64 @@
|
||||
Peer hierarchy [EXPERIMENTAL]
|
||||
=============================
|
||||
|
||||
Service net.connman
|
||||
Interface net.connman.Peer
|
||||
Object path [variable prefix]/{peer0,peer1,...}
|
||||
|
||||
Methods dict GetProperties() [deprecated]
|
||||
|
||||
Returns properties for the peer object. See the
|
||||
properties sections for available properties.
|
||||
|
||||
Usage of this method is highly discouraged. Use
|
||||
the Manager.GetPeers() method instead.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void Connect() [experimental]
|
||||
|
||||
Connect this peer.
|
||||
|
||||
This method call will only return in case of an error
|
||||
or when the peer is fully connected. So setting a
|
||||
longer D-Bus timeout might be a really good idea.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void Disconnect() [experimental]
|
||||
|
||||
Disconnect this peer. If the peer is not connected, an
|
||||
error message will be generated.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
Signals PropertyChanged(string name, variant value) [experimental]
|
||||
|
||||
This signal indicates a changed value of the given
|
||||
property.
|
||||
|
||||
Properties string State [readonly] [experimental]
|
||||
|
||||
The peer state information.
|
||||
|
||||
Valid state are "idle", "failure", "association",
|
||||
"configuration", "ready" and "disconnect".
|
||||
|
||||
string Name [readonly] [experimental]
|
||||
|
||||
Name of the peer.
|
||||
|
||||
dict IPv4 [readonly] [experimental]
|
||||
|
||||
string Address [readonly]
|
||||
|
||||
The current configured IPv4 address.
|
||||
|
||||
string Netmask [readonly]
|
||||
|
||||
The current configured IPv4 netmask.
|
||||
|
||||
array{array{byte}} Services [readonly] [experimental]
|
||||
|
||||
Array of P2P service specifications, which are
|
||||
themselves a TLV formated byte array.
|
||||
@@ -0,0 +1,164 @@
|
||||
Plugin programming interface
|
||||
****************************
|
||||
|
||||
|
||||
Plugin basics
|
||||
=============
|
||||
|
||||
The Connection Manager supports plugins for various actions. The basic plugin
|
||||
contains of plugin description via CONNMAN_PLUGIN_DEFINE and also init/exit
|
||||
callbacks defined through that description.
|
||||
|
||||
#include <connman/plugin.h>
|
||||
|
||||
static int example_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
CONNMAN_PLUGIN_DEFINE(example, "Example plugin", CONNMAN_VERSION,
|
||||
example_init, example_exit)
|
||||
|
||||
|
||||
Infrastructure for plugins
|
||||
==========================
|
||||
|
||||
The Connection Manager provides a very good infrastructure for plugins to
|
||||
interface with the core functionalities of ConnMan. The infrastructure is
|
||||
well divided into the concepts of Technology, Device and Network, among
|
||||
others.
|
||||
|
||||
Technology infrastructure
|
||||
=========================
|
||||
|
||||
A Technology in ConnMan is an abstract representation of the different
|
||||
kinds of technologies it supports such as WiFi, Ethernet, Bluetooth and
|
||||
Celullar. The technologies support are added to ConnMan through plugins, such
|
||||
as plugins/bluetooth.c for the Bluetooth Technology or plugins/wifi.c for the
|
||||
WiFi Technology. Each new technology plugin needs to register itself as a
|
||||
Technology with ConnMan. As an example we will take a look at the Bluetooth
|
||||
plugin registration. As a first step 'struct connman_technology_driver' needs
|
||||
to be defined:
|
||||
|
||||
static struct connman_technology_driver tech_driver = {
|
||||
.name = "bluetooth",
|
||||
.type = CONNMAN_SERVICE_TYPE_BLUETOOTH,
|
||||
.probe = bluetooth_tech_probe,
|
||||
.remove = bluetooth_tech_remove,
|
||||
.set_tethering = bluetooth_tech_set_tethering,
|
||||
};
|
||||
|
||||
More functions can be defined depending on the purpose of the plugin. All
|
||||
vtable's supported functions can be seen in include/technology.h. If a
|
||||
completely new technology type is added 'enum connman_service_type' in
|
||||
include/service.h needs to be extended accordingly. This inclusion comes in
|
||||
the form of Service because ultimately a new technology introduces a new
|
||||
Service. New technologies can also reuse existing Services types.
|
||||
|
||||
To make the Connection Manager aware of the new Technology plugin we need to
|
||||
register its driver by calling 'connman_technology_driver_register()' in the
|
||||
plugin initialization function, bluetooth_init() in this example:
|
||||
|
||||
connman_technology_driver_register(&tech_driver);
|
||||
|
||||
In this document the error check is supressed for the sake of simplicity.
|
||||
All plugins should check return values in driver registration functions.
|
||||
|
||||
After this call ConnMan becomes aware of the new Technology plugin and will
|
||||
call the probe() method when the new technology is recognized by the system. For
|
||||
the Bluetooth plugin for example probe() would be called when a Bluetooth
|
||||
adapter is recognized. A Technology is only probed if there exists at least
|
||||
one device of such technology plugged into the system.
|
||||
|
||||
Complementary, the technology must be unregistered on the plugin exit function
|
||||
through 'connman_technology_driver_unregister()'.
|
||||
|
||||
Device infrastructure
|
||||
=====================
|
||||
|
||||
A Device represents a real device of a given Technology, there could be many
|
||||
devices per technology. To enable ConnMan to handle Devices a device driver
|
||||
needs to be registered. Using the Bluetooth plugin as example it would have to
|
||||
define a 'struct connman_device_driver':
|
||||
|
||||
static struct connman_device_driver device_driver = {
|
||||
.name = "bluetooth",
|
||||
.type = CONNMAN_DEVICE_TYPE_BLUETOOTH,
|
||||
.probe = bluetooth_device_probe,
|
||||
.remove = bluetooth_device_remove,
|
||||
.enable = bluetooth_device_enable,
|
||||
.disable = bluetooth_device_disable,
|
||||
};
|
||||
|
||||
And to register the driver:
|
||||
|
||||
connman_device_driver_register(&device_driver);
|
||||
|
||||
'connman_device_driver_register()' is called during the plugin initialization
|
||||
process, not necessarily at the plugin init function.
|
||||
|
||||
In this document the error check is supressed for the sake of simplicity.
|
||||
All plugins should check return values in driver registration functions.
|
||||
|
||||
Additionally code to handle the detection of new devices needs to be written
|
||||
for each plugin, the bluetooth plugin does so by registering watchers for the
|
||||
BlueZ D-Bus interface. Once a new Bluetooth Device appears the plugin needs to
|
||||
notify ConnMan core by calling connman_device_create(), for the bluetooth
|
||||
plugin the call would be:
|
||||
|
||||
struct connman_device *device;
|
||||
|
||||
device = connman_device_create("bluetooth",
|
||||
CONNMAN_DEVICE_TYPE_BLUETOOTH)
|
||||
|
||||
ConnMan core will then register the bluetooth device as a Device entity and
|
||||
call the probe() function from the bluetooth plugin device driver. If a
|
||||
Technology entity for the Device type doesn't exist it will be created and
|
||||
Technology probe() function in the bluetooth technology driver is called.
|
||||
|
||||
For the Bluetooth plugin a Device represents the local Bluetooth Adapter
|
||||
plugged in the system.
|
||||
|
||||
To learn how to use the connman_device_*() functions such as
|
||||
connman_device_set_powered() and connman_device_ref() see src/device.c for
|
||||
its API documentation.
|
||||
|
||||
Network infrastructure
|
||||
======================
|
||||
|
||||
The Connection Manager provides a mean to plugins handle the specifics of
|
||||
establishing/handling a connection for each type of Technology. For the
|
||||
bluetooth plugin a connman_network_driver needs to be registered:
|
||||
|
||||
static struct connman_network_driver network_driver = {
|
||||
.name = "bluetooth",
|
||||
.type = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
|
||||
.probe = bluetooth_pan_probe,
|
||||
.remove = bluetooth_pan_remove,
|
||||
.connect = bluetooth_pan_connect,
|
||||
.disconnect = bluetooth_pan_disconnect,
|
||||
};
|
||||
|
||||
And then call the register function:
|
||||
|
||||
connman_network_driver_register(&network_driver);
|
||||
|
||||
In this document the error check is supressed for the sake of simplicity.
|
||||
All plugins should check return values in driver registration functions.
|
||||
|
||||
The next step would be the probe of a Network entity, for the bluetooth
|
||||
plugin this would happen when a new device that supports the PAN NAP role is
|
||||
paired with the system. ConnMan then call connman_device_add_network() to
|
||||
associate the new Network with the existing Device entity (the local Bluetooth
|
||||
Adapter).
|
||||
|
||||
Then in the vtable's connect method all the needed pieces to perform a
|
||||
connection shall be perfomed.
|
||||
|
||||
To learn how to use the connman_network_*() functions such as
|
||||
connman_network_set_index() and connman_network_set_connected() see
|
||||
src/network.c for its API documentation.
|
||||
@@ -0,0 +1,494 @@
|
||||
Service hierarchy
|
||||
=================
|
||||
|
||||
Service net.connman
|
||||
Interface net.connman.Service
|
||||
Object path [variable prefix]/{service0,service1,...}
|
||||
|
||||
Methods dict GetProperties() [deprecated]
|
||||
|
||||
Returns properties for the service object. See
|
||||
the properties section for available properties.
|
||||
|
||||
Usage of this method is highly discouraged. Use
|
||||
the Manager.GetServices() method instead.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void SetProperty(string name, variant value)
|
||||
|
||||
Changes the value of the specified property. Only
|
||||
properties that are listed as read-write are
|
||||
changeable. On success a PropertyChanged signal
|
||||
will be emitted.
|
||||
|
||||
Properties cannot be set for hidden WiFi service
|
||||
entries or provisioned services.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.InvalidProperty
|
||||
|
||||
void ClearProperty(string name)
|
||||
|
||||
Clears the value of the specified property.
|
||||
|
||||
Properties cannot be cleared for hidden WiFi service
|
||||
entries or provisioned services.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.InvalidProperty
|
||||
|
||||
void Connect()
|
||||
|
||||
Connect this service. It will attempt to connect
|
||||
WiFi or Bluetooth services.
|
||||
|
||||
For Ethernet devices this method can only be used
|
||||
if it has previously been disconnected. Otherwise
|
||||
the plugging of a cable will trigger connecting
|
||||
automatically. If no cable is plugged in this method
|
||||
will fail.
|
||||
|
||||
This method call will only return in case of an
|
||||
error or when the service is fully connected. So
|
||||
setting a longer D-Bus timeout might be a really
|
||||
good idea.
|
||||
|
||||
Calling Connect() on a hidden WiFi service entry will
|
||||
query the missing SSID via the Agent API causing a
|
||||
WiFi service with the given SSID to be scanned,
|
||||
created and connected.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void Disconnect()
|
||||
|
||||
Disconnect this service. If the service is not
|
||||
connected an error message will be generated.
|
||||
|
||||
On Ethernet devices this will disconnect the IP
|
||||
details from the service. It will not magically
|
||||
unplug the cable. When no cable is plugged in this
|
||||
method will fail.
|
||||
|
||||
This method can also be used to abort a previous
|
||||
connection attempt via the Connect method.
|
||||
|
||||
Hidden WiFi service entries cannot be disconnected
|
||||
as they always stay in idle state.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void Remove()
|
||||
|
||||
A successfully connected service with Favorite=true
|
||||
can be removed this way. If it is connected, it will
|
||||
be automatically disconnected first.
|
||||
|
||||
If the service requires a passphrase it will be
|
||||
cleared and forgotten when removing.
|
||||
|
||||
This is similar to setting the Favorite property
|
||||
to false, but that is currently not supported.
|
||||
|
||||
In the case a connection attempt failed and the
|
||||
service is in the State=failure, this method can
|
||||
also be used to reset the service.
|
||||
|
||||
Calling this method on Ethernet devices, hidden WiFi
|
||||
services or provisioned services will cause an error
|
||||
message. It is not possible to remove these kind of
|
||||
services.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void MoveBefore(object service)
|
||||
|
||||
If a service has been used before, this allows a
|
||||
reorder of the favorite services.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void MoveAfter(object service)
|
||||
|
||||
If a service has been used before, this allows a
|
||||
reorder of the favorite services.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void ResetCounters() [experimental]
|
||||
|
||||
Reset the counter statistics.
|
||||
|
||||
Possible Errors: None
|
||||
|
||||
Signals PropertyChanged(string name, variant value)
|
||||
|
||||
This signal indicates a changed value of the given
|
||||
property.
|
||||
|
||||
Properties string State [readonly]
|
||||
|
||||
The service state information.
|
||||
|
||||
Valid states are "idle", "failure", "association",
|
||||
"configuration", "ready", "disconnect" and "online".
|
||||
|
||||
The "ready" state signals a successfully
|
||||
connected device. "online" signals that an
|
||||
Internet connection is available and has been
|
||||
verified.
|
||||
|
||||
See doc/overview-api.txt for more information about
|
||||
state transitions.
|
||||
|
||||
string Error [readonly]
|
||||
|
||||
The service error status details.
|
||||
|
||||
When error occur during connection or disconnection
|
||||
the detailed information is represented in this
|
||||
property to help the user interface to present the
|
||||
user with alternate options.
|
||||
|
||||
This property is only valid when the service is in
|
||||
the "failure" state. Otherwise it might be empty or
|
||||
not present at all.
|
||||
|
||||
Current defined error code is "dhcp-failed".
|
||||
|
||||
string Name [readonly]
|
||||
|
||||
The service name (for example "Wireless" etc.)
|
||||
|
||||
This name can be used for directly displaying it in
|
||||
the application. It has pure informational purpose
|
||||
and no attempt should be made to translate it.
|
||||
|
||||
For Ethernet devices and hidden WiFi networks this
|
||||
property is not present.
|
||||
|
||||
string Type [readonly]
|
||||
|
||||
The service type (for example "ethernet", "wifi" etc.)
|
||||
|
||||
This information should only be used to determine
|
||||
advanced properties or showing the correct icon
|
||||
to the user.
|
||||
|
||||
Together with a missing Name property, this can
|
||||
be used to identify hidden WiFi networks.
|
||||
|
||||
array{string} Security [readonly]
|
||||
|
||||
If the service type is WiFi, then this property is
|
||||
present and contains the list of security methods
|
||||
or key management settings.
|
||||
|
||||
Possible values are "none", "wep", "psk", "ieee8021x"
|
||||
and also "wps".
|
||||
|
||||
This property might be only present for WiFi
|
||||
services.
|
||||
|
||||
uint8 Strength [readonly]
|
||||
|
||||
Indicates the signal strength of the service. This
|
||||
is a normalized value between 0 and 100.
|
||||
|
||||
This property will not be present for Ethernet
|
||||
devices.
|
||||
|
||||
boolean Favorite [readonly]
|
||||
|
||||
Will be true if a cable is plugged in or the user
|
||||
selected and successfully connected to this service.
|
||||
|
||||
This value is automatically changed and to revert
|
||||
it back to false the Remove() method needs to be
|
||||
used.
|
||||
|
||||
boolean Immutable [readonly]
|
||||
|
||||
This value will be set to true if the service is
|
||||
configured externally via a configuration file.
|
||||
|
||||
The only valid operation are Connect() and of
|
||||
course Disconnect(). The Remove() method will
|
||||
result in an error.
|
||||
|
||||
boolean AutoConnect [readwrite]
|
||||
|
||||
If set to true, this service will auto-connect
|
||||
when no other connection is available.
|
||||
|
||||
The service won't auto-connect while roaming.
|
||||
|
||||
For favorite services it is possible to change
|
||||
this value to prevent or permit automatic
|
||||
connection attempts.
|
||||
|
||||
boolean Roaming [readonly]
|
||||
|
||||
This property indicates if this service is roaming.
|
||||
|
||||
In the case of Cellular services this normally
|
||||
indicates connections to a foreign provider when
|
||||
traveling abroad.
|
||||
|
||||
array{string} Nameservers [readonly]
|
||||
|
||||
The list of currently active nameservers for this
|
||||
service. If the server is not in READY or ONLINE
|
||||
state than this list will be empty.
|
||||
|
||||
Global nameservers are automatically added to this
|
||||
list. The array represents a sorted list of the
|
||||
current nameservers. The first one has the highest
|
||||
priority and is used by default.
|
||||
|
||||
When using DHCP this array represents the nameservers
|
||||
provided by the network. In case of manual settings,
|
||||
the ones from Nameservers.Configuration are used.
|
||||
|
||||
array{string} Nameservers.Configuration [readwrite]
|
||||
|
||||
The list of manually configured domain name
|
||||
servers. Some cellular networks don't provide
|
||||
correct name servers and this allows for an
|
||||
override.
|
||||
|
||||
This array is sorted by priority and the first
|
||||
entry in the list represents the nameserver with
|
||||
the highest priority.
|
||||
|
||||
When using manual configuration and no global
|
||||
nameservers are configured, then it is useful
|
||||
to configure this setting.
|
||||
|
||||
Changes to the domain name servers can be done
|
||||
at any time. It will not cause a disconnect of
|
||||
the service. However there might be small window
|
||||
where name resolution might fail.
|
||||
|
||||
array{string} Timeservers [readonly]
|
||||
|
||||
The list of currently active timeservers for this
|
||||
service. If the server is not in READY or ONLINE
|
||||
state than this list will be empty.
|
||||
|
||||
array{string} Timeservers.Configuration [readwrite]
|
||||
|
||||
The list of manually configured time servers.
|
||||
|
||||
The first entry in the list represents the
|
||||
timeserver with the highest priority.
|
||||
|
||||
When using manual configuration this setting
|
||||
is useful to override all the other timeserver
|
||||
settings. This is service specific, hence only
|
||||
the values for the default service are used.
|
||||
|
||||
Changes to this property will result in restart
|
||||
of NTP query.
|
||||
|
||||
array{string} Domains [readonly]
|
||||
|
||||
The list of currently used search domains taken
|
||||
from Domains.Configurations if set, otherwise a
|
||||
domain name if provided by DHCP or VPNs.
|
||||
|
||||
array{string} Domains.Configuration [readwrite]
|
||||
|
||||
The list of manually configured search domains.
|
||||
|
||||
dict IPv4 [readonly]
|
||||
|
||||
string Method [readonly]
|
||||
|
||||
Possible values are "dhcp", "manual"
|
||||
and "off".
|
||||
|
||||
The value "fixed" indicates an IP address
|
||||
that can not be modified. For example
|
||||
cellular networks return fixed information.
|
||||
|
||||
string Address [readonly]
|
||||
|
||||
The current configured IPv4 address.
|
||||
|
||||
string Netmask [readonly]
|
||||
|
||||
The current configured IPv4 netmask.
|
||||
|
||||
string Gateway [readonly]
|
||||
|
||||
The current configured IPv4 gateway.
|
||||
|
||||
dict IPv4.Configuration [readwrite]
|
||||
|
||||
Same values as IPv4 property. The IPv4 represents
|
||||
the actual system configuration while this allows
|
||||
user configuration.
|
||||
|
||||
Changing these settings will cause a state change
|
||||
of the service. The service will become unavailable
|
||||
until the new configuration has been successfully
|
||||
installed.
|
||||
|
||||
dict IPv6 [readonly]
|
||||
|
||||
string Method [readonly]
|
||||
|
||||
Possible values are "auto", "manual", "6to4"
|
||||
and "off".
|
||||
|
||||
The value "fixed" indicates an IP address
|
||||
that can not be modified. For example
|
||||
cellular networks return fixed information.
|
||||
The value "6to4" is returned if 6to4 tunnel
|
||||
is created by connman. The tunnel can only be
|
||||
created if method was set to "auto" by the
|
||||
user. User cannot set the method to "6to4".
|
||||
|
||||
string Address [readonly]
|
||||
|
||||
The current configured IPv6 address.
|
||||
|
||||
uint8 PrefixLength [readonly]
|
||||
|
||||
The prefix length of the IPv6 address.
|
||||
|
||||
string Gateway [readonly]
|
||||
|
||||
The current configured IPv6 gateway.
|
||||
|
||||
string Privacy [readonly]
|
||||
|
||||
Enable or disable IPv6 privacy extension
|
||||
that is described in RFC 4941. The value
|
||||
has only meaning if Method is set to "auto".
|
||||
|
||||
Value "disabled" means that privacy extension
|
||||
is disabled and normal autoconf addresses are
|
||||
used.
|
||||
|
||||
Value "enabled" means that privacy extension is
|
||||
enabled and system prefers to use public
|
||||
addresses over temporary addresses.
|
||||
|
||||
Value "prefered" means that privacy extension is
|
||||
enabled and system prefers temporary addresses
|
||||
over public addresses.
|
||||
|
||||
Default value is "disabled".
|
||||
|
||||
dict IPv6.Configuration [readwrite]
|
||||
|
||||
Same values as IPv6 property. The IPv6 represents
|
||||
the actual system configuration while this allows
|
||||
user configuration.
|
||||
|
||||
Changing these settings will cause a state change
|
||||
of the service. The service will become unavailable
|
||||
until the new configuration has been successfully
|
||||
installed.
|
||||
|
||||
dict Proxy [readonly]
|
||||
|
||||
string Method [readonly]
|
||||
|
||||
Possible values are "direct", "auto" and
|
||||
"manual".
|
||||
|
||||
In case of "auto" method, the URL file can be
|
||||
provided unless you want to let DHCP/WPAD
|
||||
auto-discover to be tried. In such case if DHCP
|
||||
and WPAD auto-discover methods fails then
|
||||
method will be "direct".
|
||||
|
||||
In case of "direct" no additional information
|
||||
are provided. For the "manual" method the
|
||||
Servers have to be set, Excludes is optional.
|
||||
|
||||
string URL [readonly]
|
||||
|
||||
Automatic proxy configuration URL. Used by
|
||||
"auto" method.
|
||||
|
||||
array{string} Servers [readonly]
|
||||
|
||||
Used when "manual" method is set.
|
||||
|
||||
List of proxy URIs. The URI without a protocol
|
||||
will be interpreted as the generic proxy URI.
|
||||
All others will target a specific protocol and
|
||||
only once.
|
||||
|
||||
Example for generic proxy server entry would
|
||||
be like this: "server.example.com:911".
|
||||
|
||||
array{string} Excludes [readonly]
|
||||
|
||||
Used when "manual" method is set.
|
||||
|
||||
List of hosts which can be accessed directly.
|
||||
|
||||
dict Proxy.Configuration [readwrite]
|
||||
|
||||
Same values as Proxy property. The Proxy represents
|
||||
the actual system configuration while this allows
|
||||
user configuration.
|
||||
|
||||
If "auto" method is set with an empty URL, then
|
||||
DHCP/WPAD auto-discover will be tried. Otherwise the
|
||||
specified URL will be used.
|
||||
|
||||
dict Provider [readonly]
|
||||
|
||||
string Host [readonly]
|
||||
|
||||
VPN host IP.
|
||||
|
||||
string Domain [readonly]
|
||||
|
||||
VPN Domain.
|
||||
|
||||
string Name [readonly]
|
||||
|
||||
VPN provider Name.
|
||||
|
||||
string Type [readonly]
|
||||
|
||||
VPN provider type.
|
||||
|
||||
dict Ethernet [readonly]
|
||||
|
||||
string Method [readonly]
|
||||
|
||||
Possible values are "auto" and "manual".
|
||||
|
||||
string Interface [readonly]
|
||||
|
||||
Interface name (for example eth0).
|
||||
|
||||
string Address [readonly]
|
||||
|
||||
Ethernet device address (MAC address).
|
||||
|
||||
uint16 MTU [readonly]
|
||||
|
||||
The Ethernet MTU (default is 1500).
|
||||
|
||||
uint16 Speed [readonly] [deprecated]
|
||||
|
||||
Selected speed of the line.
|
||||
|
||||
This information is not available.
|
||||
|
||||
string Duplex [readonly] [deprecated]
|
||||
|
||||
Selected duplex settings of the line.
|
||||
Possible values are "half" and "full".
|
||||
|
||||
This information is not available.
|
||||
@@ -0,0 +1,184 @@
|
||||
Service unique name
|
||||
Interface net.connman.Notification
|
||||
Object path freely definable
|
||||
|
||||
Methods void Release()
|
||||
|
||||
This method gets called when the service daemon
|
||||
unregisters the session. A counter can use it to do
|
||||
cleanup tasks. There is no need to unregister the
|
||||
session, because when this method gets called it has
|
||||
already been unregistered.
|
||||
|
||||
void Update(dict settings)
|
||||
|
||||
Sends an update of changed settings. Only settings
|
||||
that are changed will be included.
|
||||
|
||||
Initially on every session creation this method is
|
||||
called once to inform about the current settings.
|
||||
|
||||
|
||||
Service net.connman
|
||||
Interface net.connman.Session
|
||||
Object path variable
|
||||
|
||||
Methods void Destroy()
|
||||
|
||||
Close the current session. This is similar to
|
||||
DestroySession method on the manager interface. It
|
||||
is just provided for convenience depending on how
|
||||
the application wants to track the session.
|
||||
|
||||
void Connect()
|
||||
|
||||
If not connected, then attempt to connect this
|
||||
session.
|
||||
|
||||
The usage of this method depends a little bit on
|
||||
the model of the application. Some application
|
||||
should not try to call Connect on any session at
|
||||
all. They should just monitor if it becomes online
|
||||
or gets back offline.
|
||||
|
||||
Others might require an active connection right now.
|
||||
So for example email notification should only check
|
||||
for new emails when a connection is available. However
|
||||
if the user presses the button for get email or wants
|
||||
to send an email it should request to get online with
|
||||
this method.
|
||||
|
||||
Depending on the bearer settings the current service
|
||||
is used or a new service will be connected.
|
||||
|
||||
This method returns immediately after it has been
|
||||
called. The application is informed through the update
|
||||
notification about the state of the session.
|
||||
|
||||
It is also not guaranteed that a session stays online
|
||||
after this method call. It can be taken offline at any
|
||||
time. This might happen because of idle timeouts or
|
||||
other reasons.
|
||||
|
||||
It is safe to call this method multiple times. The
|
||||
actual usage will be sorted out for the application.
|
||||
|
||||
void Disconnect()
|
||||
|
||||
This method indicates that the current session does
|
||||
not need a connection anymore.
|
||||
|
||||
This method returns immediately. The application is
|
||||
informed through the update notification about the
|
||||
state of the session.
|
||||
|
||||
void Change(string name, variant value)
|
||||
|
||||
Change the value of certain settings. Not all
|
||||
settings can be changed. Normally this should not
|
||||
be needed or an extra session should be created.
|
||||
However in some cases it makes sense to change
|
||||
a value and trigger different behavior.
|
||||
|
||||
A change of a setting will cause an update notification
|
||||
to be sent. Some changes might cause the session to
|
||||
be moved to offline state.
|
||||
|
||||
Settings string State [readonly]
|
||||
|
||||
This indicates if the connection is disconnected,
|
||||
connected or online. It is updated according to the
|
||||
selected ConnectionType. The session will not be
|
||||
in a useful shape (i.e.: providing a network connection
|
||||
to the owner) until its State gets updated to connected
|
||||
and/or online.
|
||||
|
||||
This maps to the useful port of the service state.
|
||||
And it is only valid for the selected bearer
|
||||
configuration. Otherwise it will be reported as
|
||||
disconnected even if connected services are present.
|
||||
|
||||
In addition the State settings notification might
|
||||
not happen right away. Notifications of this state
|
||||
can be delayed based on the speed of the bearer. It
|
||||
is done to avoid congestion on bearers like cellular
|
||||
etc.
|
||||
|
||||
string Name [readonly]
|
||||
|
||||
The Service name to which the system is connected.
|
||||
It should only be used for displaying it in the UI
|
||||
and not for getting hold on session object.
|
||||
|
||||
string Bearer [readonly]
|
||||
|
||||
This indicates the current bearer that is used
|
||||
for this session. Or an empty string if no bearer
|
||||
if available.
|
||||
|
||||
string Interface [readonly]
|
||||
|
||||
Interface name used by the service object to connect.
|
||||
This name can be used for SO_BINDTODEVICE in the
|
||||
application.
|
||||
|
||||
dict IPv4 [readonly]
|
||||
|
||||
Current IPv4 configuration.
|
||||
|
||||
dict IPv6 [readonly]
|
||||
|
||||
Current IPv6 configuration.
|
||||
|
||||
array{string} AllowedBearers [readwrite]
|
||||
|
||||
A list of bearers that can be used for this session.
|
||||
In general this list should be empty to indicate that
|
||||
any bearer is acceptable.
|
||||
|
||||
The order of the entries in AllowedBearers matters.
|
||||
The services are sorted in the order of the bearer
|
||||
entries in this list.
|
||||
|
||||
Also "*" matches any bearer. This is usefull to prefer
|
||||
certain bearers such as 'wifi' with a fallback to any
|
||||
other available bearer.
|
||||
|
||||
Invalid bearer names will be ignored and removed
|
||||
from the list. And empty AllowedBearers will
|
||||
not match to any bearer, therefore the session
|
||||
will never go online.
|
||||
|
||||
When a session is created and the provided settings
|
||||
dictionary does not contain AllowedBearers, a default
|
||||
session with "*" will be created.
|
||||
|
||||
string ConnectionType [readwrite]
|
||||
|
||||
This is used to indicate which connection is requested
|
||||
from the session. The state of the session will be
|
||||
updated accordingly. Values can be 'local',
|
||||
'internet' or 'any'.
|
||||
|
||||
'local' means the session requests to be connected,
|
||||
but does not require specifically to be online.
|
||||
Therefore State property will be set to 'connected' if
|
||||
underlying service gets ready and/or online.
|
||||
|
||||
'online' means the session requests to be connected,
|
||||
and online. State property will never get 'connected'
|
||||
but instead will switch to 'online' if underlying
|
||||
service gets online.
|
||||
|
||||
'any' means either 'local' or 'internet'.
|
||||
|
||||
Invalid values will be ignored and removed. An
|
||||
empty ConnectionType is an invalid configuration.
|
||||
|
||||
When a session is created and the provided settings
|
||||
dictionary does not contain ConnectionType, a default
|
||||
session with 'any' will be created.
|
||||
|
||||
(This setting will be removed when the unique process
|
||||
identification problem is solved.)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
Session API
|
||||
***********
|
||||
|
||||
|
||||
Connection management algorithm basics
|
||||
======================================
|
||||
|
||||
The Session core uses the normal auto-connect algorithm for selecting
|
||||
which services will be connected or disconnected. That means only
|
||||
Services with AutoConnect to set to true will be used. The Session
|
||||
core will assign a connected Service to a Session if the Service
|
||||
is matching the AllowedBearer filter.
|
||||
|
||||
By using the normal auto-connect algorithm, it is possible to
|
||||
use the Session API and the Service API at the same time.
|
||||
|
||||
|
||||
Session States and Transitions
|
||||
==============================
|
||||
|
||||
There is only one state which is called Free Ride.
|
||||
|
||||
The Free Ride state means that a session will go online if a matching
|
||||
service goes online without calling Service.Connect() itself. The idea
|
||||
behind this is that a session doesn't request a connection for itself
|
||||
instead waits until another session actively requires to go online.
|
||||
This is comparable to piggy-backing.
|
||||
|
||||
Connnect()
|
||||
+------+
|
||||
| v
|
||||
+------------+
|
||||
| Free Ride |
|
||||
+------------+
|
||||
| ^
|
||||
+-----+
|
||||
Disconnect()
|
||||
|
||||
|
||||
If an application wants to stay offline it can set an empty
|
||||
AllowedBearers list.
|
||||
|
||||
|
||||
Session application identification
|
||||
==================================
|
||||
|
||||
Application using session can be identified through different means.
|
||||
|
||||
- SELinux
|
||||
- UID
|
||||
- GID
|
||||
|
||||
ConnMan will try to identify the application in the given order above.
|
||||
If SELinux is not supported by the system or not configured, ConnMan
|
||||
will ignore it and fallback asking the D-Bus daemon about the UID of
|
||||
the application.
|
||||
|
||||
The identification is only useful in combination with the policy plugin.
|
||||
|
||||
|
||||
Policy Plugin
|
||||
=============
|
||||
|
||||
The policy plugin allows the administrator to provision/configure
|
||||
sessions. Each policy needs an application identification in order to
|
||||
match the policy to a session.
|
||||
|
||||
See session-policy-format.txt for more details.
|
||||
|
||||
|
||||
Per application routing
|
||||
=======================
|
||||
|
||||
For each session a policy routing table is maintained. Each policy
|
||||
routing table contains a default route to the selected service.
|
||||
|
||||
Per session iptables rules:
|
||||
|
||||
iptables -t mangle -A OUTPUT -m owner [--uid-owner|--gid-owner] $OWNER \
|
||||
-j MARK --set-mark $MARK
|
||||
|
||||
Global rules for all sessions:
|
||||
|
||||
iptables -t mangle -A INPUT -j CONNMARK --restore-mark
|
||||
iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark
|
||||
|
||||
Per application routing is only available when policy files are
|
||||
used. Without the policy plugin or a valid configuration, the default
|
||||
session configuration is applied.
|
||||
|
||||
The default session configuration does not enable the per application
|
||||
routing. Sessions are still useful in this setup, because the
|
||||
notification of sessions is still available, e.g. the online/offline
|
||||
notification.
|
||||
@@ -0,0 +1,83 @@
|
||||
ConnMan policy file format
|
||||
**************************
|
||||
|
||||
The session policy pluging allows to configure/provision a session.
|
||||
ConnMan will be looking for policy files in STORAGEDIR/session_policy_local
|
||||
which by default points to /var/lib/connman. Policy file names must
|
||||
not include other characters than letters or numbers and must have
|
||||
a .policy suffix. Policy files are text files.
|
||||
|
||||
It is possible to add, remove or update a policy file during run-time.
|
||||
The corresponding sessions will be updated accordingly.
|
||||
|
||||
Policy group [policy_*]
|
||||
=======================
|
||||
|
||||
Each policy group must start with as [policy_*] tag. '*' has no
|
||||
semantic meaning but should consist just out of characters.
|
||||
|
||||
Required fields:
|
||||
|
||||
Exactly one and only one of the required fields need to be present
|
||||
per policy group.
|
||||
|
||||
- uid: This policy group will be applied to any session
|
||||
with given user ID.
|
||||
|
||||
- gid: This policy group will be applied to any session
|
||||
with given group ID.
|
||||
|
||||
- selinux: This policy group will be applied to any session
|
||||
with given SELinux context.
|
||||
|
||||
Allowed fields:
|
||||
|
||||
- AllowedBearers: see session-api.txt
|
||||
The policy AllowedBearers overrules the settings done via
|
||||
D-Bus. For example the policy AllowedBearers is 'ethernet' then
|
||||
the D-Bus API will only accept an empty string or 'ethernet'.
|
||||
|
||||
- ConnectionType: see session-api.txt
|
||||
The policy ConnectionType overrules the settings done via
|
||||
D-Bus.
|
||||
|
||||
- Priority: A boolean which tells ConnMan to preferred the session
|
||||
over other Sessions. This priority value is more for application
|
||||
that want to push themselves up in the asychronization notification
|
||||
queue once a bearer becomes online.
|
||||
|
||||
This actual priority order also depends on the allowed bearers and
|
||||
other factors. This is setting is just a little indicator of one
|
||||
application being notified before another one.
|
||||
|
||||
- RoamingPolicy: The allowed roaming behavior.
|
||||
|
||||
Valid policies are "national", "international", "default", "always"
|
||||
and "forbidden".
|
||||
|
||||
"national" allows roaming within a country. "international" allows
|
||||
roaming in a country and between countries.
|
||||
|
||||
"default" is used to tell the session to use the global roaming
|
||||
setting.
|
||||
|
||||
"always" will overwrite the default "forbidden" value which is
|
||||
useful for emergency application.
|
||||
|
||||
Default value is "forbidden".
|
||||
|
||||
- EmergencyCall: A boolean which tells ConnMan whenever the
|
||||
Connect() method is called for this session, all other
|
||||
session are disconnected.
|
||||
|
||||
Note only services matching the AllowedBearers rule will be
|
||||
considered.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
example@example:[~]$ cat /var/lib/connman/session_policy_local/auser.policy
|
||||
[policy_auser]
|
||||
uid = auser
|
||||
AllowedBearers = wifi cellular
|
||||
RoamingPolicy = forbidden
|
||||
@@ -0,0 +1,99 @@
|
||||
Technology hierarchy
|
||||
====================
|
||||
|
||||
Service net.connman
|
||||
Interface net.connman.Technology
|
||||
Object path [variable prefix]/{technology0,technology1,...}
|
||||
|
||||
Methods dict GetProperties() [deprecated]
|
||||
|
||||
Returns properties for the technology object. See
|
||||
the properties section for available properties.
|
||||
|
||||
Usage of this method is highly discouraged. Use
|
||||
the Manager.GetTechnologies() method instead.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
|
||||
void SetProperty(string name, variant value)
|
||||
|
||||
Changes the value of the specified property. Only
|
||||
properties that are listed as read-write are
|
||||
changeable. On success a PropertyChanged signal
|
||||
will be emitted.
|
||||
|
||||
Possible Errors: [service].Error.InvalidArguments
|
||||
[service].Error.InvalidProperty
|
||||
|
||||
void Scan()
|
||||
|
||||
Trigger a scan for this specific technology. The
|
||||
method call will return when a scan has been
|
||||
finished and results are available. So setting
|
||||
a longer D-Bus timeout might be a really good
|
||||
idea.
|
||||
|
||||
Results will be signaled via the ServicesChanged
|
||||
signal from the manager interface.
|
||||
|
||||
In case of P2P technology, results will be signaled
|
||||
via the PeersChanged signal from the manager
|
||||
interface.
|
||||
|
||||
Signals PropertyChanged(string name, variant value)
|
||||
|
||||
This signal indicates a changed value of the given
|
||||
property.
|
||||
|
||||
Properties boolean Powered [readwrite]
|
||||
|
||||
Boolean representing the power state of the
|
||||
technology. False means that the technology is
|
||||
off (and is available RF-Killed) while True means
|
||||
that the technology is enabled.
|
||||
|
||||
boolean Connected [readonly]
|
||||
|
||||
Boolean representing if a technology is connected.
|
||||
|
||||
This is just a convience property for allowing the
|
||||
UI to easily show if this technology has an active
|
||||
connection or not.
|
||||
|
||||
If this property is True it means that at least one
|
||||
service of this technology is in ready state.
|
||||
|
||||
string Name [readonly]
|
||||
|
||||
Name of this technology.
|
||||
|
||||
string Type [readonly]
|
||||
|
||||
The technology type (for example "ethernet" etc.)
|
||||
|
||||
This information should only be used to determine
|
||||
advanced properties or showing the correct icon
|
||||
to the user.
|
||||
|
||||
boolean Tethering [readwrite]
|
||||
|
||||
This option allows to enable or disable the support
|
||||
for tethering. When tethering is enabled then the
|
||||
default service is bridged to all clients connected
|
||||
through the technology.
|
||||
|
||||
string TetheringIdentifier [readwrite]
|
||||
|
||||
The tethering broadcasted identifier.
|
||||
|
||||
This property is only valid for the WiFi technology,
|
||||
and is then mapped to the WiFi AP SSID clients will
|
||||
have to join in order to gain internet connectivity.
|
||||
|
||||
string TetheringPassphrase [readwrite]
|
||||
|
||||
The tethering connection passphrase.
|
||||
|
||||
This property is only valid for the WiFi technology,
|
||||
and is then mapped to the WPA pre-shared key clients
|
||||
will have to use in order to establish a connection.
|
||||
@@ -0,0 +1,158 @@
|
||||
Agent hierarchy
|
||||
===============
|
||||
|
||||
Service unique name
|
||||
Interface net.connman.vpn.Agent
|
||||
Object path freely definable
|
||||
|
||||
Methods void Release()
|
||||
|
||||
This method gets called when the service daemon
|
||||
unregisters the agent. An agent can use it to do
|
||||
cleanup tasks. There is no need to unregister the
|
||||
agent, because when this method gets called it has
|
||||
already been unregistered.
|
||||
|
||||
void ReportError(object service, string error)
|
||||
|
||||
This method gets called when an error has to be
|
||||
reported to the user.
|
||||
|
||||
A special return value can be used to trigger a
|
||||
retry of the failed transaction.
|
||||
|
||||
Possible Errors: net.connman.vpn.Agent.Error.Retry
|
||||
|
||||
dict RequestInput(object service, dict fields)
|
||||
|
||||
This method gets called when trying to connect to
|
||||
a service and some extra input is required. For
|
||||
example a password or username.
|
||||
|
||||
The return value should be a dictionary where the
|
||||
keys are the field names and the values are the
|
||||
actual fields. Alternatively an error indicating that
|
||||
the request got canceled can be returned.
|
||||
|
||||
Most common return field names are "Username" and of
|
||||
course "Password".
|
||||
|
||||
The dictionary arguments contains field names with
|
||||
their input parameters.
|
||||
|
||||
Possible Errors: net.connman.vpn.Agent.Error.Canceled
|
||||
|
||||
void Cancel()
|
||||
|
||||
This method gets called to indicate that the agent
|
||||
request failed before a reply was returned.
|
||||
|
||||
Fields string Username
|
||||
|
||||
Username for authentication. This field will be
|
||||
requested when connecting to L2TP and PPTP.
|
||||
|
||||
string Password
|
||||
|
||||
Password for authentication.
|
||||
|
||||
boolean SaveCredentials
|
||||
|
||||
Tells if the user wants the user credentials
|
||||
be saved by connman-vpnd.
|
||||
|
||||
string Host
|
||||
|
||||
End point of this VPN link i.e., the VPN gateway
|
||||
we are trying to connect to.
|
||||
|
||||
string Name
|
||||
|
||||
Name of the VPN connection we are trying to connect to.
|
||||
|
||||
string OpenConnect.CACert
|
||||
|
||||
Informational field containing a path name for an
|
||||
additional Certificate Authority file.
|
||||
|
||||
string OpenConnect.ClientCert
|
||||
|
||||
Informational field containing a pkcs11 URL or a path
|
||||
name for the client certificate.
|
||||
|
||||
string OpenConnect.Cookie
|
||||
|
||||
Return the OpenConnect cookie value that is used for
|
||||
authenticating the VPN session.
|
||||
|
||||
string OpenConnect.ServerCert
|
||||
|
||||
Return the OpenConnect server hash used to identify
|
||||
the final server after possible web authentication
|
||||
logins, selections and redirections.
|
||||
|
||||
string OpenConnect.VPNHost
|
||||
|
||||
Return the final VPN server to use after possible
|
||||
web authentication logins, selections and redirections.
|
||||
|
||||
Arguments string Type
|
||||
|
||||
Contains the type of a field. For example "password",
|
||||
"response", "boolean" or plain "string".
|
||||
|
||||
string Requirement
|
||||
|
||||
Contains the requirement option. Valid values are
|
||||
"mandatory", "optional", "alternate" or
|
||||
"informational".
|
||||
|
||||
The "alternate" value specifies that this field can be
|
||||
returned as an alternative to another one.
|
||||
|
||||
All "mandatory" fields must be returned, while the
|
||||
"optional" can be returned if available.
|
||||
|
||||
Nothing needs to be returned for "informational", as it
|
||||
is here only to provide an information so a value is
|
||||
attached to it.
|
||||
|
||||
array{string} Alternates
|
||||
|
||||
Contains the list of alternate field names this
|
||||
field can be represented by.
|
||||
|
||||
string Value
|
||||
|
||||
Contains data as a string, relatively to an
|
||||
"informational" argument.
|
||||
|
||||
Examples Requesting a username and password for L2TP network
|
||||
|
||||
RequestInput("/vpn1",
|
||||
{ "Username" : { "Type" : "string",
|
||||
"Requirement" : "mandatory"
|
||||
} }
|
||||
{ "Password" : { "Type" : "password",
|
||||
"Requirement" : "mandatory"
|
||||
} }
|
||||
{ "SaveCredentials" : { "Type" : "boolean",
|
||||
"Requirement" : "optional"
|
||||
}
|
||||
}
|
||||
==> { "Username" : "foo", "Password" : "secret123",
|
||||
"SaveCredentials" : true }
|
||||
|
||||
Requesting a OpenConnect cookie
|
||||
|
||||
RequestInput("/vpn2",
|
||||
{ "OpenConnect.Cookie" : { "Type" : "string",
|
||||
"Requirement" : "mandatory"
|
||||
} }
|
||||
{ "Host" : { "Type" : "string",
|
||||
"Requirement" : "informational"
|
||||
} }
|
||||
{ "Name" : { "Type" : "string",
|
||||
"Requirement" : "informational"
|
||||
} }
|
||||
==> { "OpenConnect.Cookie" : "0123456@adfsf@asasdf" }
|
||||
@@ -0,0 +1,235 @@
|
||||
Connman configuration file format for VPN
|
||||
*****************************************
|
||||
|
||||
Connman VPN uses configuration files to provision existing providers.
|
||||
vpnd will be looking for its configuration files at VPN_STORAGEDIR
|
||||
which by default points to /var/lib/connman-vpn. Configuration file names
|
||||
must not include other characters than letters or numbers and must have
|
||||
a .config suffix. Those configuration files are text files with a simple
|
||||
key-value pair format organized into sections. Values do not comprise leading
|
||||
trailing whitespace. We typically have one file per provisioned network.
|
||||
|
||||
If the config file is removed, then vpnd tries to remove the
|
||||
provisioned service. If an individual service entry inside a config is removed,
|
||||
then the corresponding provisioned service is removed. If a service
|
||||
section is changed, then the corresponding service is removed and immediately
|
||||
re-provisioned.
|
||||
|
||||
|
||||
Global section [global]
|
||||
=======================
|
||||
|
||||
These files can have an optional global section describing the actual file.
|
||||
The two allowed fields for this section are:
|
||||
- Name: Name of the network.
|
||||
- Description: Description of the network.
|
||||
|
||||
|
||||
Provider section [provider_*]
|
||||
=============================
|
||||
|
||||
Each provisioned provider must start with the [provider_*] tag.
|
||||
Replace * with an identifier unique to the config file.
|
||||
|
||||
Allowed fields:
|
||||
- Type: Provider type. Value of OpenConnect, OpenVPN, VPNC, L2TP or PPTP
|
||||
|
||||
VPN related parameters (M = mandatory, O = optional):
|
||||
- Name: A user defined name for the VPN (M)
|
||||
- Host: VPN server IP address (M)
|
||||
- Domain: Domain name for the VPN service (M)
|
||||
- Networks: The networks behind the VPN link can be defined here. This can
|
||||
be missing if all traffic should go via VPN tunnel. If there are more
|
||||
than one network, then separate them by comma. Format of the entry
|
||||
is network/netmask/gateway. The gateway can be left out. (O)
|
||||
Example: 192.168.100.0/24/10.1.0.1,192.168.200.0/255.255.255.0/10.1.0.2
|
||||
For IPv6 addresses only prefix length is accepted like this 2001:db8::1/64
|
||||
|
||||
OpenConnect VPN supports following options (see openconnect(8) for details):
|
||||
Option name OpenConnect option Description
|
||||
OpenConnect.ServerCert --servercert SHA1 certificate fingerprint of the
|
||||
final VPN server after possible web
|
||||
authentication login, selection and
|
||||
redirection (O)
|
||||
OpenConnect.CACert --cafile File containing other Certificate
|
||||
Authorities in addition to the ones
|
||||
in the system trust database (O)
|
||||
OpenConnect.ClientCert --certificate Client certificate file, if needed
|
||||
by web authentication (O)
|
||||
VPN.MTU --mtu Request MTU from server as the MTU
|
||||
of the tunnel (O)
|
||||
OpenConnect.Cookie --cookie-on-stdin Cookie received as a result of the
|
||||
web authentication. As the cookie
|
||||
lifetime can be very limited, it
|
||||
does not usually make sense to add
|
||||
it into the configuration file (O)
|
||||
OpenConnect.VPNHost The final VPN server to use after
|
||||
completing the web authentication.
|
||||
Only usable for extremely simple VPN
|
||||
configurations and should normally
|
||||
be set only via the VPN Agent API.
|
||||
If OpenConnect.Cookie or OpenConnect.ServerCert are missing, the VPN Agent will
|
||||
be contacted to supply the information.
|
||||
|
||||
OpenVPN VPN supports following options (see openvpn(8) for details):
|
||||
Option name OpenVPN option Description
|
||||
OpenVPN.CACert --ca Certificate authority file (M)
|
||||
OpenVPN.Cert --cert Local peer's signed certificate (M)
|
||||
OpenVPN.Key --key Local peer's private key (M)
|
||||
OpenVPN.MTU --mtu MTU of the tunnel (O)
|
||||
OpenVPN.NSCertType --ns-cert-type Peer certificate type, value of
|
||||
either server or client (O)
|
||||
OpenVPN.Proto --proto Use protocol (O)
|
||||
OpenVPN.Port --port TCP/UDP port number (O)
|
||||
OpenVPN.AuthUserPass --auth-user-pass Authenticate with server using
|
||||
username/password (O)
|
||||
OpenVPN.AskPass --askpass Get certificate password from file (O)
|
||||
OpenVPN.AuthNoCache --auth-nocache Don't cache --askpass or
|
||||
--auth-user-pass value (O)
|
||||
OpenVPN.TLSRemote --tls-remote Accept connections only from a host
|
||||
with X509 name or common name equal
|
||||
to name parameter (O)
|
||||
OpenVPN.TLSAuth sub-option of --tls-remote (O)
|
||||
OpenVPN.TLSAuthDir sub-option of --tls-remote (O)
|
||||
OpenVPN.Cipher --cipher Encrypt packets with cipher algorithm
|
||||
given as parameter (O)
|
||||
OpenVPN.Auth --auth Authenticate packets with HMAC using
|
||||
message digest algorithm alg (O)
|
||||
OpenVPN.CompLZO --comp-lzo Use fast LZO compression. Value can
|
||||
be "yes", "no", or "adaptive". Default
|
||||
is adaptive (O)
|
||||
OpenVPN.RemoteCertTls --remote-cert-tls Require that peer certificate was
|
||||
signed based on RFC3280 TLS rules.
|
||||
Value is "client" or "server" (O)
|
||||
OpenVPN.ConfigFile --config OpenVPN config file that can contain
|
||||
extra options not supported by OpenVPN
|
||||
plugin (O)
|
||||
|
||||
VPNC VPN supports following options (see vpnc(8) for details):
|
||||
Option name VPNC config value Description
|
||||
VPNC.IPSec.ID IPSec ID your group username (M)
|
||||
VPNC.IPSec.Secret IPSec secret your group password (cleartext) (O)
|
||||
VPNC.Xauth.Username Xauth username your username (O)
|
||||
VPNC.Xauth.Password Xauth password your password (cleartext) (O)
|
||||
VPNC.IKE.Authmode IKE Authmode IKE Authentication mode (O)
|
||||
VPNC.IKE.DHGroup IKE DH Group name of the IKE DH Group (O)
|
||||
VPNC.PFS Perfect Forward Secrecy Diffie-Hellman group to use for PFS (O)
|
||||
VPNC.Domain Domain Domain name for authentication (O)
|
||||
VPNC.Vendor Vendor vendor of your IPSec gateway (O)
|
||||
VPNC.LocalPort Local Port local ISAKMP port number to use
|
||||
VPNC.CiscoPort Cisco UDP Encapsulation Port Local UDP port number to use (O)
|
||||
VPNC.AppVersion Application Version Application Version to report (O)
|
||||
VPNC.NATTMode NAT Traversal Mode Which NAT-Traversal Method to use (O)
|
||||
VPNC.DPDTimeout DPD idle timeout (our side) Send DPD packet after timeout (O)
|
||||
VPNC.SingleDES Enable Single DES enables single DES encryption (O)
|
||||
VPNC.NoEncryption Enable no encryption enables using no encryption for data traffic (O)
|
||||
|
||||
L2TP VPN supports following options (see xl2tpd.conf(5) and pppd(8) for details)
|
||||
Option name xl2tpd config value Description
|
||||
L2TP.User - L2TP user name, asked from the user
|
||||
if not set here (O)
|
||||
L2TP.Password - L2TP password, asked from the user
|
||||
if not set here (O)
|
||||
L2TP.BPS bps Max bandwith to use (O)
|
||||
L2TP.TXBPS tx bps Max transmit bandwith to use (O)
|
||||
L2TP.RXBPS rx bps Max receive bandwith to use (O)
|
||||
L2TP.LengthBit length bit Use length bit (O)
|
||||
L2TP.Challenge challenge Use challenge authentication (O)
|
||||
L2TP.DefaultRoute defaultroute Default route (O)
|
||||
L2TP.FlowBit flow bit Use seq numbers (O)
|
||||
L2TP.TunnelRWS tunnel rws Window size (O)
|
||||
L2TP.Exclusive exclusive Use only one control channel (O)
|
||||
L2TP.Redial redial Redial if disconnected (O)
|
||||
L2TP.RedialTimeout redial timeout Redial timeout (O)
|
||||
L2TP.MaxRedials max redials How many times to try redial (O)
|
||||
L2TP.RequirePAP require pap Need pap (O)
|
||||
L2TP.RequireCHAP require chap Need chap (O)
|
||||
L2TP.ReqAuth require authentication Need auth (O)
|
||||
L2TP.AccessControl access control Accept only these peers (O)
|
||||
L2TP.AuthFile auth file Authentication file location (O)
|
||||
L2TP.ListenAddr listen-addr Listen address (O)
|
||||
L2TP.IPsecSaref ipsec saref Use IPSec SA (O)
|
||||
L2TP.Port port What UDP port is used (O)
|
||||
|
||||
Option name pppd config value Description
|
||||
PPPD.EchoFailure lcp-echo-failure Dead peer check count (O)
|
||||
PPPD.EchoInterval lcp-echo-interval Dead peer check interval (O)
|
||||
PPPD.Debug debug Debug level (O)
|
||||
PPPD.RefuseEAP refuse-eap Deny eap auth (O)
|
||||
PPPD.RefusePAP refuse-pap Deny pap auth (O)
|
||||
PPPD.RefuseCHAP refuse-chap Deny chap auth (O)
|
||||
PPPD.RefuseMSCHAP refuse-mschap Deny mschap auth (O)
|
||||
PPPD.RefuseMSCHAP2 refuse-mschapv2 Deny mschapv2 auth (O)
|
||||
PPPD.NoBSDComp nobsdcomp Disables BSD compression (O)
|
||||
PPPD.NoPcomp nopcomp Disable protocol compression (O)
|
||||
PPPD.UseAccomp accomp Disable address/control compression (O)
|
||||
PPPD.NoDeflate nodeflate Disable deflate compression (O)
|
||||
PPPD.ReqMPPE require-mppe Require the use of MPPE (O)
|
||||
PPPD.ReqMPPE40 require-mppe-40 Require the use of MPPE 40 bit (O)
|
||||
PPPD.ReqMPPE128 require-mppe-128 Require the use of MPPE 128 bit (O)
|
||||
PPPD.ReqMPPEStateful mppe-stateful Allow MPPE to use stateful mode (O)
|
||||
PPPD.NoVJ no-vj-comp No Van Jacobson compression (O)
|
||||
|
||||
|
||||
PPTP VPN supports following options (see pptp(8) and pppd(8) for details)
|
||||
Option name pptp config value Description
|
||||
PPTP.User - PPTP user name, asked from the user
|
||||
if not set here (O)
|
||||
PPTP.Password - PPTP password, asked from the user
|
||||
if not set here (O)
|
||||
|
||||
Option name pppd config value Description
|
||||
PPPD.EchoFailure lcp-echo-failure Dead peer check count (O)
|
||||
PPPD.EchoInterval lcp-echo-interval Dead peer check interval (O)
|
||||
PPPD.Debug debug Debug level (O)
|
||||
PPPD.RefuseEAP refuse-eap Deny eap auth (O)
|
||||
PPPD.RefusePAP refuse-pap Deny pap auth (O)
|
||||
PPPD.RefuseCHAP refuse-chap Deny chap auth (O)
|
||||
PPPD.RefuseMSCHAP refuse-mschap Deny mschap auth (O)
|
||||
PPPD.RefuseMSCHAP2 refuse-mschapv2 Deny mschapv2 auth (O)
|
||||
PPPD.NoBSDComp nobsdcomp Disables BSD compression (O)
|
||||
PPPD.NoDeflate nodeflate Disable deflate compression (O)
|
||||
PPPD.RequirMPPE require-mppe Require the use of MPPE (O)
|
||||
PPPD.RequirMPPE40 require-mppe-40 Require the use of MPPE 40 bit (O)
|
||||
PPPD.RequirMPPE128 require-mppe-128 Require the use of MPPE 128 bit (O)
|
||||
PPPD.RequirMPPEStateful mppe-stateful Allow MPPE to use stateful mode (O)
|
||||
PPPD.NoVJ no-vj-comp No Van Jacobson compression (O)
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
This is a configuration file for a VPN providing L2TP, OpenVPN and
|
||||
OpenConnect services.
|
||||
|
||||
|
||||
example@example:[~]$ cat /var/lib/connman/vpn/example.config
|
||||
[global]
|
||||
Name = Example
|
||||
Description = Example VPN configuration
|
||||
|
||||
[provider_l2tp]
|
||||
Type = L2TP
|
||||
Name = Connection to corporate network
|
||||
Host = 1.2.3.4
|
||||
Domain = corporate.com
|
||||
Networks = 10.10.30.0/24
|
||||
L2TP.User = username
|
||||
|
||||
[provider_openconnect]
|
||||
Type = OpenConnect
|
||||
Name = Connection to corporate network using Cisco VPN
|
||||
Host = 7.6.5.4
|
||||
Domain = corporate.com
|
||||
Networks = 10.10.20.0/255.255.255.0/10.20.1.5,192.168.99.1/24,2001:db8::1/64
|
||||
OpenConnect.ServerCert = 263AFAB4CB2E6621D12E90182008AEF44AEFA031
|
||||
OpenConnect.CACert = /etc/certs/certificate.p12
|
||||
|
||||
[provider_openvpn]
|
||||
Type = OpenVPN
|
||||
Name = Connection to corporate network using OpenVPN
|
||||
Host = 3.2.5.6
|
||||
Domain = my.home.network
|
||||
OpenVPN.CACert = /etc/certs/cacert.pem
|
||||
OpenVPN.Cert = /etc/certs/cert.pem
|
||||
OpenVPN.Key = /etc/certs/cert.key
|
||||
@@ -0,0 +1,181 @@
|
||||
vpn connection
|
||||
==============
|
||||
|
||||
Service net.connman.vpn
|
||||
Interface net.connman.vpn.Connection
|
||||
Object path [variable prefix]/{connection0,connection1,...}
|
||||
|
||||
Methods dict GetProperties() [experimental]
|
||||
|
||||
Returns properties for the connection object. See
|
||||
the properties section for available properties.
|
||||
|
||||
Possible Errors: [connection].Error.InvalidArguments
|
||||
|
||||
void SetProperty(string name, variant value) [experimental]
|
||||
|
||||
Changes the value of the specified property. Only
|
||||
properties that are listed as read-write are
|
||||
changeable. On success a PropertyChanged signal
|
||||
will be emitted.
|
||||
|
||||
Possible Errors: [connection].Error.InvalidArguments
|
||||
[connection].Error.InvalidProperty
|
||||
|
||||
void ClearProperty(string name) [experimental]
|
||||
|
||||
Clears the value of the specified property.
|
||||
|
||||
Possible Errors: [connection].Error.InvalidArguments
|
||||
[connection].Error.InvalidProperty
|
||||
|
||||
void Connect() [experimental]
|
||||
|
||||
Connect this VPN connection. It will attempt to connect
|
||||
to the VPN connection. The Connect() will wait until
|
||||
the connection is created or there is an error. The
|
||||
error description is returned in dbus error.
|
||||
|
||||
Possible Errors: [connection].Error.InvalidArguments
|
||||
[connection].Error.InProgress
|
||||
|
||||
void Disconnect() [experimental]
|
||||
|
||||
Disconnect this VPN connection. If the connection is
|
||||
not connected an error message will be generated.
|
||||
|
||||
Possible Errors: [connection].Error.InvalidArguments
|
||||
|
||||
Signals PropertyChanged(string name, variant value) [experimental]
|
||||
|
||||
This signal indicates a changed value of the given
|
||||
property.
|
||||
|
||||
Properties string State [readonly]
|
||||
|
||||
The connection state information.
|
||||
|
||||
Valid states are "idle", "failure", "configuration",
|
||||
"ready", "disconnect".
|
||||
|
||||
string Type [readonly]
|
||||
|
||||
The VPN type (for example "openvpn", "vpnc" etc.)
|
||||
|
||||
string Name [readonly]
|
||||
|
||||
The VPN name.
|
||||
|
||||
string Domain [readonly]
|
||||
|
||||
The domain name used behind the VPN connection.
|
||||
This is optional for most VPN technologies.
|
||||
|
||||
string Host [readonly]
|
||||
|
||||
The VPN host (server) address.
|
||||
|
||||
boolean Immutable [readonly]
|
||||
|
||||
This value will be set to true if the connection is
|
||||
configured externally via a configuration file.
|
||||
|
||||
The only valid operation are Connect(), Disconnect()
|
||||
and GetProperties()
|
||||
|
||||
int Index [readonly]
|
||||
|
||||
The index of the VPN network tunneling interface.
|
||||
If there is no tunneling device, then this value
|
||||
is not returned.
|
||||
|
||||
dict IPv4 [readonly]
|
||||
|
||||
string Address
|
||||
|
||||
The current configured IPv4 address.
|
||||
|
||||
string Netmask
|
||||
|
||||
The current configured IPv4 netmask.
|
||||
|
||||
string Gateway
|
||||
|
||||
The current configured IPv4 gateway.
|
||||
|
||||
string Peer
|
||||
|
||||
The current configured VPN tunnel endpoint
|
||||
IPv4 address.
|
||||
|
||||
dict IPv6 [readonly]
|
||||
|
||||
string Address
|
||||
|
||||
The current configured IPv6 address.
|
||||
|
||||
string PrefixLength
|
||||
|
||||
The prefix length of the IPv6 address.
|
||||
|
||||
string Gateway
|
||||
|
||||
The current configured IPv6 gateway.
|
||||
|
||||
string Peer
|
||||
|
||||
The current configured VPN tunnel endpoint
|
||||
IPv6 address.
|
||||
|
||||
array{string} Nameservers [readonly]
|
||||
|
||||
The list of nameservers set by VPN.
|
||||
|
||||
array{dict} UserRoutes [readwrite]
|
||||
|
||||
int ProtocolFamily
|
||||
|
||||
Protocol family of the route. Set to 4
|
||||
if IPv4 and 6 if IPv6 route.
|
||||
|
||||
string Network
|
||||
|
||||
The network part of the route.
|
||||
|
||||
string Netmask
|
||||
|
||||
The netmask of the route.
|
||||
|
||||
string Gateway
|
||||
|
||||
Gateway address of the route.
|
||||
|
||||
The list of currently active user activated
|
||||
routes.
|
||||
|
||||
array{dict} ServerRoutes [readonly]
|
||||
|
||||
int ProtocolFamily
|
||||
|
||||
Protocol family of the route. Set to 4
|
||||
if IPv4 and 6 if IPv6 route.
|
||||
|
||||
string Network
|
||||
|
||||
The network part of the route.
|
||||
|
||||
string Netmask
|
||||
|
||||
The netmask of the route.
|
||||
|
||||
string Gateway
|
||||
|
||||
Gateway address of the route.
|
||||
|
||||
The VPN server activated route. These routes
|
||||
are pushed to connman by VPN server.
|
||||
|
||||
There can be other properties also but as the VPN
|
||||
technologies are so different, they have different
|
||||
kind of options that they need, so not all options
|
||||
are mentioned in this document.
|
||||
@@ -0,0 +1,50 @@
|
||||
vpn manager
|
||||
===========
|
||||
|
||||
Service net.connman.vpn
|
||||
Interface net.connman.vpn.Manager
|
||||
Object path /
|
||||
|
||||
Method object Create(dict settings) [experimental]
|
||||
|
||||
Create a new VPN connection and configuration using
|
||||
the supplied settings.
|
||||
|
||||
void Remove(object vpn) [experimental]
|
||||
|
||||
Remove the previously created VPN configuration.
|
||||
|
||||
array{object,dict} GetConnections() [experimental]
|
||||
|
||||
Returns a list of tuples with VPN connection object
|
||||
path and dictionary of their properties.
|
||||
|
||||
Possible Errors: [manager].Error.InvalidArguments
|
||||
|
||||
void RegisterAgent(object path) [experimental]
|
||||
|
||||
Register new agent for handling user requests.
|
||||
|
||||
Possible Errors: [manager].Error.InvalidArguments
|
||||
|
||||
void UnregisterAgent(object path) [experimental]
|
||||
|
||||
Unregister an existing agent.
|
||||
|
||||
Possible Errors: [manager].Error.InvalidArguments
|
||||
|
||||
Signals ConnectionAdded(object path, dict properties) [experimental]
|
||||
|
||||
Signal that is sent when a new VPN connection
|
||||
is added.
|
||||
|
||||
It contains the object path of the VPN connection
|
||||
and also its properties.
|
||||
|
||||
ConnectionRemoved(object path) [experimental]
|
||||
|
||||
Signal that is sent when a VPN connection
|
||||
has been removed.
|
||||
|
||||
The object path is no longer accessible after this
|
||||
signal and only emitted for reference.
|
||||
@@ -0,0 +1,60 @@
|
||||
VPN daemon overview
|
||||
*******************
|
||||
|
||||
|
||||
Manager interface
|
||||
=================
|
||||
|
||||
Manager interface described in vpn-manager-api.txt is to be used
|
||||
by both the connectivity UI and by ConnMan. The Create(),
|
||||
Remove(), RegisterAgent() and UnregisterAgent() functions are for
|
||||
UI usage. The GetConnections() method and ConnectionAdded() and
|
||||
ConnectionRemoved() signals are for ConnMan VPN plugin to use.
|
||||
|
||||
The UI should use the methods like this:
|
||||
- Ask VPN properties (like certs, usernames etc) from the user.
|
||||
- Call Manager.Create() to create a VPN connection (note that
|
||||
the system does not yet try to connect to VPN at this point)
|
||||
- Register an agent to vpnd so that vpnd can ask any extra
|
||||
parameters etc from the user if needed.
|
||||
- If the user wants to connect to VPN gateway, then the
|
||||
connection attempt should be done in ConnMan side as
|
||||
there will be a service created there.
|
||||
- If the user wishes to remove the VPN configuration, the UI
|
||||
can call the Manager.Remove() which removes the VPN connection.
|
||||
If the VPN was in use, the VPN connection is also disconnected.
|
||||
- When UI is terminated, the UI should call the UnregisterAgent()
|
||||
|
||||
The ConnMan calls VPN daemon like this:
|
||||
- There is a VPN plugin which at startup starts to listen the
|
||||
ConnectionAdded() and ConnectionRemoved() signals.
|
||||
- The VPN plugin will call GetConnections() in order to get
|
||||
available VPN connections. It will then create a provider service
|
||||
for each VPN connection that is returned.
|
||||
- User can then connect to the VPN by calling the service Connect()
|
||||
method
|
||||
- The existing ConnMan Manager.ConnectProvider() interface can still
|
||||
work by calling vpn.Manager.Create() and then call vpn.Connection.Connect()
|
||||
but this ConnectProvider() interface will be deprecated at some
|
||||
point.
|
||||
|
||||
|
||||
|
||||
Connection interface
|
||||
====================
|
||||
|
||||
The Manager.Create() will return the object path of the created
|
||||
vpn.Connection object and place it in idle state. Note that
|
||||
vpn.Connection.PropertyChanged signal is not called when Connection
|
||||
object is created because the same parameters are returned via
|
||||
vpn.Manager.ConnectionAdded() signal.
|
||||
The vpn.Connection object can be connected using the Connect() method
|
||||
and disconnected by calling Disconnect() method. When the connection
|
||||
is established (meaning VPN client has managed to create a connection
|
||||
to VPN server), then State property is set to "ready" and PropertyChanged
|
||||
signal is sent. If the connection cannot be established, then
|
||||
State property is set to "failure".
|
||||
After successfull connection, the relevant connection properties are sent
|
||||
by PropertyChanged signal; like IPv[4|6] information, the index of the
|
||||
VPN tunneling interface (if there is any), nameserver information,
|
||||
server specified routes etc.
|
||||
@@ -0,0 +1,54 @@
|
||||
WiFi P2P Functionality [experimental]
|
||||
*************************************
|
||||
|
||||
Note: Nothing about WiFi P2P Services is exposed, this is yet to be specified.
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
WiFi P2P is supported as follows:
|
||||
- if hardware and wpa_supplicant supports it, a "p2p" technology will appear
|
||||
in the technology list
|
||||
- "p2p" technology, as for "wifi" technology, supports a Scan() method. Such
|
||||
method will trigger a P2P find process. The results will be available
|
||||
through the Manager interface, comparable to services being available
|
||||
through this same interface after a Scan() on "wifi" technology.
|
||||
- the result of a "p2p" Scan() consists into a list of "peer" objects
|
||||
- it is then possible to access peer information, connecting and disconnecting
|
||||
it via the Peer API.
|
||||
|
||||
|
||||
API Usage
|
||||
=========
|
||||
|
||||
The UI willing to access to WiFi P2P technology should proceed this way:
|
||||
- Request Manager.GetTechnologies() and figure out from the result if "p2p"
|
||||
technology is provided. What comes next implies this successful case.
|
||||
- Add a listener to signal Manager.PeersChanged(): this signal will provide
|
||||
the results of a "p2p" technology Scan().
|
||||
- From the "p2p" technology object, request a Technology.Scan() method. This
|
||||
will run for a while a P2P find process.
|
||||
- If P2P peers are found, it will be signaled through Manager.PeersChanged().
|
||||
Objects are "Peer" objects. UI might use Manager.GetPeers() instead, if
|
||||
listening to a signal is not the preferred way.
|
||||
- Once selected the proper Peer object, request a Peer.Connect() method on it
|
||||
so it will connect to it. Peer.Disconnect() will disconnect.
|
||||
|
||||
Internals
|
||||
=========
|
||||
|
||||
Through such API, everything is made to hide irrelevant informations for the
|
||||
applications, which are:
|
||||
|
||||
- Everything related to the P2P group and the Group Owner (GO)
|
||||
- All low level peer settings
|
||||
- All Service Request Discovery mechanism
|
||||
|
||||
Hiding this mean ConnMan will handle it properly behind.
|
||||
|
||||
For instance, if you connect to a Peer which happens to be a persistent GO
|
||||
ConnMan will notice it and store the Group information for a later connection
|
||||
to speed up such connection.
|
||||
|
||||
For Service Discovery (SD), this will be handled the same way: silently
|
||||
behind, by ConnMan.
|
||||
@@ -0,0 +1,3 @@
|
||||
<filter name='allow-arp' chain='arp'>
|
||||
<rule direction='inout' action='accept'/>
|
||||
</filter>
|
||||
@@ -0,0 +1,24 @@
|
||||
<filter name='allow-dhcp-server' chain='ipv4'>
|
||||
|
||||
<!-- accept outgoing DHCP requests -->
|
||||
<!-- note, this rule must be evaluated before general MAC broadcast
|
||||
traffic is discarded since DHCP requests use MAC broadcast -->
|
||||
<rule action='accept' direction='out' priority='100'>
|
||||
<ip srcipaddr='0.0.0.0'
|
||||
dstipaddr='255.255.255.255'
|
||||
protocol='udp'
|
||||
srcportstart='68'
|
||||
dstportstart='67' />
|
||||
</rule>
|
||||
|
||||
<!-- accept incoming DHCP responses from a specific DHCP server
|
||||
parameter DHPCSERVER needs to be passed from where this filter is
|
||||
referenced -->
|
||||
<rule action='accept' direction='in' priority='100' >
|
||||
<ip srcipaddr='$DHCPSERVER'
|
||||
protocol='udp'
|
||||
srcportstart='67'
|
||||
dstportstart='68'/>
|
||||
</rule>
|
||||
|
||||
</filter>
|
||||
@@ -0,0 +1,21 @@
|
||||
<filter name='allow-dhcp' chain='ipv4'>
|
||||
|
||||
<!-- accept outgoing DHCP requests -->
|
||||
<!-- not, this rule must be evaluated before general MAC broadcast
|
||||
traffic is discarded since DHCP requests use MAC broadcast -->
|
||||
<rule action='accept' direction='out' priority='100'>
|
||||
<ip srcipaddr='0.0.0.0'
|
||||
dstipaddr='255.255.255.255'
|
||||
protocol='udp'
|
||||
srcportstart='68'
|
||||
dstportstart='67' />
|
||||
</rule>
|
||||
|
||||
<!-- accept incoming DHCP responses from any DHCP server -->
|
||||
<rule action='accept' direction='in' priority='100' >
|
||||
<ip protocol='udp'
|
||||
srcportstart='67'
|
||||
dstportstart='68'/>
|
||||
</rule>
|
||||
|
||||
</filter>
|
||||
@@ -0,0 +1,3 @@
|
||||
<filter name='allow-incoming-ipv4' chain='ipv4'>
|
||||
<rule direction='in' action='accept'/>
|
||||
</filter>
|
||||
@@ -0,0 +1,3 @@
|
||||
<filter name='allow-ipv4' chain='ipv4'>
|
||||
<rule direction='inout' action='accept'/>
|
||||
</filter>
|
||||
@@ -0,0 +1,30 @@
|
||||
<filter name='clean-traffic' chain='root'>
|
||||
<!-- An example of a traffic filter enforcing clean traffic
|
||||
from a VM by
|
||||
- preventing MAC spoofing -->
|
||||
<filterref filter='no-mac-spoofing'/>
|
||||
|
||||
<!-- preventing IP spoofing on outgoing, allow all IPv4 in incoming -->
|
||||
<filterref filter='no-ip-spoofing'/>
|
||||
|
||||
<rule direction='out' action='accept' priority='-650'>
|
||||
<mac protocolid='ipv4'/>
|
||||
</rule>
|
||||
|
||||
<filterref filter='allow-incoming-ipv4'/>
|
||||
|
||||
<!-- preventing ARP spoofing/poisoning -->
|
||||
<filterref filter='no-arp-spoofing'/>
|
||||
|
||||
<!-- accept all other incoming and outgoing ARP traffic -->
|
||||
<rule action='accept' direction='inout' priority='-500'>
|
||||
<mac protocolid='arp'/>
|
||||
</rule>
|
||||
|
||||
<!-- preventing any other traffic than IPv4 and ARP -->
|
||||
<filterref filter='no-other-l2-traffic'/>
|
||||
|
||||
<!-- allow qemu to send a self-announce upon migration end -->
|
||||
<filterref filter='qemu-announce-self'/>
|
||||
|
||||
</filter>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE codermap [
|
||||
<!ELEMENT codermap (coder)*>
|
||||
<!ELEMENT coder (#PCDATA)>
|
||||
<!ATTLIST coder magick CDATA #REQUIRED>
|
||||
<!ATTLIST coder name CDATA #REQUIRED>
|
||||
]>
|
||||
<!--
|
||||
Associate an image format with the specified coder module.
|
||||
|
||||
ImageMagick has a number of coder modules to support the reading and/or
|
||||
writing of an image format (e.g. JPEG). Some coder modules support more
|
||||
than one associated image format and the mapping between an associated
|
||||
format and its respective coder module is defined in this configuration
|
||||
file. For example, the PNG coder module not only supports the PNG image
|
||||
format, but the JNG and MNG formats as well.
|
||||
-->
|
||||
<codermap>
|
||||
<!-- <coder magick="GIF87" name="GIF"/> -->
|
||||
<!-- <coder magick="JPG" name="JPEG"/> -->
|
||||
<!-- <coder magick="PGM" name="PNM"/> -->
|
||||
</codermap>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE colormap [
|
||||
<!ELEMENT colormap (color)+>
|
||||
<!ELEMENT color (#PCDATA)>
|
||||
<!ATTLIST color name CDATA "0">
|
||||
<!ATTLIST color color CDATA "rgb(0,0,0)">
|
||||
<!ATTLIST color compliance CDATA "SVG">
|
||||
]>
|
||||
<!--
|
||||
Associate a color name with its red, green, blue, and alpha intensities.
|
||||
|
||||
A number of methods and options require a color parameter. It is often
|
||||
convenient to refer to a color by name (e.g. white) rather than by hex
|
||||
value (e.g. #fff). This file maps a color name to its equivalent red,
|
||||
green, blue, and alpha intensities (e.g. for white, red = 255, green =
|
||||
255, blue = 255, and alpha = 0).
|
||||
-->
|
||||
<colormap>
|
||||
<!-- <color name="none" color="rgba(0,0,0,0)" compliance="SVG, XPM"/> -->
|
||||
<!-- <color name="black" color="rgb(0,0,0)" compliance="SVG, X11, XPM"/> -->
|
||||
<!-- <color name="red" color="rgb(255,0,0)" compliance="SVG, X11, XPM"/> -->
|
||||
<!-- <color name="magenta" color="rgb(255,0,255)" compliance="SVG, X11, XPM"/> -->
|
||||
<!-- <color name="green" color="rgb(0,128,0)" compliance="SVG"/> -->
|
||||
<!-- <color name="cyan" color="rgb(0,255,255)" compliance="SVG, X11, XPM"/> -->
|
||||
<!-- <color name="blue" color="rgb(0,0,255)" compliance="SVG, X11, XPM"/> -->
|
||||
<!-- <color name="yellow" color="rgb(255,255,0)" compliance="SVG, X11, XPM"/> -->
|
||||
<!-- <color name="white" color="rgb(255,255,255)" compliance="SVG, X11"/> -->
|
||||
</colormap>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user