Initial commit

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
This commit is contained in:
Patrick McCarty
2016-02-24 09:34:13 -08:00
commit c42f8a3aa1
236 changed files with 18402 additions and 0 deletions
+41
View File
@@ -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
+353
View File
@@ -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
View File
@@ -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"
+5
View File
@@ -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.
+25
View File
@@ -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-----
+40
View File
@@ -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-----
+14
View File
@@ -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-----
+35
View File
@@ -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-----
+34
View File
@@ -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-----
+32
View File
@@ -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-----
+89
View File
@@ -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
+93
View File
@@ -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
+9
View File
@@ -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
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Periodic check for new OS version
[Timer]
OnBootSec=5m
OnUnitActiveSec=1h
AccuracySec=20m
[Install]
WantedBy=multi-user.target
+75
View File
@@ -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";
}
}
+305
View File
@@ -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)
+54
View File
@@ -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
+41
View File
@@ -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_ */
+17
View File
@@ -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
+23
View File
@@ -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
+11
View File
@@ -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
View File
@@ -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
+42
View File
@@ -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
View File
@@ -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, &current_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;
}
+159
View File
@@ -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(&current_version, &current_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;
}
+154
View File
@@ -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);
}
}
+138
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+85
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+131
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&current_version, &current_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
View File
File diff suppressed because it is too large Load Diff
+134
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
}
+173
View File
@@ -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
View File
@@ -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
View File
@@ -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(&current_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, &current_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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+36
View File
@@ -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"
+121
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
diffs/
errors/
failed/
fulldownload/
patched/
results/
+124
View File
@@ -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 %}
+25
View File
@@ -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 %}
@@ -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 %}
@@ -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.
+305
View File
@@ -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.
+64
View File
@@ -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.
+164
View File
@@ -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>
+22
View File
@@ -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>
+28
View File
@@ -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